On passport.js, specific use cases

In the previous post, I talked about the authentication flow and the flow for subsequent requests using passportjs. This post will cover some specific use cases.

What user information to store in the session and what not?

It's best to keep the session information small and only attach user information you actually need (note: some user info should be kept - like for instance the user ID, otherwise passport cannot use its Session Strategy).

However, if you use the deserializeUser method to go and load the user from the db based on the user ID attached to the session, it's better to store the entire user object in the session. This will prevent a roundtrip to the database on every request just to fetch user information.

How to split up passportjs configuration between multiple files?

In the example, all password configuration and Local Strategy definition was specified in the main app.js file.

The passport instance we get back from require('passport') is a singleton. So we can just configure express to use the passport middleware in app.js while configuring passport in another file. They all need to require('passport').

There's no need to pass the password instance around.

What if my form elements are named email and password instead of username and password?

Configure which values passport should use from the request body by the options object passed while configuring the Local Strategy.

passport.use(new LocalStrategy(
    {
        usernameField: 'email',
        passwordField: 'password'   
    },
    function verify(email, password, done) {

        findByEmail(email, function(err, user) {
            if (err) return done(err);
            if (!user) return done(null, false);
            passwordHash.compare(password, user.password, function(err, res) {
                if (err) return done(err);
                if (!res) return done(null, false);
                done(null, user);
            });
        });
    }
));

What if I don't like how passport.authenicate redirects the user but want to respond with a JSON response?

Don't use passport.authenticate as middleware but invoke it directly in the request handler. We need to manually call login for this to work.

app.post('/login', function handleLocalAuthentication(req, res, next) {

    passport.authenticate('local', function(err, user, info) {
        if (err) return next(err);
        if (!user) {
            return res.json(403, {
                message: "no user found"
            });
        }

        // Manually establish the session...
        req.login(user, function(err) {
            if (err) return next(err);
            return res.json({
                message: 'user authenticated',
            });
        });

    })(req, res, next);
};

How to restrict access to certain pages?

Just define your own middleware to be invoked on certain routes to restrict access.

// Define it
module.exports = function restrict(req, res, next) {
    if (req.isUnAuthenticated()) return req.json(403, {message: 'Access denied, please log in'});

    next();
}

Use it when setting up your routes.

// Use the restrict middleware
app.get('/route-with-restricted-access', restrict, function(res, res, next) {
    // Handle request...
});

If the user object contains role information, we could make additional checks on which role a user has and depending on that restrict access.