Nodejs - Expressjs - Verify shopify webhook

☆樱花仙子☆ 提交于 2019-12-07 21:40:04

问题


I am trying to verify the hmac code sent from a shopify webhook 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);
});

回答1:


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



回答2:


Short Answer

The body parser in express does not handle BigInt well, and things like order number which are passed as integer get corrupted. Apart from that certain values are edited such as URLs are originally sent as "https://...", which OP also found out from the other code.

To solve this, do not parse the data using body parser and instead get it as raw string, later on you can parse it with json-bigint to ensure none of it has been corrupted.

Long Answer

Although the answer by @bknights works perfectly fine, it's important to find out why this was happening in the first place.

For a webhook I made on the "order_created" event from Shopify I found out that the id of the request being passed to the body was different than what I was sending from my test data, this turned out to be an issue with body-parser in express which did not play nice with big integers.

Ultimately I was deploying something to Google cloud functions and the req already had raw body which I could use, but in my test environment in Node I implemented the following as a separate body parser as using the same body parser twice overwrote the raw body with JSON

var rawBodySaver = function (req, res, buf, encoding) {
    if (buf && buf.length) {
      req.rawBody = buf.toString(encoding || 'utf8');
    }
}
app.use(bodyParser.json({verify: rawBodySaver, extended: true}));

Based on this answer

I later on parse the rawBody using json-bigint for use in code elsewhere as otherwise some of the numbers were corrupted.



来源:https://stackoverflow.com/questions/36745204/nodejs-expressjs-verify-shopify-webhook

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!