Cómo codificar el cuerpo de una solicitud usando HMAC sha 256 y base64.
El objeto de solicitud que recibo de xero webhook.
HEADER: "x-xero-signature" : HASH_VALUE PAYLOAD: { "events": [], "lastEventSequence": 0, "firstEventSequence": 0, "entropy": "S0m3r4Nd0mt3xt" }La nota de la documentación de xero dice: "Si la carga útil se codifica mediante HMACSHA256 con su clave de firma de webhook y codificada en base64, debe coincidir con la firma en el encabezado. Esta es una carga útil firmada correctamente. Si la firma no coincide con la carga útil codificada, es una carga útil firmada incorrectamente".
Y seguí este ejemplo: https://devblog.xero.com/using-xero-webhooks-with-node-express-hapi-examples-7c607b423379
const express = require("express"); const router = express.Router(); const base64 = require('base-64'); const crypto = require('crypto') const bodyParser = require('body-parser') const xero_webhook_key = '00fRRlJBYiYN4ZGjmTtG+g/pulyb1Eru68YYL3PFoLsa78dadfQtGrOMuISuVBHxpXeEYo0Yy1Gc+hHMhDkSI/EEcgtrA=='; let options = { type: 'application/json' }; let itrBodyParser = bodyParser.raw(options); router.post("/", itrBodyParser, async (req, res, next) =>{ // console.log('::::WebhookPost:::'); const reSign = req.headers['x-xero-signature']; console.log(req.headers['x-xero-signature']); console.log('::::::::'); console.log(req.body); console.log("Body: "+JSON.stringify(req.body)) console.log(req.body.toString()); console.log("Xero Signature: "+ reSign); console.log('Server key::::',xero_webhook_key); // Create our HMAC hash of the body, using our webhooks key let hmac = crypto.createHmac("sha256", xero_webhook_key).update(req.body.toString()).digest('base64'); console.log("Resp Signature: ",hmac) if (req.headers['x-xero-signature'] == hmac) { res.statusCode = 200 } else { res.statusCode = 401 } console.log("Response Code: "+res.statusCode) return res.send(); });Oye, recientemente hice un video sobre la implementación de webhooks con Xero, avísame si esto te ayuda. Descubrí que tratar de pasar itrBodyParser en la ruta de la forma en que lo hiciste no funcionaba para mí, así que lo cambié con una declaración app.use en mi punto final específico de webhooks. Si prefiere una guía escrita en lugar de un video, aquí está la publicación del blog .
¡Lo resolví usando esta solución! Estaba usando Express Framework y la solicitud no se recibía como una solicitud sin procesar. ToString no funcionó como se menciona en la documentación de xero.
const server = http.createServer(async (req, resp) => { try { console.log(`::::Webhook::: ${webhookPort}`); console.log("::::x-xero-signature:::"); console.log(req.headers["x-xero-signature"]); console.log(`--------------------------------------`); if (req.method === "POST") { if(req.headers["x-xero-signature"]){ const rData = await new Promise((resolve, reject) => { return collectRequestData(req, (result) => { console.log(result); let hmac = crypto .createHmac("sha256", xero_webhook_key) .update(result) .digest("base64"); console.log("Resp Signature: ", hmac); return resolve({ hmac,result }); }); }); console.log(">>Resp Signature: ", rData); console.log('>>x-xero-signature:::',req.headers["x-xero-signature"]); if(rData.result){ const result = JSON.parse(rData.result); console.log('result:::',result); for(let { resourceId } of result.events) { console.log('::INVOICE ID = ',resourceId); getInvoiceData(resourceId); } } if(rData.hmac == req.headers["x-xero-signature"] ){ console.log('::YES'); resp.statusCode = 200; }else{ console.log('::NO'); resp.statusCode = 401; } } resp.end(); } console.log("::::Webhookgetsssss:::"); resp.message = 'Get API' resp.end(); } catch (error) { resp.statusCode = 200; resp.end(); } }); server.listen(webhookPort); function collectRequestData(request, callback) { let body = ""; request.on("data", (chunk) => { body += chunk.toString(); }); request.on("end", () => { callback(body); }); }