16

I'm trying to get authentication working over sockets with sailsjs and passport.

The challenge seems to be the fact that a socket connection doesn't have a session, and that sailsjs mocks a request object, causing it to not have Passport middleware setup. This caused nodejs to throw an error, saying that the req object didn't have a method called logIn.

So, I've tried following the code snippet as provided by @xdissent here: Sails.js + Passport.js authentication through websockets which, indeed allows me to sign in without throwing errors. Or does it..? It turns out that it does something, But I have no idea what. Because upon fetching req.user through a different (socket) request, I get an empty object returned.

I've looked in redis, too. This came out of it:

redis 127.0.0.1:6379> keys *
1) "waterline:broadcasting:_sequences:id"
2) "sess:aDJI0YHzh17E3AMjtKsZSijs"
redis 127.0.0.1:6379> get "sess:aDJI0YHzh17E3AMjtKsZSijs"
"{\"cookie\":{\"httpOnly\":true,\"path\":\"/\"}}"
redis 127.0.0.1:6379>

So there is a session, just no user stored in it.

So long story short, how do I get Passport and sailsjs to play nice over sockets.

Update: I'd like some information about sessions, sockets and cookies with sails in general. So if I set stuff in a session, and refresh the browser, I'd like it to still be there. If I make an xhr call on the same page as the socket connection, shouldn't that be the same session?

Community
  • 1
  • 1
Wesley Overdijk
  • 1,176
  • 10
  • 18

1 Answers1

8

Thanks to Kasper Isager there will be a passport generator for sails.js in the near future (Sails.js Version 0.10).

He implement Passport by using policies (sails middleware).

api/services/passport.js

var passport = require('passport');

passport.serializeUser(function(user, next) {
    next(null, user.id);
});

passport.deserializeUser(function(id, next) {
    User.findOne(id).done(next);
});

// Put your Passport config logic here

// Make passport globally available
module.exports = passport;

api/policies/passport.js

module.exports = function (req, res, next) {

  // Initialize Passport
  passport.initialize()(req, res, function () {
    // Use the built-in sessions
    passport.session()(req, res, function () {
      // Make the user available throughout the frontend
      res.locals.user = req.user;

      next();
    });
  });

};

config/policies.js

module.exports.policies = {

    '*': [ 'passport' ],

    // MyCustomController: {
    //  update: [
    //      'passport',
    //      'authorize'
    //  ]
    // }

};

This will make the the passport request methods (logIn, etc.) available in socket requests as well.

After a successful login your server-side session object will look like this:

{
    // Express
    cookie: {
        originalMaxAge: null,
        expires: null,
        httpOnly: true,
        path: '/'
    },
    // Passport
    passport: {
        user: '52fc98e108b31348a537fa43' // userId
    }
}

You may access it in any policy with req.session or even on socket callbacks like:

config/sockets.js

onConnect: function(session, socket){}
onDisconnect: function(session, socket){}

If you want to see the Kaspers full implementation check out his repository: sails-generate-auth

HaNdTriX
  • 28,732
  • 11
  • 78
  • 85
  • 1
    I might misunderstand how passport works, but shouldn't `passport.serializeUser` and deserialize be done in the service? otherwise we are re-declaring the (de)serialization functions at every request? – Hudon Feb 20 '14 at 21:28
  • 1
    Thanks for sharing w/ everyone (and of course thanks to Kasper). This is excellent. – mikermcneil Mar 01 '14 at 20:41
  • @Hudon Yes I guess you are right. Both works but your way is better since ```serializeUser``` and ```deserializeUser``` is only initialized once. – HaNdTriX Mar 02 '14 at 12:40
  • Does this method (not the generate) work with 0.9.x? – TheSharpieOne Mar 04 '14 at 22:51
  • Followed this and set everything up the same way. I stepped through to ensure the policies were running, but I get `TypeError: Object # has no method 'logIn'` – TheSharpieOne Mar 06 '14 at 15:46
  • if ```req.logIn``` is not set you probably haven't included the ```passport```-policy. You Policies file should look similar to this: ```...'*':['passport'], MyContr: {find: ['passport','authorize']}...``` – HaNdTriX Mar 06 '14 at 15:59
  • That is there, the policy runs. It puts a `_passport` object on the req, but none of the methods (like `logIn`) – TheSharpieOne Mar 06 '14 at 16:33
  • I configured 2 different ones (local and SAML). I ended up going with http://stackoverflow.com/questions/17365444/sails-js-passport-js-authentication-through-websockets/18343226#18343226 and it solved the problem with the socket req not having the methods. – TheSharpieOne Mar 06 '14 at 18:32
  • @HaNdTriX: I followed your steps above, but it didn't work. Error is `TypeError: Object # has no method 'logIn'`. – anhnt May 13 '14 at 06:37
  • @anhnt check out: https://github.com/kasperisager/sails-generate-auth/blob/master/templates/api/controllers/AuthController.js – HaNdTriX May 13 '14 at 09:36
  • @HaNdTriX this is my passport.js in api/services/passport.js. Seem there is no User model here :( https://gist.github.com/anhnt/e59c4e8180ffc294d0ee – anhnt May 13 '14 at 10:21
  • Just for the record: sails-generate-auth doesn't create a user model for you. You have to create it on your own and set up the passport model as a role. – HaNdTriX May 31 '14 at 17:16
  • Great answer, all my other policies works as before (when I only used HTTP requests) without any change to them! – tkarls Apr 29 '16 at 08:14