Search code examples
node.jsexpressexpress-session

Non-Persistent req.session in Express.js (node.js, mongoDB)


In my Node.js backend application, I attempted to use Express and save the email as a way to identify whether the user is logged in and which account I should reference.

However, after switching some cookie settings to account for HTTPS and local development, suddenly, this field no longer persists in secure environments.

(For additional context, I'm using axios in React.js for the frontend to make API calls.)

req.session.save() has not worked in my favor.

I have double checked whether credentials were being sent through, and the axios application I used has withCredentials set to true.

I have also looked at the MongoDB store, and have found that the field does not appear in secure contexts, but it sometimes appears in insecure contexts, for some reason. This might be a bug, and if it is, please let me know. The cookie is present in all cases.

Here is my code for reference (I have omitted pieces that I considered irrelevant.)

(client.jsx, frontend)

axios.defaults.withCredentials = true
const client = axios.create({
  baseURL: process.env.REACT_APP_BACKEND_URL, // "http://localhost:8080",
  headers: {
  },
  responseType: 'json'
});

(server.js, backend)

const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const { uri, ss, initial } = require('./src/api/config/config');

const fifteen_min = 15 * 60 * 1000;

const store = new MongoDBStore({
  uri: uri,
  collection: 'sessions',
  expires: fifteen_min,
  connectionOptions: { useNewUrlParser: true, useUnifiedTopology: true }
});

const insecure_setting = {
  secret: ss,
  cookie: {
      maxAge: fifteen_min,
      secure: false,
      httpOnly: true,
  },
  store: store
}
const secure_setting = {
  secret: ss,
  cookie: {
      maxAge: fifteen_min,
      secure: true,
      httpOnly: true,
      sameSite: 'none',
  },
  store: store
}
const sess = process.env.MODE === "prod" ? secure_setting : insecure_setting;

app.use(session(sess));

/* ... */

app.use('/session',cors(corsOptions), requireAuth, sessionPriv);

(Within an endpoint; sessionpublic.js; backend)

        if (msg !== "Successful login.") {
            body = {
                msg: msg,
                success: false
            }
            res.send(body);
            return;
        }
        
        req.session.email = fields['email'];
        req.session.save();
        console.log(req.session); // 'email' is included here!

(permissions.js; backend)

function requireAuth (req, res, next) {
    console.log(req.session.email); // undefined in secure contexts!
    if (!req.session || !req.session.email) {
      console.log("redirect made.");
      /* redirecting code */
      res.send();
      return;
  } else {
    req.session.foobar = Date.now();
    req.session.touch();
    next();
  }
}

I have set saveUninitialized: true in the secure settings. It didn't work either.

If you have any questions, I'll try my best to answer them. Thank you in advance!

====================== To show this behavior in action, here are the output logs from my AWS Beanstalk application.

Sep 22 12:51:32 ip-172-31-7-83 web[295290]: Origin received: incoming from https://frontend.com
Sep 22 12:51:33 ip-172-31-7-83 web[295290]: Origin received: incoming from https://frontend.com
Sep 22 12:51:33 ip-172-31-7-83 web[295290]: login try in progress.
Sep 22 12:51:33 ip-172-31-7-83 web[295290]: Session {
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:  cookie: {
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    path: '/',
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    _expires: 2023-09-22T13:06:33.056Z,
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    originalMaxAge: 900000,
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    httpOnly: true,
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    secure: true,
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    sameSite: 'none',
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:    saveUninitialized: true
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:  },
Sep 22 12:51:33 ip-172-31-7-83 web[295290]:  email: '[email protected]'
Sep 22 12:51:33 ip-172-31-7-83 web[295290]: }
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: Origin received: incoming from https://frontend.com
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: Origin received: incoming from https://frontend.com
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: undefined
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: Session {
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:  cookie: {
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    path: '/',
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    _expires: 2023-09-22T13:06:34.430Z,
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    originalMaxAge: 900000,
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    httpOnly: true,
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    secure: true,
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    sameSite: 'none',
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:    saveUninitialized: true
Sep 22 12:51:34 ip-172-31-7-83 web[295290]:  }
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: }
Sep 22 12:51:34 ip-172-31-7-83 web[295290]: redirect made.

Solution

  • Just an update. I did some version rollbacks to a version that was working, and I found that one line of code was unexpectedly deleted.

    Readding this code solved my poblem.

    if (process.env.MODE === "prod") {
      app.set('trust proxy', 1);
    }
    

    This was likely a problem since the front-end and back-end APIs work in different applications, so we needed this setting for them to interact properly.