Node+Passport.js + Sessions + multiple servers - node.js

Passport is great. I now discovered that I have some problem with how it handles sessions.
I must be using it wrong.
All works well for me with login + sessions + user data I store in my database.
However I find that when I move to production environment (cloud on EC2 with multiple servers), I lose the login session each time.
This is now clear to me - probably happens since the session is unique to each server.
So my question is - how do I get around this..
I guess I will need to store my own cookie on the user's browser?
Does this mean that I cannot use express.session at all?
Thanks,
Ilan

OK,
So basically what I was looking for (not sure it would be the same answer for everyone else) was a way to store session data between loadbalanced instances without making a DB call for every page view, which seems excessive to me, since I just need to keep the user signed in to Google/FB.
It seems that the answer I was looking for was the cookie-session middleware
https://github.com/expressjs/cookie-session
This needs to replace the default express.session mechanism which uses MemoryStore. BTW MemoryStore itself gives you a warning when run that it will not scale past a single process, and also that it may cause a memory leak.
Which if I understand correctly is serializing the session data itself into the session cookie (encrypted) instead of just using a session ID in the session cookie.
This seems perfect to me. Obviously I don't expect it to work if you have a lot of session data, since a cookie is limited in size. In my case, I just needed the name, ID and avatar url, so I think this will suffice.
Thanks for everyone who helped.

You need to store your session data in a 'global' area, that is accessible to all your servers. This could be redis or another DB.
Take the example from MEAN.JS. Here they use express-session with a MongoDB storage container (since they are a MEAN stack ; ), via connect-mongo. Their project is super easy to set up, if just for an example.
Code while setting up express is like this:
//top of file
var session = require( 'express-session' )
mongoStore = require( 'connect-mongo' )( {
session: session
} );
//...later in setup
// Express MongoDB session storage
app.use( session( {
saveUninitialized: true,
resave: true,
secret: config.sessionSecret,
store: new mongoStore( {
db: db.connection.db,
collection: config.sessionCollection
} )
} ) );
// use passport session
app.use( passport.initialize() );
app.use( passport.session() );

Related

Save data in express session

I would need to save some token in express session. So, I would need help how to save this token in session object.
Any example would be more helpful.
Also is it a good practice to save such information in session object or do I need to use some persistent storage like redis cache DB.
Yes, you can store a token in the session. This is generally done as follows:
app.use(session({
token : your_token_value
})
}));
Or, as an alternative way:
app.get('/', function(req, res, next) {
var sessData = req.session;
sessData.token = your_token_value;
res.send('Returning with some text');
});
Regarding the storage place. It is a kind of a different layer under the session. The values which you store in the session can be placed in different locations: in the application memory, in memcache, a database or in cookies.
For production you can use Memory Cache. For instance, https://github.com/balor/connect-memcached:
It can be achieved as follows:
app.use(session({
token : your_token_value,
key : 'test',
proxy : 'true',
store : new MemcachedStore({
hosts: ['127.0.0.1:11211'], //this should be where your Memcached server is running
secret: 'memcached-secret-key' // Optionally use transparent encryption for memcache session data
})
}));

Saving session data across multiple node instances

