Search code examples
node.jsexpresspassport.jspassport-facebookpassport-jwt

Web page keeps loading indefinitely if removing (req, res, next) on passport.authenticate(...)


I downloaded one sample project from internet. Below there are some fragments of the code:

On the routes file I have the following (just a fragment):

var authController = require('./controllers/authController'),
var passport = require('passport');
var authLoginFacebook =
    passport.authenticate(
        'facebook',
        {
            session: false,
            scope: ['public_profile', 'email']
        }
    );
var checkJwt = function(req, res, next) {
    passport.authenticate(
        'jwt',
        {session: false },
        function (err, user, info) {
            next();
        }
    )(req, res, next);
}

module.exports = function(app) {
    // ...
    app.get(
        '/api/auth/login/facebook/callback',
        checkJwt,
        authLoginFacebook,
        authController.login
    );
    // ...
}

On the passport file I have the following (just a fragment):

var User = require('../models/user');
var credentials = require('./credentials');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;

module.exports = function(passport) {

    passport.use(
        new JwtStrategy({
                secretOrKey: credentials.secret,
                jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
            },
            function(payload, done) {
                User.findById(
                    payload._id,
                    function(err, user) {
                        if (err) {
                            return done(err, false);
                        }
                        if (user) {
                            return done(null, user);
                        } else {
                            return done(null, false);
                        }
                    }
                );
            }
        )
    );

    var fbStrategy = credentials.facebook;
    fbStrategy.passReqToCallback = true;
    passport.use(new FacebookStrategy(fbStrategy,
        function(req, token, refreshToken, profile, done) {
            // asynchronous
            process.nextTick(function() {
                // check if the user is already logged in
                if (!req.user) {
                    User.findOne({
                        'facebook.id': profile.id
                    }, function(err, user) {
                        if (err)
                            return done(err);
                        if (user) {
                            // if there is a user id already but no token (user was linked at one point and then removed)
                            if (!user.facebook.token) {
                                user.facebook.token = token;
                                user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
                                user.facebook.email = (profile.emails[0].value || '').toLowerCase();
                                user.save(function(err) {
                                    if (err)
                                        return done(err);
                                    return done(null, user);
                                });
                            }
                            return done(null, user); // user found, return that user
                        } else {
                            // if there is no user, create them
                            var newUser = new User();
                            newUser.facebook.id = profile.id;
                            newUser.facebook.token = token;
                            newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
                            newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();
                            newUser.save(function(err) {
                                if (err)
                                    return done(err);
                                return done(null, newUser);
                            });
                        }
                    });
                } else {
                    // user already exists and is logged in, we have to link accounts
                    var user = req.user; // pull the user out of the session
                    user.facebook.id = profile.id;
                    user.facebook.token = token;
                    user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
                    user.facebook.email = (profile.emails[0].value || '').toLowerCase();
                    user.save(function(err) {
                        if (err)
                            return done(err);
                        return done(null, user);
                    });
                }
            });
        })
    );

    // ...
};

I have few questions here:

  1. why on: passport.authenticate('jwt', ... are passed these arguments: (req, res, next) and on passport.authenticate('facebook', ... don't while they are used in the same line one next to other?

    app.get(
        '/api/auth/login/facebook/callback',
        checkJwt,
        authLoginFacebook,
        authController.login
    );
    

    If I remove those arguments, then the web page keeps loading indefinitely.

  2. why inside: passport.use(new FacebookStrategy is defined: req.user? where was declared the field: user for the object req?

Thanks!


Solution

  • *Edit: this is a function invoking another function...which is required because of the callback using next(). The facebook function doesn't have that.

    In other words, when you invoke passport.authenticate, the return value is actually going to be a function expecting the parameters req, res, next. Normally you don't need to wrap it, because it just works. However, in this case there is a callback function being passed in as an argument, and that callback function needs access to the next parameter. So you have to wrap the whole thing to gain access to that next parameter.

    *Note: the wrapped function isn't actually returning anything to/through the wrapping function. passport.authenticate() returns a function, and then this return function is self-invoked with the parameter group that follows. But this second self-invoked function result isn't captured as a variable or returned or anything.

    The reason is that the important thing is either sending a response using the res parameter or allowing express to continue to the next layer of middleware/etc by calling the next() callback parameter. It's all happening asynchronously, and flow is directed using the callbacks.

    var checkJwt = function(req, res, next) {
        passport.authenticate(
            'jwt',
            {session: false },
            function (err, user, info) {
                next();
            }
        )(req, res, next);
    }
    

    the req.user would be a user from a previous login, which i believe passport.js normally stores using the express-session package.