HI WELCOME TO SIRIS

Simple Nodejs Authentication System Using Passport

Leave a Comment
Simple Nodejs Authentication System Using Passport is today’s leading topic. In this tutorial, you will learn how to use a passport.js to authenticate the user. So, what is Passport.jsPassport.js is a simple, unobtrusive Node.js authentication middleware for Node.js.Passport.js can be used in any Express.js-based web application. So we will use the Node.js as a platform, Express as a web framework, MongoDBas a database and Passport.js as a Middleware library for the web application.

Nodejs Authentication System Using Passport

We will build an authentication system step by step, and it is going to be a long ride. So stay tuned.
Note: If any step you will find any errors then please go to my Github code. A link is at the below of the post.

Step 1: Install the NPM dependencies

First, we need to install all the required dependencies for this project. So create one file called package.json. We can create this file using the terminal. Go to the root of the project and hit the following command.
npm init
So answer the all the questions, and it will create a file.
Next, you will write some of its dependencies as follow.
{
  "name": "expresslogin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon ./server.js --exec babel-node -e js"
  },
  "dependencies": {
    "bcryptjs": "*",
    "body-parser": "*",
    "connect-flash": "*",
    "cookie-parser": "*",
    "express": "*",
    "ejs": "*",
    "express-messages": "*",
    "express-session": "*",
    "express-validator": "*",
    "mongodb": "*",
    "mongoose": "*",
    "nodemon": "*",
    "passport": "*",
    "passport-http": "*",
    "passport-local": "*"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1"
  },
  "author": "Sireesh Kantamaneni",
  "license": "ISC"
}
Here I have to install dependencies and dev dependencies. Copy this two objects dependencies and dev dependencies in your project and then hit the following command.
npm install
Also, we have installed babel-cli because it can transform our ES6 code into ES5 and run the Node.js code on the server. Create one more file called .babelrc.
{
  "presets": [
    "es2015", "stage-0"
  ]
}

Step 2: Make server.js file and start the node server

In root directory, create one file called server.js.
import express from 'express';
import path from 'path';
import cookeParser from 'cookie-parser';
import bodyParser from 'body-parser';
import ejs from 'ejs';
import expressValidator from 'express-validator';
import flash from 'connect-flash';
import session from 'express-session';
import passport from 'passport';
const LocalStrategy = require('passport-local').Strategy;
import mongoose from 'mongoose';

const app = express();
const PORT = 3000;

mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/passport', {
  useMongoClient: true
});

app.get('/', (req, res) => {
  res.send('Node and express are properly running');
});

app.listen(PORT, function(){
  console.log('Server is running on',PORT);
});
Now,  go to the terminal and hit the following command.
npm start
It will start the server, and at the port 3000, our application is running. So go to the http://localhost:3000
Node and express are properly running.”
Next, we need to set the view engine and middlewares for our application.

// app.js

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(cookeParser());

app.use(passport.initialize());
app.use(passport.session());

app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true, }));

app.use(expressValidator());

app.use(flash());

app.use(function(req, res, next){
  res.locals.success_message = req.flash('success_message');
  res.locals.error_message = req.flash('error_message');
  res.locals.error = req.flash('error');
  next();
});
We have set the view engine to ejs and set the express validatorsessions and flash message middlewares.
Also, we have initialized the passport library.

Step 3: Making required directories

In the root folder, make one directory called views and inside that directory, create one directory called pages.
Inside pages directory, make following two files.
  1. index.ejs
  2. users.ejs
Also, we need to make one more folder inside the views directory called partials. This folder contains a header, footer and sidebar files. 
Right now, I am just creating a header.ejs file.
<!-- header.ejs -->

<nav class="navbar navbar-default" role="navigation">
  <div class="container-fluid">
      <div class="navbar-header">
          <a class="navbar-brand" href="#">
              Express Auth App
          </a>
          <ul class="nav navbar-nav">
              <li><a href="/">Home</a></li>
              <li><a href="/users/register">Register</a></li>
              <li><a href="/users/login">Login</a></li>
          </ul>
     </div>
   </div>
</nav>
Next, make one directory in the root called, routes and inside that, create two files.
  1. index.js
  2. users.js
Write the following code inside the index.js
// routes > index.js

import express from 'express';
let router =  express.Router();

router.get('/', function(req, res){
  res.render('index');
});

export default router;
Also, write the following code inside the users.js file.
// routes > users.js

import express from 'express';
let router =  express.Router();

