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

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions