rajan
rajan

Reputation: 33

Why is `req.user` undefined for socket requests in Sails.js, Angular.js and Passport.js?

I am trying to get the questions for the current user with a request like this:

http://localhost:1337/question/forme

As part of the GET request, I am able to access req.user.username and serve the JSONresponse when accessing it from the browser.

When I try to make the same Socket.get() request from my Angular client.js file, it doesn't work. I also tried to access it using browser console and it fails for socket.get() requests.

Upvotes: 3

Views: 2093

Answers (3)

Jason Kulatunga
Jason Kulatunga

Reputation: 5894

I spent a large time figuring out all the moving parts to get passport working with sails on my blog

I'll repost the relevant parts below, with an additional policy change I made.

I created a passport.js file to manage my passport configuration/strategy registration

/config/passport.js

var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;

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

passport.deserializeUser(function(id, done) {
    User.findOneById(id).done(function (err, user) {
        done(err, user);
    });
});

passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    },
    function(email, password, done) {
    User.findOne({ email: email}).done(function(err, user) {
          if (err) { return done(err); }
            if (!user) { return done(null, false, { message: 'Unknown user ' + email }); }
            if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
            return done(null, user);
        });
    }
)); 

And then I used a policy to verify authentication. It makes use of the passport sessions if a socket request is made.

/api/policies/isAuthenticated.js
module.exports = function(req, res, next) {

  // User is allowed, proceed to the next policy, 
  // or if this is the last policy, the controller
    // Sockets
    if(req.isSocket)
    {
        if(req.session &&
            req.session.passport &&
            req.session.passport.user)
        {
            //Use this:

            // Initialize Passport
            sails.config.passport.initialize()(req, res, function () {
                // Use the built-in sessions
                sails.config.passport.session()(req, res, function () {
                    // Make the user available throughout the frontend
                    //res.locals.user = req.user;
                    //the user should be deserialized by passport now;
                    next();
                });
            });

            //Or this if you dont care about deserializing the user:
            //req.user = req.session.passport.user;
            //return next();

        }
        else{
            res.json(401);
        }


    }
    else if (req.isAuthenticated()) {
        return next();
    }
    else{
        // User is not allowed
        // (default res.forbidden() behavior can be overridden in `config/403.js`)
        return res.redirect('/account/login');
    }
};

Upvotes: 7

Eugene Obrezkov
Eugene Obrezkov

Reputation: 2986

You can inject authentication methods into socket requests. For this just make a little hijack into config/bootstrap.js.

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

module.exports.bootstrap = function(cb) {
    sails.removeAllListeners('router:request');
    sails.on('router:request', function(req, res) {
        initialize(req, res, function() {
            session(req, res, function(error) {
                if (error) {
                    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();
};

After this hijack you can use isAuthenticated() and other methods from Passport in sockets.

And then inject user variable through policies like Scott says.

Upvotes: 1

sgress454
sgress454

Reputation: 24948

Sails is great in that it routes socket requests in the same manner as HTTP requests, so that you can hit the same endpoint and execute the same code with either transport. However, there are limits, and you're seeing one here. Passport is Express middleware, and the socket request isn't being handled by Express, so the middleware isn't applied.

I'm no expert on Passport, but I imagine it still works using the session, which is available to your socket request, so I think you could probably handle this in one of two ways:

  1. Read the username from the underlying session variable in your controller action
  2. Use a policy to apply "user" to the req object if it's not already available (i.e. for non-http requests).

Upvotes: 0

Related Questions