You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Webhook触发GET请求时req.user为undefined,如何获取用户凭据?

根本原因

你前端发请求时,会自动携带登录态凭证(比如Cookie、Authorization头中的JWT等),后端的身份认证中间件(比如Passport、自定义JWT校验逻辑)会校验这些凭证,解析出对应用户信息挂载到req.user上。但第三方Webhook触发请求时,不会主动携带你站点的用户身份凭证,认证中间件无法识别请求对应的用户,req.user自然为undefined。

可行解决方案
  • 方案1:Webhook URL中带用户唯一标识+签名校验
    给每个用户配置独立的Webhook回调地址,把和凭据绑定的用户唯一标识放在URL参数中,同时额外加签名参数避免恶意调用:
    回调地址示例格式:https://your-domain.com/api/list-conversations?user_id=xxx&sign=xxxxxx
    后端先校验签名合法性,再通过user_id从数据库/缓存中查询对应的Twilio凭据,代替从req.user取值的逻辑。
  • 方案2:自定义Webhook请求头带认证信息
    如果配置Webhook的平台支持自定义请求头,你可以直接把用户的认证Token(比如JWT)放在自定义请求头里,让后端认证中间件兼容从该请求头取参数校验身份,不需要改原有接口逻辑即可正常挂载req.user。
  • 方案3:固定用户场景直接配置凭据
    如果这个Webhook只对应单个固定用户使用,可以直接把凭据写在后端配置文件或者环境变量中,不需要从请求身份中取值。
代码调整示例(以方案1为例)
const listConversationMessages = async (req, res) => {
    console.log("list conversation Messages triggered")
    console.log(req.body)
    let subActServiceSid, subActAuthToken, convoServiceSid
    // 兼容两种请求来源
    if (req.user) {
        // 前端调用原有逻辑
        ({ subActServiceSid, subActAuthToken, convoServiceSid} = req.user)
    } else {
        // Webhook调用逻辑
        const { user_id, sign } = req.query
        // 先校验签名合法性,校验不通过直接返回403
        if (!validateSign(user_id, sign)) {
            return res.status(403).send('Invalid request')
        }
        // 从数据库查询对应用户的凭据
        const userCred = await getUserCredFromDB(user_id)
        subActServiceSid = userCred.subActServiceSid
        subActAuthToken = userCred.subActAuthToken
        convoServiceSid = userCred.convoServiceSid
    }
    // 原有逻辑保持不变
    const subClient = require('twilio')(subActServiceSid, subActAuthToken)
    const allConversationSids = []
    const allMessages = []
    const sortedMessages = []
    const options = { weekday: 'long'}
    const monthOptions = { month: 'long'}
    

    await subClient.conversations.services(convoServiceSid)
    .conversations
    .list()
    .then(conversations => conversations.forEach(c => allConversationSids.push(c.sid)));
    await Promise.all(allConversationSids.map( async (convo) => {
       await subClient.conversations.services(convoServiceSid)
       .conversations(convo)
       .messages
       .list({limit: 20})
       .then(messages => messages.forEach(m => allMessages.push({"messagesId": m.conversationSid, "participantSid": m.participantSid,  "message": m.body, "time": { "day": new Intl.DateTimeFormat('en-US', options).format(m.dateCreated.getDay()), "hour": m.dateCreated.getHours() + ":" + m.dateCreated.getMinutes(), "date": new Intl.DateTimeFormat('en-US', monthOptions).format(m.dateCreated.getMonth()) + " " + m.dateCreated.getDate() + ", " + m.dateCreated.getFullYear()}})));
    }))
   let response
    if(allMessages.length < 1 ) {
    response = [{"messagesId": 1 , "content": [{ "participantSid": 2,  "message": "test"} ]}]
    } else {
    response = allMessages
}

result = await response.reduce(function (accumulator, indexOfObject) {
    accumulator[indexOfObject.messagesId] = accumulator[indexOfObject.messagesId] || [];
    accumulator[indexOfObject.messagesId].push(indexOfObject);
    return accumulator;
}, Object.create({}));

sortedMessages.push(result)
const finalResult = Object.entries(sortedMessages[0]).map(([id, content]) => ({ id, content }))
res.json(finalResult)
}
注意事项
  • 所有Webhook请求的身份参数必须加签名校验,不要明文传递敏感凭据,避免被第三方截获伪造请求。
  • 如果使用请求头传Token,要确保Webhook传输链路是HTTPS加密的,避免凭证泄露。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:15:03