Reputation: 763
How can I save session data in express.js and have access to it inside socket.io events?
I'm developing a webapp using express.js v4, socket.io v1, and the basic express-session middleware.
I spent several hours trying to figure this out, but all of the current answers on Stack Overflow only apply to express v3 and socket.io v0.9. Unfortunately I can't use express.io since it's simply a wrapper using those old versions as well.
My current solution is a total hack:
app.get('/auth', function(req, res) {
if(verified(req.query)) {
authed[req.sessionID] = true;
}
});
io.sockets.on('connection', function(websocket) {
var cookies = cookie.parse(websocket.handshake.headers.cookie);
var decrypted = cookieParser.signedCookies(cookies, random);
var sessionID = decrypted['connect.sid'];
websocket.on('input', function(input) {
if(authed[sessionID]) {
// Do stuff...
}
});
});
I'm saving my "session data" in an object as a key/value store based on session IDs. This works fine for single process applications, but it's really not a good solution. It doesn't actually give me access anything saved in req.session and all my saved data will be lost if the process ends.
On top of all that, I need to load the 3rd party cookie package to parse the cookie string from socket.io. It seems like the new version of cookie-parser did away with its function to parse cookies from a string. At the very least, it's not documented.
There's got to be a better way!
Edit: In the end, I decided to use express redis and wrote a function in socket.io to get information from redis based on the session ID.
Upvotes: 6
Views: 902
Reputation: 95
I had a similar problem using express.js 4.0 and socket.io 0.9. To solve it my case, I set the 'authorization' process with my own function which is a modification of some solution I found on the net. In this function you will add the session to the data property and access it from the handshake property of the socket. Have a look at the code.
I created and index.js in the sockets folder with this content:
sio.set('authorization', function(data, accept){
/* NOTE: To detect which session this socket is associated with,
* we need to parse the cookies. */
if (!data.headers.cookie) {
return accept(null, true);
}
data.cookie = cookie.parse(data.headers.cookie);
data.cookie = parse.signedCookies(data.cookie, sessionContainer.secret);
data.sessionID = data.cookie[sessionContainer.key];
sessionContainer.store.get(data.sessionID, function(err, session){
if (err) {
return accept('Error in session store.', false);
} else if (!session) {
return accept('Session not found.', false);
}
if(!session.username){
return accept('Session not authenticated', true);
}
data.session = session;
return accept(null, true);
});
});
And then to use the data stored in the session:
sio.sockets.on('connection', function (socket) {
var hs = socket.handshake;
/* NOTE: At this point, you win. You can use hs.sessionID and
* hs.session. */
if(hs.sessionID){
// logged in user
console.log('A socket with sessionID '+hs.sessionID+' connected.');
// here you can access whatever you stored in the session
console.log('User ' + hs.session.username);
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
clientSockets[hs.sessionID] = socket;
// handle the disconnect
socket.on('disconnect', function () {
if(clientSockets[hs.sessionID]){
console.log('deleted unused socket ' + hs.sessionID);
delete clientSockets[hs.sessionID];
}
});
}
});
You can find the whole project in the following link https://github.com/landreus/dimiblog/blob/master/sockets/index.js
Hope it helps!
Upvotes: 0
Reputation: 5109
You could either store the sessions in Memcached or Redis. Then you can get session data from either one of those stores.
Both memcache and redis packages are available for nodejs.
Also notice you can use middleware for your authentication in socket.io. Then you don't have to put the authentication logic into your connection event handler.
var authorization = require('./socket/authorization')(app);
io.use(authorization.authorize);
And as example this memcached auth stuff which is in our case reading a cookie stored by our php.
var memcached = require('memcached'),
cookie = require('cookie),
debug = require('debug')('socket.io:authorization');
module.exports = function(app) {
var authorize = function(socket, next) {
var handshakeData = socket.request;
if (handshakeData.headers.cookie) {
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
if (typeof handshakeData.cookie['node'] === 'undefined') {
next(new Error('No cookie transmitted.'));
} else {
var loginEndIndex = handshakeData.cookie['node'].indexOf(',');
handshakeData.node = handshakeData.cookie['node'].slice(0, loginEndIndex);
var memcached = new Memcached(app.config.memcached.server, app.config.memcached.options);
memcached.on('failure', function(details) {
debug('Server: %s went down due to %s', details.server, details.messages.join(' '));
});
memcached.on('reconnecting', function(details) {
debug('Total downtime caused by server %s: %sms', details.server, details.totalDownTime);
});
memcached.get(handshakeData.cookie['PHPSESSID'], function(err, result) {
if (!err && result !== false) {
var pipeIndex = result.indexOf('|'),
phpData = result.slice(pipeIndex + 1),
obj = php.unserialize(phpData);
if (handshakeData.node === obj.userLogin) {
debug('coockie-accepted: %s, %s', handshakeData.node, obj.userLogin);
next();
} else {
debug('cookie-revoked; %s, %s', handshakeData.node, obj.userLogin);
next(new Error('Cookie is invalid.'));
}
} else {
debug('error: %s', err);
next(new Error('Cookie is invalid.'));
}
memcached.end();
});
}
} else {
debug('error: No cookie transmitted.');
next(new Error('No cookie transmitted.'));
}
};
return {
authorize: authorize
};
};
Upvotes: 1