socket.io and Express. Tying it all together.

NOTE: This article was written for Express 2.x.x. It might not work for Express 3 without modification.
Express is a great web development framework for node.js. It provides easy access to stuff like routing, requests and sessions. socket.io is an abstraction layer for Websockets, with Flash and XHR fallbacks, that runs in both node.js and the client.

The Basics

You can have socket.io run with Express easily. By simply invoking socket.io’s listen method and passing it the Express app as a parameter:

var io = require('socket.io'),
    express = require('express'),
    app = express.createServer();
 
app.configure(function () {
    app.use(express.cookieParser());
    app.use(express.session({secret: 'secret', key: 'express.sid'}));
    app.use(function (req, res) {
        res.end('<h2>Hello, your session id is ' + req.sessionID + '</h2>');
    });
});
 
app.listen();
var sio = io.listen(app);
 
sio.sockets.on('connection', function (socket) {
    console.log('A socket connected!');
});

However, socket.io will not be aware of Express – and the other way around. So if a socket connects, we do not know to which Express session it belongs, but in most scenarios this is an essential information. Since socket.io 0.7 however we can easily obtain this information through the handshake/authorization mechanism. Through this we can tell socket.io to invoke a user-defined function whenever a new Websocket connection is incoming. The important point is, that it is called, before the connection is completed. This provides multiple benefits. For one, we can accept or revoke the connection based on various conditions and for the other part it allows us to inspect the header information of the HTTP request that is trying to establish the Websocket connection – including the cookie. The cookie will contain the sessionID of our Express session.

var parseCookie = require('connect').utils.parseCookie;
 
sio.set('authorization', function (data, accept) {
    // check if there's a cookie header
    if (data.headers.cookie) {
        // if there is, parse the cookie
        data.cookie = parseCookie(data.headers.cookie);
        // note that you will need to use the same key to grad the
        // session id, as you specified in the Express setup.
        data.sessionID = data.cookie['express.sid'];
    } else {
       // if there isn't, turn down the connection with a message
       // and leave the function.
       return accept('No cookie transmitted.', false);
    }
    // accept the incoming connection
    accept(null, true);
});

All the attributes, that are assigned to the data object are now accessible through the handshake attribute of the socket.io connection object.

sio.sockets.on('connection', function (socket) {
    console.log('A socket with sessionID ' + socket.handshake.sessionID 
        + ' connected!');
});

Getting, serious.

But what I find much more interesting is that we can not only extract the sessionID from the cookie, but we can also load the actual session and use, modify or even destroy it. For that we need to get hold of the session store that Express uses to save our sessions. By default this is a MemoryStore and will be created by Express. Instead we can create our own and pass it to Express. That way we have a reference to that store for ourself.

var io = require('socket.io'),
    express = require('express'),
    MemoryStore = express.session.MemoryStore,
    app = express.createServer(),
    sessionStore = new MemoryStore();
 
app.configure(function () {
    app.use(express.cookieParser());
    app.use(express.session({store: sessionStore
        , secret: 'secret'
        , key: 'express.sid'}));
    app.use(function (req, res) {
        res.end('<h2>Hello, your session id is ' + req.sessionID + '</h2>');
    });
});

Now, in our handshake function, we can not only get the sessionID from the cookie, but actually load the session data from the session store.

sio.set('authorization', function (data, accept) {
    if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        // (literally) get the session data from the session store
        sessionStore.get(data.sessionID, function (err, session) {
            if (err || !session) {
                // if we cannot grab a session, turn down the connection
                accept('Error', false);
            } else {
                // save the session data and accept the connection
                data.session = session;
                accept(null, true);
            }
        });
    } else {
       return accept('No cookie transmitted.', false);
    }
});

All the good stuff

Now we have access to all of the session’s data through socket.handshake.session. But to be able to change the session data we will need to create an actual Express Session object. The constructor for the Express session object requires the request associated with the session aswell as the session data. We just acquired the session data from the session store, so we have that. But we do not have access to the HTTP request that is associated to the session. socket.io does not expose it to us. If you look closer at the Session prototype though you see, that there are only 3 properties, that are required on the request object passed to the constructor: sessionID, session and sessionStore. This is good news, since our handshake data object already has the properties sessionID and session. Both (after we created the Session object) with the values that the Session prototype expects to be there. Well and the last property, sessionStore, we can easily add to the data object.

var Session = require('connect').middleware.session.Session;
sio.set('authorization', function (data, accept) {
    if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        // save the session store to the data object 
        // (as required by the Session constructor)
        data.sessionStore = sessionStore;
        sessionStore.get(data.sessionID, function (err, session) {
            if (err || !session) {
                accept('Error', false);
            } else {
                // create a session object, passing data as request and our
                // just acquired session data
                data.session = new Session(data, session);
                accept(null, true);
            }
        });
    } else {
       return accept('No cookie transmitted.', false);
    }
});

Now we can use the session object to modify and even destroy the session. Eg. you could use Session’s touch() method to keep the session from timing out for as long as the Websocket connection remains

