Node.js - Session doesn't persist through res.redirect() - node.js

I'm (almost) successfully using Node.js with Express and Redis to handle sessions.
The problem I'm having is that the session is not kept when I use res.redirect().
Here is how I can see it :
req.session.username = username.toString();
console.log(req.session);
res.redirect('/home');
The console.log() prints :
{ lastAccess: 1322579131762,
cookie:
{ path: '/',
httpOnly: true,
_expires: Tue, 29 Nov 2011 15:06:31 GMT,
originalMaxAge: 60000 },
username: 'admin' }
Now, here is the following code :
app.get('/home', [app.requireLogin], function(req, res, next) {
// Not showing the rest as it's not even getting there
// Instead, here is what's interesting
app.requireLogin = function(req, res, next) {
console.log(req.session);
This console.log() prints out this :
{ lastAccess: 1322579131775,
cookie:
{ path: '/',
httpOnly: true,
_expires: Tue, 29 Nov 2011 15:06:31 GMT,
originalMaxAge: 60000 } }
Clearly, the 'username' object has disappeared. The session has not kept it, and just rebuilt a new one.
How can I solve this? Don't hesitate if you need any information.
Here is the code where I set the session management :
app.configure(function() {
// Defines the view folder and engine used.
this.set('views', path.join(__dirname, 'views'));
this.set('view engine', 'ejs');
// Allow parsing form data
this.use(express.bodyParser());
// Allow parsing cookies from request headers
this.use(express.cookieParser());
// Session management
this.use(express.session({
// Private crypting key
secret: 'keyboard cat',
store: new RedisStore,
cookie: {
maxAge: 60000
}
}));
this.use(app.router);
});
Here is the whole project (I mean, parts of it), on gist : https://gist.github.com/c8ed0f2cc858942c4c3b (ignore the properties of the rendered views)

Alright, I found the solution. The problem is that the time in maxAge was added to the current date. So, in the browser side, the cookie was set to expire at the GMT time shown.
The problem was the following : I use a virtual machine to test node.js, and, you know... sometimes, you suspend your machine.
Well, what happened is that the machine's time was two days late. So, whenever the cookie was set on the server side, the client side thought the cookie was already expired, since my host machine was not two days late.
Another stupid outcome.

Did you try with different browsers ? Are you keeping the same session id between page redirects ?
You could add req.session.cookie.expires = false; before redirecting...

Your code looks pretty solid, but is there a reason you're using client.end()? It forcibly closes the redis connection and is not clean. I don't think you need it at all:
https://github.com/mranney/node_redis/issues/74
I am not sure about the underlying architecture for connect-redis, but I'm wondering if calling client.end is what's resetting your sessions. What happens if you take those out?

I was having a similar problem in that I was setting something on the session that was not persisting outside the app.get() it was set in.
My problem turned out to be that I was not doing a res.redirect() at the end of my app.get(). Looks like I was setting something on a request object and then allowing it to get garbage collected.
I added a res.redirect( '/nextmethod' ) and the data persists just fine.

Surely you need to save that session in some way, this might work.
req.session.regenerate(function(){
req.session.username = username.toString();
res.redirect('/home');
});

Related

cookie confusion with session storage

I am using the following Node.js code to create a 'session store' to keep user information. These text files are kept in a folder called "session-store":
var session = require('express-session');
var FileStore = require('session-file-store')(session);
app.use(session({
store: new FileStore({path: './session-store'}),
// using FileStore with express-session
// as the store method, replacing the default memory store
secret: 'secret_key',
name: 'myadmin',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 60000, httpOnly: true, secure: false }
}));
app.get('/', function (req, res) {
if (req.session.views) {
req.session.views++;
} else {
req.session.views = 1;
}
});
My text file which contains the session information seems to generate properly. However I have some confusion regarding its proper usage. In my cookie (.txt) file, it reports the "views" correctly, such as ""views":1". However if I define another variable, such as "req.session.broadcast = String(req.body.usr);" I do not see this information in my text file...why would this not be inclusive? Is this because the .txt file containing the cookie session storage information is generated only on the initial 'route' (i.e. the "app.get('/', function (req, res) {...do stuff}") and no other place in the Node.js code? (NOTE: I have "req.session.broadcast" set in a different route other than the 'homepage' route...which I guess is when the cookie file gets generated...possibly?).
I am simply attempting to properly manage users...therefore I want to be able to have certain session variables properly set and tracked for each unique user browser.
Are the "req.session.user" and/or req.session variable(s) 'hard coded' variables? I believe they get set when the browser is directed at the webpage, however how can they be used to terminate a session? Do they automatically become null if the user closes their browser (using the "x" in the right top corner) OR if the user directs their browser to another webpage...?
Any help with these questions and any other advice would be GREATLY appreciated.

Node.js - Trying to avoid globals...how do I keep track of a user object?

Ok, so here is the scenario. I have the user log into my app with facebook. When this happens Passport saves it to the Session (req.user). This works all well and good when I am working in a page that has access to the request object, but I find myself in the situation where I don't have access to request, but I need to check the user object.
Case in point. I am using socket.io and when I am working with the sockets and the methods surrounding them, I don't have access to the request object and therefore I can't get user info.
I keep hearing that I need to stay away from globals whenever possible, but I can't see a way around it.
Thoughts?
Below is an example of me working with sockets. This module is called from my server.js file.
function loadSockets(io)
{
var articleCommand = require('./middleware/ArticleCommand');
io.sockets.on('connection', function (socket) {
socket.on('getArticles', function(){
if (NEED USER INFO HERE !== null){
articleCommand.getArticlesByUser(NEED USER INFO HERE, function(error, articles){
io.sockets.emit('articlesRetrieved', error, articles);
});
}
});
});
}
exports.loadSockets = loadSockets;
Update
Ok, so based on the commenters advice I have installed this: https://github.com/aviddiviner/Socket.IO-sessions and applied it...but my session is always null in my sockets.
Here is what I have.
var socket = sio.enable({
socket: io.listen(server, {'log level': 1, 'heartbeat': false}),
store: mystore, // this is my Redis store
parser: express.cookieParser()
});
later on...when processing my sockets
socket.sockets.on('connection', function (socket, session) { ...
The session is always null...even though my passport has loaded up my session correctly.
Thoughts?
Ok, so for posterity's sake, here is what I did to fix this issue.
You can see from my update above that I tried to use the SocketIO-sessions module, but that didn't work.
After more research I found that I needed to make sure I got the passport session since that is what is doing my authentication.
I found this little gem: https://github.com/jfromaniello/passport.socketio
This worked like a charm. The only thing that took some figuring out is getting the key setup correctly (they assume you know how to do that in the example).
sio.set("authorization", passportSocketIo.authorize({
key: 'MYKEY',
secret: 'SECRET',
store: mySessionStore
}));
Out of the box, your key is not set. To do that simply set it up with your app like so:
app.use(express.session({
secret: "SECRET",
store: mySessionStore,
key: 'MYKEY',
cookie: { secure: false, maxAge:86400000 }
}));
Hope this helps someone.
David

Not cookie based session management in node.js

I am looking for a non-cookie based session management in node.js, something like pass a parameter in the URL like &session_id=. It will know that a session has expired when a request comes with an session_id. I've looked at connect library, but it looks that it is cookie based only.
Warning
Passing the session id as a GET parameter is considered bad practice. Why? It is dangerous because people don't usually care about session id and they will probably publish/share links with their session ids inside.
It's also a problem because when a user clicks an external link on your web, and goes to another site, that new site will be able to see the session_id in the referrer link.
So I don't think it is a good idea. Cookies are more secure.
Have a look at: Session Hijacking
For every request you receive, you will get all of the client cookies accordingly.
You can also set client cookies in the response HTTP headers using "Set-Cookie."
Using a GET parameter is unsafe. Any user could accidently share their session ID, but if you want 100% security, I'd share session IDs via cookies, and I would use HTTPS to prevent snoopers from stealing cookies.
You can use localstorage or sessionStorage..
almost same as cookie
not a cookie
better than a cookie!
More info: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
It's very -very- easy to use... in Js for example:
<script>
// check if Storage is avaible
if(typeof(Storage)!=="undefined") {
// Save data to local storage (no exiparion date)
localStorage.setItem("name_always", "bxx");
// Save data to the current session (removes when the tab is closed)
sessionStorage.setItem("name_now", "bxx");
} else {
// No Storage support...
}
// Access to stored data
alert( "For only now, my name is: " + sessionStorage.getItem("name_now"));
alert( "Every day, my name is: " + localStorage.getItem("name_always"));
</script>
Tags: javascript html5 local-storage session-storage
You can use sessions with a store in node.js. For example, you have express application and want to use session like system in your webapp. You can use connect-mongo module for this. This will let you store your session in db. In your app.js
var express = require('express'),
, mongoStore = require('connect-mongo')(express);
var app = express();
app.configure('all', function () {
app.use(express.session({
secret: "terces",
cookie: { maxAge: 24 * 60 * 60 * 1000 },
store: new mongoStore({
url: your_db_url
})
}));
app.use(function(req, res, next) {
res.locals.session = req.session;
next();
});
});
With basic code above, you have session in express that you can use it in your controllers and views directly. In your controller;
app.post('/blog/create/?', function(req, res, next) {
if (!req.session.user) {
next("You need to login in order to create blog!");
}
});
In your view, you can use session.user in order to generate profile menu for example.

ExpressJS session expiring despite activity

Bringing this question to SO since the express group didn't have an answer.
I'm setting the session maxAge = 900000 and I see that the the expires property on the session cookie is set correctly.
However, on subsequent requests the timeout is not being extended. It is never extended and the cookie eventually expires.
The session middleware docs say that Session#touch() isn't necessary because the session middleware will do it for me. I actually tried calling req.session.touch() manually and that did nothing, I also tried setting the maxAge on the req.session.cookie as well and that did nothing :-(
Am I missing a setting somewhere to automatically extend active sessions? Short of recreating the cookie manually on each request is there any other way to extend a session timeout after end-user activity?
EDIT: I experienced this problem in express v3. I'm not 100% sure but I think this note from the express changelog may have been the culprit:
changed session() to only set-cookie on modification (hashed session json)
Rolling sessions now exist in express sessions. Setting the rolling attribute to true in the options, it will recalculate the expiry value by setting the maxAge offset, applied to the current time.
https://github.com/expressjs/session/issues/3
https://github.com/expressjs/session/issues/33
https://github.com/expressjs/session (search for rolling)
For example, note the rolling:
app.use(session({
secret: 'a secret',
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: 10 * 60 * 1000
},
rolling: true
}));
Here is the solution in case anyone else has the same issue:
function (req, res, next) {
if ('HEAD' == req.method || 'OPTIONS' == req.method) return next();
// break session hash / force express to spit out a new cookie once per second at most
req.session._garbage = Date();
req.session.touch();
next();
}

Shared Sessions between Node Apps?

I currently have two separate node apps running on two different ports but share the same backend data store. I need to share users sessions between the two apps so that when a user logs into through one app, their session is available and they appear to logged into the other app. In this case, its' a public facing website and an administrative backend.
Our setup is the following:
node with express
passport is being used to handle auth with Local Strategy
we're using connect-redis to allow us to share sessions via redis.
our domains look like this: www.mydomain.com and adm.mydomain.com
The config for for session stuff (and redis) is the same for both apps:
session: {
options: {
secret: "my secret",
cookie: {
domain: "mydomain.com",
maxAge:1000*60*60*24
}
},
redis: {
host: 'my host',
maxAge: 86400000,
secret: "my secret"
}
}
The config for session stuff in app.js looks like this:
if ( app.settings.env === "production" ) {
session.options.store = new RedisStore(session.redis);
}
app.use(express.session(session.options));
app.use(passport.initialize());
app.use(passport.session({ secret: 'a different secret' }));
What I expect it to do: Allow us to see the same session id in the cookie between the two apps.
So my question is: How do I set up express, redis and passport so that you can have sessions shared across different subdomains?
Maybe a bit outdated, but at this time, Express-session can recognise domain option for cookie. According to source:
function session(options){
var options = options || {}
// name - previously "options.key"
, name = options.name || options.key || 'connect.sid'
, store = options.store || new MemoryStore
, cookie = options.cookie || {}
...
And this is for setting cookie:
var Cookie = module.exports = function Cookie(options) {
this.path = '/';
this.maxAge = null;
this.httpOnly = true;
if (options) merge(this, options);
...
So, something like this will work for current 1.10.1 master:
secret: "my secret",
cookie: {
domain: "mydomain.com",
Express-session does not seem to recognize the "domain" option for cookies hence your problem. The cookie storing the session id is automatically tied to the domain for each app and so it cannot be shared.
One option is to write your own single-sign-on module to share sessions across webapps. It would probably live in an app.use() declaration fairly early in the execution order and would simply create a separate cookie (which would be cross-domain), create a separate SSO session id, and store the SSO id in this new cookie. Afterwards, you simply cross-populate req.session and req.sso-session as needed.

Resources