Read the webhook signature from the X-Webhook-Signature header Fizzy actually sends

Merged
JE
2mo ago
+45-8

What

Fizzy sends the HMAC-SHA256 delivery signature in X-Webhook-Signature — app/models/webhook/delivery.rb builds the delivery headers as "X-Webhook-Signature" => signature, the webhook settings page says the same, and docs/api/sections/webhooks.md refers to it by that name. The SDK told consumers to read X-Fizzy-Signature instead.

  • TypeScript — WebhookReceiver defaulted signatureHeader to x-fizzy-signature. Now x-webhook-signature.
  • Swift / Kotlin — the WebhookVerifier / verifyWebhookSignature doc-strings and examples pointed at X-Fizzy-Signature. Corrected.
  • SECURITY.md — documented X-Fizzy-Signature. Corrected.

Failure mode on the old TypeScript default

Fail-closed, not fail-open. handleRequest only consults the header when a secret is configured; it looked up x-fizzy-signature, found nothing on a real Fizzy delivery, and threw WebhookVerificationError — so every receiver configured with a secret rejected every genuine delivery as unsigned. Nothing was ever accepted unverified. A receiver without a secret skips verification regardless of header, unchanged.

Tests

New WebhookReceiver signature header block in typescript/tests/fizzy.test.ts:

  • a delivery signed under x-webhook-signature is accepted by default (fails on the old default);
  • a delivery with no signature header is rejected with WebhookVerificationError;
  • a delivery signed under x-fizzy-signature alone is rejected (pins the header name from the other side);
  • signatureHeader override still honored; no-secret path still skips verification.

Not changed

  • Go and Ruby verification takes the signature string as an argument and carries no header name in code or docs — nothing to fix there.
  • Rust (open rust-sdk branch) already uses X-Webhook-Signature; untouched.
  • Spec / conformance — neither spec/fizzy.smithy nor the conformance fixtures carry the signature header name (the Smithy model covers the Webhook resource and delivery request shape, and the conformance suite exercises the client side only), so there is no single constant the languages could derive from today. Each language hand-writes the name; this PR aligns them rather than adding pipeline machinery for one string.

Gates

npm test (97 passing), npm run lint (only the pre-existing gating.ts unused-import warning), npm run typecheck clean. Swift and Kotlin changes are doc comments only.


Summary by cubic

Reads webhook signatures from the X-Webhook-Signature header Fizzy actually sends, instead of the wrong X-Fizzy-Signature name.

  • The TypeScript receiver defaulted to x-fizzy-signature, so any receiver with a secret rejected every real delivery as unsigned (fail-closed). Now defaults to x-webhook-signature.
  • Corrects the header name in Swift and Kotlin doc-strings and in SECURITY.md.
  • signatureHeader override and the no-secret skip path are unchanged; Go, Ruby, and Rust already use the right header.