97

I'm using express framework. I want to reach session data from socket.io. I tried express dynamicHelpers with client.listener.server.dynamicViewHelpers data, but i can't get session data. Is there a simple way to do this? Please see the code

app.listen(3000);

var io = require('socket.io');
var io = io.listen(app);

io.on('connection', function(client){
    // I want to use session data here
    client.on('message', function(message){
        // or here
    });
    client.on('disconnect', function(){
        // or here
    }); 
});
hexacyanide
  • 76,426
  • 29
  • 148
  • 154
sfs
  • 1,113
  • 1
  • 9
  • 9
  • 6
    Or may use this VERY NICE post: http://www.danielbaulig.de/socket-ioexpress/ – Fabiano Soriani Sep 02 '11 at 22:28
  • 3
    @FabianoPS - won't work anymore - connect not longer provides the parseCookie method on which this relies. – UpTheCreek Aug 01 '12 at 07:53
  • 1
    @UpTheCreek - Since connect doesn't use the parseCookie method anymore, how can this be possible? – Aust Oct 09 '12 at 04:34
  • @Aust - Good question, I don't know what the best approach is now. This is really a mess IMO. Annoyingly most of the workaround discussions are also focuessed on socket.io (not everyone is using socket.io!). There's a parseSignedCookie function now, but that's private too, so its at risk of breaking changes too. – UpTheCreek Oct 09 '12 at 09:00
  • @UpTheCreek - After a few months of trying different modules, I finally just made my own. [session.io](http://stackoverflow.com/a/15669888/1408717). – Aust Mar 27 '13 at 21:58
  • 2
    For newer versions of Socket.IO (1.x) and Express (4.x) check this SO question: http://stackoverflow.com/questions/25532692/how-to-share-sessions-with-socket-io-1-x-and-express-4-x – tkit Sep 23 '14 at 16:27

10 Answers10

68

This won't work for sockets going over the flashsocket transport (it doesn't send the server the needed cookies) but it reliably works for everything else. I just disable the flashsocket transport in my code.

To make it work, in the express/connect side, I explicitly define the session store so I can use it inside socket:

MemoryStore = require('connect/middleware/session/memory'),
var session_store = new MemoryStore();
app.configure(function () {
  app.use(express.session({ store: session_store }));
});

Then inside my socket code, I include the connect framework so I can use its cookie parsing to retrieve the connect.sid from the cookies. I then look up the session in the session store that has that connect.sid like so:

var connect = require('connect');
io.on('connection', function(socket_client) {
  var cookie_string = socket_client.request.headers.cookie;
  var parsed_cookies = connect.utils.parseCookie(cookie_string);
  var connect_sid = parsed_cookies['connect.sid'];
  if (connect_sid) {
    session_store.get(connect_sid, function (error, session) {
      //HOORAY NOW YOU'VE GOT THE SESSION OBJECT!!!!
    });
  }
});

You can then use the session as needed.

pr0zac
  • 704
  • 5
  • 4
  • 3
    It looks like socket_client.request has been removed in latest versions of socket.io-node – Art Aug 28 '11 at 09:21
  • 6
    Beware... there are new conventions for doing this now... I would suggest using Socket.IO authentication instead. See post from @Jeffer – BMiner Oct 10 '11 at 02:07
  • 2
    Unfortunately this wont work any more - parseCookie is no longer part of connect utils. Apparently it was never part of the public api. There's a messy alternative - parseSignedCookie, but this is also private, so I guess its at risk of disappearing too.. – UpTheCreek Aug 01 '12 at 07:55
  • Now that I am thinking about this.... why not just set the store for the cookies and the store for the socket to the same store? – Heavy Gray Feb 02 '13 at 19:59
35

The Socket.IO-sessions module solution exposes the app to XSS attacks by exposing the session ID at the client (scripting) level.

Check this solution instead (for Socket.IO >= v0.7). See docs here.

Jeffer
  • 535
  • 4
  • 4
  • 1
    @Jeffer - that second solution wont work anymore with the current version of connect. – UpTheCreek Aug 01 '12 at 07:50
  • 2
    -1 `socket.io` is what is exposing the session ID, not that module. This is not a big deal since the session ID is stored client-side in a cookie anyways... Also that tutorial doesn't work for the latest version of Express. – Aust Mar 27 '13 at 22:13
  • 1
    your solution link simply redirects to `socket.io` github page..? – T J Nov 24 '15 at 13:53
8

I suggest not to entirely reinvent the wheel. The tools you need is already an npm package.I think this is what you need: session.socket.io I am using it in these days and it will be very helpful I guess!! Linking the express-session to the socket.io layer will have so many advantages!

Francesco
  • 494
  • 6
  • 15
  • 6
    Here is an updated module that supports socket.io-express-sessions for socket.io >= 1.0 https://github.com/xpepermint/socket.io-express-session – blnc Nov 25 '15 at 22:49
4

Edit: After trying some modules that didn't work, I've actually gone and written my own library to do this. Shameless plug: go check it out at https://github.com/aviddiviner/Socket.IO-sessions. I'll leave my old post below for historical purposes:


