feat(webhooks): add verifyAndDecodeWebhook for compressed payloads#1735
Open
nijeesh-stream wants to merge 1 commit intomasterfrom
Open
feat(webhooks): add verifyAndDecodeWebhook for compressed payloads#1735nijeesh-stream wants to merge 1 commit intomasterfrom
nijeesh-stream wants to merge 1 commit intomasterfrom
Conversation
…HA-3071) Adds two new helpers on `StreamChat` so customers can decode + verify gzip-compressed and base64-wrapped webhook payloads in one call: - `decompressWebhookBody(rawBody, contentEncoding?, payloadEncoding?)` - `verifyAndDecodeWebhook(rawBody, xSignature, contentEncoding?, payloadEncoding?)` Both helpers are no-ops when the encoding arguments are null / undefined / empty, so existing HTTP webhook integrations behave identically. The HMAC signature is always checked against the innermost (uncompressed, base64-decoded) JSON, so the same call works for HTTP webhooks and for SQS / SNS firehose envelopes (pass `payloadEncoding: 'base64'`). A new `WebhookSignatureError` class is exported from the package barrel for typed error handling. The legacy `verifyWebhook` and the underlying `CheckSignature` helper are intentionally untouched for backward compatibility. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Size Change: +4.35 kB (+1.14%) Total Size: 385 kB 📦 View Changed
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stream chat backend can now compress outbound webhook payloads with gzip and (for SQS / SNS firehose) base64-wrap them so they stay valid UTF-8 over the queue. This PR adds two new methods on
StreamChatso customers can decompress + verify in one call.Linear: CHA-3071
New public surface (all in
src/client.ts/ re-exported from the package barrel)decompressWebhookBody(rawBody, contentEncoding?, payloadEncoding?): Buffer— reverses the encoding wrappers Stream applies to outbound webhook / SQS / SNS payloads.verifyAndDecodeWebhook(rawBody, xSignature, contentEncoding?, payloadEncoding?): Buffer— decompresses (when needed) and verifies the HMAC, returning the uncompressed JSON Buffer. Throws a newWebhookSignatureErroron mismatch.WebhookSignatureError— typed error so consumers caninstanceof-check.The HMAC is always computed over the innermost (uncompressed, base64-decoded) JSON, so the verification rule is invariant across HTTP webhooks and SQS / SNS.
SQS / SNS support
Pass
payloadEncoding: 'base64'(alias'b64'also accepted) when receiving Stream webhooks via SQS or SNS. The helper base64-decodes, then gunzips, then HMAC-verifies in that order.Backward compatibility
verifyWebhookand the underlyingCheckSignaturehelper insrc/signing.tsare intentionally untouched.null/undefined/''for either encoding makesdecompressWebhookBodyandverifyAndDecodeWebhookbehave identically to the existing plain-JSON HTTP webhook flow.Encoding handling
'GZIP','BASE64','b64'all work).Content-Encoding(br,brotli,zstd,deflate,compress,lz4) and unsupportedpayload_encoding(hex,url,binary) throw a clearError.WebhookSignatureErrorbefore the body reaches the HMAC step, since silently accepting it would corrupt the signed bytes.Error('failed to decompress webhook body: ...').Build / browser
zlibwas added topackage.jsonbrowser: { ... }so esbuild shims it to an empty module on browser bundles, mirroring the existingcrypto: falseshim. The new helpers are server-only — they live insrc/signing.tsnext to the existingCheckSignature.Files changed
src/signing.ts— new helpers, newWebhookSignatureErrorclass.src/client.ts— thin wrappers onStreamChatthat delegate to the helpers.package.json—browser.zlib = falseshim.docs/webhooks.md(new) — end-to-end Express + SQS / SNS guide.README.md— link to the new doc.test/unit/webhook-compression.test.ts(new) — 31 cases covering passthrough, gzip, base64, base64+gzip, case-insensitive encoding names, every requested unsupported encoding, malformed gzip / base64, signature happy paths, and signature-mismatch rejection (including the "signature was computed over the wrapped bytes" attack).Test plan
yarn lint(prettier + eslint,--max-warnings 0)yarn build(tsc + esbuild for browser-cjs / node-cjs / browser-esm)yarn types(tsc --noEmit)yarn test-unit run— 2831 passed / 1 skippedyarn test-unit run test/unit/webhook-compression.test.ts— 31 / 31 passwebhook_compression_algorithm = "gzip"(left for reviewer / staging)Made with Cursor