Xero Webhook Intent to Receive验证失败,始终返回401求助
我尝试在Pipedream中创建并连接Xero webhook,但无法完成「Intent to Receive」验证。先后使用两种Node.js实现方式,均始终返回401,即使Xero发送了正确签名的请求。第一种代码中通过console.log查看计算出的HMAC和Xero签名,二者从未匹配。还尝试用.toString()替代JSON.stringify()、硬编码示例payload,都未解决问题。
第一种实现代码
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', }) } }, })
第二种基于旧GitHub仓库的实现代码
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 }); } })
使用原始请求体计算HMAC:Xero的签名基于未解析的原始HTTP请求体生成,而Pipedream的
steps.trigger.event.body是解析后的JSON对象,用JSON.stringify()或.toString()处理会改变原始字节结构(比如空格、键顺序)。应直接使用Pipedream提供的原始请求体属性,比如steps.trigger.rawBody(如果可用),传入update()方法计算HMAC。核对Webhook密钥:确保代码中的密钥与Xero Webhook配置页面生成的密钥完全一致,注意大小写、无多余空格或换行,密钥是严格匹配的。
确认编码一致性:Xero使用UTF-8编码计算HMAC,若请求体是字符串,需确保其为UTF-8编码;若为Buffer类型,直接传入
update()即可,无需额外转换。禁止修改请求体:不要对请求体做格式化、转义、调整键顺序等操作,任何修改都会导致HMAC结果不匹配。
本地验证计算逻辑:用Xero提供的示例payload(代码中注释的内容),手动按规则计算HMAC,对比代码输出,确认本地计算逻辑是否正确。
检查请求头大小写:部分代理会修改请求头大小写,尝试用
steps.trigger.event.headers["x-xero-signature"]或steps.trigger.event.headers["X-Xero-Signature"]获取签名,确保能正确拿到Xero发送的签名值。
内容的提问来源于stack exchange,提问作者AJT