EDIT - Oct 22, 2017
There was more than one reason our sessions weren't persisting, I've had to change ourexpress-session options to this:
api.use(session({
secret: 'verysecretsecret',
resave: false,
saveUninitialized: false,
cookie: {
path: '/',
httpOnly: true,
domain: 'domain.dev',
maxAge: 1000 * 60 * 24
},
store: new MongoStore({ mongooseConnection: mongoose.connection, autoReconnect: true })
}));
Apparently domain: 'localhost' causes express-session to start a new session every single time someone starts a session and then refreshes/navigates away and back when you have a seperate node instance for session handling.
I've solved this issue by doing the following:
Added 127.0.0.1 domain.dev to my hosts file located in C:\Windows\System32\drivers\etc.
I needed a place to store sessions as per the answers given below, so we chose MongoDB. This meant I had to add the following to my express-session options:store: new MongoStore({ mongooseConnection: mongoose.connection, autoReconnect: true })
Added the httpOnly: true property to the express-session options.
Because we use jQuery for our ajax requests, I had to enable some settings in the front-end web app before making calls to the back-end:$.ajaxSetup({
xhrFields: { withCredentials: true },
crossDomain: true,
});
ORIGINAL POST
I'm currently working on a platform for which was decided to have the API running on port 3001 whilst the web application itself is running on port 3000. This decision was made to make monitoring traffic more easy.
Now, we're talking about express applications so we defaulted to using the express-session npm package. I was wondering if it's at all possible to save session data stored on the node instance running on port 3001 and be retrieved by the node instance running on port 3000 if that makes sense.
To elaborate, this is our user authentication flow:
User navigates to our web app running on port 3000.
User then signs in which sends a POST request to the API running on port 3001, session data is stored when the credentials are correct, response is sent so the web app knows the user is authenticated.
User refreshes the page or comes back to the web app after closing their browser so web app loses authenticated state. So on load it always sends a GET request to the API on port 3001 to check if there's session data available, which would mean that user is logged in, so we can let the web app know user is authenticated.
(If my train of thought is at fault here, please let me know)
The problem is that express-session doesn't seem to be working when doing this.
I've enabled CORS so the web app is able to send requests to the API. And this is what the express-session configuration looks like:
api.use(session({
secret: 'verysecretsecret',
resave: false,
saveUninitialized: false,
cookie: {
path: '/',
domain: 'localhost',
maxAge: 1000 * 60 * 24
}
}));
Preferably help me solve this problem without using something like Redis, I'd simply like to know if solving this problem is possible using just express-session and node.
Preferably help me solve this problem without using something like Redis
You want us to help you solve this problem preferably without using the right tool for the job.
Without Redis you will need to use some other database. Without "something like Redis" (i.e. without a database) you will need to implement some other way to handle something that is a book example use case for a database.
And if you're going to use a database then using a database like Redis or Memcached is most reasonable for the sort of things where you need fast access to the data on pretty much every request. If you use a slower database than that, your application's performance will suffer tremendously.
I'd simply like to know if solving this problem is possible using just express-session and node.
Yes. Especially when you use express-session with Redis, as is advised in the documentation of express-session module:
https://github.com/expressjs/session#session-store-implementation
If all of your instances work on the same machine then you may be able to use a database like SQLite that stores the data in the filesystem, but even when all of your instances are on the same box, my advice would be still to use Redis as it will be much simpler and more performant, and in the case when you need to scale out it will be very easy to do.
Also if all of your session data can fit in a cookie without problems, then you can use this module:
https://www.npmjs.com/package/cookie-session
that would store all of the session data in a cookie. (Thanks to Robert Klep for pointing it out in the comments.)

Node/Express with connect-redis, how handle session expiry

