MEAN application, getting all sockets during a request

In my MEAN application I had a need to get the list of all open websockets during a particular request. Turns out with socket.io this is easily doable if can save a reference to the socketio variable during initialization.

For a typical socket.io init in Express, the app.js would contain the following code:

var app = express();
var socketio = require('socket.io')(server, {
    path: '/socket.io-client'
});
require('./config/socketio')(socketio);

So what you need to do is save a reference to the “socketio” variable, which can be retrieved from the request passed down in Express, and used to get all sockets. To do so, you can set it as an application-wide variable in Express like so in app.js:

app.set('socketio', socketio);

Next, a typical Express function handling a request looks like so:

function(req, res) {
}

From your request (the “req” variable) you can get a reference to the Express app, which allows you to get a reference to the socketio variable you set application wide, which you can use to get the list of sockets. Like so:

function(req, res)  {
    var sockets = req.app.get('socketio').sockets.sockets;
    //...
}

And now you can loop through all the sockets like below, and do whatever with them you’d like:

for (var socketId in sockets) {
    var socket = sockets[socketId];
}

As easy as that!

socket.io: Getting all connected sockets in Node/Express

Sometimes you need a list of all connected sockets in your server side API. This could be, for example, if you want to loop through all the sockets and emit something when a certain event happens, such as some a data model updating or something else.

As of socket.io version 1.3, you have access to all sockets using “socketio.sockets.sockets” programatically.

So for example if you needed all sockets in an Express controller running on node.js, you could access it this way.

In your server side app.js, socket.io is usually configured in express this way:

var app = express();
//...
var socketio = require('socket.io')(server, {
  //...
});

And then, you can save the reference for socketio for later using Express’s app.set() and app.get(). In app.js you would do:

app.set('socketio', socketio);

Then in an express controller, you can access socketio like so:

exports.whateverController = function(req, res, next) {
  var socketio = req.app.get('socketio');
  //...
}

And then you can loop through the sockets:

var sockets = socketio.sockets.sockets;
for(var socketId in sockets)
{
  var socket = sockets[socketId]; //loop through and do whatever with each connected socket
  //...
}

Simple, right?!

MEAN stack: associating a socket with a user

