在 Express.js 中为 Todoist Webhook API 验证请求负载

Validating request payload in Express.js for Todoist Webhook API

我正在尝试整合 Webhooks API from Todoist。我得到了正确的 header 和 body 信息,但无法验证 X-Todoist-Hmac-SHA256 header。来自 Todoist 文档:

To verify each webhook request was indeed sent by Todoist, an X-Todoist-Hmac-SHA256 header is included; it is a SHA256 Hmac generated using your client_secret as the encryption key and the whole request payload as the message to be encrypted. The resulting Hmac would be encoded in a base64 string.

现在这是我的 webhook 路由代码,使用 express.js 和 Node.js 加密库进行解密:

app.post("/webhooks/todoist", async (req, res) => {

    // this is my stored client secret from Todoist
    const secret = keys.todoistClientSecret 
    
    // Using the Node.js Crypto library
    const hash = crypto.createHmac('sha256', secret)
      .update(req.toString()) // <-- is this the "whole request payload"?
      .digest("base64");


    // These 2 are not equal
    console.log("X-Todoist-Hmac-SHA256:", req.header("X-Todoist-Hmac-SHA256"))
    console.log("Hash:", hash)

    res.status(200).send()
  })

我已经发现 req 是 IncomingMessage 类型。 Crypto 库只接受某些类型,如果我传递 req object 本身,我会收到以下错误:

The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView.

将 "whole request payload" 传递给加密库的正确方法是什么?

好的,在试错了一些其他变体之后,我找到了解决方案。您必须将 JSON.stringify(req.body) 作为要加密的消息传递。也许这对其他人有帮助。

    const hash = crypto.createHmac('sha256', secret)
      .update(JSON.stringify(req.body)) // <-- this is the needed message to encrypt
      .digest("base64");

特殊字符编码导入更新

我在快速设置中使用了 app.use(bodyParser.json())。这不适用于具有特殊字符(如德语变音符号 ä、ö、ü)的请求正文。相反,我必须像这样设置正文解析器:

app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf
  }
}))

然后,在加密中而不是传递JSON.stringify(req.body)它必须是req.rawBody。所以加密库的最终代码如下所示:

    const secret = keys.todoistClientSecret
    const hash = crypto.createHmac('sha256', secret)
      .update(req.rawBody)
      .digest("base64");