Webhook HMAC Signature Verification
Verify a hex HMAC signature over a webhook body with a timestamp tolerance, using constant-time comparison.
Code
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook({ body, signature, timestamp, secret, toleranceMs = 300000, now = Date.now() }) {
if (typeof body !== 'string' || typeof signature !== 'string' || typeof secret !== 'string') {
throw new TypeError('body, signature and secret must be strings');
}
const ts = Number(timestamp);
if (!Number.isFinite(ts)) throw new TypeError('timestamp must be numeric');
if (Math.abs(now - ts) > toleranceMs) return { ok: false, reason: 'timestamp out of tolerance' };
const expected = createHmac('sha256', secret).update(ts + '.' + body).digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(signature, 'hex');
if (a.length !== b.length) return { ok: false, reason: 'signature length mismatch' };
return timingSafeEqual(a, b) ? { ok: true } : { ok: false, reason: 'signature mismatch' };
}Report a problem with this exampleOpens a pre-filled issue on GitHub.