Search code examples
node.jsexpressexpress-session

Express session doesn't save ... unless I refresh my browser?


I'm trying to implement a simple login with node and express, storing the logged-in user's info in an express session. Here's what I've got so far

Application:

var express = require("express");
var cookieParser = require("cookie-parser");
var session = require("express-session");
var bodyParser = require("body-parser");

var app = express();
app.use( cookieParser(SESSION_SECRET) );
app.use( bodyParser.json() );
app.use( bodyParser.urlencoded({extended:true}) );
app.use( session({secret:SESSION_SECRET, resave:true, saveUninitialized:true, cookie:{httpOnly: false, secure:false, maxAge:COOKIE_MAX_AGE}}) );
// a couple app.use statements to serve static files from a public folde

My login route does this:

router.post("/login", function(request, response)
{
  // authentication stuff. if the user is authenticated:
  request.session.userId = user._id;
  request.session.save();
  response.json({success:true, user: user._id});
});

Elsewhere, I have this to automatically inject user info for my other routes:

// inject currently-logged-in user info into all requests
router.use(function(request, response, next)
{
    // No session and no user ID means no user. So just go on.
    if( null == request.session || null == request.session.userId )
    {
        console.log("No session here.");
        return next();
    }

    // Try to find the user. Look up the user id in the db, then do this:
    request.user = dbUser;
    next();
});

// External Routes 
require("./routes/Login")(router); // this is where router.post("/login") is
require("./routes/User")(router); // another route that looks up user info

What's weird is that after I log in the first time, on subsequent requests I see that the session doesn't exist ("No session here." in the TTY) but when I refresh my browser and log in a second time, it's there.

EDIT: So, the simplified order of events are:

  1. client: http.post("/login", {userid:..,pw:...});
  2. server user-injector route: fails, no session (that's ok because we're about to create one), so call next()
  3. server login route: credentials ok, so it should create a session. then it returns the response to the client.
  4. client: server says we're good to go, so: http.get("/protectedstuff")
  5. server user-injector route: fails, no sesion (what? it should have been created by this point)

EDIT 2: Some more info. It gets weirder? This seems to only happen right after the server restarts, and only for the first login attempt. Once the first user is able to login and get a session, any other attempt to login will be successful. Even if the first user logs out.

At this point I'm beginning to wonder if there's a bug in express-session. That first call to store a session variable isn't working for some reason.


I'm not really sure where the problem is, either. My browser is making pretty simple http requests with angular, and it's storing the user id properly. I can see quite clearly that the json response's data object is {success: true, user: (user id)}, so everything looks ok on that end. I'm stumped. Why isn't express saving my session info in the login route?

Package versions:

  • body-parser: 1.12.3
  • cookie-parser: 1.3.4
  • express: 4.12.3
  • express-session: 1.11.1
  • angular: 1.3.15

Solution

  • After a lot of messing around I finally managed to fix the problem. Or maybe it's just a workaround. The issue was that I wasn't specifying a default store for the session data on the server. So by default, express is using a memory store which, apparently, is not so great.

    I'm using connect-mongo as the session store now and things seem to be working, although I still suspect that there's a bug in express somewhere.

    For completeness sake, here's how I'm setting up express now:

    var express = require("express");
    var session = require("express-session");
    var mongoose = require("mongoose");
    var MongoStore = require("connect-mongo")(session);
    
    var app = express();
    // same cookieparser, bodyparser, etc setup as before
    app.use( session( {
      secret:SESSION_SECRET, 
      resave: true, 
      saveUninitialized: false,
      rolling: true,
      store: new MongoStore({mongooseConnection:mongoose.connection}),
      cookie: {
          httpOnly: false, 
          secure: false, 
          maxAge:COOKIE_MAX_AGE
      }
    }));