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

Node.js部署Vercel后Google认证无Session ID问题求助

谷歌OAuth登录部署Vercel后无Session ID问题排查

我通过Node.js、Express.js搭配passport-google-oauth20与express-session实现谷歌账号登录功能,本地环境运行正常,但将Vite全栈应用部署到Vercel后出现异常——无法创建Session ID,且deserializeUser函数未执行。以下是后端Index.js代码,请求排查谷歌认证成功后无Session ID返回的原因:

//SET UP CORS MIDDLEWARE
app.use(
  cors({
    origin: true,
    credentials: true,
  })
);

//MIDDLEWARE TO PARSE DATA IN REQUEST BODY AND MAKE IT AVAILABLE IN REQ.BODY
app.use(express.json());

//APPLY HELMET TO ADD SOME HTTP HEADERS FOR SECURITY ENHANCEMENT
app.use(helmet());

//APPLY COOKIE PARSER TO PARSE DATA STORED IN COOKIE AND MAKE IT AVAILABLE IN REQ.COOKIES
app.use(cookieparser());

//SET UP THE EXPRESS SESSION MIDDLEWARE
app.use(
  expressSession({
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    cookie: {
      secure: true,
      maxAge: 60 * 60 * 1000,
      httpOnly: true,
      sameSite: "none",
    },
  })
);

//INITIALIZE PASSPORT
app.use(passport.initialize());

// PASSPORT SESSION CALL THE DESERIALIZEUSER FUNCTION AND PASS THE USER ID TO THE PARAMETER OF THIS FUNCTION
app.use(passport.session());

//IMPLEMENT GOOGLE STRATEGY
passport.use(
  new GoogleStrategy(
    {
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: `https://nasa-app-backend.vercel.app/auth/google/callback`,
    },
    function (_accessToken, _refreshToken, profile, done) {
      const { id: google_id, name: nameObj } = profile;

      //CHECK TO SEE IF THIS USER IS IN DATABASE
      User.findOne({ google_id: google_id, username: nameObj.givenName })
        .then((user) => {
          //IF THIS USER EXISTS IN DATABASE
          if (user) {
            done(null, { _id: user._id });
          }

          //IF THIS IS A NEW USER
          else {
            User.create({ google_id: google_id, username: nameObj.givenName })
              .then((res) => {
                done(null, { _id: res._id });
              })
              .catch((e) => console.log(`Error creating a user ${e}`));
          }
        })
        .catch((e) => console.log(`Error fetching a user ${e}`));
    }
  )
);

//SET UP SERIALIZEUSER
passport.serializeUser((userObj, done) => {
  console.log(userObj);
  done(null, userObj._id);
});

//SSET UP DESERIALIZEUSER
passport.deserializeUser((userId, done) => {
  console.log(userId);
  User.findOne({ _id: userId })
    .then((user) => done(null, { _id: user._id, username: user.username }))
    .catch((e) => {
      console.log("Error finding user");
    });
});

//CONNECT TO THE MONGODB DATABASE
handleConnectMongoDBServer();

//APPLY ROUTES
app.use("/auth", googleAuthRoute); //Put the /auth first to ensure that req.user is set before the JWT check token middlewares are invoked
app.use("/user-profile", userProfileRoute);
app.use("/login", loginRoute);
app.use("/signup", signupRoute);
app.use("/logout", logoutRoute);

//START THE SERVER
app.listen(PORT, (req, res) => {
  console.log(`Website is listening to the port http://localhost:${PORT}`);
});

核心排查点及解决方法

1. express-session默认内存存储不兼容Vercel无服务器环境

express-session默认用内存存储Session,但Vercel的API路由是无状态的,每次请求可能分配到不同的实例,内存中的Session无法跨实例共享,导致Session ID无法持久化。

解决:改用持久化会话存储,比如connect-mongo(基于MongoDB的会话存储):

  • 安装依赖:npm install connect-mongo
  • 修改session配置:
const MongoStore = require('connect-mongo');

// 信任Vercel反向代理,确保HTTPS识别正确
app.set('trust proxy', 1);

app.use(
  expressSession({
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: false, // 避免创建空会话,更安全
    store: MongoStore.create({ 
      mongoUrl: process.env.MONGODB_URI, // 你的MongoDB连接字符串
      ttl: 60 * 60 // 会话过期时间,和cookie maxAge保持一致
    }),
    cookie: {
      secure: process.env.NODE_ENV === 'production', // 生产环境启用HTTPS cookie,本地开发设为false
      maxAge: 60 * 60 * 1000,
      httpOnly: true,
      sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', // 本地用lax,生产用none配合secure
    },
  })
);

2. 未信任Vercel反向代理导致Cookie设置失败

Vercel通过反向代理转发请求,express默认不会信任代理的HTTPS标识,导致secure: true的Cookie无法正确设置。添加app.set('trust proxy', 1)让express信任代理,才能正确识别请求的协议(HTTPS)。

3. deserializeUser未执行的连锁问题

deserializeUser不执行的根本原因是Session未被持久化,passport.session()无法从Session中读取用户ID,自然不会触发该函数。解决Session存储问题后,该函数会自动正常执行。

另外注意:当前deserializeUser的catch块只打印日志未调用done(e),会导致请求挂起,建议修改为:

passport.deserializeUser((userId, done) => {
  console.log(userId);
  User.findOne({ _id: userId })
    .then((user) => done(null, { _id: user._id, username: user.username }))
    .catch((e) => {
      console.log("Error finding user", e);
      done(e); // 必须调用done传递错误
    });
});

4. CORS配置优化

当前CORS的origin: true会允许所有来源,生产环境建议指定具体的前端域名,避免安全风险:

app.use(
  cors({
    origin: process.env.FRONTEND_URL, // 比如https://your-vite-app.vercel.app
    credentials: true,
  })
);

同时确保前端请求时携带credentials: 'include',比如:

fetch('/auth/google', { credentials: 'include' });

内容的提问来源于stack exchange,提问作者Thành Nhân

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 14:42:22