Verification
Verify webhook authenticity with createHmacVerifier or a custom verify function.
verify is the authenticity gate for incoming requests.
Concept:
verifyruns before schema validation and before the route handler.- If
verifythrows, request handling stops.
Why:
- prevents untrusted payloads from reaching business logic
- avoids spending resources validating/processing forged requests
HMAC helper
The package exports createHmacVerifier from @zap-studio/webhooks/verify. It is runtime-agnostic: the helper uses the Web Crypto API rather than Node crypto, so it works anywhere globalThis.crypto.subtle is available.
import { createWebhookRouter } from "@zap-studio/webhooks";
import { createHmacVerifier } from "@zap-studio/webhooks/verify";
const router = createWebhookRouter({
verify: createHmacVerifier({
headerName: "x-hub-signature-256",
secret: process.env.GITHUB_WEBHOOK_SECRET!,
algo: "sha256", // optional
}),
});createHmacVerifier:
- reads the signature from the configured header
- computes HMAC from
req.rawBody - compares signatures in constant time
- depends on Web Crypto, not Node-specific APIs
- expects a string secret
- throws
VerificationErroron verifier setup or signature failures
import { createHmacVerifier } from "@zap-studio/webhooks/verify";
import { VerificationError } from "@zap-studio/webhooks/errors";
const verify = createHmacVerifier({
headerName: "x-hub-signature-256",
secret: process.env.GITHUB_WEBHOOK_SECRET!,
});
try {
await verify(req);
} catch (error) {
if (error instanceof VerificationError) {
console.error("verification failed", error.message);
}
}Why constant-time comparison:
- avoids timing side channels when comparing signatures
Custom verification
For providers with custom signing, pass your own verify function.
import Stripe from "stripe";
import { createWebhookRouter } from "@zap-studio/webhooks";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const router = createWebhookRouter({
verify: async (req) => {
const signature = req.headers.get("stripe-signature");
if (!signature) {
throw new Error("Missing Stripe signature");
}
stripe.webhooks.constructEvent(req.rawBody, signature, process.env.STRIPE_WEBHOOK_SECRET!);
},
});Use custom verification when a provider:
- uses a non-HMAC signature format
- requires SDK-specific verification logic
- includes timestamp or replay-protection checks
Edit on GitHub
Last updated on