I'm developing a node.js app and I use nodemon to run it. My question is how I do to keep the session open when I make a change in the server, and nodemon reboot.
This answer assumes that you're using Express or Koa, two of the more popular node.js web frameworks.
In your application, you probably have a line that says the following.
Express 4
var app = require( 'express' )()
var session = require( 'express-session' )
app.use( session() ) // this is the session middleware
KoaJS
var app = require( 'koa' )()
var session = require( 'koa-session' )
app.use( session() ) // this is the session middleware
The guiding wisdom in web applications is to make the world-facing side of it as stateless as possible. This means that your node.js server holds no information between requests and can be destroyed and restarted anytime.
A session is a very stateful thing. Because of this, you need to store session data in something designed to keep the data uncorrupted in the long run. This is most commonly done through a database (more secure) or a browser cookie (less secure).
The express-session module holds session information in memory by default. This violates the stateless ideals and will lose session information on reboot.
The koa-session module uses cookies by default. This is stateless, but raises some security concerns in that it may be possible for users to modify their own session variables, so don't use this for billing or other sensitive operations.
In both of the above modules, you can override the defaults with your own session store.
Redis Session Store
I normally store session data in a database like redis.
Express
This can be easily wired into express with a module like connect-redis.
Quoting from the Readme:
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore(options),
secret: 'keyboard cat'
}));
KoaJS
Using redis to store session data is also possible in koa with the koa-redis module.
Paraphrasing from the Readme:
var app require('koa')();
var session = require('koa-generic-session');
var redisStore = require('koa-redis');
app.keys = ['keys', 'keykeys'];
app.use(session({
store: redisStore()
}));
app.use(function *() {
this.session.name = 'koa-redis';
});
No Databases Session Store
Express
Express can store session data in cookies with the cookie-session extension.
Koa
As mentioned before, the koa-session module stores sessions in cookies by default.
Related
It is unclear what are the correct configuration parameters to use are in the situation of using Redis Cloud and Heroku, and can't find a functioning example online.
Here is my current code:
const express = require('express')
const session = require('express-session')
const RedisStore = require('connect-redis')(session);
...
const server = express()
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({ extended: false }))
server.use(cookieParser())
server.use(session({
secret: token_secret,
// create new redis store.
store: new RedisStore({ url: 'redis://rediscloud:...#...redislabs.com:11111'}),
resave: true,
saveUninitialized: true
}));
Should I have resave and saveUnitialized set to true or false in the case of Redis Cloud and Heroku as the session store (using express-session)?
Additionally, does the cookieParser affect the session and need to be there? Or is that separate and only to parse the cookie that is coming from the client, and unrelated to the server-side session storage with Redis? Also, should the cookie parser have a secret passed into the function?
And finally, should bodyParser come before or after the server.use(session), and should urlencoded extended be set to true or false?
Let's go by parts, as Jack the Ripper said...
It is unclear what are the correct configuration parameters to use are
in the situation of using Redis Cloud and Heroku, and can't find a
functioning example online.
RedisCloud on Heroku (Node Express Example) # GitHub
Should I have resave and saveUnitialized set to true or false in the
case of Redis Cloud and Heroku as the session store (using
express-session)?
app.use(expressSession({resave: false, saveUninitialized: false})) reduces the number of times the session store will be accessed. This benefits hardware resources and performance (Normally is better to set them to false).
Additionally, does the cookieParser affect the session and need to be
there?
Not anymore : express-session middleware, used to require cookie-parser, (but the current version of express-session reads/writes cookies directly).
And finally, should bodyParser come before or after the
server.use(session)
The body-parser middleware parses the bodies of incoming HTTP requests. Populating the req.body property which is then available in your routes and middlewares. So the order doesn't influence the behaviour.
and should urlencoded extended be set to true or
false?
The parsers you will need depend on the request types your server has to deal with.
The difference regarding extended: false and extended: true is already explained in this answer.
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.
So I have a Node.js + Express app I can access with a couple of hostnames: example1.com and example2.com (they are not subdomain! I know how to do it if they are subdomains).
I use Express Session with MongoDB Store and passport to manage authentication.
I'd like to use the same session with both the domains but I can't
figure out how to do it.
Each time I access the two domains it creates two different sessions and I cannot understand where I could check if the session is already up and use that for both the domains.
Basically this is my current express init file (I removed a lot of things, just to focus on sessions):
'use strict';
/**
* Module dependencies.
*/
var express = require('express'),
bodyParser = require('body-parser'),
session = require('express-session'),
cookieParser = require('cookie-parser'),
passport = require('passport'),
MongoDBStore = require('connect-mongodb-session')(session),
config = require('./config');
module.exports = function(db) {
// Initialize express app
var app = express();
// CookieParser should be above session
app.use(cookieParser());
var store = new MongoDBStore(
{
uri: config.db,
collection: config.sessionCollection
});
// Express MongoDB session storage
app.use(session({
saveUninitialized: true,
resave: true,
secret: config.sessionSecret,
store: store,
}));
app.use(passport.initialize());
app.use(passport.session());
return app;
};
Any hint?
IMO the only way to do that is, if you are able to figure out on server side that the request is coming from the same browser/machine. Because, browser would not the share the session Id cookie(as the requests are for two different domains) and on server you would think it of as a new request for session, and you would always grant a new sessionId to the client.
If you really want to have single session store, I recommend writing your own session store with the below idea:
Firstly, don't use cookies for session, and use browser's localstorage and sign the sessionId from the server that needs to be stored on the client, which protects it from being tampered. This session Id would be included in the requests using your AJAX and client-side JS in each requests acting as a sessionId cookie.
Secondly, Open an iframe of the other domains when ever a domain is opened, let say if example1.com is opened you should open an iframe of example2.com and vice-versa. In your iframes write code to send signed localstorage information back to parent frame using window.postMessage() Since, you are owner of all the domains you should be able to allow window.postMessage() communication across your domains.
Since, you would receive the same session Id in each request across all domains you can detect unique session across domains and devise session store on server side to store only one entry across domains.
I am using Redis and Express like this:
// app.js
var redis = require('redis'),
redisClient = redis.createClient(),
session = require('express-session'),
RedisStore = require('connect-redis')(session);
session = session({
store: new RedisStore({client: redisClient}),
secret: 'secretKey',
resave: true,
saveUninitialized: true
})
app.use(session);
In my index route file, I set a user session like this:
req.session.user = user;
When this user comes back (or opens another tab), how could I detect how many sessions he is currently having and block him access if he already has one active session?
express-session working via cookies. So you had cookie named session with some sessionId. All tabs inside browsed share cookies and you can't rely on cookies if you want to strict working via only one browser tab.
You need to create your own Sessions for each opened browser tab. Something like generating tabId at client side and saving {userId, tabId} to Redis
Or you can use websockets (http://socket.io/) for that, a little overhead, but doing exactly what you want
I've attached passportjs to authenticate user on my site. I use localStrategy and all works fine. But I have some problem (may be it's feature of passportjs) - after restart node, req.user is undefined and users should login again.
What should I do to initialize req.user on start server??
LocalStrategy stores user credentials in memory after login. This means that when you restart node, the credentials will inevitably be lost.
However, passport does use the session cookie functionality in express, so you can persist cookies in the standard express way.
This is quite easy using the RedisStore module, to which you pass the express object:
var express = require('express');
var RedisStore = require('connect-redis')(express);
Before you set up passport, enable session persistence to redis in express:
app.use(express.cookieParser());
app.use(express.session({
secret: "a-secret-token",
store : new RedisStore({
host : 'redis-hostname',
port : 'redis-port',
user : 'redis-username',
pass : 'redis-password'
}),
cookie : {
maxAge : 604800 // one week
}
}));
app.use(passport.initialize());
app.use(passport.session());
You'll need to install redis on your machine, and change the details above to match the redis instance you want to connect to.
You need to store user login info in Redis for e.g. because after restart, all your variables that you set will be undefined