philiprehberger-webhook-signature
HMAC-based webhook signature generation and verification with timing-safe comparison.
Installation
pip install philiprehberger-webhook-signature
Usage
Signing a Payload
from philiprehberger_webhook_signature import sign
signed = sign(payload='{"event": "order.created"}', secret="whsec_abc123")
print(signed.signature) # HMAC hex digest
print(signed.timestamp) # Unix timestamp
print(signed.to_header()) # "t=1234567890,sha256=abc..."
Verifying a Signature
from philiprehberger_webhook_signature import verify, parse_header
# Parse the signature header
header = request.headers["X-Webhook-Signature"]
signature, timestamp = parse_header(header)
# Verify (raises on failure)
verify(
payload=request.body,
secret="whsec_abc123",
signature=signature,
timestamp=timestamp,
max_age=300.0, # reject signatures older than 5 minutes
)
Key Rotation
Use verify_with_rotation for zero-downtime secret rotation. It tries the current secret first and falls back to the previous secret if verification fails:
from philiprehberger_webhook_signature import verify_with_rotation, parse_header
header = request.headers["X-Webhook-Signature"]
signature, timestamp = parse_header(header)
verify_with_rotation(
payload=request.body,
signature=signature,
current_secret="whsec_new_secret",
previous_secret="whsec_old_secret", # optional fallback
tolerance=300,
timestamp=timestamp,
)
One-call sign and verify
For the common case of producing a header dict and verifying an incoming header in one step:
from philiprehberger_webhook_signature import sign_headers, verify_header
# Producer side: build HTTP-ready headers
headers = sign_headers('{"event": "order.created"}', secret="whsec_abc123")
# {"X-Webhook-Signature": "t=1700000000,sha256=..."}
# Consumer side: verify the incoming header value in one call
verify_header(
payload=request.body,
secret="whsec_abc123",
header_value=request.headers["X-Webhook-Signature"],
max_age=300.0,
)
Use header_name="X-Stripe-Signature" (or any custom name) to match your provider's convention.
Error Handling
from philiprehberger_webhook_signature import (
verify,
SignatureError,
SignatureExpiredError,
SignatureMismatchError,
)
try:
verify(payload, secret, signature, timestamp)
except SignatureExpiredError as e:
print(f"Signature too old: {e.age}s > {e.max_age}s")
except SignatureMismatchError:
print("Invalid signature")
except SignatureError as e:
print(f"Verification failed: {e}")
Custom Algorithm
signed = sign(payload="data", secret="secret", algorithm="sha512")
verify(payload="data", secret="secret", signature=sig, timestamp=ts, algorithm="sha512")
Disable Expiry Check
verify(payload, secret, signature, timestamp, max_age=None)
API
| Function / Class | Description |
|---|---|
sign(payload, secret, algorithm, timestamp) |
Generate an HMAC signature for a webhook payload |
sign_headers(payload, secret, header_name, algorithm, timestamp) |
Sign a payload and return an HTTP-ready headers dict |
verify(payload, secret, signature, timestamp, algorithm, max_age) |
Verify a webhook signature with timing-safe comparison |
verify_header(payload, secret, header_value, algorithm, max_age) |
Parse a signature header and verify it in one call |
verify_with_rotation(payload, signature, current_secret, previous_secret, tolerance, algorithm, timestamp) |
Verify with key rotation support (tries current then previous secret) |
parse_header(header, prefix) |
Parse a signature header string into (signature, timestamp) tuple |
SignedPayload |
Signed payload with signature, timestamp, body, and to_header() |
SignatureError |
Base exception for signature errors |
SignatureExpiredError |
Raised when signature age exceeds max_age |
SignatureMismatchError |
Raised when signature verification fails |
Development
pip install -e .
python -m pytest tests/ -v
Support
If you find this project useful:
License
Release files for philiprehberger-webhook-signature 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| philiprehberger_webhook_signature-0.3.0.tar.gz | 194.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| philiprehberger_webhook_signature-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 200.9 kB
Release files / philiprehberger_webhook_signature-0.3.0.tar.gz
| Download URL | philiprehberger_webhook_signature-0.3.0.tar.gz |
|---|---|
| Size | 194.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
afd371e604e555433973a4116e5c9efa4221ffa6bc11384f111d444a2201234d
|
|
BLAKE2b-256 checksum How to use checksums |
b6f376396f82d2887591b9670f079c7f294807b0cca9fddbb104dda85c07fc26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / philiprehberger_webhook_signature-0.3.0-py3-none-any.whl
| Download URL | philiprehberger_webhook_signature-0.3.0-py3-none-any.whl |
|---|---|
| Size | 6.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b53035ffc4b29188cd2444ddf1343950e8d83aee10a6c20f4841d5f57a6d1192
|
|
BLAKE2b-256 checksum How to use checksums |
d3e16af94c9dc2b91112a832ae1c7738139e660c5cdc594512a5f030fb543f59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|