sio.sockets.on('connection', function (socket) {
    var hs = socket.handshake;
    console.log('A socket with sessionID ' + hs.sessionID 
        + ' connected!');
    // setup an inteval that will keep our session fresh
    var intervalID = setInterval(function () {
        // reload the session (just in case something changed,
        // we don't want to override anything, but the age)
        // reloading will also ensure we keep an up2date copy
        // of the session with our connection.
        hs.session.reload( function () { 
            // "touch" it (resetting maxAge and lastAccess)
            // and save it back again.
            hs.session.touch().save();
        });
    }, 60 * 1000);
    socket.on('disconnect', function () {
        console.log('A socket with sessionID ' + hs.sessionID 
            + ' disconnected!');
        // clear the socket interval to stop refreshing the session
        clearInterval(intervalID);
    });
 
});

Sum it all up

From this code base you can keep going. You have everything set up to make your Express/socket.io hybrid application run and interact with each of it’s components as you wish. If you have any comments or suggestions, please feel free to use the comment box below. If you would like to see socket.io and Express in action together, have a look at my demo application.

148 thoughts on “socket.io and Express. Tying it all together.

  1. Do you have any tips on accessing socket connection in express routing function? ie

    app.get(‘/foo’, function(req, res) {
    // how do we get the socket connect in here?
    });

  2. The most obvious way to do this would be to store the session/socket pair in a hashmap upon connection. Eg.

    var sessionsConnections = {};
    sio.on('connection', function (socket) {
        // do all the session stuff
        sessionsConnections[socket.handshake.sessionID] = socket;
    });

    However, this harbours some problems:

    a) What if a session has multiple sockets (eg. multiple browser windows)?
    b) You will have to manage and household that hashmap manually.
    c) It does not scale accross multiple processes (not yet an issue, sonce socket.io generally does not, but this is in the works afaik)

    Turns out though that socket.io already provides a transparent solution for all this: rooms/channels. What you can do is have a seperate room for each session. The name of the room would simply be the sessionID. That way you can send whatever information you want to that room instead of the session. All the connections in that room will receive that information. So if your user has multiple tabs open, he will receive the information on all of his tabs, which is what you want in most cases:

    sio.on('connection', function (socket) {
        // do all the session stuff
        socket.join(socket.handshake.sessionID);
        // socket.io will leave the room upon disconnect
    });
     
    app.get('/', function (req, res) {
        sio.sockets.in(req.sessionID).send('Man, good to see you back!');
    });
    • I have been looking for an example of how to publish app changes back through socket (outside of the typical .on(‘connection’) examples). Thank you!

      • I have a question about how the session leave this room when this session ends. Just call socket.leave(sessionId)?
        Thank you.

    • After my test, I found the session leave this room automatically, when this session ends. No need to call socket.leave(sessionId).

    • i’m a completly noob but… I think that code have a problem; EX:

      - 1st host connect; returns socket.id = XXX
      - 2nd host connect returns socket.id = YYY
      - When 1st host do an express request, the socket.id is = YYY because YYY is the last socket.id connected. EX:
      – Now if host1 do a socket.join(ZZ); the result is wrong. Cos its host2 who joins ZZ.

      Sorry for my english :P

      i do something:

      app.get(‘/’, function (req, res) {
      JSON.stringify(request.cookies);
      var hss = request.cookies.expresssid;
      var sockets = io.sockets.clients(hss);
      for (var i in sockets) {
      var sock_id = sockets[i].id;
      var socket = io.sockets.sockets[sock_id];
      }

      //now i can do socket.join(ZZ); or something ^_^

      });

      PD: in the code i changed the coockie name (the same but without dots)

      the question is.. can i do that easyer? thx!!

  3. Pingback: JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: SugarJS, TameJS, wii-js

  4. Hey Daniel – great article but I think I’m being really really stupid. Any idea why this all works fine for Chrome (which I initially tested with) and yet fails when FF and Opera connect (due to no cookie being sent at the initial auth) from the same machine ? I had assumed that the cookie was browser specific but does this mean that it is machine specific ? I also thought that the problem might lie in FF and Opera’s default value of having websockets turned off (and thus using a long polling socket) but enabling this on both doesn’t seem to fix the issue.

    Cheers

    N

    • Ok, so now I got it to work fine although without changing any of the code ! I actually having a landing page that redirects to the page that sets up the websocket. In my original post I was just loading this second page by entering the URL directly (and thus not getting a cookie). However when I go to the landing page and node redirects me, the cookie is set fine. No idea why this is !

      • After doing some more testing, it seems that (irrespective of browser) if you visit any non-socket enabled page the express cookie gets set and you’re then good to go but if you visit a socket enabled page direct (and first), express doesn’t set the cookie and thus it fails. Weirdly though, in this instance, Chrome seems to pass the

        if (data.headers.cookie) {

        test (as there is cookie info, just no express.sid) but FF and Opera don’t – I don’t know enough about cookies (I’m predominantly using local storage) to know why this is.

        • Cookies are set by the HTTP cookie headers. There are various limitations to cookies. One, for example, is that they are bound to the domain where they are set. So if your socket.io application runs on a different domain, than the application that created the session, the cookies will no be transmitted to your socket.io application.

          The connect session middleware functions as follows:
          In the beginning there’s no cookie set. So the client issues a GET request without a cookie header. The connect session middleware recognizes this and will create a new session. Now, in the GET response, the session middleware will include a cookie header telling the client to set the express.sid value of the cookie to the specific session id. The session information is then stored in a (in-memory) database.
          The next time a GET request is issued to the same domain the cookie will also be transmitted using the HTTP cookie header. The session middleware (or our socket.io authentification in this case) can use the information stored in the cookie to identifiy the session belonging to that connection and retrieve the session data from the database.

          Of course the connect session middleware must be active for whatever URI you access to have it create a session. If the “socket enabled page” does not use the connect session middleware, no session will be created and no cookie set.

          • Yep, that was the conclusion I came to – the landing page has no socket so the cookie gets set but then when it hands off to the other pages (where sockets are enabled), the GET doesn’t fire and so no cookie. It seems that Chrome automatically adds some cookie info (__utma and __utmz) which is why that was passing the test.

            N

          • I am not sure, because I do not completely grasp your scenario, but I believe you’re doing something wrong. If a session was created, the cookie containing the session id it should be transmitted when the Websocket connection is established.

            Could you please specify what you mean by “socket enabled page” and how you create the socket.io connection from the client? I strongly suspect that the page that creates the socket.io connection is not on the same domain as the page creating the session. This would lead to the cookie not beeing transmitted when socket.io establishes the connection.

            __utma and __utmz are btw Google Analytics cookies.

          • As you mentioned this is caused by cross domain requests. At first I thought I had done something wrong, but it was simply using localhost and 127.0.0.1 when they should have been the same. Thanks for pointing me in the right direction.

    • Hello!

      Great article, a rare piece of very well explained code!

      Until yesterday, I had this code working (only tested on Chromium), but today I started getting this issue of not having a cookie setup. Also tried FF 3.6 and got the same issue.

      If not by coincidence, today I had a browser update to: 12.0.742.112 (90304) Ubuntu 10.04
      Could it be that now my transportation is not sending cookies anymore?

      My logic is very similar to Nick,
      User GET /qrcode?code=abcde; some stuff is set on the session and redirects to 302 to /, where a socket.io is served.

      I have made a gist with the code, and the log; https://gist.github.com/1198485

      I will try to rebuild from zero and have up to the topic “All the good stuff”

  5. In running the code here, every thing goes well, but the cookie never gets set. Where in the process is that supposed to happen? I was assuming express took care of it. The data object I get back in the handshake function has no cookie and so auth is impossible

    Regards
    Dan

    • The cookie should be set by the connect session middleware added to the express stack with the following two middlewares:

      app.use(express.cookieParser());
      app.use(express.session({secret: 'secret', key: 'express.sid'}));
      

      This means two things that could prevent the cookie from showing up:

      a) Your user must have issued a regular HTTP request to an URI that triggers this middleware and thus sets the cookie.

      b) Your webpage / -application must sit on the same domain as your websocket interface, or else the same origin policy will prevent the cookie from beeing sent to your socket.io application.

      Did this help? If not, I would require more information, pobably code.

      • Yes! That did it. turns out it was a bit of both. On my client page I was connecting with the server IP instead of the domain (virtual) so even though cookie was set for the domain, it wasn’t being passed in.

        Thanks for the help.

      • Hi, Danil
        I still have same problem – condition “if (data.headers.cookie)” is always false.
        Server side:

        app.configure(‘development’, function() {
        app.use(Global.express.logger();
        app.use(Global.express.cookieParser());
        app.use(Global.express.bodyParser());
        app.use(Global.express.session({ secret: ‘secret, key: ‘express.sid’}));


        var io = require(‘socket.io’);
        var sio = io.listen(app);

        sio.set(‘authorization’, function (data, accept) {
        if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        } else {
        return accept(‘No cookie transmitted.’, false);
        }
        ..

        Client:

        $(document).ready(function() {
        socket = io.connect(‘http://localhost:8080′);
        socket.on(‘connect’, function () {
        alert(‘Yeah!’); // I see this only if remove condition “if (data.headers.cookie)”
        });

        Firebug shows me error 500 with “handshake error” text

        Can you help me?
        ___
        ps sorry for my bad english :)

  6. My ‘connection’ event wasn’t working after settings this up. After going through the socket.io 0.7 announcement I figured out I needed to do sio.sockets.on(‘connection’, …) instead of sio.on(‘connection’).

    I don’t know if this is for a specific scenario but figured i’d point out what fixed it for me.

    Awesome article, thanks a lot :)

  7. The socket.handshake property is undefined for me. I.e.:
    sio.sockets.on(‘connection’, function (socket) {
    console.log(socket.handshake); //undefined
    });

    I used the exact same authorization callback as you… any ideas?

    • Which socket.io version are you using? This guide was written for 0.7, but socket.io moved to version 0.8 recently. Maybe something changed. From a quick look at their updated docs though the handshake API still looks pretty much the same.

      What you are doing should work. Is the authorization function called for you when a client connects?

    • It doesn’t really matter. io.configure is just a convenient way for declaring different configurations for different environments (like production or testing). It basically just calls the function you pass to it, if you are in the given environment. If no environment is given, it simply calls the given function.

  8. Pingback: about:benjie » Blog Archive » NodeJS: Express/Socket.io/RedisStore headache

  9. Brilliant – thanks!

    Perhaps you could extend the article with the “separate room for each session” comment.

  10. more recent versions of Connect have the Store.prototype.load(sid, callback) where the callback is passed a Session instance. I’ve been meaning to get that in for quite some time but it should help clean things up a little

    • io.set('authorization', function(data, fn){
        var cookies = utils.parseCookie(data.headers.cookie)
          , sid = cookies['connect.sid'];
        app.store.load(sid, function(err, sess){
          if (err) return fn(err);
          data.session = sess;
          fn(null, true);
        });
      });
      • Thanks TJ
        Do you know in which version of connect this can be found, because in the latest stable npm version of connect this has not worked (I tried that 2 days ago)

        Kind regards

  11. There is a problem with accessing sessions when using RedisStore instead of MemoryStore on Socket.io:

    warn – handshake error Converting circular structure to JSON

    /var/www/node_apps/chat/node_modules/socket.io/lib/stores/redis.js:103
    this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args }));

    Any ideas? :)

    • i don’t know if this is relevant,
      using this code it is not possible to add data to the session,

      e.g. req.session.thing=2;

      because the handshake will fail because in the handshake function when you retreive the session you check that the returned object is a session:

      if(err||!session){…

      i’m guessing that if you add data to it, it is not a session anymore

      • The second condition does not check if the object is a session, but if the value in the session variable is “trueish”, that is neither an empty string, false, null or undefined. Also, even if you add a member to an object, it would not change it’s prototype chain nor it’s constructor function, which would be the closest to “is a session”.

    • When you create Session object, below codes will not cause “handshake error Converting circular structure to JSON” error.

      var req = {
      sessionStore: sessionStore,
      sessionID: data.sessionID
      };
      data.session = new Session(req, session);

  12. Ok so TJ’s last post was really really useful and it help me fix that bug.

    In addition, I would like to say one more thing which you didn’t mention in the article but I found it very important: if you are using socket.io-client in your app somewhere (to connect to Socket.IO from the server side), you need to make sure it passes through the auth process even though it doesn’t have cookies.

    One way to do that is through query strings: io.connect(‘http://localhost:8080/chat?token=aXoqIwP’);

    Here’s a snippet from my app:

        io.set('authorization', function (handshake_data, accept) {
          var data = handshake_data;
          // console.log(data);
          if (data.headers.cookie) {
            data.cookie = parseCookie(data.headers.cookie);
            data.sessionID = data.cookie['rtchat.sid'];
            // (literally) get the session data from the session store
            that.session_store.load(data.sessionID, function (err, session) {
              if (err) {
                // if we cannot grab a session, turn down the connection
                accept(err.message, false);
              } else {
                data.session = session;
                accept(null, true);
              }
            });
          } else {
            // Check to see if the conection is made from the server
            // ~ auth with token
            if (data.query.secret_keyword &amp;&amp;
                (data.query.secret_keyword === configs.secret_keyword))
            {
              return accept(null, true);
            }
            return accept('No cookie transmitted.', false);
          }
        });
  13. Looking at the source for the load function on the store object, it will return an empty result if a session object is not found for the sessionID you provide. The code provided on this site does not take this fact into account. Make sure you do a null check

    sessionStore.load(data.sessionID, function (err, session) {
    ……

    if (!session) {
    accept(“UM?”, false);
    return;
    }

    Otherwise you might end up with this crap

    TypeError: Cannot read property ‘expires’ of undefined
    at Array. (c:\program files\nodejs\node_modules\connect\lib\middl
    eware\session\memory.js:48:47)

    • Thank you for pointing this out. I fixed the code so it’s able to handle this case, too. Note though, that the code given is for demonstration only and neither security nor reliability where of primary concern. Please keep this in mind, if you copy & paste anything from here to your projects.

  14. I’ve just started playing with Node.js (and express and socket.io) this past week so this might be a silly question, but if I am serving the socket.io client from a regular index page using Express routes, how can I make sure the cookie is set before my socket.io connection is attempted? Basically from a fresh load (no cookies), the first time I always get a “warn – handshake error, no cookie” logged on the server, but if I then refresh the page everything is fine. Is there an elegant way around this? I don’t really want to redirect the user as I want the app to basically be a single page.

  15. Hi,

    Thanks for your article. It really helped me a lot in learning socket.io & express. However, I am facing one problem.

    If client connects to the one of the predefined routes, for example ‘/’ or some route that I have created with `app.get`, there is a problem. It seems that authorization process produces an error with ‘No cookie transmitted’.

    I did console.log(data.headers) to check if there is any cookie transmitted, but there wasn’t.

    For example, below code

    app.get(‘/’, function(req, res){
    res.render(‘index’, {title: 123});
    console.log(req.sessionID);
    });

    prints out sessionID successfully, it does not seem to send `data.header.cookie` so that ‘No cookie transmitted error is produced.

    What configuration in client side or server side do I have to go through so that cookies are sent from a client to the server?

  16. Hi Daniel!

    Maybe I will not be original, but I can not get cookies from a handshake object.
    While the cookies are set for the current domain and they are exist, the browser console shows them too.

    What you think about it?

  17. How do I catch the message sent by this server statement on the client side?

    sio.sockets.in(req.sessionID).send(‘Man, good to see you back!’);

  18. Just a fyi (spent half day on this problem)

    if you modify a session (at least on connect-redis), you will have to save the session by calling socket.handshake.session.save(). Otherwise it won’t be reflected..

    • You are right. If you want to change session data and have that change reflected in your database you will have to call the Session.prototype.save method as can be seen in the second snippet of the “All the good stuff” section.

  19. Hi thanks for this great article

    Some of my events needs authentication. Should I activate the listeners once user has been authenticated (if sid.user !== null)
    or should I add some kind of middleware on every protected event that need authentication ?

    Thanks for your advices

  20. Daniel, thanks for a great article, it was very useful to me. I did encounter a problem though: Basically both hs.session.reload() and .save() were expecting an hs.session.req.sessionStore object, which wasn’t there, so it was causing an exception and process exit.

    The problem was fixed with this awkward hack: “hs.session.req.sessionStore = sessionStore;” added after “var hs=socket.handshake;” in your example of …socket.on(‘connection’…

    The only difference in my code is that I’m using redis-connect instead of memorystore. Do you have any idea of how this could be done proper, i.e. without hacks?

    • You have probably omitted this part of the authentication:

      // save the session store to the data object
      // (as required by the Session constructor)
      data.sessionStore = sessionStore;

      I did similarly and had the same errors. Setting data.sessionID also is important for the Session constructor.

  21. Hi Daniel

    Thanks for this useful tutorial.
    I have a problem with this one, i’m using socket.io 0.8.7.

    app.get(“/”, function(req, res) {
    if(req.session.user) {
    console.log(req.headers);

    gives me the following output:

    { host: ‘localhost:3000′,
    connection: ‘keep-alive’,
    ‘cache-control’: ‘max-age=0′,
    ‘user-agent’: ‘Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11′,
    accept: ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8′,
    referer: ‘http://localhost:3000/login’,
    ‘accept-encoding’: ‘gzip,deflate,sdch’,
    ‘accept-language’: ‘de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4′,
    ‘accept-charset’: ‘ISO-8859-1,utf-8;q=0.7,*;q=0.3′,
    cookie: ‘express.sid=..cookie code..’ }

    doing this
    io.set(‘authorization’, function (data, accept) {
    // check if there’s a cookie header
    console.log(data);
    });

    gives me this output:

    { host: ’127.0.0.1:3000′,
    connection: ‘keep-alive’,
    origin: ‘http://localhost:3000′,
    ‘user-agent’: ‘Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11′,
    accept: ‘*/*’,
    referer: ‘http://localhost:3000/’,
    ‘accept-encoding’: ‘gzip,deflate,sdch’,
    ‘accept-language’: ‘de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4′,
    ‘accept-charset’: ‘ISO-8859-1,utf-8;q=0.7,*;q=0.3′ },

    Do you know this issue?
    Now i found this thread as well:
    https://github.com/LearnBoost/socket.io/pull/630

    Any idea please?

    Regards

    • You should make sure the cookie is set for the correct domain. Also make sure the cookie isn’t set for a different subdomain.
      The pull request you linked, might be related. Maybe you should try updating to the latest version of socket.io and see if the problem remains.

  22. When you do session.touch().save() it updates the session object in redis or memorystore or whatever, but does it update the cookie in the browser if the user is idle ie. no activity, only heartbeats from socket.io? Does the cookie and headers get sent when socket.io is in websocket mode or only with html-file, json-polling etc

    • No, it won’t for WebSocket or Flash transports. It will make the cookie expire in the browser. Thus the method described here will actually not prolong the session lifetime for the next HTTP request. I’m curios that no one ever noticed it up until now. Basically it’s fixable if the way Connect handles it’s sessions would be changed. Connect does not differentiate between the lifetime of a session and the lifetime of the cookie holding the session id. What you would need is to increase the cookie lifetime to never expire, while keeping the lifetime of the actual session bound to the lifetime of the server side session data. Please see this Connect issue for additional information. The patch was never made afaik.

      • I think a useful workaround for this is to actually close the socket connection if the session has expired. Client side you could listen to the ‘disconnect’ event and tell the user that he is not connected anymore…

  23. Could you throw a super simple app on github with this marriage between socket.io and express.js? Maybe even throw in a couple different branches or tags for the different versions? I think that would be a kick ass way to keep everyone’s code contributions visible via pull requests.

        • Im not sure why but when I install the dependancies it throws a stack:

          node.js:201
          throw e; // process.nextTick error, or ‘error’ event on first tick
          ^
          TypeError: Object function createApplication() {
          var app = connect();
          utils.merge(app, proto);
          app.request = { __proto__: req };
          app.response = { __proto__: res };
          app.init();
          return app;
          } has no method ‘createServer’
          at Object. (/root/Dropbox/school/2011-3/Programming/sessionExample/app.js:6:19)
          at Module._compile (module.js:441:26)
          at Object..js (module.js:459:10)
          at Module.load (module.js:348:31)
          at Function._load (module.js:308:12)
          at Array.0 (module.js:479:10)
          at EventEmitter._tickCallback (node.js:192:40)

  24. Great stuff!

    One question: When the server gets shut back down and started up again while the client is still sending heartbeats, socket.io reconnects nicely and resends the ‘authorization’ event. I get my same cookie back with the session id, however, since the server has shut down and restarted, and the client hasn’t requested anything from the server yet, its connect.session hasn’t been recreated. Any suggestions on how to force the session to be re-established by connect given the session id from the cookie? (without forcing the client to re-request the page or something destructive like that ideally)

    Also, is this something that could be wrapped up nicely as a connect middleware?

    Perhaps it would require access to the store, which as far as I can tell isn’t currently exposed as suggested by TJ above via app.store.load(sid, fn). Too bad, as that would clean things up nicely.

  25. Excellent article, helped a lot — thanks!

    I am having an issue that is perplexing me. My current setup is such that a web application and node server (created by Express) are sitting on the same host and protocol, but not the same port, thus violating the same origin policy. The conditional statement checking for the presence of cookies in the request header during socket.io authorization passes, but there is no cookie with the key specified in the Express configuration.

    Do you know why this may be happening? Additionally, are you aware of whether my cookies will always be passed in the request to the server, independent of the browser or transport mechanism being used? I have tested on Safari, Chrome using Websockets (powered by socket.io).

    Thanks!

    • If you don’t receive a cookie, make sure it is set for the correct domain. I am not sure if the port of the connection has any effect on if the cookie is transmitted.

      I am pretty sure the cookie will always be submitted independent of browser and transport. Basically all transports but the flash transport will transmit the cookie anyway and I think the flash transport is just an implementation of the WebSocket protocol in flash – which will initialize it’s connection using a HTTP upgrade (and thus will transmit the cookie).

  26. Hi,

    Thanks for this great post!

    I’m trying to add some properties to the session in the socket:
    socket.handshake.session.newProp = ‘test’;
    But the property isn’t added to the Redis Store. How can I fix it?

    Regards,
    Phil

  27. Awesome solution, but I have a question re: expiring sessions.

    I’ve defined a maxAge value within my session config like so:

    app.use express.session(
    store: sessionStore
    secret: “xxxx”
    key: “express.sid”
    cookie:
    path: “/”
    httpOnly: false
    maxAge: 30000
    )

    Is there a way to automatically kill the Socket.IO connection when this maxAge is reached? Currently it looks like the connection remains active as long as the browser window is open (even if I manually delete the cookie file on within the browser cache). For security reasons, I’d like the socket.io connection to disconnect after x minutes. I know I can do this quite easily on the client-side by calling socket.disconnect(), however is it possible to set this on the server-side?

    Thanks!

    • You should be able to use setTimeout to kill the connection server side when the sessions times out. Make sure to check if the session wasn’t refreshed in the meanwhile though.

      • Thanks Daniel, would it be possible for you to provide an example of using setTimeout to kill the connection once the session express.session.cookie.maxAge has elapsed?

        I tried adding the following setTimeout code within the sio.sockets.on “connection”, (socket) -> function – it should kill the session after 20 seconds, however my server ends up crashing with a TypeError: Cannot convert null to object at Timer. error:

        setInterval (->
        console.log “20 secs elapsed – destroying session”
        hs.session.destroy
        ), 20000

        I’ve been trying to debug this with little luck, but I have a feeling it’s something obvious that I’m just missing. Could you please assist by providing an example? Thanks!

        • Seriously, it should be obvious that I do not use Coffeescript.
          Besides that you are trying to destroy the session (that at this stage probably doesn’t exist anymore, if it timed out). Instead you should simply disconnect the socket.
          Also, do not use setInterval, but setTimeout instead. If the session did not time out at that stade (hs.session !== null) you should not disconnect the socket, but instead reset setTimeout to a later point.

  28. What about express 3? It responses 404 (Not Found) to every socket.io request. Is it possible to make it work like with express 2.5.9?

      • This does not work for me, either.

        I understand what the method in this post is doing and why it is called for, but almost one year later is there still not a cleaner way?

      • Hmm… so to be honest I’m not quite sure why the temporary fix you linked to does not work.

        I can verify that both request.sessionID from the Express route and data.sessionID from the socket.io authentication function are the same. Furthermore, inside any socket connection I find that socket.handshake.sessionID is also the same, as expected. Yet manipulations made to the session variable within the socket.io authentication function are not reflected in request.session.

        • This guide was written for Express 2.x.x
          I have not yet worked with Express 3 at all. I do not know any of it’s features or specifics.

          Regarding the manipulation of the session variable: have you explicitly saved the manipulated value back to the store?

            socket.handshake.session.member = 'data';
            socket.handshake.session.save();
          • No, Daniel, I have not. Thanks!

            As far as working with express 3.x, it works great. I thought I was having problems modifying the session, but it was just because I did not call .save() like you pointed out. I have not tried doing this yet, but I am confident it will work.

            I find it funny that a year ago when I wanted to do this, I used this very post. And now when I wanted to do the same thing, this is still the go-to method. Very good post, Daniel; thanks.

          • To clarify, the solution referenced above by Yuri works great in express 3.x:

            “`data.sessionID = data.cookie.engine.split(‘.’)[0];“`

            Because TJ added some new (and apparently unused/forgotten?) cookie signing to the cookies in 3.x.

        • I couldnt get this to work so this is what i got and it works form me, rather than splitting i use a substring of the sid:

          data.sessionID = data.cookie['connect.sid'].substring(0,24);

          • So, uh…

            I’m using a combo och express 3.x, RedisStore, & socket.io.

            data.cookie['connect.sid'].substring(2,26); did the trick…

            Check the format the cookies are being saved to redis in:
            redis-cli
            >>KEYS *

            console.log(data.cookie['connect.sid']);

  29. This article was extremely useful for me. I used redis as sessionStore.
    One question. In the logs of my app i saw that some socket connections are rejected because the session cannot be grabbed.
    This happen on the line 8 of this Gist https://gist.github.com/2776371.
    If cookie is correctly setted and express is configured to use sessions, why this happen?
    Thank you

    • This could have lots of different reason. Eg. expired sessions for which the cookies haven’t expired yet, forged cookies, malformed HTTP requests, a bug in your session store and many more. You should look into which requests produce those null-sessions (e.g. log those requests) and try to find a correlation between them.

      • Following your suggestion, i logged the headers of the “bad” sockets and i found that session cookie is missing in those headers.
        The browser was always firefox.

  30. Pingback: Migrating to Express.js v3 | Freelance Front End Web Developer, Surrey & London UK

  31. Hi!

    I have a question regarding a private chat. Is there a way to connect two members based on their session?

    io.sockets.socket(‘how do i get the client here?’).emit(‘chat’, {message:message, user:socket.handshake.session.user});

    Thanks

      • Would this be a way of having private chats? I based in on the members userid instead of sessionid:
        Server:
        io.on(‘connection’, function (socket) {
        var hs = socket.handshake;
        socket.join(hs.session.userid);
        socket.on(‘chat’, function (data) {
        socket.join(data.userid);
        io.sockets.in(data.userid).emit(‘chat’, { user:hs.session.user, message:data.message });
        });
        });

        Client:

        var socket = io.connect(‘https://localhost/’);

        $(‘#send_message’).click(function (e) {
        socket.emit(‘chat’,{message: $(‘.message’).val(), userid: $(.userid).val() //Get this from hidden field in chat form });
        $(‘.message’).val(”);
        e.preventDefault();
        });

        socket.on(‘chat’, function (data) {
        $(‘ol’).prepend(” + data.user + ” + data.message + ”);
        });

        • I don’t think that would work. You would have to join people into their private channels upon connection and emit to those private channels upon receiving a chat event.

          Maybe my chat module based on socket.io is of interest for you. At least it can give you a hint how to implement private messaging.

  32. Hi! Is there a way to join clients from separate rooms into a new room? Like :io.sockets.clients(room1 , room2).join(room 3); Or preferably with your implementation with sessions, find 2 users based on their sessionid and make a new room and join those two clients in that room? io.sockets.clients(user1, user2).join(room 3);

  33. I don’t understand this at all. Could you explain it in more simple terms? This is where everyone sends me and I never understand it. All’s I want to do is make a simple chat app using express and socket.io.

  34. Quick question: is it possible to set cookies inside a socket.io event?

    * I read all the comments and couldn’t came up with a clear conclusion. Looks like Socket and FlashSocket doesn’t transmit cookies? What I need is to create a cookie in user’s browser to do a permanent logged in, but the authentication is done via socket.io events, there’s no page refresh involved.

    Has anyone archived this?

    • Setting a cookie directly from within socket.io is not possible, since Cookies can only be set using an HTTP response header. Your best bet to set a cookie is to push a message to the client using socket.io, telling it to establish a HTTP connection using XHR and setting the cookies through that.

  35. This definitely doesn’t work with the latest Connect module – parseCookie is no longer available in the utils, replaced by parseSignedCookie and parseJSONCookie. Any chance you know how to leverage these methods to the same effect? I’ve tried parseSignedCookie now in the same way as you used parseCookie, with or without the “secret” passed in, with no luck.

  36. Pingback: Sharing Sessions Between Rails and Node.js Using Redis

  37. So, after trying out this example I’ve come to the understanding that this isn’t tying an existing Express sessionID to the corresponding Socket.IO SessionID. Rather, it’s giving a separate SessionID to the socket as to the express connection.

    Why isn’t it giving the Express given sessionID to the Socket.IO connection? Instead we’re given two completely different ID’s with no relationship between each other.

    • You are right, this is not about giving both sessions the same ID. I am not sure if this is even easily achievable. If you want to tie a WebSocket session to an express session without them sharing the same ID, you can however use this approach.

      • Check the “handleHandshake” method of “manager.js”, and you’ll find it generates a new session anyway after successful handshake.

  38. “Express integrates with Socket.IO” nonesence.

    Express is a layer built on top of the default HTTP server built-into node.
    As Express exports the default HTTP server you are in fact passing a regular HTTP server to socket.io…

    • I do not know where this quote is from, but you are of course right: Express is built ontop of http.Server. This is the reason why you can easily call io.listen passing it the Express application object – infact you could pass it anything that looks and behaves like an http.Server (e.g. a testing mock), which is one of the beatiful aspects of JavaScript.

      Note however, that Express does more than simply beeing a HTTP server that understands routing. Especially the session management part is used heavily here to be able to identify socket.io connections that belong to a specific Express session.

  39. Pingback: Express: Links, News and Resources (1) « Angel ”Java” Lopez on Blog

  40. I have a website with PHP – Zend Framework and MongoDB as the database.
    I got node inside to support real time notifications and carried out the following.

    1. User authenticates in the PHP application and session is stored in redis(Used redis as the session store to share session with node application)
    2. User connects to node server, the session cookie created by PHP is parsed at node and sessionid is grabed from the cookie
    3. Node trades with redis and grabs the session data (UserId and User Name) stored against the sessionid.
    4. Established session using express methods at node(Session is stored at redis).
    5. Created a hash in redis on userid and stored the socketid.
    6. For private chat i did a lookup on the hash and emitted to the right socket..

    Issues

    1. What happens if the user open multiple tabs, should i store all the socketid in the hash, if yes should I emit to all the sockets?
    2. When user closes the tab how do i delete the right socket id from hash?

    Overall is my approach correct ? Please shower your suggestions for improvement..
    Wishing you all “Merry Chirstmas and Happy New Year”.

    • Hi Deva,

      yes, your approach should work fine. To resolve your issue with multiple tabs have a look at this comment of mine. It describes a way to use rooms to solve the problem of multiple sockets for a single session.
      Deleting the right socket should be easy, since the socket.io disconnect event will include the socket reference. Also, when using rooms, the socket should be removed automatically from the room by socket.io.

    • hi Dev,

      I have some doubts regarding ur approach.So you have php website which runs on apache???
      So there are two servers apache and nodejs running on same port??How redis can be shared accross two servers???
      How can data which is stored by apache in redis can be accessed via nodejs?

  41. I have problems with firefox 17.0.1 when authorization “data.headers.cookie” is undefined (cookie not transmitted)

    Anybody knows the solution to this issue?

    Thanks an sorry for my poor english

    • If no cookies are transmitted, make sure they are actually set (inspect document.cookie in your browser). Also, due to security policies on cookies, the socket.io server and the page that sets the cookies need to be hosted from the same domain.

  42. Pingback: Session-based Authorization in socket.io - BigPanda's Blog

  43. I am RedisStore in below manner.
    app.use(express.session({
    store: new RedisStore({host:’172.16.216.163′,port:6379, prefix:’chs-sess’}),
    secret: ‘lolcat’,
    key :’express.sid’ }));
    in my application I have to access the ‘session’ details , how can i get those values and where this value store(either in redis or in some oher place).

    thanks in advance

  44. Pingback: websocket api to replace rest api? | Everyday I'm coding

  45. Pingback: How to Create Realtime Multi-player Games in HTML5 | OSOS

  46. hi Daniel,

    can u please let me know how to run this demo application..I cannot execute make run on windows…

  47. That saved my day some time ago… Thanks!
    But now I’m stuck… Retrieving/saving session for the current socket is easy (with your help :). Is it possible to store some data in socket so it is persisted in socket.io store (redis) and can be easily retrieved for any socket? Specifically I need to retrieve the arbitrary information for all sockets in the room.
    Thanks!

  48. Pingback: Session-based Authorization with Socket.IO | InfoLogs

  49. Pingback: Socket.IO + Express 3 sessions - Leeroy Brun - Leeroy Brun

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>