I have a Node/Express application that use redis as session store.
I have a question concerning the handling of the expiry of the session.
I'd like have an active session until the browser is closed, so I didn't set a session expiration time.
Doing that the session cookie works fine, but I have a doubt about Redis.
It seems that the couple Key/Value stored in Redis DB never expire.
How is the right way to handle this?
There is a way to configure redis to destroy a value stored with a certain idle time?
Or is better set a TTL when the connect-redis is invoked inside the application?
Actual configuration of the session inside the application:
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({port:6379, host: 'localhost'}),
secret: "my-secret-here",
resave: false,
saveUninitialized: true }));
Using Redis with express-session, you can use the touch() method from express-session to reset the TTL. So if you set a TTL when creating the session, do something like this on the routes where you don't want the session to expire:
api.get("/someRoute/", (req, res) => {
req.session.touch();
// Whatever else you need to do
res.sendStatus(200);
}
That will reset the TTL on Redis and prevent the session from expiring assuming the client is still hitting your API - I'm assuming that if the client doesn't interact with your API for long enough, that implies the browser is closed or otherwise finished using your app.
You can specify a ttl while creating the session store.
You can find more options in the readme.
app.use(session({
store: new RedisStore(options),
secret: 'keyboard cat',
ttl : 20 // ttl is in seconds. From the readme.
}));

Maintaining a reliable session in express

I am using Nodejs and express to create a web app. But i am finding some difficulty in maintaining session. i can use req.session.userid = userid , but it is not so reliable. if the server goes down for some time and it has to reboot, the session will be lost.. Is there any way to store the session more effectively?
You can either use a database as stated above, or use the in memory store, like redis. Redis is the preferred way to go when handling user session, since its several factors faster then reading from disk.
Additionally, you may want to look into Json Web Token, so you don't have to store sessions at all, rather just keep a reference to the user token in your database (or redis). This will allow you to easily authenticate on mobile. It can also help prevent csrf attacks if you store the token on a users localstorage (rather then cookie)
You can read about them here: https://scotch.io/tutorials/the-ins-and-outs-of-token-based-authentication, https://scotch.io/tutorials/the-anatomy-of-a-json-web-token, https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
I prefer using the npm module called "connect-mongodb-session". It uses mongodb to store all the sessions. Go to your project directory and install "connect-mongodb-session" using
sudo npm install connect-mongodb-session
And add this to your package.json as dependencies. and this is how you can use it..
Sample code...
var express = require('express');
var session = require('express-session');
var MongoDBStore = require('connect-mongodb-session')(session);
var app = express();
var store = new MongoDBStore({
uri: 'mongodb://localhost:27017/connect_mongodb_session_test',
collection: 'mySessions'
});
// Catch errors
store.on('error', function(error) {
assert.ifError(error);
assert.ok(false);
});
app.use(require('express-session')({
secret: 'This is a secret',
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
},
store: store
}));
server = app.listen(3000);
And you are good to go.. use req.session when ever you want, and your sesion will be stored save in mongodb.
for example..
app.post("/login",function(req,res){
//validate login
req.session.userid = userid;
})
even if the server has to reboot, your session will not be lost.

Working with Sessions in Express.js

I need help understanding the concept of sessions for a web application. I am running a Node.js server with Express 3.0.
My goals are to:
Create a session for each user that logs in
Store this session and use it for validating if the user is already logged in (prevent two devices using the same user at the same time) and to limit access to certain pages (by matching session ID to some other data)
I will be using MemoryStore to save the sessions (seems easiest). If the above goals make sense can you provide a thorough explanation of how to achieve them?
Express has nice examples in the github repo. One of them deals with authentication and shows how to attach the user to the req.session object. This is done inside the app.post('/login') route.
To limit access to certain pages add a simple middleware to those routes
function restrict(req, res, next) {
if (req.session.user) {
next();
} else {
req.session.error = 'Access denied!';
res.redirect('/login');
}
}
app.get('/restricted', restrict, function(req, res){
res.send('Wahoo! restricted area, click to logout');
});
As Brandon already mentioned you shouldn't use the MemoryStore in production. Redis is a good alternative. Use connect-redis to access the db. An example config looks like this
var RedisStore = require('connect-redis')(express);
// add this to your app.configure
app.use(express.session({
secret: "kqsdjfmlksdhfhzirzeoibrzecrbzuzefcuercazeafxzeokwdfzeijfxcerig",
store: new RedisStore({ host: 'localhost', port: 3000, client: redis })
}));
Use MemoryStore in express ONLY if you are not creating multiple instances (such as with the cluster module). If you are load balancing across machines, your load balancer will need to use sticky / persistent sessions.
If you meet those requirements, then all you need to do is upon login, once the credentials are validated, set a session variable to indicate logged in, for example:
req.session.loggedIn = true;
If you want to check if a user is logged in, simply check that variable.
if (req.session.loggedIn) {
// user is logged in.
}
else {
// user is not logged in.
}
You mentioned preventing a single user from having sessions more than one session at a time. To achieve that, you may need to store something in a database indicating that the user is logged in. I warn you, this can be dangerous because of stale sessions. For example, what if a user logs in, but never logs out? What if they close their browser window so the session is gone forever?
Express has no concept of an idle session expiration. I have implemented such a thing by storing all sessions in the database along with a last accessed timestamp and then periodically clean up the session data based on the time. But then you need to update your list of who is logged in as well.

Resources