router.get('/', function(req, res){
  res.render('pages/users');
});
router.get('/register', function(req, res){
  res.render('pages/register');
});

export default router;
Now, import this files into the server.js file. I am displaying final server.js file code.
// server.js

import express from 'express';
import path from 'path';
import cookeParser from 'cookie-parser';
import bodyParser from 'body-parser';
import ejs from 'ejs';
import expressValidator from 'express-validator';
import flash from 'connect-flash';
import session from 'express-session';
import passport from 'passport';
const LocalStrategy = require('passport-local').Strategy;
import mongoose from 'mongoose';
import index from './routes/index';
import users from './routes/users';

mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/CRMDB', {
  useMongoClient: true
});
let db = mongoose.connect;

const app = express();
const PORT = 3000;

app.use(express.static(__dirname + '/public'));

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookeParser());

app.use(session({
 secret: 'keyboard cat',
 resave: false,
 saveUninitialized: true,
}));

app.use(passport.initialize());
app.use(passport.session());

app.use(expressValidator());

app.use(flash());

app.use(function(req, res, next){
 res.locals.success_message = req.flash('success_message');
 res.locals.error_message = req.flash('error_message');
 res.locals.error = req.flash('error');
 res.locals.user = req.user || null;
 next();
});

app.use('/', index);
app.use('/users', users);

app.listen(PORT, function(){
 console.log('Server is running on',PORT);
});

Step 4: Create pages of login and register

First, we need to include the bootstrap css framework to design the web pages.
Now, include the header.ejs part in the index.ejs file.
<!-- index.ejs -->

<html>
 <head>
   <title>INDEX</title>
   <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css">
 </head>
 <body>
  <div class="container">
        <% include ../partials/header %>
    Home Page
   </div>
</body>
</html>
Now, create a register.ejs file inside views  >>  pages folder.
<!-- register.ejs -->

<html>
  <head>
    <title>Register</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <div class="container">
      <% include ../partials/header %>
      <div class="panel panel-primary">
        <div class="panel-heading">
          Register The User
        </div>
        <div class="panel-body">
          <form method="post" action="/register">
            <div class="form-group">
              <label class="col-md-4">Name</label>
              <input type="text" class="form-control" name="name"/>
            </div>
            <div class="form-group">
              <label class="col-md-4">Email</label>
              <input type="text" class="form-control" name="email"/>
              </div>
              <div class="form-group">
                <label class="col-md-4">Password</label>
                <input type="password" class="form-control" name="password"/>
              </div>
              <div class="form-group">
                <label class="col-md-4">Confirm Password</label>
                <input type="password" class="form-control" name="cfm_pwd"/>
              </div>
              <div class="form-group">
                <button type="submit" class="btn btn-primary">Add User</button>
              </div>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

Step 5: Server side validation.

In the routes >> users.js file, paste the following code.
// users.js

router.post('/register', function(req, res){
  let name = req.body.name;
  let email = req.body.email;
  let password = req.body.password;
  let cfm_pwd = req.body.cfm_pwd;

  req.checkBody('name', 'Name is required').notEmpty();
  req.checkBody('email', 'Email is required').notEmpty();
  req.checkBody('email', 'Please enter a valid email').isEmail();
  req.checkBody('password', 'Password is required').notEmpty();
  req.checkBody('cfm_pwd', 'Confirm Password is required').notEmpty();
  req.checkBody('cfm_pwd', 'Confirm Password Must Matches With Password').equals('req.body.password');

  let errors = req.validationErrors();
  if(errors){
    res.render('pages/register',{errors: errors});
  }
  else{
    console.log('Success');
  }
});
Now, we have put the server side validation, so we need to display the errors, so what we have done is put the error and success messages inside the header.ejs file.
<%if (success_message != '') { %>
 <div class="alert alert-success">
  <%= success_message %>
 </div>
<% } %>
<%if (error_message != '') { %>
 <div class="alert alert-danger">
  <%= error_message %>
 </div>
<% } %>
<%if (error != '') { %>
 <div class="alert alert-danger">
  <%= error %>
 </div>
<% } %>

Step 6: Save the User in the database.

Make one folder in the project root called models and inside make one file called User.js
// User.js

import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';

let UserSchema = mongoose.Schema({
  name: {
    type: String,
    index: true
  },
  email: {
    type: String
  },
  password: {
    type: String
  }
});

export const User = mongoose.model('User', UserSchema);

