Skip to content

feat: verify webhooks with the endpoint's webhook secret - #3

Merged
toocool merged 1 commit into
mainfrom
fix/webhook-secret-verification
Oct 17, 2026
Merged

toocool merged 1 commit into
mainfrom
fix/webhook-secret-verification

Conversation

@toocool

@toocool toocool commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

What

Webhook deliveries are signed with each endpoint's own webhook secret (whsec_…, shown in the console), not the app's API secret. client.verifyWebhook passed the API secret, so it could never verify a real delivery. It now takes the webhook secret as its first argument. The standalone verify function keeps its signature.

Also:

  • With a max age set, timestamps more than 300s in the future are rejected, consistently across all four SDKs.
  • The shared conformance fixture gains verify_cases (array headers, skew, expiry, wrong secret, missing header), run here.
  • Docs and examples use the webhook secret, a 5-minute max age, the payload shape {id, event, app_id, channel, user_id, time_ms}, and dedupe on id.

Release

Version and changelog are left to release-please. The commit is feat: (minor bump). The client-method signature change is technically breaking, but the old method could not verify any real delivery, so there's nothing working to break. Don't publish until apinator-io/backend#49 is deployed, so the docs match a live feature.

Testing

Full test suite passes on current main.

Deliveries are signed with each endpoint's own webhook secret (whsec_..., shown
in the console), not the app's API secret. client.verifyWebhook passed the API
secret, so it could never verify a real delivery. It now takes the webhook
secret as its first argument; the standalone verify function is unchanged.

Also:
- With a max age set, timestamps more than 300s in the future are rejected in
  every SDK (previously node/go rejected any future timestamp, python/php
  accepted all).
- Shared conformance fixture gains verify_cases (array headers, skew, expiry,
  wrong secret), run by every SDK.
- Docs and examples use the webhook secret, a 5 minute max age, the event
  payload shape ({id, event, app_id, channel, user_id, time_ms}) and dedupe on id.
- List/tuple header values use the first entry; a non-ASCII signature returns
  False instead of raising.
@toocool
toocool merged commit b742d7c into main Oct 17, 2026
3 checks passed
@toocool
toocool deleted the fix/webhook-secret-verification branch October 17, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants