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

Express + Passport JS环境下Session Cookie无法设置问题

MERN应用中Passport JS Session Cookie无法在React前端设置的问题

在MERN栈应用中使用Passport JS实现用户认证,需要保护路由仅允许已认证用户访问。使用Thunder Client测试/login路由及受保护路由时功能正常,但在React前端登录成功后,Passport生成的Session Cookie虽出现在响应中,却未被浏览器设置,导致后续无法完成认证。

已尝试直接通过res.cookie("test", "tester")设置测试Cookie,这种方式可以正常生效,但Passport的Session Cookie始终无法被浏览器保存,且更换不同浏览器测试后问题依旧。

后端地址为localhost:3001,响应中存在Cookie但浏览器未设置的截图如下:

响应截图

浏览器中无Cookie的状态:

无Cookie截图


Auth.mjs

import UsersModel from "../Models/User.mjs";
import bcrypt from "bcrypt";
import { omit, pick } from "../helpers.mjs";

// ---------------------------- //
// -----   AUTH ROUTES    ----- //
// ---------------------------- //

const auth = (app, checkAuthenticated, checkNotAuthenticated, passport) => {

  app.delete("/logout", checkAuthenticated, (req, res) => {
    req.logout((err) => {
      if (err) { return next(err); }
      res.json({ msg: "Logged out"});
    });
  });

  // BAD ROUTE - cookie SHOULD be attached 
  app.post("/login", checkNotAuthenticated, passport.authenticate("local"), (req, res) => {
    res.json({
      msg: "Authenticated",
      user: req.user
    })
  });

  app.post("/register", checkNotAuthenticated, async (req, res) => {
    try {
      const hashedPassword = await bcrypt.hash(req.body.password, 15)
      await UsersModel.create({
        name: req.body.name,
        password: hashedPassword,
        email: req.body.email
      })
      res.json({ msg: "Registered new user"})
    } catch {
      res.json({ msg: "Error"})
    }
  })
}
export default auth;

Passport.mjs

import passportLocal from "passport-local";
import bcrypt from "bcrypt";

const LocalStrategy = passportLocal.Strategy;

const initialize = async (passport, getUserByEmail, getUserById) => {
  const authenticateUser = async (email, password, done) => {
    const user = await getUserByEmail(email);
    if (user == null) {
      return done(null, false, {msg: "No user with that email"})
      console.log("LOL")
    }

    try {
      console.log(`pass: ${password}, hash: ${user.password}`)
      if (await bcrypt.compare(password, user.password)) {
        console.log(user)
        return done(null, user);
      } else {
        return done(null, false, {msg: "Password incorrect"})
      }
    } catch (e) {
      return done(e)
    }
  }
  passport.use(new LocalStrategy({ usernameField: "email"}, authenticateUser))
  passport.serializeUser((user, done) => done(null, user._id));
  passport.deserializeUser(async (id, done) => done(null, await getUserById(id)));
}

export default initialize;

主服务器文件

import dotenv from "dotenv";
if (process.env.NODE_ENV !== "production") {
  dotenv.config();
}

import express from "express";
import mongoose from 'mongoose';
import cors from 'cors';
import UsersModel from './Models/User.mjs';
import passport from "passport";
import flash from "express-flash";
import session from "express-session";
import initialize from "./Routes/Passport.mjs";
import mongoStore from 'express-session-mongo';
import cookieParser from "cookie-parser";

// route imports
import items from "./Routes/Items.mjs";
import departments from "./Routes/Departments.mjs";
import houses from "./Routes/Houses.mjs";
import lists from "./Routes/Lists.mjs";
import people from "./Routes/People.mjs";
import auth from "./Routes/Auth.mjs";

const getUserByEmail = async (email) => {
  return await UsersModel.findOne({email: email});
}

const getUserById = async (id) => {
  return await UsersModel.findById(id);
}

// new express app
const app = express()

// ---------------------------- //
// -----     MIDDLEWARE   ----- //
// ---------------------------- //

app.use(cors({credentials: true}))
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
app.use(flash())
app.use(cookieParser())
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
}))
app.use(passport.initialize())
app.use(passport.session())


// connect to mongoDB
mongoose.connect(process.env.DB)