export const createUser = (newUser, callback) => {
  bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(newUser.password, salt, function(err, hash) {
        newUser.password = hash;
        newUser.save(callback);
    });
  });
}
Now, import this User model into the routes  >> users.js file.
So final file looks like this.
import express from 'express';
import { User, createUser } from '../models/User';
let router =  express.Router();

router.get('/', function(req, res){
  res.render('pages/users');
});
router.get('/register', function(req, res){
  res.render('pages/register');
});
router.post('/register', function(req, res){
  let name = req.body.name;
  let email = req.body.email;
  let password = req.body.password;
  let cfm_pwd = req.body.cfm_pwd;

  req.checkBody('name', 'Name is required').notEmpty();
  req.checkBody('email', 'Email is required').notEmpty();
  req.checkBody('email', 'Please enter a valid email').isEmail();
  req.checkBody('password', 'Password is required').notEmpty();
  req.checkBody('cfm_pwd', 'Confirm Password is required').notEmpty();
  req.checkBody('cfm_pwd', 'Confirm Password Must Matches With Password').equals(password);

  let errors = req.validationErrors();
  if(errors)
  {
    res.render('pages/register',{errors: errors});
  }
  else
  {
    let user = new User({
      name: name,
      email: email,
      password: password
    });
    createUser(user, function(err, user){
        if(err) throw err;
        else console.log(user);
    });
    req.flash('success_message','You have registered, Now please login');
    res.redirect('login');
  }
});

router.get('/login', function(req, res){
  res.render('pages/login');
});

router.post('/login', function(req, res){
  res.render('pages/login');
});

export default router;
Also, we need to make a login page in the views >> pages folder.
<!-- login.ejs -->

<!doctype html>
  <head>
    <title>Login User</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css">
  </head>
  <body>
    <div class="container">
      <% include ../partials/header %>
      <div class="panel panel-primary">
        <div class="panel-heading">
          Login User
        </div>
        <div class="panel-body">
          <form method="post" action="/users/login">
            <div class="form-group">
              <label class="col-md-4">Email</label>
              <input type="text" class="form-control" name="email"/>
              </div>
              <div class="form-group">
                <label class="col-md-4">Password</label>
                <input type="password" class="form-control" name="password"/>
              </div>
              <div class="form-group">
                <button type="submit" class="btn btn-primary">Login</button>
              </div>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>
Change the header navigation also.
<!-- header.ejs -->

<nav class="navbar navbar-default" role="navigation">
  <div class="container-fluid">
      <div class="navbar-header">
          <a class="navbar-brand" href="#">
              Express Auth App
          </a>
          <ul class="nav navbar-nav">
              <li><a href="/">Home</a></li>
              <li><a href="/users/register">Register</a></li>
              <li><a href="/users/login">Login</a></li>
          </ul>
      </div>
    </div>
    </nav>
If everything is okay, then you can see the user has been created, saved in the MongoDB database and you can also see the user in the console.

Step 7: Write the logic for Login.

Next step would be to write the passport authenticate middleware inside the post route of the login and the login.
// routes  >>  users.js

router.post('/login', passport.authenticate('local', {
    failureRedirect: '/users/login', failureFlash: true
    }), 
    function(req, res){
        req.flash('success_message', 'You are now Logged in!!');
    res.redirect('/');
    }
);
Now, we need to use local passport strategy. So write the code inside the users.js file.
// routes >> users.js

passport.use(new LocalStrategy({
 usernameField: 'email',
 passwordField: 'password',
 passReqToCallback : true
},
 function(req, email, password, done) {
   getUserByEmail(email, function(err, user) {
   if (err) { return done(err); }
   if (!user) {
    return done(null, false, req.flash('error_message', 'No email is found'));
   }
   comparePassword(password, user.password, function(err, isMatch) {
    if (err) { return done(err); }
    if(isMatch){
    return done(null, user, req.flash('success_message', 'You have successfully logged in!!'));
    }
    else{
        return done(null, false, req.flash('error_message', 'Incorrect Password'));   }
    });
   });
  }
));

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

passport.deserializeUser(function(id, done) {
  getUserById(id, function(err, user) {
    done(err, user);
  });
});
Now, the whole file will look like this.
// users.js

import express from 'express';
import { User, createUser, comparePassword, getUserByEmail, getUserById } from '../models/User';
import passport from 'passport';

let LocalStrategy = require('passport-local').Strategy;
let router =  express.Router();

