Inbound provider callbacks are authenticated by HMAC over the raw request body and a timestamp window, with key rotation, rejection of unsigned traffic, and no JSON parse before verification.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Inbound integration callbacks are untrusted until a signature over the raw body verifies under a current or still-valid previous key. The verifier reads exact socket bytes, concatenates them with the provider timestamp using the profile construction, and compares HMAC in constant time. JSON parse, schema validation, and command dispatch happen only after success. Unsigned, mistimed, or mismatched bodies are rejected with a single failure code and no mutation. Duplicate provider event ids replay the stored outcome.
Scope
- HTTP endpoints listed as inbound-integrations that receive provider callbacks.
- Raw-body capture, timestamp skew, key rotation, unsigned rejection, replay cache, and log redaction of signatures.
Outside this block
- Mapping verified provider event types to domain commands.
- IP allowlists as a substitute for signatures, browser CSRF, and outbound delivery retries from this product.
Contract
- The HMAC input is the raw request body plus the timestamp construction from the profile; re-serialized JSON is never the signed payload.
- Requests lacking a signature header, or presenting an empty signature, are rejected before parse.
- Timestamps outside the skew window are rejected even if the HMAC would otherwise match, to bound replay.
- During rotation, a previous key id remains acceptable until the profile marks it invalid; unknown key ids fail closed.
- Provider event identity is recorded atomically with the first successful command so a later signed duplicate does not mutate twice.
- Logs may store key id and verification result, not the shared secret or the full signature header.
Implementation guidance
- Disable middleware that parses JSON before the webhook controller; capture raw bytes at the edge. Compare digests with a constant-time function.
- Bind the route so session cookies cannot authorize a callback. Size-limit the raw body before HMAC.
Failure handling and safeguards
- A framework that exposes only a parsed object is a shipping blocker; a whitespace-mutating fixture must fail if the implementation canonicalizes first.
- If the replay cache is down, fail closed rather than executing blindly.
Verification and operations
- Tests supply captured on-wire bytes, not pretty-printed JSON. Rotate keys in staging and prove previous-key acceptance then denial after invalidation.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.