Node.js + express.js + passport.js : stay authenticated between server restart

node.jsExpress

node.js Problem Overview


I use passport.js to handle auth on my nodejs + express.js application. I setup a LocalStrategy to take users from mongodb

My problems is that users have to re-authenticate when I restart my node server. This is a problem as I am actively developing it and don't wan't to login at every restart... (+ I use node supervisor)

Here is my app setup :

app.configure(function(){
    app.use('/static', express.static(__dirname + '/static'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({secret:'something'}));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(app.router);
});

And session serializing setup :

passport.serializeUser(function(user, done) {
    done(null, user.email);
});

passport.deserializeUser(function(email, done) {
    User.findOne({email:email}, function(err, user) {
        done(err, user);
    });
});

I tried the solution given on a blog (removed the link as it does not exist any more) using connect-mongodb without success

app.use(express.session({
    secret:'something else',
    cookie: {maxAge: 60000 * 60 * 24 * 30}, // 30 days
        store: MongoDBStore({
        db: mongoose.connection.db
    })
}));

EDIT additional problem : only one connection should be made (use of one connexion limited mongohq free service)

EDIT 2 solution (as an edition as I my reputation is to low to answer my question by now

Here is the solution I finally found, using mongoose initiated connection

app.use(express.session({
    secret:'awesome unicorns',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(
        {db:mongoose.connection.db},
        function(err){
            console.log(err || 'connect-mongodb setup ok');
        })
}));

node.js Solutions


Solution 1 - node.js

There's an opensource called [connect-mongo][1] that does exactly what you need - persists the session data in mongodb

usage example (with a reuse of mongoose opened connection) :

var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/sess');
app.use(express.session({
    secret:'secret',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(
    // Following lines of code doesn't work
    // with the connect-mongo version 1.2.1(2016-06-20).
    //    {db:mongoose.connection.db},
    //    function(err){
    //        console.log(err || 'connect-mongodb setup ok');
    //   }
    {mongooseConnection:mongoose.connection}
    )        
}));

you can read more about it here: https://github.com/kcbanner/connect-mongo [1]: https://github.com/kcbanner/connect-mongo

Solution 2 - node.js

i use connect-mongo like so:

var MongoStore = require('connect-mongo');

var sess_conf = {
  db: {
    db: mydb,
    host: localhost,
    collection: 'usersessions' // optional, default: sessions
  },
  secret: 'ioudrhgowiehgio'
};

 app.use(express.session({
    secret: sess_conf.secret,
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(sess_conf.db)
  }));


[...]

// Initialize Passport!  Also use passport.session() middleware, to support
  // persistent login sessions (recommended).
  app.use(passport.initialize());
  app.use(passport.session());

Solution 3 - node.js

This is because you use MemoryStore (default) for sessions. Look at this code from memory.js (part of Connect framework):

var MemoryStore = module.exports = function MemoryStore() {
  this.sessions = {};
};

and this snippet from session.js (Express)

function session(options){
    /* some code */
    , store = options.store || new MemoryStore
    /* some code */
}

Now you should understand that every server restart resets the MemoryStore. In order to keep the data you have to use some other session store. You can even write your own (shouldn't be too difficult), although Redis (see this library) might be a good choice (and it is well supported by Express).

// EDIT

According to the Connect documentation it is enough for you if you implement get, set and destroy methods. The following code should work:

customStore = {
    get : function(sid, callback) {
        // custom code, for example calling MongoDb
    },
    set : function(sid, session, callback) {
        // custom code
    },
    destroy : function(sid, callback) {
        // custom code
    }
}    

app.use(express.session({
    store: customStore
}));

You just need to implement calling MongoDb (or any other Db although I still recommend using nonpermament one like Redis) for storing session data. Also read the source code of other implementations to grab the idea.

Solution 4 - node.js

This is probably obvious to experienced node users but it caught me out:

You need to configure the node session - e.g.

app.use(session({secret: "this_is_secret", store: ...}));

before initializing the passport session - e.g.

app.use(passport.initialize());
app.use(passport.session());

If you call passport.session() first it won't work (and it won't warn you). I thought the problem was with the serialize/deserialize user functions and wasted hours.

Solution 5 - node.js

I'm using mongoose, I tried the code presented in the answers above and it didn't work for me. I got this error when I did:

Error: db object already connecting, open cannot be called multiple times

However, this works for me:

app.use(express.session({
    secret:'secret',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore({mongoose_connection:mongoose.connection})
}))

Note: If you don't have MongoStore for whatever reason, you need to do:

npm install connect-mongo --save

then:

var MongoStore = require('connect-mongo')(express)

Solution 6 - node.js

What I ended up doing:

var expressSession = require('express-session');
var redisClient = require('redis').createClient();
var RedisStore = require('connect-redis')(expressSession);
...
app.use(expressSession({
	resave: true,
	saveUninitialized: true,
	key: config.session.key,
	secret: config.session.secret,
	store: new RedisStore({
		client: redisClient,
		host: config.db.host,
		port: config.db.port,
		prefix: 'my-app_',
		disableTTL: true
	})
}));

Works for me.

Solution 7 - node.js

You need to change the store you are using for your sessions. The default one 'MemoryStore' does not continue to store the session when you're application stops. Check out express-session on github to find out more about what other stores there are like the mongo one. (Can't remember the name)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArnaud RinquinView Question on Stackoverflow
Solution 1 - node.jsArnaud RinquinView Answer on Stackoverflow
Solution 2 - node.jstoxinlabsView Answer on Stackoverflow
Solution 3 - node.jsfreakishView Answer on Stackoverflow
Solution 4 - node.jsharryrobbinsView Answer on Stackoverflow
Solution 5 - node.jsarg20View Answer on Stackoverflow
Solution 6 - node.jsKevin SuttleView Answer on Stackoverflow
Solution 7 - node.jsJames Brian LagoView Answer on Stackoverflow