How can I use the same Express session for multiple hostnames? - node.js

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.

Related

Why are my express sessions not persisting and why am I getting a new session (or multiple sessions) created for each request

I have a basic node.js express app using express-sessions.
Please can someone help with why the sessions are not persisting and why a new session is created for every request.
The app itself is quite large so i have added a reduced case of the important settings below.
const express = require('express');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
// initialise express
const app = express();
// initialise db session store
const store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions',
// can also set expire here for auto cleanup by mongodb
});
app.use(session({
secret: 'secret password',
resave: false,
saveUninitialized: false,
httpOnly: false,
secure: app.get('env') === 'production',
store,
}));
...routes
in a user login route the app sets the request user to the session and saves the session. it is expected that this req,session.user will persist between pages but it does not. on each page request i can see a new session (or sometimes multiple sessions, 1 for each file request) being created.
UPDATE
TL:DR;
- robots.txt causes issues if not dealt with, set DEBUG env to express-session to troubleshoot
After a lot of hair pulling I've found a solution and some useful troubleshooting tips.
when running your app, run it with debug set to express-session.
so for those of you that are quite new to this like myself, run your app with a command similar to this:
DEBUG=express-session node 'bin/www.js'
or
DEBUG=express-session node app.js
depending on how you have your app entry point setup.
Doing this will print session related log msgs so you can troubleshoot if the cookie is actually getting sent with each request or not. the error messages will look like something this:
express-session fetching 0wgmO1264PsVvqeLqaIIXd6T0ink0zts +34s
express-session session found +49ms
To troubleshoot the issue of multiple requests causing multiple sessions per page load, Add a middleware at the top of your app before any other middleware. this will allow us to see the request URL and troubleshoot which requests may be interfering with our sessions.
// see what requests are being sent and which ones contain cookies
app.use((req, res, next) => {
const { url } = req;
const isCookieSent = req.headers.cookie;
console.log({ url });
console.log({ isCookieSent });
next();
});
from doing this I found out that the culprit was robots.txt file, Apparently the only path that is ignored by default is favicon.ico.
Because this robots.txt path wasn't handled properly, nor was it sending a cookie, it was causing the duplicate requests and also causing the cookies not to persist.
to fix this you either need to handle or ignore this request prior to getting to the session middleware.
i did this using this middleware, once again fairly high up.
app.get('/robots.txt', (req, res) => {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
});
I am new to node.js so if there is anyone with more knowledge feel free to chip in with extra info or cleaner ways of solving this problem. Hopefully this saves some of you a lot of hassle!

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.

Express and Redis - If session exists for this user, don't allow access

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

Preserving session between node.js server reboots

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.

req.user after restart node

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

Resources