Search code examples
node.jsexpresscookies

Passport not writing onto cookie using cookie-session, but does for express-session


I've got an API for an instant messaging app that currently uses the express-session library for the login (and works), but I'm trying to switch to using the cookie-session library instead. I took a look at the basic syntax of cookie-session and it sure LOOKS to me like this should work, and yet Chrome Dev Tools isn't showing any cookie being sent... You can see I've commented out the version that used the express-session library (and works fine) to compare.

Can anybody familiar with cookie-session see what the issue is? I've removed some parts of the code that are irrelevant (some socket.io and mongoDB stuff and the routing):

const express = require("express");
const cors = require("cors");
const passport = require("passport");
const bcrypt = require("bcrypt");
const session = require("express-session");
const cookieSession = require("cookie-session");
const methodOverride = require("method-override");
const app = express();
const router = express.Router();
const { Users } = require("./models");
require("dotenv").config();
const http = require("http").createServer(app);
const io = require("socket.io")(http, {
  cors: {
    origin: [
      "https://admin.socket.io/",
      "http://localhost:5173",
    ],
    credentials: true,
  },
});
const initializePassport = require("./passport-config");

//----------------------------------------- END OF IMPORTS---------------------------------------------------
// Middleware
initializePassport(passport);

app.use(express.json());
app.use(
  cors({
    credentials: true,
    origin: "http://localhost:5173",
    methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allowedHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept"],
  })
);

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "http://localhost:5173");
  res.header("Access-Control-Allow-Credentials", "true");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
});
app.use(express.urlencoded({ extended: false }));
// below is the express-session I've used; this works fine
/* app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
  })
); */
// below is the cookie-session version that i can't make work
app.use(
  cookieSession({
    name: "session",
    keys: ["key1", "key2"],
    maxAge: 24 * 60 * 60 * 1000,
  })
);
app.use(passport.initialize());
app.use(passport.session());
app.use(methodOverride("_method"));

//----------------------------------------- END OF MIDDLEWARE---------------------------------------------------

// Routes
// removed for brevity

//----------------------------------------- END OF ROUTES---------------------------------------------------
//Start Server
app.listen(4000, () => {
  console.log("server listening on port 4000");
});

After playing around a little bit I've found that the cookie WILL be created if I do something else to write onto it; for example if I add

app.use((req, res, next) => {
req.session.test = "hello";
next()
}

it will be sent. So the issue appears to be that Passport is not writing to req.session when it is produced by the cookie-session library, but does so when I use express-session. Is there something in the way I've got Passport configured?:

// PASSPORT CONFIG:
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const { Users } = require("./models");

function initialize(passport) {
  const authenticateUser = async (username, password, done) => {
    console.log("logging in: ", username);
    const user = await Users.findOne({ username: username });
    if (user == null) {
      return done(null, false, { message: "No user with that username" });
    }

    try {
      if (await bcrypt.compare(password, user.password)) {
        return done(null, user);
      } else {
        return done(null, false, { message: "Password incorrect" });
      }
    } catch (e) {
      return done(e);
    }
  };

  passport.use(
    new LocalStrategy({ usernameField: "username" }, authenticateUser)
  );
  passport.serializeUser((user, done) => {
    console.log("serializing");
    done(null, user.id);
  });
  passport.deserializeUser(async (id, done) => {
    try {
      const user = await Users.findById(id).exec();
      console.log("deserializing");
      done(null, user);
    } catch (error) {
      done(error);
    }
  });
}

module.exports = initialize;


Solution

  • Okay, I got it. Apparently recent versions of Passport have a compatibility issue with cookie-session according to this blog post. Passport calls a couple of methods that cookie-session doesn't come with, so you need to add them in to shut it up. I gave it

    app.use((req, res, next) => {
      if (req.session) {
        req.session.regenerate = (cb) => cb();
        req.session.save = (cb) => cb();
      }
      next();
    });

    just after passport.session() and now it's okay.