NodeJS redis connect session ID is regenerated when it should stay persisted - node.js

I tried to use the standard session persistance with Redis in NodeJS:
var express = require('express');
var RedisStore = require('connect-redis')(express);
var app = module.exports = express.createServer();
// Configuration
app.configure(function(){
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({
secret: "keyboard cat",
store: new RedisStore,
key: 'sid'
}));
});
app.get('/', function (req, res) {
if (req.session.isValid) {
console.log("There is an existing session.");
}
else {
req.session.isValid = true;
console.log("New session.");
console.log('Old session ID: ' + req.header('Cookie'));
console.log('New session ID: ' + req.session.id);
}
res.render('index', {'title': s});
});
app.listen(4000);
In theory I should see the line "New session." once and all subsequent calls of the website site should lead to "There is an existing session".
Unfortunately on every call a new session ID is regenerated. The cookie in the browser is working fine, the content is correctly transmitted and I can see it in
req.header('Cookie')
This is what the console log looks like:
[app.js] New session.
[app.js] Old session ID: undefined
[app.js] New session ID: nuoHKZj2j0AoRkvqT4xE5h6W.zF+DNv2rzr3kpeO2IyD7sa4xdamFQMugjfQvY6OYymE
[app.js] New session.
[app.js] Old session ID: sid=neLUc5PXxPoj1yFqukerv49x.BHzYKiuAfFSNHKd4fCAkv8wNwZO%2FxykJPN5R5tjAlQc
[app.js] New session ID: FvuzjnXvchCkmVqsq5mrodL2.5YlT3InfTbvOwEUc0dNpPLT77tcdJpNuhbFGVYkLneQ
[app.js] New session.
[app.js] Old session ID: sid=pFbyVdlNZXtF5vZ35CW9sfmq.nJ1RBjJu59iUJJjmZv9TCYiYLcvycme%2BJh8sQC6%2FzEE
[app.js] New session ID: KdPhwqwwgmOnPZEuVapy7EJe.I0TGT9HSSQQSporwCNsxl11rXDxR/ysjTeZb0lD5uwI
At the same time I get the following output when running the "MONITOR" command in redis-cli:
sess:nuoHKZj2j0AoRkvqT4xE5h6W.zF+DNv2rzr3kpeO2IyD7sa4xdamFQMugjfQvY6OYymE
*2
$3
get
$73
sess:Q6Z06GL4hdRytKA2MToCIgVw.JWxImSB/m20Urn+IYMQqnNqfQp4ygAESiyBLORn3Iuo
*2
$3
get
$73
sess:neLUc5PXxPoj1yFqukerv49x.BHzYKiuAfFSNHKd4fCAkv8wNwZO/xykJPN5R5tjAlQc
*2
$3
get
$73
sess:FvuzjnXvchCkmVqsq5mrodL2.5YlT3InfTbvOwEUc0dNpPLT77tcdJpNuhbFGVYkLneQ
*2
$3
get
$73
sess:zvCWdwzowgAfl6jH8m0D31vL.b5tK5VZUJtPHrdvH09A/hjhjoOg6bT0CmAcWWRf99SI
*2
$3
get
$73
sess:pFbyVdlNZXtF5vZ35CW9sfmq.nJ1RBjJu59iUJJjmZv9TCYiYLcvycme+Jh8sQC6/zEE
*2
$3
get
$73
sess:KdPhwqwwgmOnPZEuVapy7EJe.I0TGT9HSSQQSporwCNsxl11rXDxR/ysjTeZb0lD5uwI
*2
$3
get
$73
sess:r793eaJyOnaq2RNyw1Hmpuwv.xnonbOlaWEAlpz+LDg0SHcUeAa0sbjyw0oIcwFmlX0w
When I use MemoryStore insteand of RedisStore, everything works like expected.
Any ideas?

I had similar issue. My session was regenerated upon every request. I solved it by setting proper date on virtual machine where development takes place.

Try this..
let redisStore,redisClient;
const redis = require('redis');
redisStore = require('connect-redis')(session);
redisClient = redis.createClient();
redisClient.on('error', (err) => {
console.log('Error: ', err);
});
app.use(session({
store : new redisStore({ host: 'localhost', port: 'port', client: redisClient }),
name : 'session_name',
resave : false,
saveUninitialized: false,
rolling : true,
}));
Note: redis can't support in window/localhost.

I had the same issue when sessions had been regenerated on every request. The reason was that the 'static' middleware was placed below 'session' one. So, be sure to place middlewares in proper order, i.e:
...
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.session({ store: new RedisStore({ prefix: 'sid_' }) }));
app.use(app.router)
app.get ...
'express.session' middleware should be under 'static'.

