@@ -53,29 +53,40 @@ Use the raw callback form `(payload, req, res)` to access headers directly:
5353
5454` ` ` javascript
5555import rtms from " @zoom/rtms" ;
56- import { createHmac } from " crypto" ;
56+ import { createHmac , timingSafeEqual } from " crypto" ;
57+
58+ const WEBHOOK_SECRET = process .env .ZM_RTMS_WEBHOOK_SECRET ;
59+ if (! WEBHOOK_SECRET ) {
60+ throw new Error (" ZM_RTMS_WEBHOOK_SECRET is not set" );
61+ }
5762
5863function verifySignature (body , timestamp , signature ) {
59- const message = ` v0: ${ timestamp} : ${ body } ` ;
60- const expected = " v0=" + createHmac (" sha256" , process . env . ZM_RTMS_WEBHOOK_SECRET )
61- .update (message )
64+ if ( ! signature || ! timestamp) return false ;
65+ const expected = " v0=" + createHmac (" sha256" , WEBHOOK_SECRET )
66+ .update (` v0: ${ timestamp } : ${ body } ` )
6267 .digest (" hex" );
63- return expected === signature;
68+ if (signature .length !== expected .length ) return false ;
69+ return timingSafeEqual (Buffer .from (signature), Buffer .from (expected));
6470}
6571
6672rtms .onWebhookEvent ((payload , req , res ) => {
67- const signature = req .headers [" x-zm-signature" ];
68- const timestamp = req .headers [" x-zm-request-timestamp" ];
69-
70- // Zoom endpoint validation challenge — respond before processing events
71- if (req .headers [" x-zm-webhook-validator" ]) {
72- const token = req .headers [" x-zm-webhook-validator" ];
73+ // Endpoint validation: Zoom POSTs this once when the webhook URL is set
74+ // or changed. Respond with plainToken + HMAC-SHA256(plainToken, secret)
75+ // to prove the endpoint holds the secret.
76+ if (payload .event === " endpoint.url_validation" ) {
77+ const plainToken = payload .payload ? .plainToken ;
78+ const encryptedToken = createHmac (" sha256" , WEBHOOK_SECRET )
79+ .update (plainToken)
80+ .digest (" hex" );
7381 res .writeHead (200 , { " Content-Type" : " application/json" });
74- res .end (JSON .stringify ({ plainToken: token }));
82+ res .end (JSON .stringify ({ plainToken, encryptedToken }));
7583 return ;
7684 }
7785
78- if (! signature || ! timestamp || ! verifySignature (JSON .stringify (payload), timestamp, signature)) {
86+ const signature = req .headers [" x-zm-signature" ];
87+ const timestamp = req .headers [" x-zm-request-timestamp" ];
88+
89+ if (! verifySignature (JSON .stringify (payload), timestamp, signature)) {
7990 res .writeHead (401 );
8091 res .end (JSON .stringify ({ error: " Unauthorized" }));
8192 return ;
0 commit comments