Skip to content

Instantly share code, notes, and snippets.

@emerleite
Created October 28, 2024 14:14
Show Gist options
  • Save emerleite/beebcd91c8a42c20371979d656c9ed12 to your computer and use it in GitHub Desktop.
Save emerleite/beebcd91c8a42c20371979d656c9ed12 to your computer and use it in GitHub Desktop.

Revisions

  1. emerleite created this gist Oct 28, 2024.
    68 changes: 68 additions & 0 deletions hono-cf-middleware.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    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;
    }