The middleware used for authentication in Node is Passport. It is designed to serve a singular purpose: authenticate requests. Normally users log in by providing a username and password. But today using an OAuth provider (such as Facebook or Twitter), it has become a popular authentication method.

Here, we use Passport for local authentication with a MongoDB database.

Topic Covered

  • How to use Passport
    • Authenticate
    • Configure
  • Example of Passport

Install it by using npm:

// Installing package locally 
$npm install passport

Authenticate

An authenticating request is made by calling passport.authenticate() function and specifying which strategy to use.

app.post('/login',
  passport.authenticate('local'),// for local strategy
  function(req, res) {
   // When authentication was successful this function get called.
   // `req.user` contains the authenticated user.
  res.redirect('/users/' + req.user.username);
  });
Redirects

After authenticating a request, it will redirect to the following page:

app.post('/login',
  passport.authenticate('local', { successRedirect: '/',
                                   failureRedirect: '/login' 
}))

The user will be redirected to the homepage if authentication is successful. Otherwise, the user will be redirected back to the login page.

Flash Messages

Flash messages can be used with redirects in order to display status information to the user.

app.post('/login',
  passport.authenticate('local', { successRedirect: '/',
                                   failureRedirect: '/login',
                                   failureFlash: true ,
                                   successFlash: 'Welcome!'
}));

Configure

To use Passport for authentication we have to configure:

  • Authentication strategies
  • Application middleware
  • Session
Authentication  Strategies

To authenticate requests strategies are used in the passport. Strategies range from verifying a username and password, or using OAuth or using OpenID. The strategy must be configured before an authenticated request is made by passport. The use() function are used to supply strategies and their configuration. Following uses the LocalStrategy for username/password authentication.

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

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));
Verify Callback

Authentication strategies use a verify callback function, to find the user that possesses a set of parameters. When Passport authenticates a request, it parses the parameters contained in the request. It then invokes the verify callback function with those parameters (username and password) as arguments. If the parameters are valid, done should be invoked to supply Passport to the user that authenticated.

return done(null, user);

If the parameters are not valid, done should be invoked with false instead of a user to indicate an authentication failure.

return done(null, false);

If an exception occurred while verifying the parameters, done should be invoked with an error, in conventional node style.

return done(err);
Application  Middleware

In a Express passport.initialize( ) middleware is required to initialize Passport. passport.session( ) middleware must be used if application uses persistent login sessions.

var session = require("express-session");
var  bodyParser = require("body-parser");

app.use(express.static("public"));
app.use(session({ secret: "mysecret" }));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
Sessions

The parameter used to authenticate a user can only be transmitted during the login request. If the authentication succeeds, a session will be established and maintained via a cookie set in the user’s browser.
Each subsequent request will not contain parameters, but rather the unique cookie that identifies the session. To support login sessions, Passport will serialize and deserialize user instances to and from the session.

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

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

Example:

Passport Authentication with local strategy:

First, install the following  npm module in root folder 

npm install express --save
npm install express-session --save
npm install connect-mongo --save
npm install body-parser --save
npm install passport --save
npm install passport-local --save
npm install mongoose --save

Create a login form in a file called  login.html  in the root folder of application

<!--                                 login.html                             -->
<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Passport</title>
</head>
<!-- login.html-->
<body>
 <form action="/" method="post">
   <div>
     <label>Username:</label>
     <input type="text" name="username" />
     <br/>
   </div>
   <div>
     <label>Password:</label>
     <input type="password" name="password" />
   </div>
   <div>
     <input type="submit" value="Submit" />
   </div>
 </form>
</body>
</html>

Create a file  app.js  in the root folder of the application,

                                                 /* app.js */
//  For Express  
const express = require('express');
const app = express();
var session = require('express-session');

const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));

// For MONGOOSE 
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/PassportDB');
var db = mongoose.connection;
const Schema = mongoose.Schema;
const userinfo = new Schema({
  username: String,
  password: String
});
const usersinfos = mongoose.model('users', userinfo, 'users');
var MongoStore = require('connect-mongo')(session);
app.get('/', (req, res) => res.sendFile('login.html', { root: __dirname }));

//  For Passport  
const passport = require('passport');
app.use(session({
  secret: 'secretsshh',
  resave: true,
  saveUninitialized: false,
  store: new MongoStore({
    mongooseConnection: db
  })
}));
app.use(passport.initialize());
app.use(passport.session());

app.get('/success', (req, res) => res.send("Welcome " + req.query.username + "!!" + "<a href='/logout'>logout</a>"));
app.get('/error', (req, res) => res.send("error logging in"));

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

passport.deserializeUser(function (id, cb) {
 userinfos.findById(id, function (err, user) {
    cb(err, user);
  });
});

// For Passport Local Authentication
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
  function (username, password, done) {
    userinfos.findOne({
      username: username
    }, function (err, user) {
      if (err) {
        return done(err);
      }
      if (!user) {
        return done(null, false);
      }
      if (user.password != password) {
        return done(null, false);
      }
      return done(null, user);
    });
  }
));

app.post('/',
  passport.authenticate('local', { failureRedirect: '/error' }),
  function (req, res) {
    res.redirect('/success?username=' + req.user.username);
  });

app.get('/logout', function (req, res, next) {
  if (req.session) {
    // For Delete Session Object
    req.session.destroy(function (err) {
      if (err) {
        return next(err);
      } else {
        return res.redirect('/');
      }
    });
  }
});

app.listen(8080, function () {
  console.log('App listening on port 8080')
});

The serializeUser() function serializes the user instance with the information we pass to it and store it in the session via a cookie. The deserializeUser() invoked every subsequent request to deserialize the instance, providing it with the unique cookie identifier as a “credential”.

We require the passport–local strategy. Then we tell Passport to use an instance of the LocalStrategy we required. There we find a record based on the username. If a record is found and the password matches, the above code returns the user object. Otherwise, it returns false.

In strategy implementation, with the authenticate method which attempts to authenticate with the given strategy on its first parameter local. It will redirect to /error if it fails. Otherwise, redirect to the /success route, sending the username as a parameter.

First, create database passportDB and create collection users with username and password as a field and insert a record with  user1 as username  and  userpass as a password  in MongoDB.

start server with the command
node app.js
now point the browser to:

http://localhost:8080

and login with user1 as username and userpass as a password.

Get More Knowledge: