在 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");
我正在尝试整合 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");