I’m working on an application in NodeJS and Express to manage Channel Points reward redemptions for my own channel, but I’m running into an issue trying to validate the signature of the challenge callback response. I can successfully send the user to the page to get authorization, and they get sent back to my callback URL just fine. The issue comes when I try the POST to set up the subscription itself and I need to send the challenge token back so Twitch knows I own the URL. I get that response just fine, and I do get the challenge token I can send back - the issue is the validation of the signature from the header. I’m using the following code found from another topic:
const bodyParser = require('body-parser')
const crypto = require('crypto');
...
router.use(bodyParser.json({
verify: function(req, res, buf, encoding) {
req.twitch_eventsub = false;
if (req.headers && req.headers['twitch-eventsub-message-signature']) {
req.twitch_eventsub = true;
const signature = req.headers['twitch-eventsub-message-signature'].split('=');
req.twitch_hex = crypto.createHmac(signature[0], process.env.TWITCH_SIGNING_KEY).update(buf).digest('hex');
req.twitch_signature = signature[1];
}
}
}));
I have verified the buffer data is the request that I sent in the subscription request, such as
{
"type": "channel.channel_points_custom_reward_redemption.add",
"version": "1",
"condition": {
"broadcaster_user_id": "<MY_ID>"
},
"transport": {
"method": "webhook",
"callback": "<MY_CALLBACK>",
"secret": "<MY_SIGNING_KEY>"
}
}
and the response data to the subscription request (I’m making this call from Postman ATM for testing):
{
"data": [
{
"id": "00000000-0000-0000-0000-000000000000",
"status": "webhook_callback_verification_pending",
"type": "channel.channel_points_custom_reward_redemption.add",
"version": "1",
"condition": {
"broadcaster_user_id": "<MY_ID>",
"reward_id": ""
},
"created_at": "2021-04-19T13:46:56.857685374Z",
"transport": {
"method": "webhook",
"callback": "<MY_CALLBACK>"
},
"cost": 0
}
],
"limit": 10000,
"total": 21,
"max_total_cost": 10000,
"total_cost": 0
}
But, inside the POST handler where the challenge token is sent, the check for twitch_hex and twitch_signature fails because the values don’t match. Here’s the handler:
router.post('/channelpoints', function(req, res) {
try {
if (req.twitch_eventsub && (req.twitch_hex === req.twitch_signature)) {
const challenge = req.body.challenge;
res.send(challenge);
} else {
res.status(403).send('Invalid signature')
}
} catch(error) {
res.status(403).send(`Error validating signature: ${error.message}`)
}
});
Not sure what I’m doing wrong here. Again, everything appears to be “working” except the signature validation.