33

When I use Sails.js with Passport.js, the isAuthenticated method does not exist on the req object when requested through websocket.

Could anyone tell me why this happens?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Elliot Lings
  • 1,096
  • 1
  • 9
  • 16

4 Answers4

25

Alternatively, you can hijack the 'router:request' event to plug in passport for socket requests. I do this in 'config/bootstrap.js':

module.exports.bootstrap = function (cb) {

  var passport = require('passport'),
    initialize = passport.initialize(),
    session = passport.session(),
    http = require('http'),
    methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'];

  sails.removeAllListeners('router:request');
  sails.on('router:request', function(req, res) {
    initialize(req, res, function () {
      session(req, res, function (err) {
        if (err) {
          return sails.config[500](500, req, res);
        }
        for (var i = 0; i < methods.length; i++) {
          req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req);
        }
        sails.router.route(req, res);
      });
    });
  });
  cb();
};

With this approach you don't need special handling for checking socket request authentication in policies. You do still need to hook up passport for non socket requests by way of express middleware.

xdissent
  • 949
  • 7
  • 7
  • 1
    My problem was with PUT and POST requests requiring authentication, and only @xdissent's answer solved it. – rowanu Dec 22 '13 at 00:23
  • 1
    This is a great workaround-- here's why, from the [FAQ in `lib/router`](https://github.com/balderdashy/sails/tree/master/lib/router#faq): > + When an HTTP request hits the server, does it hit the Sails router before it hits the Express router? > + No- it only hits the Express router. > > + OK.. what requests DO hit the Sails router? > + Requests to other attached servers that don't have their own routers, e.g. the Socket.io interpreter, will hit the Sails router's wildcard handler, which will then talk to the attached server and simulate the appropriate route. – mikermcneil Mar 01 '14 at 19:15
  • This worked perfectly. It even allowed me to authenticate over Websockets so I didn't have to use a XHR request. The req when in socket mode is missing some things specific to express and it's middleware as it was mentioned. – geilt May 12 '14 at 21:35
  • Just curious, what's the `return sails.config[500](500, req, res);` line doing? Under Sails 0.10.x should this now be `return serverError();` – Dave Sag Aug 10 '14 at 06:37
  • In Sails 0.11, the socket server has been upgraded. Any pointers as to how this can be made to work with Socket.IO 1.0? – amarprabhu Jun 10 '15 at 15:39
  • Here is a [workarround that works for 0.11](http://stackoverflow.com/questions/31373595/passport-js-doesnt-work-on-sails-socket-enviroment/31384838#31384838) – Alexis N-o Jul 16 '15 at 14:16
14

Please try to check for req.session.passport.user. It will contain user info when logged in, and will be undefined otherwise. Works for me with any type of request.

I think this happens because in case of WebSocket request, "req" is actually fake request object. It created and passed directly to Express' router, bypassing all Express' middleware, including Passport's one

ataman
  • 2,644
  • 17
  • 22
  • 1
    Exactly right- for an explanation of how this works in Sails core, check out the [README in `lib/router`](https://github.com/balderdashy/sails/tree/master/lib/router#which-hooks-attach-servers--use-the-router). This will likely be simplified in a future version of Sails-- but there are some other considerations to figure out first (see [issue #141](https://github.com/balderdashy/sails/pull/1431#issuecomment-35816845) for more info) – mikermcneil Mar 01 '14 at 19:12
  • While this solved the "is logged in problem" how can I access the actual user object. With normal http passport is running a deserialize user function and I can access it from req.user. Now with socket connections I don't get any user object. – tkarls Apr 28 '16 at 12:00
11

@ataman is exactly right. Express middleware that you install using the express.customMiddleware config is only applied to Express HTTP requests.

To get passport to work for all requests, use it in your policies:

// e.g. 
// config/policies.js
module.exports = {
  SomeController: {
    someaction: function somePassportMiddlewareFn() {}
  }
};
mikermcneil
  • 11,141
  • 5
  • 43
  • 70
0

Another way to extend socket.io req with the passport defined methods login, logout, ... is to rewrite the policy or middleware as below

module.exports = (req, res, next) ->
    if req.isSocket
        req = _.extend req, _.pick(require('http').IncomingMessage.prototype, 'login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated')
    middleware = passport.authenticate('bearer', { session: false })
    middleware(req, res, next)
Alexis N-o
  • 3,954
  • 26
  • 34
Tommy Tang
  • 126
  • 1
  • 6