Passport JS与express-session生产环境req.isAuthenticated()返回false问题
生产环境下
req.isAuthenticated()返回false的问题 我通过req.isAuthenticated()判断是否向客户端返回受保护资源,应用部署在Render平台。开发环境测试功能正常,但切换到生产环境后出现异常:用户登录成功后重定向至首页,通过useEffect调用/getUserDetails接口时,因req.isAuthenticated()返回false无法获取资源。已尝试调整中间件顺序、将express-session的cookie secure设为true,相关代码如下:
import express from "express"; import dotenv from "dotenv"; import session from "express-session"; import cors from "cors"; import mongoose from "mongoose"; import passport from "passport"; import { Strategy as GoogleStrategy } from "passport-google-oauth20"; import MongoStore from "connect-mongo"; import { profileRouter } from "./routes/profileRouter.js"; //import "./utils/passport.js" import { User } from "./models/User.js"; dotenv.config({ path: "./config.env" }); const app = express(); app.use(express.json()); //DATABASE CONNECTION const DB = process.env.DB_STRING.replace("<password>", process.env.DB_PASSWORD); mongoose .connect(DB) .then(() => { console.log("DATABASE CONNECTION WAS SUCCESSFULL!"); }) .catch((error) => { console.log(`${error} --- error connecting to DATABASE `); }); app.use( cors({ origin: process.env.NODE_ENV === "development" ? process.env.CLIENT_URL_DEV : process.env.CLIENT_URL_PROD, credentials: true, methods: "GET,POST,PUT,DELETE", }) ); app.use( session({ resave: false, //changes req,session.cookie when session object is modified saveUninitialized: false, //only stores session to session store if modified secret: process.env.SECRET, store: MongoStore.create({ mongoUrl: DB, collectionName: "sessions", }), cookie: { maxAge: 1000 * 60 * 60, secure: process.env.NODE_ENV === "development" ? false : true }, }) ); app.use(passport.initialize()); app.use(passport.session()); passport.use( new GoogleStrategy( { clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: `${ process.env.NODE_ENV === "development" ? process.env.API_URL_DEV + "/auth/google/callback" : process.env.API_URL_PROD + "/auth/google/callback" }`, }, async (accessToken, refreshToken, profile, done) => { console.log(profile, " profile info "); const userExists = await User.findOne({ email: profile.emails[0].value, name: profile.displayName }) if (!userExists) { await User.create({ email: profile.emails[0].value, name: profile.displayName, profilePhoto: profile.photos[0].value }); } return done(null, profile); } ) ); passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); app.get( "/auth/google/callback", passport.authenticate("google", { access_type: "offline", scope: ["email", "profile"], }), async (req, res) => { console.log(`/auth/google/callback hit with a req --- ${JSON.stringify(req.user)} `) if (!req.user) { res.status(400).json({ error: "Authentication failed" }); } else { res.redirect( process.env.NODE_ENV === "development" ? process.env.CLIENT_URL_DEV : process.env.CLIENT_URL_PROD ); } } ); app.use("/profile", profileRouter) app.get("/", (req, res) => { console.log("get req received to / endpoint "); res.send("<h1>Hello</h1>"); }); const PORT = process.env.PORT || 6007; app.listen(PORT, () => { console.log(`server listening on port ${PORT} `); });
解决方案
1. 让Express信任Render的反向代理
Render使用反向代理,Express需要明确信任代理才能正确识别HTTPS请求,否则secure: true的cookie无法被正确传递。在session配置前添加:
app.set('trust proxy', 1);
2. 完善Session Cookie的SameSite配置
跨域场景下,生产环境的Cookie需要设置SameSite: 'none'(配合secure: true),否则浏览器会阻止Cookie传递:
cookie: { maxAge: 1000 * 60 * 60, secure: process.env.NODE_ENV === "development" ? false : true, sameSite: process.env.NODE_ENV === "development" ? 'lax' : 'none', }
3. 修正Passport的序列化逻辑
当前直接序列化整个Google Profile对象,容易导致序列化/反序列化异常,应该只存储数据库用户的唯一ID:
// 序列化:只存储用户ID passport.serializeUser(function(user, done) { done(null, user._id); }); // 反序列化:通过ID从数据库获取用户 passport.deserializeUser(async function(id, done) { try { const user = await User.findById(id); done(null, user); } catch (err) { done(err); } });
同时,在Google Strategy回调中,要返回数据库中的用户对象,而非Google Profile:
async (accessToken, refreshToken, profile, done) => { let user = await User.findOne({ email: profile.emails[0].value }); if (!user) { user = await User.create({ email: profile.emails[0].value, name: profile.displayName, profilePhoto: profile.photos[0].value }); } return done(null, user); // 返回数据库用户对象 }
4. 确保前端请求携带Credentials
前端调用/getUserDetails时,必须配置携带Cookie:
// Axios示例 axios.get('/profile/getUserDetails', { withCredentials: true }); // Fetch示例 fetch('/profile/getUserDetails', { credentials: 'include' });
5. 检查Render环境变量配置
确认Render平台上的NODE_ENV、CLIENT_URL_PROD、API_URL_PROD、SECRET等环境变量都正确设置,尤其是NODE_ENV需设为production。
内容的提问来源于stack exchange,提问作者DaObliterator
相关产品推荐
相关产品推荐

