I am trying to implement the webhook validation here: Using webhooks in a cloudflare worker.
I have replicated the code in the example using the subtle crypto library built into CF Workers.
When I generate the hashed signature, the string generated does not match the signature in the header.
My CF Worker code is below:
async function handleRequest(request) {
const req = await request.json()
const ZOOM_WEBHOOK_SECRET_TOKEN = 'XXXXXXXXXXXXXXXXXXXXXX'
// https://marketplace.zoom.us/docs/api-reference/webhook-reference/#verify-webhook-events
let requestHeaders = Object.fromEntries(request.headers)
let response
const message = `v0:${requestHeaders['x-zm-request-timestamp']}:${JSON.stringify(req)}`
const hashForVerify = await digestMessage(message)
const signature = `v0=${hashForVerify}`
console.log(req)
console.log(requestHeaders['x-zm-signature'])
console.log(signature)
if (requestHeaders['x-zm-signature'] === signature) {
// Webhook request came from Zoom
// business logic here, example make API request to Zoom or 3rd party
response = {
message: 'Authorized request',
status: 200
}
} else {
// Webhook request did not come from Zoom
response = {
message: 'Request not authorized',
status: 401
}
}
// return the reponse (authorized or not)
console.log(response)
return new Response(response.message, {
status: response.status
})
}
async function digestMessage(message) {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
return hashHex;
}
Anyone else successfully done this in a worker? Is there anywhere I’ve not correctly implemented the verification algorithm?
Also posted here: https://community.cloudflare.com/t/verify-zoom-webhook-in-worker/425280/2