Skip to content

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

@abaksha-sc

Description

@abaksha-sc

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
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}}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions