Setup passportjs for local authentication and authorization using expressjs

Signup and login are the most com­monly found use cases in web appli­ca­tions. In this post, I will doc­u­ment how you can use Pass­portjs to setup authen­ti­ca­tion and autho­riza­tion for your appli­ca­tions locally. Pass­portjs also lets you man­age OAUTH based authen­ti­ca­tion and we will see more exam­ples of using Pass­port with OAUTH in the future posts.


Pack­ages

npm install --save passport passport-local passport-github connect-flash

For the pur­pose of this demo, I will also be using a library called ‘async’ that makes it easy and clear to process mul­ti­ple asyn­chro­nous functions.

npm install --save async

Pass­port Setup

Next, we will cre­ate a file where we con­fig­ure pass­port to per­form the authen­ti­ca­tion. In our case, I will be cre­at­ing a config/passport.js file.

Lets setup the skele­ton of this file

config/passport.js

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

// Create a list of users just for demo.
// In reality, you will probably query a database
// to fetch this user
var users = {
  [email protected]': {
    id: 'userOne',
    password: 'one',
    email: [email protected]'
  },
  [email protected]': {
    id: 'userTwo',
    password: 'two',
    email: [email protected]'
  }
};

function createAccount (payload, callback) {
    // Call API to create account
    users[newUser.email] = newUser;
    callback(null, newUser);
}

// Invokes callback with an error if email already exists, null otherwise
function findExistingEmail (email, callback) {
    // Call API to check if the email already exists
    if(users[email]) {
      callback({err: 'Email Already Exists'});
    } else {
      callback(null, null);
    }
}

module.exports = function (passport) {
    // All our authentication code for signup and signin will go here
}

Con­tin­u­ing with our module.exports, we will now define the func­tions that han­dle the ses­sion data once a user has suc­cess­fully authenticated.

config/passport.js

module.exports = function (passport) {

  passport.serializeUser(function(user, done) {
    // This is what passport will save in the session/cookie
    done(null, user.email); 
  });

  passport.deserializeUser(function(email, done) {
    // Use the email saved in the session earlier
    // to fetch the actual user
    var user = users[email];
    done(null, user);
  });

}


SignIn

Lets add our first cru­cial func­tion where we will per­form the actual task of login.

config/passport.js

module.exports = function (passport) {

    // ...

    // We name our strategy 'local-login'.
    // You can use any name you wish
    // This is the name you will refer to later in a route
    // that handles the login on client request
    passport.use('local-login', new LocalStrategy({
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true
    },
    function(req, email, password, done) {
      if(users[email] && users[email].password === password) {
        return done(null, users[email]);
      } else {
        done(null, false, req.flash('message', 'Invalid email or password'));
      }
    }));

}

As you can tell in the code above, I expect the login form to have two fields with the names — ‘email’ and ‘pass­word’ respec­tively. Also, by set­ting the pass­Re­q­To­Call­back to true, I can access any other fields on the request object if need be.


SignUp

Now, lets write some code that han­dles signup. For our case, I will use async to chain the func­tion. We will first check if the email already exists. If not, asynch will pro­ceed with invok­ing the func­tion that cre­ates an account.

config/passport.js

passport.use('local-signup',
  new LocalStrategy({
    usernameField : 'email',
      passwordField : 'password',
      passReqToCallback : true // Pass back the entire request to the callback
    }, function (req, email, password, done) {

      process.nextTick(function() {

        async.auto({
          doesEmailAlreadyExist: function (cb, results) {
            findExistingEmail(email, cb);
          },
          newAccount: ['doesEmailAlreadyExist',
            function (cb, results) {

              var newUser = {
                email: email,
                password: password
              };
              createAccount(newUser, cb);
            }
          ]
        }, function (err, results) {
          if (err) {
            done(null, false, req.flash('signupMessage', err.message));
          } else {
            done(null, results.newAccount);
          }
        });

      });
  })
);

With that out of the way, lets setup our app.js and make it pass­port aware.


Setup app.js

Add the fol­low­ing lines of code just below your first set of vari­able declarations.

app.js

// --- Authentication ---
var passport = require('passport'),
  flash = require('connect-flash');

require('./config/passport')(passport);

app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// --------

Since we are using a ses­sion, pass­port requires express.session to be ini­tial­ized before it can be used. In our code, the express ses­sion was already ini­tial­ized a few lines ago such as

app.js

app.use(session({
  secret: config.session.SESSION_SECRET, // This can be any string
  saveUninitialized: true,
  resave: true
}));

We will also include the pass­port ref­er­ence in our set­tings object that is passed down to all the routes

app.js

var settings = {
    config: config,
    passport: passport
};


Route han­dlers

Now that we have every­thing in place, lets cre­ate sim­ple route han­dlers for the login and signup actions.

In our routes folder, I will cre­ate a file called auth.js that will han­dle all the login actions. In the future, we will add route han­dlers for OAUTH based login strate­gies in the same file.

routes/auth.js

module.exports = function(app, settings){
  var url = require('url'),
    express = require('express'),
    router = express.Router(),
    passport = settings.passport;

  router.post('/local-login', passport.authenticate('local-login', {
    successRedirect : '/home.html',
    failureRedirect : '/login.html',
    failureFlash : true
  }));

  router.post('/local-signup', passport.authenticate('local-signup', {
    successRedirect : '/home.html',
    failureRedirect : '/signup.html',
    failureFlash : true
  }));

  app.use('/auth',router);
};

If you look at the code above, in my router, I han­dle the local-signup and local-signin actions tand spec­ify the han­dler func­tion to be pass­port authen­ti­cate. The first argu­ment for authen­ti­cate must match the name of the strat­egy that we used in our pass­port con­fig­u­ra­tion file we cre­ated ear­lier. Note that I named every­thing the same for con­sis­tency and ease of maintenance.

Also, we add this newly added route han­dler file to our routes/index.js

routes/index.js

// Divide all of your modules in different files and
// require them here
module.exports = function(app, settings){
    require('./main')(app, settings);
    require('./auth')(app, settings);
    require('./home')(app, settings);
};

And thats pretty much it. Now you can cre­ate a login and signup form that sub­mits to the above url’s and you’re all set to use pass­port as a way to login and reg­is­ter for your application.

The full source code for this demo can be found in the passport-local-login branch of the node-starter-kit repository.