router.get('/', function(req, res){
    res.render('pages/users');
});
router.get('/register', function(req, res){
   res.render('pages/register');
});
router.post('/register', function(req, res){
   let name = req.body.name;
    let email = req.body.email;
    let password = req.body.password;
    let cfm_pwd = req.body.cfm_pwd;

    req.checkBody('name', 'Name is required').notEmpty();
    req.checkBody('email', 'Email is required').notEmpty();
    req.checkBody('email', 'Please enter a valid email').isEmail();
    req.checkBody('password', 'Password is required').notEmpty();
    req.checkBody('cfm_pwd', 'Confirm Password is required').notEmpty();
    req.checkBody('cfm_pwd', 'Confirm Password Must Matches With Password').equals(password);

    let errors = req.validationErrors();
    if(errors)
    {
        res.render('pages/register',{errors: errors});
    }
    else
    {
        let user = new User({
        name: name,
        email: email,
        password: password
        });
        createUser(user, function(err, user){
            if(err) throw err;
            else console.log(user);
        });
        req.flash('success_message','You have registered, Now please login');
        res.redirect('login');
    }
});

router.get('/login', function(req, res){
   res.render('pages/login');
});

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback : true
},
    function(req, email, password, done) {
     getUserByEmail(email, function(err, user) {
        if (err) { return done(err); }
       if (!user) {
            return done(null, false, req.flash('error_message', 'No email is found'));
       }
       comparePassword(password, user.password, function(err, isMatch) {
         if (err) { return done(err); }
         if(isMatch){
           return done(null, user, req.flash('success_message', 'You have successfully logged in!!'));
         }
         else{
           return done(null, false, req.flash('error_message', 'Incorrect Password'));
        }
     });
     });
  }
));

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

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

router.post('/login', passport.authenticate('local', {
    failureRedirect: '/users/login', failureFlash: true
    }), 
    function(req, res){
        req.flash('success_message', 'You are now Logged in!!');
    res.redirect('/');
    }
);

router.get('/logout', function(req, res){
    req.logout();
    req.flash('success_message', 'You are logged out');
    res.redirect('/users/login');
});

export default router;
Also, we have imported models  >>  User.js.
// models  >>  User.js

import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';
import passport from 'passport';

let UserSchema = mongoose.Schema({
    name:{
    type: String,
    index: true
   },
   email:{
    type: String
   },
   password:{
    type: String
   }
});

export const User = mongoose.model('User', UserSchema);

export const createUser = (newUser, callback) => {
 bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(newUser.password, salt, function(err, hash) {
        newUser.password = hash;
        newUser.save(callback);
    });
   });
}

export const getUserByEmail = (email, callback) => {
  let Obj = {email: email}
  User.findOne(Obj, callback);
}

export const comparePassword = (password, hash, callback) => {
 bcrypt.compare(password, hash, function(err, isMatch){
    if(err) throw err;
    callback(null, isMatch);
 });
}

export const getUserById = (id, callback) => {
   User.findById(id, callback);
}
Now, we need to write isLoggedIn middleware in the routes  >>  index.js file.
import express from 'express';
let router =  express.Router();

router.get('/', isLoggedIn, function(req, res){
   res.render('pages/index');
});

function isLoggedIn(req, res, next){
    if(req.isAuthenticated()){
        next();
    }
    else{
        res.redirect("/users/login");
    }
}

export default router;
Also, our header.ejs file looks like this.
<!-- header.ejs -->

<header>
 <nav class="navbar navbar-default" role="navigation">
      <div class="container-fluid">
    <div class="navbar-header">
        <a class="navbar-brand" href="#">
           Express Auth App
        </a>
        <ul class="nav navbar-nav">
         <% if(user){ %>
          <li><a href="/">Home</a></li>
          <li><a href="/users/logout">Logout</a></li>
             <% } else{ %>  
           <li><a href="/users/register">Register</a></li>
           <li><a href="/users/login">Login</a></li>
          <% } %>
             </ul>
           </div>
    </div>
    </nav>
</header>
<%if (success_message != '') { %>
 <div class="alert alert-success">
    <%= success_message %>
 </div>
<% } %>
<%if (error_message != '') { %>
  <div class="alert alert-danger">
    <%= error_message %>
  </div>
<% } %>
<%if (error != '') { %>
 <div class="alert alert-danger">
    <%= error %>
 </div>
<% } %>

Github Code

If you still find any error then please go to my Github Code.

Our tutorial on Nodejs Authentication System Using Passport is over.

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.