I got this work quite neatly without having to bypass the flashsocket transport as per pr0zac's solution above. I am also using express with Socket.IO. Here's how.

First, pass the session ID to the view:

app.get('/', function(req,res){
  res.render('index.ejs', {
    locals: { 
      connect_sid: req.sessionID
      // ...
    }
  });
});

Then in your view, link it in with Socket.IO client-side:

<script>
  var sid = '<%= connect_sid %>';
  var socket = new io.Socket();
  socket.connect();
</script>
<input type="button" value="Ping" onclick="socket.send({sid:sid, msg:'ping'});"/>

Then in your server-side Socket.IO listener, pick it up and read/write the session data:

var socket = io.listen(app);
socket.on('connection', function(client){
  client.on('message', function(message){
    session_store.get(message.sid, function(error, session){
      session.pings = session.pings + 1 || 1;
      client.send("You have made " + session.pings + " pings.");
      session_store.set(message.sid, session);  // Save the session
    });
  });
});

In my case, my session_store is Redis, using the redis-connect library.

var RedisStore = require('connect-redis');
var session_store = new RedisStore;
// ...
app.use(express.session({ store: session_store }));

Hope this helps someone who finds this post while searching Google (as I did ;)

Dave
  • 3,476
  • 2
  • 34
  • 36
  • 9
    Putting your session id in the client html is not a good idea from a security perspective... – UpTheCreek Aug 01 '12 at 07:51
  • 4
    Why is putting the session id in the HTML such a bad idea when the session ID is already stored in a local cookie? – Gavin Jul 10 '14 at 10:29
  • 4
    Because cookies can't be accessed from javascript when set as HttpOnly cookie. By putting session.sid in the HTML you give attacker everything they need in the DOM. – rochal Oct 31 '15 at 09:57
3

See this: Socket.IO Authentication

I would suggest not fetching anything via client.request... or client.listener... as that is not directly attached to the client object and always point to the last logged in user!

Community
  • 1
  • 1
Shripad Krishna
  • 10,045
  • 4
  • 50
  • 63
2

You can make use of express-socket.io-session .

Share a cookie-based express-session middleware with socket.io. Works with express > 4.0.0 and socket.io > 1.0.0 and won't be backward compatible.

Worked for me!!

nonybrighto
  • 5,736
  • 2
  • 28
  • 44
2

Check out Socket.IO-connect

Connect WebSocket Middleware Wrapper Around Socket.IO-node https://github.com/bnoguchi/Socket.IO-connect

This will allow you to push the Socket.IO request(s) down the Express/Connect middleware stack before handling it with Socket.IO event handlers, giving you access to the session, cookies, and more. Although, I'm not sure that it works with all of Socket.IO's transports.

BMiner
  • 14,934
  • 11
  • 47
  • 52
1

You can have a look at this: https://github.com/bmeck/session-web-sockets

or alternatively you can use:

io.on('connection', function(client) { 
  var session = client.listener.server.viewHelpers; 
  // use session here 
});

Hope this helps.

intellidiot
  • 10,540
  • 4
  • 31
  • 41
  • I know session-web-sockets but i don't want to use a library for this, i'm looking for a simple solution. I tried client.listener.server.viewHelpers; but it returned this: {} – sfs Jan 12 '11 at 12:27
  • 2
    though this seems to work at first, it leads to race conditions. I suggest you avoid it completely. – Shripad Krishna Jan 24 '11 at 16:31
1

I am not sure that I am doing it right. https://github.com/LearnBoost/socket.io/wiki/Authorizing

With the handshake data, you can access to the cookies. And in the cookies, you can grab connect.sid which is the session id for each client. And then use the connect.sid to get the session data from database (I am assuming you are using RedisStore)

Tan Nguyen
  • 3,281
  • 3
  • 18
  • 18
  • this is exactly the way to go but your not cool so you cant get stack overflow points. https://github.com/tcr/socket.io-session and https://github.com/functioncallback/session.socket.io I like the first a little better its cleaner. The second one is a bit better documented. – Heavy Gray Feb 02 '13 at 19:19
  • @TJ I am sorry about that but do you realize that the answer was 2 years ago? And it probably wouldn't work now – Tan Nguyen Dec 02 '15 at 20:22
  • @TanNguyen I know, that is why I notified you so that you can update the answer necessarily than pointing people to dead links – T J Dec 03 '15 at 04:56
  • @TJ Thank you for that. Unfortunately, I moved away from socket.io due to performance problem 2 years ago. Therefore, I don't know what is the current way of dealing with session in socket.io – Tan Nguyen Dec 03 '15 at 08:10
0

For future readers - There is an elegant and easy way to access the session inside socket.io with express-session. From socket.io documentation:

Most existing Express middleware modules should be compatible with Socket.IO, you just need a little wrapper function to make the method signatures match:

 const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);

The middleware functions that end the request-response cycle and do not call next() will not work though.

Example with express-session:

const session = require("express-session");
io.use(wrap(session({ secret: "cats" })));
io.on("connection", (socket) => {
   const session = socket.request.session; // here you get access to the session :)
});