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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 22:35:55