Related

Express session won't persist, even with MongoStore using connect-mongo

When I restart my server, my session ends and I am logged out. This does not happen on a regular page refresh. I am using connect-mongo to remedy this:
var session = require('express-session');
const MongoStore = require('connect-mongo')(session);
Here is the code I am using to store my session, reusing an existing Mongo connection called thisDb:
app.use(session({
secret: secretHash,
saveUninitialized: true,
resave: true,
secure: false,
store: new MongoStore({ db: thisDb })
}));
During a successful log in:
var day = 60000*60*24;
req.session.expires = new Date(Date.now() + (30*day));
req.session.cookie.maxAge = (30*day);
In my Mongo shell, I can verify that a new session is created when I log in:
db.sessions.find()
{"cookie":{"originalMaxAge":2592000000,"expires":"2017-11-17T20:36:12.777Z","httpOnly":true,"path":"/"},"user":{"newNotifications":false,"username":"max","admin":"true","moderator":"true"},"expires":"2017-11-17T20:36:10.556Z"}
Well, almost 3 years later i was having this issue. Don't know if OP was using Passport but i resolved this issue by moving this functions from inside the passport.use function to outside:
passport.serializeUser(function(user, done){
done(null,user.id);
});
passport.deserializeUser((id,done) => {
User.findById(id, (err,user) => {
done(null,user);
});
});

Error: req.flash() requires sessions

I'm new to node and I'm pretty sure I've set up the middle ware and express to use flash messaging however I still get the error:
Error: req.flash() requires sessions
Setup
//express.js
var flash = require('connect-flash')
module.exports = function (app, config, passport) {
app.use(flash());
};
//route js
exports.loginGet = function (req, res) {
res.render('users/login', {
title: 'Login',
message: req.flash('error') //error in question
});
};
What else can I do to make sure I have everything set up correctly and get it working?
From the readme (emphasis mine):
Flash messages are stored in the session. First, setup sessions as usual by enabling cookieParser and session middleware. Then, use flash middleware provided by connect-flash.
Using express-sessions with express 4, cookieParser is no longer required.
var session = require('express-session');
//...
app.use(session({ cookie: { maxAge: 60000 },
secret: 'woot',
resave: false,
saveUninitialized: false}));
In my case the issue was that Redis was not listening. I found that out by enabling the logErrors property:
new RedisStore({
host: 'localhost',
port: '6379',
logErrors: true,
});
Which resulted in messages like these:
Warning: connect-redis reported a client error: Error: Redis connection to localhost:6379 failed - connect ECONNREFUSED 127.0.0.1:6379
Please check mongodb connections. there may be an mongo error like "mongoError: Topology was destroyed".
To fix this issue, check here
i was having these issues and I solve them by respecting the cascading
app.use(passport.initialize());
app.use(passport.session());
//SESSION FLASH
app.use(flash());

session is undefined using sesssion.socket.io-express4

