Simple Nodejs Authentication System Using Passport

In this tutorial, you will learn how to use 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 Node.js as a platform, Express as a web framework, MongoDB as 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 below to 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 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": "KRUNAL LATHIYA",
  "license": "ISC"
}

Here I have to install dependencies and dev dependencies. Copy these 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. Finally, create one more file called .babelrc.

{
  "presets": [
    "es2015", "stage-0"
  ]
}

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

In the 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 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 validator, sessions, 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 the pages directory, make the 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 these files into the server.js file. I am displaying the 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 the 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.

The 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 a 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 errors, then please go to my Github Code.

Fork Me On Github

That’s it for this tutorial.

1 thought on “Simple Nodejs Authentication System Using Passport”

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.