import { createMiddleware } from 'hono/factory' export function metaValidateWebhookSignature() { return createMiddleware(async (c, next) => { const requestBody = await c.req.arrayBuffer() const payloadSignature = c.req.header('X-Hub-Signature-256') const validRequest = await verifySignature(c.env.META_API_SECRET, requestBody, payloadSignature) console.log('Meta Valid Request?', validRequest) if (!validRequest) { return c.json({ error: 'Invalid signature' }, 403) } await next() }) } /* Meta Docs https://developers.facebook.com/docs/messenger-platform/webhooks/ https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components https://developers.facebook.com/docs/graph-api/webhooks/getting-started#create-endpoint https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/payload-examples/ Research https://developers.facebook.com/docs/whatsapp/sample-app-endpoints https://stackoverflow.com/questions/72813196/how-to-register-whatsapp-webhooks-in-nodejs https://glitch.com/edit/#!/whatsapp-cloud-api-echo-bot https://www.linkedin.com/pulse/facebook-webhook-payload-signature-verification-aspnet-rehman-tw8xf/ */ async function verifySignature(secret, payload, signatureHeader) { if (!signatureHeader || signatureHeader.length == 0) { return false; } const signature = signatureHeader.split('sha256=')[1] if (!signature || signature.length == 0) { return false; } // Encode the secret and the order const encoder = new TextEncoder(); const encodedSecret = encoder.encode(secret); const encodedPayload = new Uint8Array(payload).buffer; // Import the secret key const key = await crypto.subtle.importKey( 'raw', encodedSecret, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); // Create the HMAC signature const signatureArrayBuffer = await crypto.subtle.sign( 'HMAC', key, encodedPayload ); // Convert the ArrayBuffer to a hexadecimal string const calculatedSignature = Array.from(new Uint8Array(signatureArrayBuffer)) .map(byte => byte.toString(16).padStart(2, '0')) .join(''); return signature === calculatedSignature; }