Express – Authentication with PassportJS

In this blog post we will see how to integrate passportjs with express

Passport is authentication middleware for express. Its very simple, unobtrusive and supports many authentication mechanisms.

To Install Passport

$ npm install passport --save

Basics

Most websites employ a basic username/password login strategy. Lets see how to implement it using Passport.

Passport divides authentication broadly into 3 parts
1. Authenticator
2. Authentication Strategy
3. Session

The Authenticator mainly decides the flow of code, i.e how will authentication error, success be handled. What information goes to session, how to handle db operations on success ,error etc.

The Strategy handles the actual authentication process, i.e facebook authentication, google authentication, user/password based etc.

The Session, handles what data is stored in session and how. This is optional, since many cases might not require session store. e.g API’s require each request to be authenticated.

Username/Password Authentication

Password provide a ‘Strategy’ called local, which can be used for this. To use this install the express session and flash modules as well.

$ npm install passport-local --save
$ npm install express-session --save
$ npm install connect-flash --save

In your app.js file add these

var session = require('express-session');
var flash = require('connect-flash');

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

app.use(session({secret: 'keyboard cat'}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());

Do all the above before your routes middleware.

passport.use(new LocalStrategy({
    usernameField: 'email',  //your login form input type names
    passwordField: 'passwd'
},
function (username, password, done) {
    console.log(username + 'username');
    //authenticate only if username is test
    if (username != 'test') {
        return done(null, false, {message: 'Incorrect username.'});
    } else {
       //dummy user object below
        return done(null, {
            id: 1,
            email: 'test@as.com',
            username: 'test'
        });
    }
}
));
passport.serializeUser(function (user, done) {
    console.log('seralize');
    console.log(user);
    //push entire object to session
    done(null, user);
});

passport.deserializeUser(function (user, done) {
    console.log('de seralize');
    console.log(user);
    done(null, user);
});

In the above code if username is test if allows user to login and stores the entire user object in session.

In your routes file

//login form submit
router.post('/login',
        passport.authenticate('local', {
            successRedirect: '/users/login',
            failureRedirect: '/users/login',
            failureFlash: true})
        );

In Your Login Form, i am using express handlebar as view engine

<br>
{{message}}
<br>
{{#with user}}
Login ID: {{id}}
<a href="#/users/logout">Logout</a>
{{/with}}
<br>
<form method="post" action="/users/login">
    <div>
        <label>Username:</label>
        <input type="text" name="email">
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="passwd">
    </div>
    <div>
        <input type="submit" value="Log In">
    </div>
</form>
var express = require('express');
var router = express.Router();
var passport = require('passport');
var flash = require('connect-flash');


//login form route
router.get('/login', function (req, res) {
    console.log(req.user); //if authentication will show object else null
    res.render('index', {
        message: req.flash('error'),
        user: req.user
    }
    );
});
//logout route
router.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/users/login');
})

Above we see a session based authentication using password. To modularize your code further you can move all passport code from app.js to a custom middleware as shown here.

Authentication for API
Suppose your are building an api and want every request to be authentication. This is how to do it. Also since no sessions are required, those can be removed. So this would be the entire code in app.js


passport.use(new LocalStrategy({
    usernameField: 'api_key',
    passwordField: 'api_secret'
},
function (username, password, done) {
    console.log(username + 'username');
    if (username != 'test') {
        return done(null, false, {message: 'Incorrect username.'});
    } else {
        return done(null, {
            id: 1,
            email: 'test@as.com',
            username: 'test'
        });
    }
}
));

app.use('/api', function (req, res,next) {
//    req.body.api_key
//    req.body.api_secret
// api key and secret should be set in your req body either as a json request payload //or post data
    passport.authenticate('local', {session: true}, function (err, user) {
        if (!user) {
            return res.sendStatus(403);
        }
        //authenticated
        req.user = user;
        next();
    })(req, res);
});

Integrate with MongoDB

To integrate with mongodb we just need to make changes to our strategry


var mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1/db');
var conn = mongoose.connection;

var user_schema = mongoose.Schema({}, {
    strict: false,
    collection: 'user'
});
var User = conn.model('user', user_schema);
user_schema.methods.validPassword = function (password, cb) {
    return this.model('User').findOne(
            {
                username: this.username,
                password: password
            },
    function (err, row) {
        if (row) {
            cb(true);
        } else {
            cb(false);
        }
    });
};

passport.use(new LocalStrategy({
    usernameField: 'email',  //your login form input type names
    passwordField: 'passwd'
},
function (username, password, done) {
    console.log(username + 'username');
     User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      user.validPassword(password,function(valid){
          if(!valid){
               return done(null, false, { message: 'Incorrect password.' });
          }
      });
      
      return done(null, user);
    });
}
));

So above is entire implementation of passport with expressjs. For further details and features visit documentation. Reference Documentation Links : http://passportjs.org/guide/

There are many more authentication strategy like facebook, google, twitter, tumbler, etc which can be seen in their documentation.