// initialize passport
initialize(passport, getUserByEmail, getUserById)

// check auth/not auth
const checkAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
// ALWAYS HITS BELOW \/
  return res.status(401).json({msg: "Must be authenticated"});
}

const checkNotAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return res.json({msg: "Can't access, authenticated"});
  } 

  return next();
}


// ---------------------------- //
// -----      ROUTES      ----- //
// ---------------------------- //

items(app, checkAuthenticated, checkNotAuthenticated);
departments(app, checkAuthenticated, checkNotAuthenticated);
houses(app, checkAuthenticated, checkNotAuthenticated);
lists(app, checkAuthenticated, checkNotAuthenticated);
people(app, checkAuthenticated, checkNotAuthenticated);
auth(app, checkAuthenticated, checkNotAuthenticated, passport);

// ---------------------------- //
// -----   START SERVER   ----- //
// ---------------------------- //

app.listen(process.env.PORT, () => {
  console.log("--- Server is UP and running ---")
})

// ---------------------------- //
// ---------------------------- //

React前端代码

import React, { useContext, useState } from 'react';
import "../css/Auth.css";
import { useMutation } from '@tanstack/react-query';
import { Context } from '../App';

const Authenticate = () => {
  const [isLogin, setIsLogin] = useState(false)

  const [loginEmail, setLoginEmail] = useState("");
  const [loginPwd, setLoginPwd] = useState("");

  const [registerName, setRegisterName] = useState("");
  const [registerEmail, setRegisterEmail] = useState("");
  const [registerPwd, setRegisterPwd] = useState("");
  const [registerPwdConfirm, setRegisterPwdConfirm] = useState("");

  const {user, authenticated} = useContext(Context);
  const [userVal, setUserVal] = user;
  const [authenticatedVal, setAuthenticatedVal] = authenticated;

  const fetchLoginQuery = async () => {
    const req = await fetch(`${import.meta.env.VITE_REACT_APP_API}/login`, {
            method: 'post',
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              email: loginEmail,
              password: loginPwd
            })
          });
      return req.json();
  }


  const fetchRegisterQuery = async () => {
    const req = await fetch(`${import.meta.env.VITE_REACT_APP_API}/register`, {
            method: 'post',
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              email: loginEmail,
              password: loginPwd
            })
          });
      return req.json();
  }

  const loginMutation = useMutation({
    mutationFn: fetchLoginQuery,
    onSuccess: (data) => {
        if (data.msg == "Authenticated") {
          setAuthenticatedVal(true);
          setUserVal(data.user)
        }
        // queryClient.invalidateQueries({ queryKey: [""]})
    }
  }) 

  const registerMutation = useMutation({
    mutationFn: fetchRegisterQuery,
    onSuccess: () => {
        // queryClient.invalidateQueries({ queryKey: [""]})
    }
  }) 

  const handleLogin = () => {
    loginMutation.mutate();
  }

  const handleRegister = () => {
       //TODO
  }

  return (
    <div>
      <h1>Welcome to the Grocery List App</h1>
      <button onClick={() => setIsLogin(!isLogin)}>{isLogin ? "Create an account?" : "Log in to existing account"}</button>

      {isLogin ? 
      <>
        <input type="text" placeholder="email" value={loginEmail} onChange={(e) => {setLoginEmail(e.target.value)}}/>
        <input type="text" placeholder="password" value={loginPwd} onChange={(e) => {setLoginPwd(e.target.value)}}/>
        <button onClick={handleLogin}>Login</button>
      </>
      :
      <>
        <input type="text" placeholder="name" value={registerName} onChange={(e) => {setRegisterName(e.target.value)}}/>
        <input type="email" placeholder="email" value={registerEmail} onChange={(e) => {setRegisterEmail(e.target.value)}}/>
        <input type="text" placeholder="password" value={registerPwd} onChange={(e) => {setRegisterPwd(e.target.value)}}/>
        <input type="text" placeholder="password again" value={registerPwdConfirm} onChange={(e) => {setRegisterPwdConfirm(e.target.value)}}/>
        <button onClick={handleRegister}>Register</button>
      </>
      }
    </div>
  )
}

export default Authenticate;

内容的提问来源于stack exchange,提问作者jc22920

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:19:53