Passport JS Google OAuth2回调Cookie写入顺序异常排查(Next.js场景)
根因定位
你遇到的时序错位问题核心来自两点叠加:
passport-google-oauth20的策略实现和Facebook策略存在流程感知差异:FacebookStrategy 会持有整个流程锁,直到验证回调完成才交回控制权给Express中间件链;GoogleStrategy 在调用你传入的验证回调时,如果检测不到同步的done()调用,会提前触发next()把流程往下传,不会主动等待异步操作完成- 你在两个策略共用的
verifyCallback里,用process.nextTick把远程API请求的异步逻辑和同步执行栈完全拆开,导致GoogleStrategy 误判流程完成,提前把控制权交给后续路由处理函数
实际错位的执行链路是:
- 收到Google回调请求后,GoogleStrategy 拉取完授权用户信息,调用
verifyCallback - 你的
verifyCallback立刻同步返回,把远程API请求丢到下一个事件循环tick - GoogleStrategy 因为没捕获到同步的
done()信号,直接调用next(),把流程走到路由末尾的重定向逻辑,触发res.redirect('/profile') - 等
process.nextTick里的远程API请求完成,调用done(null, user)把用户信息挂载到req上,才触发setCookie中间件执行写入Cookie —— 此时302响应头已经生成发送,Cookie写入晚于响应,浏览器收不到对应Cookie,跳转/profile时无有效登录态被踢回首页
另外附带一个小配置问题:你Google登录的scope参数里,两个授权URL的末尾都带了多余空格,虽然不是时序问题的诱因,但会触发Google侧的scope校验异常,建议同步修正。
修复步骤
- 移除
verifyCallback里多余的process.nextTick包裹,直接把远程API请求逻辑写成async函数,让Passport策略能正确感知异步流程的完成时机 - 删除Google scope配置里两个授权URL末尾的多余空格
- 重写Google回调路由的处理逻辑,改用Passport的自定义回调模式,彻底消除多中间件依赖带来的时序问题:
function googleCallback(app) { return app.get( '/auth/callback/google', (req, res, next) => { Passport.authenticate('google', { failureRedirect: '/', session: false }, (err, user) => { if (err || !user) return res.redirect('/') // 手动挂载登录态 req.login(user, { session: false }, (loginErr) => { if (loginErr) return next(loginErr) // 先完成Cookie写入 res.cookie('token', req.user.auth.token, { httpOnly: true, sameSite: 'strict', path: '/', secure: process.env.NODE_ENV !== 'development', }) // 最后执行重定向 return res.redirect('/profile') }) })(req, res, next) } ) }
验证方法
- 在各流程节点打日志标记执行顺序,确认链路为:Google授权回调接收 -> 远程API校验完成 -> Cookie写入 -> 重定向到/profile
- 打开浏览器开发者工具,在网络面板查看Google回调请求的响应,确认302状态码返回的同时,响应头中就已经携带了对应的Set-Cookie字段
内容的提问来源于stack exchange,提问作者littlechad
相关产品推荐
相关产品推荐

