Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can I get sessions to work using redis, express & socket.io?

So I am trying to get Sessions to work inside my socket.on('connection', ...) I am trying to get this working using recent versions: Socket.io - 0.9.13, Express - 3.1.0 and latest versions of other modules.

Anyway I have tried using both modules 'connect-redis' and 'session.socket.io' and they both have similar problems.

In my code I have 2 redis stores (socketio.RedisStore and require('connect-redis')(express)), now this program all runs fine, but because express and socket.io need to share session data, I was wondering if this setup will use sessions correctly? do the session stores need to be the same object for express/socketio? A bit of a gray area to me, because the 2 RedisStore's will use the same db in the background?

I have tried using either the socket.io redisStore or the connect-redis redisStore in both places, but socket.io doesnt like the connect-redis redisStore and express doesnt like the socketio.redisStore.

If I use the connect-redis RedisStore then socket.io/lib/manager.js complains: this.store.subscribe(... TypeError Object # has no method 'subscribe'

If I use socketio.RedisStore then express/node_modules/connect/lib/middleware/session.js complains: TypeError: Object # has no method 'get'

*Note I would rather get the session.socket.io plugin working, but when I do the same setup with that plugin, express (also) complains: TypeError: Object # has no method 'get'

So is it ok that I use 2 different RedisStores for sessions, or do I need to somehow get one or the other working for both, and if so any ideas on how to fix?

My current code looks like this:

var
    CONST = {
        port: 80,
        sessionKey: 'your secret sauce'
    };

var 
    redis = require('redis');

var 
    express = require('express'),
    socketio = require('socket.io'),
    RedisStore = require('connect-redis')(express);

var 
    redisStore = new RedisStore(),
    socketStore = new socketio.RedisStore();

var 
    app = express(),
    server = require('http').createServer(app),
    io = socketio.listen(server);

app.configure(function(){
    app.use(express.cookieParser( CONST.sessionKey ));
    app.use(express.session({ secret: CONST.sessionKey, store: redisStore }));
    app.use(express.static(__dirname + '/test'));
    app.get('/', function (req, res) {res.sendfile(__dirname + '/test/' + 'index.htm');});
});

io.configure(function(){
    io.set('log level', 1);
    io.enable('browser client minification');
    io.enable('browser client etag');
    io.enable('browser client gzip');
    io.set('store', socketStore);
});

io.sockets.on('connection', function(socket){
    socket.emit('message', 'Test 1 from server')
});

server.listen( CONST.port );

console.log('running...');
like image 245
Josh Mc Avatar asked Mar 02 '13 00:03

Josh Mc


People also ask

Can Redis be used as a session store?

Scaling an Express Application with Redis as a Session Store. We're building our application as a series of components, with Redis as a data store and cache. Most of these components talk to the user via the HTTP request / response cycle, which is inherently stateless.

How to add Redis support in express?

To add support of Redis you have to use Redis client and connect-redis. Create express-session and pass it to connect-redis object as parameter. This will initialize it. Then in session middleware, pass the Redis store information such as host, port, and other required parameters. Here is a sample express code with Redis support. Have a look.

Why do I need Redis and upstash?

Why do I need Redis? Default session store loses the session data when the process crashes. Moreover, it does not scale. You can not utilize multiple web servers to serve your sessions. Why Upstash? You can use any Redis offering or self hosted one.

How do I authenticate a user using Redis?

The Authentication Service will check the supplied credentials against what's stored in Redis at key ncc:user:77 and create a session object for that user, adding the email address to the "user" property in the session. Postman should show a 200 (success) response code and the text "OK".


1 Answers

inside the io.configure, you have to link the socket with the http session.

Here's a piece of code that extracts the cookie (This is using socket.io with xhr-polling, I don't know if this would work for websocket, although I suspect it would work).

var cookie = require('cookie');
var connect = require('connect');

var sessionStore = new RedisStore({
  client: redis // the redis client
});

socketio.set('authorization', function(data, cb) {
  if (data.headers.cookie) {
    var sessionCookie = cookie.parse(data.headers.cookie);
    var sessionID = connect.utils.parseSignedCookie(sessionCookie['connect.sid'], secret);
    sessionStore.get(sessionID, function(err, session) {
      if (err || !session) {
        cb('Error', false);
      } else {
        data.session = session;
        data.sessionID = sessionID;
        cb(null, true);
      }
    });
  } else {
    cb('No cookie', false);
  }
});

Then you can access the session using:

socket.on("selector", function(data, reply) {
  var session = this.handshake.session;
  ...
}

This also has the added benefit that it checks there is a valid session, so only your logged in users can use sockets. You can use a different logic, though.

like image 149
Pascal Belloncle Avatar answered Sep 19 '22 22:09

Pascal Belloncle