You need to enable JavaScript to run this app. Express中Twitch EventSub webhook回调请求未触发验签中间件
优惠活动
大模型
产品
解决方案
定价
更多

Express中Twitch EventSub webhook回调请求未触发验签中间件

Twitch Eventsub Webhook 签名校验中间件不触发问题排查

问题描述

  • 依据Twitch官方Eventsub文档实现订阅创建逻辑,可正常收到Twitch订阅申请接口的返回响应,但回调请求到达/notification路由时,预期运行的签名校验中间件完全未执行,流程直接中断
  • 已排查路由注册顺序,未定位到问题根因

现有实现代码

app.post('/createWebhook/:broadcasterId', (req, res) => {
  const createWebHookParams = {
    host: "api.twitch.tv",
    path: "helix/eventsub/subscriptions",
    method: 'POST',
    headers: {
      "Content-Type": "application/json",
      "Client-ID": clientId,
      "Authorization": "Bearer " + authToken
    }
  }

  const createWebHookBody = {
    "type": "channel.follow",
    "version": "1",
    "condition": {
      "broadcaster_user_id": req.params.broadcasterId
    },
    "transport": {
      "method": "webhook",
      "callback": "ngrokURL/notification",
      "secret": webhookSecret
    }
  }

  let responseData = ""
  const webhookReq = https.request(createWebHookParams, (result) => {
    result.setEncoding('utf8')
    result.on('data', (d) => {
        responseData = responseData + d
      })
      .on('end', (result) => {
        const responseBody = JSON.parse(responseData)
        console.log(responseBody)
        res.send(responseBody)
      })
  })
  webhookReq.on('error', (e) => {
    console.log("Error")
  })

  webhookReq.write(JSON.stringify(createWebHookBody))
  webhookReq.end()
});

// 签名校验中间件未被触发
app.use(express.json({
  verify: verifyTwitchSignature
}));

// 接收Twitch通知的POST路由
app.post('/notification', (req, res) => {
  console.log("incoming notificatin", req.body)
  res.status(200).end();
})

// 签名校验逻辑实现
const crypto = require("crypto");
const twitchSigningSecret = process.env.SECRET;

const verifyTwitchSignature = (req, res, buf, encoding)=>{
  const messageId = req.header("Twitch-Eventsub-Message-Id");
  const timeStamp = req.header("Twitch-Eventsub-Message-Timestamp")
  const messageSignature = req.header("Twitch-Eventsub-Message-Signature")

  console.log(`Message ${messageId} Signature: `,messageSignature)

  if (!twitchSigningSecret){
    console.log(`Twitch signing secret is empty`);
    throw new Error ("Twitch signing secret is empty.");
  }

  const computedSignature = "sha256=" + crypto.createHmac("sha256", twitchSigningSecret).update(messageId + timeStamp + buf).digist("hex");
  console.log(`Message ${messageId} Computed Signature: `, computedSignature)

  if (messageSignature !== computedSignature) {
    throw new Error("Invalid Signature.");
  }else {
    console.log("Verification Successful");
  }
}

module.exports = verifyTwitchSignature

具体问题与修复方案

  • 变量声明时序错误,导致中间件挂载失败
    • 当前代码中,app.use(express.json(...))的执行位置早于verifyTwitchSignature的常量定义
    • 由于verifyTwitchSignature通过const声明,存在暂时性死区,执行到app.use代码行时会直接抛出ReferenceError: Cannot access 'verifyTwitchSignature' before initialization,整个express.json中间件未被成功挂载到Express实例上,自然不会触发校验逻辑
    • 修复:将所有依赖导入、自定义常量、自定义中间件函数的定义,全部移动到app.use、业务路由注册的代码之前
  • 方法名拼写错误,导致校验逻辑运行时崩溃
    • HMAC签名计算环节,错误调用了不存在的digist("hex")方法,正确方法名为digest("hex")
    • 即便解决了时序问题,执行到这行代码时会直接抛出TypeError,被Express默认错误捕获逻辑拦截,表现为流程中断
  • 中间件挂载位置不规范
    • 当前express.json中间件被放置在两个业务路由中间,Express按照代码书写顺序从上到下匹配请求,这种写法会导致挂载位置之前的路由完全不经过JSON解析逻辑,后续维护极易出现兼容问题
    • 修复:将app.use(express.json(...))移动到所有业务路由之前,保证所有进入Express的请求都按顺序经过中间件处理
  • 未处理Twitch Webhook的首次验证流程
    • Twitch注册webhook时,会先向配置的callback地址发送类型为webhook_callback_verification的请求,要求将请求体中的challenge值以text/plain格式原样返回,否则Twitch会直接判定订阅无效,不会推送后续事件
    • 当前/notification路由收到请求后直接返回200空响应,未处理验证流程,即便前面的中间件问题修复,也无法收到后续事件通知
  • 回调地址为未替换的占位符
    • transport配置中的callback字段为"ngrokURL/notification"占位内容,需要替换为公网可访问的完整HTTPS地址(如ngrok生成的域名拼接/notification路径),否则Twitch的回调请求无法到达本地服务

调整后的代码结构参考

  1. 先导入所有依赖模块
  2. 定义签名校验的自定义中间件函数
  3. 挂载全局express.json中间件,绑定verify配置
  4. 注册所有业务路由(包含创建webhook的接口、接收通知的接口)
  5. 在/notification路由中优先判断消息类型,若为初始验证请求,直接返回对应的challenge值

内容的提问来源于stack exchange,提问作者John Yacoub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 18:15:03