Initial question: I'm using passportJS in my backend, and MongoStore as the session store, with a react frontend.
What I'm trying to do is allow the user to have a persistent login session. There's already a cookie that's getting automatically set by express-session called connect.sid. However the session doesn't seem to be persistent because req.user
is undefined on subsequent requests using the same cookie that was set on login. req.user
keeps returning undefined which means that passport is not verifying the session cookie somehow.
According to passport's docs that shouldn't happen.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session. In order to support login sessions, Passport will serialize and deserialize user instances to and from the session.
So what I'm trying to understand is how exactly does passportJS deal with cookies sent by the client?
Can someone help me by explaining the steps that passport takes when it comes to that?
I'm also using passport-local-mongoose plugin in my mongoose model which includes built in authenticate/serialize/deserialize methods used in auth.js below.
Relevant parts of my code for reference:
app.js: (didn't include the full file so it can be clear since it's 100+ lines, if someone suggests it could be an issue of middleware order I'll include the full code)
//session and passport initialization
const sessionStore = new MongoStore({
mongooseConnection: mongoose.connection,
collection: "sessions",
});
app.use(
session({
secret: process.env.SERVER_SECRET_KEY,
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
path:"/",
httpOnly: true,
expires: 9999999999999999
}
})
);
app.use(passport.initialize());
app.use(passport.session());
auth.js
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const config = require("./config");
const User = require("./models/userModel");
passport.use(
new LocalStrategy({ usernameField: "userName", passwordField: "password" }, User.authenticate())
);
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
exports.userLogin = passport.authenticate("local", {
session: true,
failureRedirect: '/users/loginfailed'
});
users.js (login request which returns the new cookie if successful)
usersRouter
.route("/login")
.post(auth.userLogin, (req, res, next) => {
console.log(req.user); // returns a valid user on login
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.json({ success: true, message: "Login successful!" });
});
movies.js (separate request after login, previous cookie included in headers)
moviesRouter
.route("/top")
.get((req, res, next) => {
console.log(req.user); // undefined
//includes a mongodb query here and a corresponding server response
})
UPDATE (thanks to this answer): I found the issue, it was as simple as removing the cookie option from the object that was passed to session().
changed to this:
app.use(
session({
secret: process.env.SERVER_SECRET_KEY,
resave: false,
saveUninitialized: false,
store: sessionStore,
})
);
But I still don't understand why Passport likes to ignore my cookie options. What if I want to use secure or disable httpOnly or anything else I may want to try with my cookies?