I'm trying to create a Xero webhook and connect it up to Pipedream but I can't get the "Intent to receive" validation working properly. I've tried a couple of different approaches (using Node.js). This was my first attempt:
import crypto from "crypto";
export default defineComponent({
async run({ steps, $ }) {
const key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
payload = steps.trigger.event.body.toString(),
// payload = JSON.stringify({"events": [],"lastEventSequence": 0,"firstEventSequence": 0,"entropy": "S0m3r4Nd0mt3xt"}),
calculatedHmac = crypto.createHmac('sha256', key).update(payload).digest('base64'),
xeroSignature = steps.trigger.event.headers["x-xero-signature"];
// console.log(calculatedHmac.trim());
// console.log(xeroSignature);
if (calculatedHmac.trim() === xeroSignature) {
await $.respond({
status: 200,
headers: {},
body: 'ok',
})
} else {
await $.respond({
status: 401,
headers: {},
body: 'unauthorised',
})
}
},
})
And this was my second attempt based on an old GitHub repo I found:
export default defineComponent({
async run({ steps, $ }) {
const { createHmac } = await import('crypto');
const xero_webhook_key = 'xxxxxxxxxxxxxxxxxxxxxxxx'
const body_string = JSON.stringify(steps.trigger.event.body)
const xero_hash = steps.trigger.event.headers["x-xero-signature"]
let our_hash = createHmac('sha256', xero_webhook_key).update(body_string).digest("base64") // Generate the hash Xero wants
let statusCode = xero_hash == our_hash ? 200 : 401 // If the hashes match, send a 200, else send a 401
await $.respond({
status: statusCode
});
}
})
Both methods are returning 401 every time, even when a correctly signed signature is sent from Xero. In the first lot of code you'll see I've used console.log to show the calculated HMAC and the Xero Signature, but they never match.
Based on some other threads I found I've also tried using .toString() rather than JSON.stringify(), and tried hardcoding an example payload but none of that worked either.
Any ideas?
You have to allow for raw body request type in your API.
This is how I did it using Nest JS:
and just regular Express JS