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
相关产品推荐
相关产品推荐