I'm working on a NodeJs with the following modules:
express 4.9.8
express-session 1.9.3
socket.io 1.2.1
session.socket.io-express4 0.0.3
I'm trying to get a session from session.socket.io-express4 in app.js using the following code:
first the init of the all thing in app.js:
var server = app.listen(3000, function() {
debug('Express server listening on port ' + server.address().port);
});
var io = require('socket.io')(server);
var sessionstore = require('sessionstore');
var sessionStore = sessionstore.createSessionStore();
var cookieParserVar = cookieParser('bingo');
app.use(session({
secret: 'bingo',
cookie: {httpOnly: true, secure: true, maxAge: new Date(Date.now() + 3600000)},
store: sessionStore,
resave: true,
saveUninitialized: true
}));
var SessionSockets = require('session.socket.io-express4');
sessionSockets = new SessionSockets(io, sessionStore, cookieParserVar);
require('./models/socket.js')(sessionSockets);
my socket.js:
function handleSocket(sessionSockets) {
sessionSockets.on('connection', function (err, socket, session) {
console.log("session : " + session);
});
}
module.exports = handleSocket;
the problem is that session is undefined. any ideas ?
update
so i tried using socket.io-sessions instead.
this is my code:
var sessionstore = require('sessionstore');
var sessionStore = sessionstore.createSessionStore();
var cookieParserVar = cookieParser();
app.use(session({
secret: 'bingo',
key: 'express.sid',
store: sessionStore,
resave: true,
saveUninitialized: true
}));
io.set("authorization", socketIoSessions({
key: 'express.sid', //the cookie where express (or connect) stores its session id.
secret: 'bingo', //the session secret to parse the cookie
store: sessionStore //the session store that express uses
}));
require('./models/socket.js')(io);
and my models/socket.js file includes:
function handleSocket(sock) {
sock.on('connection',function(socket)
{
socket.handshake.getSession(function (err, session) {
console.log("HERHERHEHREHREHRHEHERHERHRHERHERHEHREHREHREHR");
console.log(session);
console.log(err);
});
require('../commands/echo.js')(sock);
});
}
module.exports = handleSocket;
this is the error that I get:
/mnt/storage/home/ufk/work-projects/bingo/server/bingo-server/models/socket.js:8
socket.handshake.getSession(function (err, session) {
^
TypeError: Object #<Object> has no method 'getSession'
at Namespace.<anonymous> (/mnt/storage/home/ufk/work-projects/bingo/server/bingo-server/models/socket.js:8:34)
at Namespace.emit (events.js:95:17)
at Namespace.emit (/mnt/storage/home/ufk/work-projects/bingo/server/bingo-server/node_modules/socket.io/lib/namespace.js:205:10)
at /mnt/storage/home/ufk/work-projects/bingo/server/bingo-server/node_modules/socket.io/lib/namespace.js:172:14
at process._tickCallback (node.js:419:13)
what am i missing ?
last update
socket.io-session works as expected.
the only thing that i needed to change is the authorization callback in order for it to be compatible with socket.io-1.0 i did the following:
io.use(function (socket, next) {
var handshakeData = socket.handshake;
ioSession(cookieParser(config.app.session_key), sessionStore)(handshakeData, next);
}
);
update
The explanation of the issue from the original answer is still all relevant, but the fix is much simpler (and more secure). Just use socket.io-session instead. The session.socket.io-express4 module is only necessary if you're using the connect version of cookieParser which doesn't have the same signature handling as the newer cookie-parser does.
original answer
This is due to a bug in sesssion.socket.io-express4 when using both a cookie-parser secret and an express-session secret. What's happening is sesssion.socket.io-express4 is trying to strip the signing off of the connect.sid cookie. You can see that here:
if(handshake.cookies && handshake.cookies[key]) handshake.cookies[key] = (handshake.cookies[key].match(/\:(.*)\./) || []).pop();
The signed cookie looks like this s:<value>.<signature>, and the regex selects anything between : and . which would be the value of the signed cookie. The problem is, cookie-parser will remove the signature itself if you pass in a secret. That means that session.socket.io-express4 is expecting s:<value>.<signature> but is receiving <value> instead so the regex returns undefined. To work around this you could omit the secret from cookie-parser.
I have submitted a pull request to session.socket.io-express4 with a fix for this bug so you have to use a cookie-parser with the same secret as the express-session.
tl;dr
Don't supply a secret to the cookie-parser. The following should work as you expect:
var server = app.listen(3000, function() {
debug('Express server listening on port ' + server.address().port);
});
var io = require('socket.io')(server);
var sessionstore = require('sessionstore');
var sessionStore = sessionstore.createSessionStore();
var cookieParserVar = cookieParser();
app.use(session({
secret: 'bingo',
store: sessionStore,
resave: true,
saveUninitialized: true
}));
var SessionSockets = require('session.socket.io-express4');
sessionSockets = new SessionSockets(io, sessionStore, cookieParserVar);
require('./models/socket.js')(sessionSockets);

How to save and retrieve session from Redis

I am trying to integrate Redis sessions into my authentication system written in Node.js.
I have been able to successfully set up Redis server, connect-redis and Express server.
Here is my setup (just the important bit):
var express = require("express");
var RedisStore = require("connect-redis")(express);
var redis = require("redis").createClient();
app.use(express.cookieParser());
app.use(express.session({
secret: "thisismysecretkey",
store: new RedisStore({ host: 'localhost', port: 6379, client: redis })
}));
Now... How do I actually create, read and destroy the session? I am aware that that is probably extremely simple. I have read tons of articles on how to setup connect-redis and many questions here on SO, but I swear each one stops on just the configuration and does not explain how to actually use it...
That should be all there is to it. You access the session in your route handlers via req.session. The sessions are created, saved, and destroyed automatically.
If you need to manually create a new session for a user, call req.session.regenerate().
If you need to save it manually, you can call req.session.save().
If you need to destroy it manually, you can call req.session.destroy().
See the Connect documentation for the full list of methods and properties.
Consider this code.
var express = require('express');
var redis = require("redis");
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var bodyParser = require('body-parser');
var client = redis.createClient();
var app = express();
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.use(session({
secret: 'ssshhhhh',
// create new redis store.
store: new redisStore({ host: 'localhost', port: 6379, client: client,ttl : 260}),
saveUninitialized: false,
resave: false
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/',function(req,res){
// create new session object.
if(req.session.key) {
// if email key is sent redirect.
res.redirect('/admin');
} else {
// else go to home page.
res.render('index.html');
}
});
app.post('/login',function(req,res){
// when user login set the key to redis.
req.session.key=req.body.email;
res.end('done');
});
app.get('/logout',function(req,res){
req.session.destroy(function(err){
if(err){
console.log(err);
} else {
res.redirect('/');
}
});
});
app.listen(3000,function(){
console.log("App Started on PORT 3000");
});
So you need to install connect-redis and pass your express-session instance to it.
Then in middleware initialize redisStore with server details like this.
app.use(session({
secret: 'ssshhhhh',
// create new redis store.
store: new redisStore({ host: 'localhost', port: 6379, client: client,ttl : 260}),
saveUninitialized: false,
resave: false
}));
I put ttl to 260, you can increase. After TTL reaches its limits, it will automatically delete the redis key.
In routers you can use req.session variable to SET, EDIT or DESTROY the session.
One more thing...
If you want custom cookie i.e not as same as in your Redis store you can use cookie-parser to set cookie secrets.
Hope it helps.
link : https://codeforgeek.com/2015/07/using-redis-to-handle-session-in-node-js/
You can also use the Redis monitor tool to see all the action in real time! When you refresh your app you will see the data appear in the console window.
redis-cli monitor
Sample Output for Sessions using tj/connect-redis
1538704759.924701 [0 unix:/tmp/redis.sock] "expire" "sess:F9x-YgbgXu1g7RG8tFlkwY3RV0JzHgCh" "3600"
1538704759.131285 [0 unix:/tmp/redis.sock] "get" "sess:F9x-YgbgXu1g7RG8tFlkwY3RV0JzHgCh"
1538704787.179318 [0 unix:/tmp/redis.sock] "set" "sess:Hl3LPbOBdKO44SG4zQHFn2gfdiWTwzWW" "{\"cookie\":{\"originalMaxAge\":3600000,\"expires\":\"2018-10-05T02:59:47.178Z\",\"secure\":true,\"httpOnly\":true,\"domain\":\".indospace.io\",\"path\":\"/\"},\"path\":\"/\",\"userAgent\":{\"family\":\"NewRelicPingerBot\",\"major\":\"1\",\"minor\":\"0\",\"patch\":\"0\",\"device\":{\"family\":\"Other\",\"major\":\"0\",\"minor\":\"0\",\"patch\":\"0\"},\"os\":{\"family\":\"Other\",\"major\":\"0\",\"minor\":\"0\",\"patch\":\"0\"}},\"ip\":\"184.73.237.85\",\"page_not_found_count\":0,\"city\":\"Ashburn\",\"state\":\"VA\",\"city_state\":\"Ashburn, VA\",\"zip\":\"20149\",\"latitude\":39.0481,\"longitude\":-77.4728,\"country\":\"US\"}" "EX" "3599"
1538704787.179318 [0 unix:/tmp/redis.sock] "set" "sess:Hl3LPbOBdKO44SG4zQHFn2gfdiWTwzWW" "{\"cookie\":{\"originalMaxAge\":3600000,\"expires\":\"2018-10-05T02:59:47.178Z\",\"secure\":true,\"httpOnly\":true,\"domain\":\".indospace.io\",\"path\":\"/\"},\"path\":\"/\",\"userAgent\":{\"family\":\"NewRelicPingerBot\",\"major\":\"1\",\"minor\":\"0\",\"patch\":\"0\",\"device\":{\"family\":\"Other\",\"major\":\"0\",\"minor\":\"0\",\"patch\":\"0\"},\"os\":{\"family\":\"Other\",\"major\":\"0\",\"minor\":\"0\",\"patch\":\"0\"}},\"ip\":\"184.73.237.85\",\"page_not_found_count\":0,\"city\":\"Ashburn\",\"state\":\"VA\",\"city_state\":\"Ashburn, VA\",\"zip\":\"20149\",\"latitude\":39.0481,\"longitude\":-77.4728,\"country\":\"US\"}" "EX" "3599"

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

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');
})
}));
There's an opensource called connect-mongo 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
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());
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.
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.
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)
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.
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)

Resources