import crypto from "node:crypto";
function verifyWebhook(
payload: string,
headers: {
"webhook-id": string;
"webhook-timestamp": string;
"webhook-signature": string;
},
secret: string
): boolean {
const { "webhook-id": msgId, "webhook-timestamp": timestamp, "webhook-signature": signature } = headers;
// Check timestamp is not too old (5 minutes)
const now = Math.floor(Date.now() / 1000);
if (now - parseInt(timestamp) > 300) {
return false;
}
// Extract signature (remove "v1," prefix)
const expectedSig = signature.split(",")[1];
// Compute signature
const signedPayload = `${msgId}.${timestamp}.${payload}`;
const rawSecret = secret.startsWith("whsec_") ? secret.slice(6) : secret;
const computedSig = crypto
.createHmac("sha256", rawSecret)
.update(signedPayload)
.digest("base64");
// Timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(expectedSig),
Buffer.from(computedSig)
);
}