I’m using the MEAN stack for an application I’m working on. The project was seeded using the Angular fullstack yeoman generator (https://github.com/DaftMonk/generator-angular-fullstack/).

Out of the box the project has support for websockets (using socket.io), and users (using passportjs). However, sockets on the server side in express running on node are not tied to users, out of the box.

For several reasons the application likely needs to know what user a socket belongs to. For example, if there’s a change made to a model that needs to be emitted, you may need to emit it to only users with a certain role.

To get around this, I made a bunch of modifications which I’ll detail below. Essentially, the user object will get saved within the socket object. So when a socket is being processed, say through a model level trigger (i.e. “save” or “delete”) using mongoose for example, the user object will be in the socket and can be used in whatever processing logic.

The MEAN project seeded from the angular fullstack generator uses a token generated through jwt, which is stored in a cookie, to authenticate a user. So when a user login occurs, an event can be emitted with the jwt token over the socket to register the user with the socket. Furthermore, in your socketio.on(‘connection’,…) function in express, you can read the cookie to get the jwt token, then get the user and put it in the socket. This is essential so that if a user is already logged in, and returns to your web application (or opens a new tab to your application) and a new websocket is created, the cookie can be used to associate the socket with the user, since a new login event will not be emitted at that point.

First, let’s define a function that can take a token either directly as a parameter, or read it from the cookie in a socket, and get the user. This same function can be called from a login emit event with a jwt token as the payload over the socket, or from socketio.on(‘connection’,…).

var auth = require('../auth/auth.service');
function setupUserInSocket(socket, inputToken)
{
  var tokenToCheck = inputToken;
  if(!tokenToCheck && socket && socket.handshake && socket.handshake.headers && socket.handshake.headers.cookie)
  {
    socket.handshake.headers.cookie.split(';').forEach(function(x) {
      var arr = x.split('=');
      if(arr[0] && arr[0].trim()=='token') {
        tokenToCheck = arr[1];
      }
    });
  }
  if(tokenToCheck)
  {
    auth.getUserFromToken(tokenToCheck, function (err, user) {
      if(user) {
        console.info('[%s] socket belongs to %s (%s)', socket.address, user.email, user._id);
        socket.user = user;
      }
    });
  }
}

Note that the cookie is in socket.handshake.headers.cookie. Also note that I call auth.getUserFromToken, which is another function I created that decrypts the user ID from the jwt token, queries the user from the model, and returns it. The function looks like this:

var User = require('../api/user/user.model');
var async = require('async');
var config = require('../config/environment');
function getUserFromToken(token, next)
{
  async.waterfall
  (
    [
      function(callback)
      {
        jwt.verify(token, config.secrets.session, function(err, decoded) {
          callback(err, decoded);
        });
      },
      function(decoded, callback)
      {
        if(!decoded || !decoded._id)
          callback(null, null);
        else {
          User.findById(decoded._id, function (err, user) {
            callback(null, user);
          });
        }
      },
    ],
    function(err, user)
    {
      next(err, user);
    }
  );
}

Next, let’s use socketio.on(‘connection’,…) to call the function with the socket. If the jwt token is already in the cookies, meaning the user already logged in previously, the user will be associated with the socket:

socketio.on('connection', function (socket) {
  setupUserInSocket(socket);
  //...
});

And that’s it for that particular scenario! Next, let’s worry about when a user actually logs in. Within socketio.on(‘connection’, …) we can listen for login emits from the client over the socket like so:

socket.on("login", function(token,next) {
  setupUserInSocket(socket,token);
  next({data: "registered"});
});

And on the client side, we emit the login event over the socket when a successful login occurs. This can be done in a number of ways, but I decided to do it in login.controller.js. After Auth.login() is called, I call socket.login():

angular.module('classActApp')
  .controller('LoginCtrl', function ($scope, Auth, socket, ...) {
    //...
        Auth.login({
          email: $scope.user.email,
          password: $scope.user.password
        })
        .then( function() {
                  //...
                  socket.login();
//...

And in the client side socket.service.js, the login() function does the following:

angular.module('classActApp')
  .factory('socket', function(socketFactory, $location, CONSTANTS, Auth) {
return {
login: function () {
  socket.emit("login", Auth.getToken(), function(data) {
  });
},

Note that you also need to worry about logouts. If the user logs out from your web application, but sticks around on your web application, the socket for that session will remain associated to the user they were logged in as previously. This could be undesirable for several reasons. So in your express side of things (server side), you want to listen for logout events and clear out the user from the socket like so (note, this is added within socketio.on(‘connection’,…)):

socket.on("logout", function(next) {
  if(socket && socket.user) {
    console.info('[%s] socket being disassociated from %s (%s)', socket.address, socket.user.email, socket.user._id);
    socket.user = null;
  }
  next({data: "un-registered"});
});

And in your angular side of things (client side), when the user logs out, you want to emit a logout event over the socket. In the angular fullstack seeded project I’m using, this happens in navbar.controller.js which has the logout function.

angular.module('classActApp')
  .controller('NavbarCtrl', function ($scope, $location, socket, ...) {
    //...
    $scope.logout = function() {
      //...
            socket.logout();

And in socket.service.js:

angular.module('classActApp')
  .factory('socket', function(socketFactory, $location, CONSTANTS, Auth) {
    //...
    return {
      //...
      logout: function () {
        socket.emit("logout", function(data) {
        });
      },

And that’s it! Now all your sockets have the user they belong to (if any) associated to them (accessible from socket.user on the server side). And when emitting events from the server side from the socket, or when reading events emitted from the client side over the socket, we can now know the user the socket belongs to!