Verify Webhook is failing (signatures don't match)

API Endpoint(s) and/or Zoom API Event(s)
Zoom webhook verification

Description
I have verified the url used for Zoom to send webhook messages to my server but the verification of each webhook message is failing because the signature generated does not match the signature provided. I am following the instructions and code provided in the documentation verbatim and the string generated for the signature is not the same.

I suspect there is some order that the JSON body needs to be converted to a string or a variable that’s missing because the rest of the conversion is constant.

How To Reproduce
Currently I have events sent for meeting start, meeting end, participation joined and participant report available events. These are all failing the webhook verification.

Hi @agebold
Thanks for reaching out to us and welcome to the Zoom Developer community!
Can you try to run this webhook sample app and verify whether you are still getting this error?

@agebold

Hello, I hope you’re doing well. Below is a working NodeJS sample: :point_down:

import { HttpStatusCode } from 'axios';
const crypto = require('crypto');

export class WebhookVerifier {
    // Method to verify the signature
    static verifySignature({ zmSignature, zmRequestTimeStamp, body }) {
        const message = `v0:${zmRequestTimeStamp}:${JSON.stringify(body)}`;
        const hmac = crypto.createHmac('sha256', process.env.ZOOM_EVENT_VERIFICATION_SECRET);
        const computedHash = hmac.update(message).digest('hex');
        const signature = `v0=${computedHash}`;

        // Compare computed signature with Zoom signature
        return signature === zmSignature;
    }

    // Method to process the webhook verification response
    static async verifyEventEndpoint({ plainToken }) {
        try {

            const secret = process.env.ZOOM_EVENT_VERIFICATION_SECRET;

            // Encrypt plainToken
            const encryptedToken = crypto
                .createHmac('sha256', secret)
                .update(plainToken)
                .digest('hex');

            return {
                status: HttpStatusCode.Ok,
                payload: {
                    plainToken: plainToken,
                    encryptedToken: encryptedToken,
                },
            };
        } catch (error) {
            console.error(error);
            return { status: 500, message: 'Internal Server Error' };
        }
    }
}

If still any query please ask.
Thanks

1 Like

@agebold

Please make sure to use the raw body of the webhook payload instead of the direct body.