Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sessions with Express.js v4 and Socket.io v1

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.

like image 583
rachelderp Avatar asked Aug 03 '14 22:08

rachelderp


1 Answers

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
    };
};
like image 51
Marco Avatar answered Nov 19 '22 04:11

Marco