Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use passport with express and socket.io?

I am currently trying to set up a basic authentication system for my node.js app. For now I am using express (3.0.0rc5), passport (0.1.12) and socket.io (0.9.10) with Mongoose as Store for session data. I have also been playing around with everyauth but I didn't like to work with promises.

Current situation:

Authentication through passport (facebook strategy) is successful, a session.sid cookie is set on the client after the redirect and I am able to see a session document in my database. I can access the session cookie in socket.io through socket.handshake.headers.cookie.

If I understood the passport concept correctly, after successful authentication passport.serializeUser is called which enables me to add information to the session. In my case the most important information is the email, so I set the serializer up in the following way:

passport.serializeUser(function(user, done) {   done(null, user.email); }); 

Now I am at the point where I have to use only the cookie information in my socket.io event to extract the email address from the session.

var connect = require('connect'),     parseSignedCookie = connect.utils.parseSignedCookie,     cookie            = require('express/node_modules/cookie');  io.on('connection', function(socket) {     if(socket.handshake.headers.cookie) {         var cookie = cookie.parse(socket.handshake.headers.cookie);         var sessionID = parseSignedCookie(cookie['connect.sid'], 'secret');     } });  passport.deserializeUser(function(id, done) {     // so, what is supposed to be done here? }); 

So if I am not mistaken, the task is now to use deserializeUser to access the corresponding email address.

How would I do that? Any pointers are highly appreciated.

like image 586
Patrick Avatar asked Oct 26 '12 22:10

Patrick


People also ask

Can you use Socket.IO with Express?

Learn how to install and use Socket.io with Express using the npm package manager, and create a simple chat server to see the basics of how a client and server work together. Requirements: You have an account and are logged into the Scaleway console. You have a Scaleway Instance running Ubuntu Xenial or a later version.

How do I connect to a Socket.IO server?

listen(port); // Create a Socket.IO instance, passing it our server var socket = io. listen(server); // Add a connect listener socket. on('connection', function(client){ console. log('Connection to client established'); // Success!

How do I use Socket.IO middleware?

Here is an example of the syntax: const io = require('socket.io')(); io. use(function(socket, next) { // execute some code next(); }) . on('connection', function(socket) { // Connection now authenticated to receive further events socket.


2 Answers

Here is a solution using Socket.IO 1.0 and Express 4.0. It is similar in spirit to Patrick's answer. The trick is that since Socket.IO 1.0 has a new middleware API, we can wrap Express's middleware and put it into the Socket.IO pipeline without delving into the low-level implementation of the session stores.

// Set up the Session middleware using a MongoDB session store expressSession = require("express-session"); var sessionMiddleware = expressSession({     name: "COOKIE_NAME_HERE",     secret: "COOKIE_SECRET_HERE",     store: new (require("connect-mongo")(expressSession))({         url: "mongodb://localhost/DATABASE_NAME_HERE"     }) });  // Set up the Express server var app = require("express")()     .use(sessionMiddleware)     .use(passport.initialize())     .use(passport.session())     // ... more middleware ...     .listen(8000);  // Set up the Socket.IO server var io = require("socket.io")(app)     .use(function(socket, next){         // Wrap the express middleware         sessionMiddleware(socket.request, {}, next);     })     .on("connection", function(socket){         var userId = socket.request.session.passport.user;         console.log("Your User ID is", userId);     }); 

The variable sessionMiddleware is a function that is designed to fit directly into the Express pipeline. It takes exactly three arguments: the request object, the response object, and a callback.

Socket.IO's pipeline expects its middleware to take only two arguments, however: the socket object (which contains the request object at socket.request) and a callback. Luckily sessionMiddleware does not require the response object to read the session from the store, so we simply pass it an empty object as the second argument.

Note that some comments below observe that this code renders the session read-only. This is the functionality we lose by not having a proper response object with Socket.IO.

In the above example I use a MongoDB session store (connect-mongo). You can use whatever session store fits your liking. Refer to the Connect wiki for a list of session stores.

like image 191
sffc Avatar answered Sep 21 '22 13:09

sffc


I got it to work. What I had to to is get access to the sessionStore. Here is the code in case anyone else stumbles over this particular problem:

// # app.js  var express     = require('express'),     routes      = require('./routes'),     http        = require('http'),     path        = require('path'),     app         = express(),     passport    = require('passport'),     SessionMongoose = require("session-mongoose"),     mongooseSessionStore = new SessionMongoose({         url: "mongodb://localhost/login",         interval: 120000      });  var config       = require('game/config.js'), // holds my whole server configuration     server       = require('game/lib/server.js');  // save sessionStore to config for later access config.setSessionStore(mongooseSessionStore);  // configure express to use passport and mongooseSessionStore app.configure(function(){     app.set('port', config.port);     app.set('env', config.environment);     app.set('dbPrefix', config.dbPrefix);     app.set('views', __dirname + '/views');     app.set('view engine', 'jade');     app.use(express.favicon());     app.use(express.bodyParser());     app.use(express.cookieParser());     app.use(express.session({secret : 'totallysecret', store : mongooseSessionStore })),     app.use(express.methodOverride());     app.use(passport.initialize());     app.use(passport.session());         app.use(app.router);     app.use(express['static'](path.join(__dirname, 'public'))); });  app.get('/', routes.index);  app.get('/auth/facebook', passport.authenticate('facebook', { scope: 'email' })); app.get('/auth/facebook/callback',      passport.authenticate('facebook', { successRedirect: '/',                                         failureRedirect: '/' }));   // #### Init httpserver var httpServer = http.createServer(app); httpServer.listen(app.get('port'));  // #### Server startup server.init(httpServer); 

My serialization functions look simple like this:

passport.serializeUser(function(user, done) {     // saves user.email to session.passport.user     done(null, user.email); });  passport.deserializeUser(function(obj, done) {     done(null, obj); }); 

And finally the socket.io implementation:

var util              = require('util'),     connect           = require('connect'),     parseSignedCookie = connect.utils.parseSignedCookie,     cookie            = require('express/node_modules/cookie'),     io                = require('socket.io').listen(httpServer);  var config = require('../config.js');    io.configure(function () {     io.set('authorization', function (data, callback) {         if(data.headers.cookie) {             // save parsedSessionId to handshakeData             data.cookie = cookie.parse(data.headers.cookie);             data.sessionId = parseSignedCookie(data.cookie['connect.sid'], 'totallysecret');         }         callback(null, true);     });      io.on('connection', function(socket) {         // reference to my initialized sessionStore in app.js         var sessionStore = config.sessionStore;         var sessionId    = socket.handshake.sessionId;          sessionStore.get(sessionId, function(err, session) {             if( ! err) {                 if(session.passport.user) {                     console.log('This is the users email address %s', session.passport.user);             }         });     }); }); 

Using the session-mongoose module I have access to:

sessionStore.get(sessionId, callback) sessionStore.set(sessionId, data, callback)  sessionStore.destroy(sessionId, callback)  sessionStore.all(callback)    // returns all available sessions sessionStore.clear(callback)  // deletes all session data sessionStore.length(callback) // returns number of sessions in the  
like image 29
Patrick Avatar answered Sep 22 '22 13:09

Patrick