Expo Web项目中passport-spotify的session内accessToken为undefined问题
你遇到的核心问题是:Spotify认证回调中能正常获取并存储accessToken到session,但后续请求其他路由时,req.session.accessToken和req.user.accessToken均为undefined,这通常是Passport会话序列化缺失、跨域cookie配置错误或前端未携带凭证导致的。
1. 补充Passport序列化/反序列化函数
Passport依赖serializeUser和deserializeUser维持会话中的用户数据,你的代码缺少这两个函数,导致后续请求无法恢复req.user,也无法正确持久化session中的用户信息。
添加以下代码到Passport配置后:
// 序列化用户对象到session passport.serializeUser((user, done) => { // 只存储必要字段,包括accessToken done(null, { id: user.id, accessToken: user.accessToken }); }); // 从session反序列化用户对象到req.user passport.deserializeUser((userObj, done) => { done(null, userObj); });
后续请求中,req.user会包含你在verify回调中附加的accessToken,无需手动操作req.session.accessToken。
2. 修复跨域Session Cookie配置
你的后端运行在localhost:5001,前端在localhost:8081,属于跨域场景,默认情况下浏览器不会携带session cookie到后端,需要调整CORS和session配置:
步骤1:配置CORS允许凭证
先安装并引入cors中间件:
const cors = require('cors'); app.use(cors({ origin: 'http://localhost:8081', // 允许前端域名 credentials: true // 允许携带cookie }));
步骤2:调整Session的Cookie参数
修改session配置,确保跨域下cookie能被正确携带:
app.use( session({ secret: "x", resave: false, saveUninitialized: false, cookie: { sameSite: 'none', // 跨域场景必须设置为none secure: process.env.NODE_ENV === 'production', // 生产环境设为true(HTTPS),本地开发可设为false httpOnly: true, maxAge: 3600000 // 和Spotify accessToken过期时间匹配(默认1小时) } }) );
3. 前端请求必须携带凭证
前端调用后端接口时,需要明确告知浏览器携带cookie:
- 使用
fetch时:
fetch('http://localhost:5001/spotify/get-currently-playing', { credentials: 'include' }) .then(res => res.json()) .then(data => console.log(data));
- 使用
axios时:
axios.get('http://localhost:5001/spotify/get-currently-playing', { withCredentials: true });
4. 验证会话一致性
可以在回调和目标路由中打印sessionID,确认前后请求的会话是否一致:
// 认证回调中 console.log("回调会话ID:", req.sessionID); // /spotify/get-currently-playing路由中 console.log("播放接口会话ID:", req.sessionID);
如果两个ID不一致,说明cookie未被携带,需检查跨域配置和前端请求设置。
优化后的路由示例
补充序列化函数后,你可以直接通过req.user.accessToken获取令牌,无需手动操作session:
app.get("/spotify/get-currently-playing", async (req, res) => { try { if (!req.user?.accessToken) { throw new Error("未找到访问令牌"); } const userData = await getCurrentSongInfo(req.user.accessToken); res.json(userData); } catch (err) { res.status(401).json({ error: err.message }); } });
内容的提问来源于stack exchange,提问作者Ddefin Orsstt

