Ad

Nodejs - Expressjs - Verify Shopify Webhook

- 1 answer

I am trying to verify the hmac code sent from a shopifywebhook on a dev environment. However shopify will not send a post request for a webhook to a non live endpoint, so I am using requestbin to capture the request and then use postman to send it to my local webserver.

From shopify documentation, I seem to be doing everything right and have also tried applying the method used in node-shopify-auth verifyWebhookHMAC function. But none of this has worked so far. The codes are never a match. What am I doing wrong here?

My code to verify the webhook:

 function verifyWebHook(req, res, next) {
      var message = JSON.stringify(req.body);
    //Shopify seems to be escaping forward slashes when the build the HMAC
        // so we need to do the same otherwise it will fail validation
        // Shopify also seems to replace '&' with \u0026 ...
        //message = message.replace('/', '\\/');
        message = message.split('/').join('\\/');
    message = message.split('&').join('\\u0026');
      var signature = crypto.createHmac('sha256', shopifyConfig.secret).update(message).digest('base64');
      var reqHeaderHmac = req.headers['x-shopify-hmac-sha256'];
      var truthCondition = signature === reqHeaderHmac;

      winston.info('sha256 signature: ' + signature);
      winston.info('x-shopify-hmac-sha256 from header: ' + reqHeaderHmac);
      winston.info(req.body);

      if (truthCondition) {
        winston.info('webhook verified');
        req.body = JSON.parse(req.body.toString());
        res.sendStatus(200);
        res.end();
        next();
      } else {
        winston.info('Failed to verify web-hook');
        res.writeHead(401);
        res.end('Unverified webhook');
      }
    }

My route which receives the request:

router.post('/update-product', useBodyParserJson, verifyWebHook, function (req, res) {
  var shopName = req.headers['x-shopify-shop-domain'].slice(0, -14);
  var itemId = req.headers['x-shopify-product-id'];
  winston.info('Shopname from webhook is: ' + shopName + ' For item: ' + itemId);
});
Ad

Answer

I do it a little differently -- Not sure where I saw the recommendation but I do the verify in the body parser. IIRC one reason being that I get access to the raw body before any other handlers are likely to have touched it:

app.use( bodyParser.json({verify: function(req, res, buf, encoding) {
    var shopHMAC = req.get('x-shopify-hmac-sha256');
    if(!shopHMAC) return;
    if(req.get('x-kotn-webhook-verified')) throw "Unexpected webhook verifieduched it:

app.use( bodyParser.json({verify: function(req, res, buf, encoding) {
    var shopHMAC = req.get('x-shopify-hmac-sha256');
    if(!shopHMAC) return;
    if(req.get('x-kotn-webhook-verified')) throw "Unexpected webhook verified header";
    var sharedSecret = process.env.API_SECRET;
    var digest = crypto.createHmac('SHA256', sharedSecret).update(buf).digest('base64');
    if(digest == req.get('x-shopify-hmac-sha256')){
        req.headers['x-kotn-webhook-verified']= '200';
    }
 }})); 

and then any web hooks just deal with the verified header:

if('200' != req.get('x-kotn-webhook-verified')){
    console.log('invalid signature for uninstall');
    res.status(204).send();
    return;
}
var shop = req.get('x-shopify-shop-domain');
if(!shop){
    console.log('missing shop header for uninstall');
    res.status(400).send('missing shop');
    return;
}
Ad
source: stackoverflow.com
Ad