Issues validating signature during event subscription process (Node/Express)

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.

Your signature construction for comparsion is incomplete

See this example for a correct one https://github.com/BarryCarlyon/twitch_misc/blob/main/eventsub/handlers/nodejs/receive.js#L37-L66

You need to .update with a string concat of the headers twitch-eventsub-message-id then twitch-eventsub-message-timestamp and then the raw buffer.

Your verification looks more like “old” Webhooks than EventSub

1 Like

Ah, OK. Now that you mention it, I did remember seeing that in one of the API docs. I’ll have to try that this afternoon. Have to grab some lunch shortly then head out for my second COVID shot.

That was it indeed. Everything is working as expected now. Thank you @BarryCarlyon!!

Most welcome! And thank you for the coffee!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.