twitchdev / twitch-cli

The official Twitch CLI to make developing on Twitch easier.
Apache License 2.0
604 stars 71 forks source link

[Bug] Verification fails for "event trigger" (but passed for "event verify-subscription") - regression in 1.1.23 against the 1.1.22 #329

Closed abaksha-sc closed 3 months ago

abaksha-sc commented 3 months ago

What is the problem?

I just taken the code from official example: https://dev.twitch.tv/docs/eventsub/handling-webhook-events/#simple-nodejs-example And only changed the secret to "TestTwitchWebhookSecret".

The result - command "event verify-subscription" successfully passed but "event trigger" fails.

Twitch CLI version : twitch-cli_1.1.23_Windows_x86_64 Operating System: Windows 10 Architecture Version: x64

Steps to reproduce

Trimmed from here: https://dev.twitch.tv/docs/eventsub/handling-webhook-events/#simple-nodejs-example

  1. Create a new project with npm init
  2. Run npm install express --save
  3. Create a file and name it app.js with the following content (here is only the secret is changed):
Click me - code is here ```js const crypto = require('crypto') const express = require('express'); const app = express(); const port = 8080; // Notification request headers const TWITCH_MESSAGE_ID = 'Twitch-Eventsub-Message-Id'.toLowerCase(); const TWITCH_MESSAGE_TIMESTAMP = 'Twitch-Eventsub-Message-Timestamp'.toLowerCase(); const TWITCH_MESSAGE_SIGNATURE = 'Twitch-Eventsub-Message-Signature'.toLowerCase(); const MESSAGE_TYPE = 'Twitch-Eventsub-Message-Type'.toLowerCase(); // Notification message types const MESSAGE_TYPE_VERIFICATION = 'webhook_callback_verification'; const MESSAGE_TYPE_NOTIFICATION = 'notification'; const MESSAGE_TYPE_REVOCATION = 'revocation'; // Prepend this string to the HMAC that's created from the message const HMAC_PREFIX = 'sha256='; app.use(express.raw({ // Need raw message body for signature verification type: 'application/json' })) app.post('/eventsub', (req, res) => { let secret = getSecret(); let message = getHmacMessage(req); let hmac = HMAC_PREFIX + getHmac(secret, message); // Signature to compare if (true === verifyMessage(hmac, req.headers[TWITCH_MESSAGE_SIGNATURE])) { console.log("signatures match"); // Get JSON object from body, so you can process the message. let notification = JSON.parse(req.body); if (MESSAGE_TYPE_NOTIFICATION === req.headers[MESSAGE_TYPE]) { // TODO: Do something with the event's data. console.log(`Event type: ${notification.subscription.type}`); console.log(JSON.stringify(notification.event, null, 4)); res.sendStatus(204); } else if (MESSAGE_TYPE_VERIFICATION === req.headers[MESSAGE_TYPE]) { res.set('Content-Type', 'text/plain').status(200).send(notification.challenge); } else if (MESSAGE_TYPE_REVOCATION === req.headers[MESSAGE_TYPE]) { res.sendStatus(204); console.log(`${notification.subscription.type} notifications revoked!`); console.log(`reason: ${notification.subscription.status}`); console.log(`condition: ${JSON.stringify(notification.subscription.condition, null, 4)}`); } else { res.sendStatus(204); console.log(`Unknown message type: ${req.headers[MESSAGE_TYPE]}`); } } else { console.log('403'); // Signatures didn't match. res.sendStatus(403); } }) app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); }) function getSecret() { // !!! THE SECRET CHANGED !!!! return 'TestTwitchWebhookSecret'; // !!! THE SECRET CHANGED !!!! } // Build the message used to get the HMAC. function getHmacMessage(request) { return (request.headers[TWITCH_MESSAGE_ID] + request.headers[TWITCH_MESSAGE_TIMESTAMP] + request.body); } // Get the HMAC. function getHmac(secret, message) { return crypto.createHmac('sha256', secret) .update(message) .digest('hex'); } // Verify whether our hash matches the hash that Twitch passed in the header. function verifyMessage(hmac, verifySignature) { return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(verifySignature)); } ```
  1. Run node app.js
  2. Run Twitch CLI command twitch event verify-subscription channel.raid -F http://localhost:8080/eventsub -s TestTwitchWebhookSecret and check that it's successful.
  3. Then run Twitch CLI command twitch event trigger channel.raid -F http://localhost:8080/eventsub -s TestTwitchWebhookSecret - it fails

image

Relevant log output

c:\__Work\twitch-cli_1.1.23_Windows_x86_64>twitch event verify-subscription channel.raid -F http://localhost:8080/eventsub -s TestTwitchWebhookSecret
✔ Valid response. Received challenge 888003fe-92ec-88a5-3355-c4facddf9265 in body
✔ Valid content-type header. Received type text/plain with charset utf-8
✔ Valid status code. Received status 200

c:\__Work\twitch-cli_1.1.23_Windows_x86_64>twitch event trigger channel.raid -s TestTwitchWebhookSecret -F http://localhost:8080/eventsub
✗ Invalid response. Received Status Code: 403
✗ Server Said: Forbidden
{"subscription":{"id":"","status":"enabled","type":"channel.raid","version":"1","condition":{"to_broadcaster_user_id":"22473690"},"transport":{"method":"webhook","callback":"null"},"created_at":"2024-07-01T15:27:01.4937421Z","cost":0},"event":{"to_broadcaster_user_id":"22473690","to_broadcaster_user_login":"testBroadcaster","to_broadcaster_user_name":"testBroadcaster","from_broadcaster_user_id":"57779910","from_broadcaster_user_login":"testFromUser","from_broadcaster_user_name":"testFromUser","viewers":78266}}
abaksha-sc commented 3 months ago

UPD: works fine in version 1.1.22. So seems the regression issue is actual only for 1.1.23:

image

chticer commented 3 months ago

Can reproduce the same issue on 1.1.23 but not with 1.1.22.

WindowsTerminal_DEr8NMuUzY

BarryCarlyon commented 3 months ago

Both verify and trigger:

So I suspect the commits in #322 are the fault