我正在尝试验证在开发环境中从hmac
shopify
发送的webhook
代码。但是,shopify
不会将webhook
的发布请求发送到非实时终端,因此我使用requestbin来捕获请求,然后使用postman
将其发送到我的本地网络服务器。
从shopify documentation,我似乎做了一切正确的事情,并尝试应用node-shopify-auth verifyWebhookHMAC function中使用的方法。但到目前为止,这一切都没有奏效。 代码永远不会匹配。 我在这里做错了什么?
我验证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');
}
}
收到请求的路线:
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);
});
答案 0 :(得分:3)
我做的有点不同 - 不确定我在哪里看到推荐,但我在身体解析器中进行验证。 IIRC的一个原因是,在任何其他处理者可能触及它之前,我可以访问原始身体:
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';
}
}}));
然后任何Web挂钩只处理经过验证的标头:
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;
}
答案 1 :(得分:1)
express中的body解析器不能很好地处理BigInt,并且作为整数传递的订单号之类的东西被破坏了。除了编辑某些值,例如URL最初发送为“https:// ...”,OP也从其他代码中找到。
要解决此问题,请不要使用正文解析器解析数据,而是将其作为原始字符串进行解析,稍后您可以使用json-bigint对其进行解析,以确保它们都没有损坏。
尽管@bknights的answer工作得很好,但重要的是要找出为什么会发生这种情况。
对于我在Shopify的“order_created”事件中做的webhook,我发现传递给正文的请求的ID与我从测试数据发送的不同,这结果是一个问题快递中的身体解析器,它与大整数一起玩不好。
最终我正在为Google云功能部署一些东西,而且req已经拥有了我可以使用的原始主体,但是在我的测试环境中我在Node中实现了以下作为单独的主体解析器,因为使用相同的主体解析器两次覆盖原始身体与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}));
基于this回答
我稍后使用json-bigint解析rawBody以用于其他地方的代码,否则一些数字已被破坏。
答案 2 :(得分:0)
// Change the way body-parser is used
const bodyParser = require('body-parser');
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 }));
// Now we can access raw-body any where in out application as follows
// request.rawBody in routes;
// verify webhook middleware
const verifyWebhook = function (req, res, next) {
console.log('Hey!!! we got a webhook to verify!');
const hmac_header = req.get('X-Shopify-Hmac-Sha256');
const body = req.rawBody;
const calculated_hmac = crypto.createHmac('SHA256', secretKey)
.update(body,'utf8', 'hex')
.digest('base64');
console.log('calculated_hmac', calculated_hmac);
console.log('hmac_header', hmac_header);
if (calculated_hmac == hmac_header) {
console.log('Phew, it came from Shopify!');
res.status(200).send('ok');
next();
}else {
console.log('Danger! Not from Shopify!')
res.status(403).send('invalid');
}
}