passport.socketio

access passport.js authenticated user information from socket.io

passport.socketio

Access passport.js user information from a socket.io connection.

npm install passport.socketio
 
// initialize our modules 
var io               = require("socket.io")(server),
    sessionStore     = require('awesomeSessionStore'), // find a working session store (have a look at the readme) 
    passportSocketIo = require("passport.socketio");
 
// With Socket.io < 1.0 
io.set('authorization', passportSocketIo.authorize({
  cookieParser: express.cookieParser,
  key:         'express.sid',       // the name of the cookie where express/connect stores its session_id 
  secret:      'session_secret',    // the session_secret to parse the cookie 
  store:       sessionStore,        // we NEED to use a sessionstore. no memorystore please 
  success:     onAuthorizeSuccess,  // *optional* callback on success - read more below 
  fail:        onAuthorizeFail,     // *optional* callback on fail/error - read more below 
}));
 
//With Socket.io >= 1.0 
io.use(passportSocketIo.authorize({
  cookieParser: express.cookieParser,
  key:         'express.sid',       // the name of the cookie where express/connect stores its session_id 
  secret:      'session_secret',    // the session_secret to parse the cookie 
  store:       sessionStore,        // we NEED to use a sessionstore. no memorystore please 
  success:     onAuthorizeSuccess,  // *optional* callback on success - read more below 
  fail:        onAuthorizeFail,     // *optional* callback on fail/error - read more below 
}));
 
function onAuthorizeSuccess(dataaccept){
  console.log('successful connection to socket.io');
 
  // The accept-callback still allows us to decide whether to 
  // accept the connection or not. 
  accept(null, true);
 
  // OR 
 
  // If you use socket.io@1.X the callback looks different 
  accept();
}
 
function onAuthorizeFail(datamessageerroraccept){
  if(error)
    throw new Error(message);
  console.log('failed connection to socket.io:', message);
 
  // We use this callback to log all of our failed connections. 
  accept(null, false);
 
  // OR 
 
  // If you use socket.io@1.X the callback looks different 
  // If you don't want to accept the connection 
  if(error)
    accept(new Error(message));
  // this error will be sent to the user as a special error-package 
  // see: http://socket.io/docs/client-api/#socket > error-object 
}

Always provide one. If you don't know what sessionStore to use, have a look at this list. Also be sure to use the same sessionStore or at least a connection to the same collection/table/whatever. And don't forget your express.session() middleware: app.use(express.session({ store: awesomeSessionStore })); For further info about this middleware see the official documentation.

You can also check the simple example below using a redis store.

//in your app.js 
var sessionStore = new redisStore();
 
app.use(session({
  key: 'express.sid',
  store: sessionStore,
  secret: 'keyboard cat'
}));
 
//in your passport.socketio setup 
//With Socket.io >= 1.0 (you will have the same setup for Socket.io <1) 
io.use(passportSocketIo.authorize({
  cookieParser: cookieParser,
  key:         'express.sid',       //make sure is the same as in your session settings in app.js 
  secret:      'keyboard cat',    //make sure is the same as in your session settings in app.js 
  store:       sessionStore,        //you need to use the same sessionStore you defined in the app.use(session({... in app.js 
  success:     onAuthorizeSuccess,  // *optional* callback on success 
  fail:        onAuthorizeFail,     // *optional* callback on fail/error 
}));
 

You have to provide your cookieParser from express: express.cookieParser

Defaults to 'connect.sid'. But you're always better of to be sure and set your own key. Don't forget to also change it in your express.session(): app.use(express.session({ key: 'your.sid-key' }));

As with key, also the secret you provide is optional. But: be sure to have one. That's always safer. You can set it like the key: app.use(express.session({ secret: 'pinkie ate my cupcakes!' }));

Defaults to require('passport'). If you want, you can provide your own instance of passport for whatever reason.

Callback which will be called everytime a authorized user successfuly connects to your socket.io instance. Always be sure to accept/reject the connection. For that, there are two parameters: function(data[object], accept[function]). data contains all the user-information from passport. The second parameter is for accepting/rejecting connections. Use it like this if you use socket.io under 1.0:

// accept connection 
accept(null, true);
 
// reject connection (for whatever reason) 
accept(null, false);
 
 

And like this if you use the newest version of socket.io@1.X

// accept connection 
accept();
 
// reject connection (for whatever reason) 
accept(new Error('optional reason'));
 
 

The name of this callback may be a little confusing. While it is called when a not-authorized-user connects, it is also called when there's a error. For debugging reasons you are provided with two additional parameters function(data[object], message[string], error[bool], accept[function]): (socket.io @ < 1.X)

/* ... */
function onAuthorizeFail(datamessageerroraccept){
  // error indicates whether the fail is due to an error or just a unauthorized client 
  if(error){
    throw new Error(message);
  } else {
    console.log(message);
    // the same accept-method as above in the success-callback 
    accept(null, false);
  }
}
 
// or 
// This function accepts every client unless there's an error 
function onAuthorizeFail(datamessageerroraccept){
  console.log(message);
  accept(null, !error);
}

Socket.io@1.X:

function onAuthorizeFail(datamessageerroraccept){
  // error indicates whether the fail is due to an error or just a unauthorized client 
  if(error)  throw new Error(message);
  // send the (not-fatal) error-message to the client and deny the connection 
  return accept(new Error(message));
}
 
// or 
// This function accepts every client unless there's an critical error 
function onAuthorizeFail(datamessageerroraccept){
  if(error)  throw new Error(message);
  return accept();
}

You can use the message parameter for debugging/logging/etc uses.

This property was removed in v1. See socket.request.user

This property is always available from inside a io.on('connection') handler. If the user is authorized via passport, you can access all the properties from there. Plus you have the socket.request.user.logged_in property which tells you whether the user is currently authorized or not.

Note: This property was named socket.handshake.user prior to v1

This function gives you the ability to filter all connected sockets via a user property. Needs two parameters function(io, function(user)). Example:

passportSocketIo.filterSocketsByUser(io, function(user){
  return user.gender === 'female';
}).forEach(function(socket){
  socket.emit('messsage', 'hello, woman!');
});

If you happen to have to work with Cross-Origin-Requests (marked by socket.io v0.9 as handshake.xdomain and by socket.io v1.0 as request.xdomain) then here's a workaround:

You have to provide the session-cookie. If you haven't set a name yet, do it like this: app.use(express.session({ key: 'your.sid-key' }));

// Note: ther's no readCookie-function built in. 
// Get your own in the internetz 
socket = io.connect('//' + window.location.host, {
  query: 'session_id=' + readCookie('your.sid-key')
});

Nope, there's nothing to do on the server side. Just be sure that the cookies names match.

  • Does NOT support cookie-based sessions. eg: express.cookieSession
  • If the connection fails, check if you are requesting from a client via CORS. Check socket.handshake.xdomain === true (socket.request.xdomain === true with socket.io v1) as there are no cookies sent. For a workaround look at the code above.

You are always welcome to open an issue or provide a pull-request! Also check out the unit tests:

npm test

Licensed under the MIT-License. 2012-2013 José F. Romaniello.