Table of contents
1.
Introduction
2.
What is NodeJs?
3.
What is ExpressJs? 
4.
What is PassportJs?
5.
Prerequisites
5.1.
NodeJS
5.2.
ExpressJS
5.3.
Passport Js
6.
Authentication flow
7.
Project Structure
7.1.
Installing the dependencies
7.2.
Views
7.3.
Routes
7.3.1.
login.ejs
7.3.2.
register.ejs
7.3.3.
secret-page.ejs
7.4.
style.css
7.5.
localhost/register
7.6.
localhost/login
7.7.
localhost/
7.8.
App.js
7.9.
Result
8.
Frequently Asked Questions
8.1.
What are serialization and deserialization?
8.2.
What is passport.js?
8.3.
What is passport.initialize() and passport.session()?
8.4.
What can you use Node.js for?
8.5.
Can we use Node.js to build AI and Machine learning algorithms?
9.
Conclusion
Last Updated: Mar 27, 2024

Authenticate Users With Node ExpressJS and Passport.js

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Passport.js is a very flexible and powerful middleware used for authenticating requests in node.js applications. Passport.js enables applications to use different types of strategies without using a lot of code. Its sole purpose is to authenticate the the incoming requests, which is it does using a set of plugins known as strategies.

OG image

So, in this blog we will be learning about how we can authenticate users using Node JS, Express JS and Passport JS. Let us now have a look at the authentication flow of passport.js.

What is NodeJs?

Node.js is an open-source, cross-platform Javascript runtime environment that executes JavaScript code outside of a web browser. It allows developers to run JavaScript on the server side to create server-side and networking applications. Node.js is built on the V8 JavaScript engine, the same engine that runs in Google Chrome, and it provides an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js is commonly used for web scraping, real-time data processing, and building web servers and APIs.

What is ExpressJs? 

Express.js is a web application framework for Node.js. It is designed to make the process of creating server-side and networking applications easier by providing a set of features and functions that simplify the process of handling requests, routing, and middleware.

What is PassportJs?

Passport.js is an authentication middleware for Node.js. It is designed to authenticate requests made to a web application or API by providing a simple, flexible, and modular way to authenticate users using a variety of strategies. Passport.js supports many different authentication strategies, such as local authentication using a username and password, OAuth and OAuth2 for authenticating with popular social media and other external services, and JWT for stateless authentication.

OAuth (Open Authorization) is a standard for authorization of access to resources. It is an open protocol to allow secure authorization in a simple and standard method from web, mobile, and desktop applications.

Prerequisites

NodeJS

First and foremost, NodeJS should be installed on your local machine. You can download and install the latest version of Node.js from the official websiteFor detailed installation guide you can check this link here.

ExpressJS

You can install ExpressJS only after you have installed NodeJS on your local machine. To install express run the following command in project directory. 

npm install express

Passport Js

To install Passport JS you need to install NodeJS first. Then after installing NodeJS you can run the following command in your project directory

npm install passport

Authentication flow

The main steps of authentication flow in passport.js are:

  • Installing required modules
  • Using passport.initialize() and passport.session() middleware 
  • Specifying a route that uses passport.authenticate() to authenticate a request
  • Configuring Passport using a Strategy
  • Setting up serializeUser and deserializeUser
  • Redirecting user to a route after authentication

In our application, we will authenticate users with passport.js, when a user tries to login with email and password, a POST request will be sent to /login route, from where passport.authenticate() will be executed.

The strategy used here is LocalStrategy and req.body.username,

req.body.password is passed as arguments. LocalStrategy verifies the data entered by the user by comparing it with the data present in the user's database. 

In case of successful authentication, the verify callback is invoked with the credentials. If authentication is successful, done() is invoked with (null, user) to supply a Passport that the user is authenticated with. But in case of a failure, done is invoked with (null, false) and an error message.

After the user is successfully authenticated, the user instance is serialized using serializeUser and done() is invoked with null and user.id. Here user.id is saved in the session in req.session.passport.user and is used to retrieve the user object by deserializeUser later on.

The deserializeUser deserializes user instances from the session. Deserialization takes place using the user id, and then the user object is attached to the request as req.user.

deseriaslizing user

 

The passport.initialize() middleware is executed on every request. It finds the id in the session, and then the deserializeUser is called. During deserialization the user information is loaded to the request through req.user.

Flow

Project Structure

Before starting the project, let us look at our project structure,

directory

Installing the dependencies

Type command

npm init -y

 

To create an empty npm project:

Creating empty npm project


You will notice the package.json file gets created.

Now we can start installing the dependencies we will need for this project.

npm i express bcrypt passport passport-local express-flash express-session method-override dotenv ejs


Now we will create config.env and .gitignore files.

config environment screenshot
gitignore screenshot

 

Let us now create the main file, app.js and create a basic express app with this code,

var express = require("express");
const app = express();
const dotenv = require("dotenv");
dotenv.config({path:'./config.env'});
const PORT = process.env.PORT;

// Handling the get request
app.get("/", (req, res) => {
	res.send("Hello from server");
});
// Starting the server on PORT
app.listen(PORT, () => {
	console.log(`The application started successfully!`);
});

 

Type nodemon app.js to run the project

run the project

 

Go to http://localhost/ to check whether the application is running or not. If everything is working fine, You should be able to see this message, 

Localhost

Views

We will now create a folder called ‘views’ to keep our templates.

Inside this folder, we are going to create,

  • login.ejs: user will be redirected here to login to their account.
  • register.ejs: user will be redirected here to register their account.
  • secret-page.ejs: user will be redirected to this page only after he/she is logged in.
Views

 

Routes

app.get("/", (req, res) => {
   res.render("secret-page.ejs");
 });
 //Login
 app.get("/login", (req, res) => {
   res.render("login.ejs");
 });
 // Register
 app.get("/register", (req, res) => {
   res.render("register.ejs");
 });

login.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/style.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"
      rel="stylesheet"
    />
    <link
      href="//cdn.muicss.com/mui-0.10.3/css/mui.min.css"
      rel="stylesheet"
      type="text/css"
    />
    <script src="//cdn.muicss.com/mui-0.10.3/js/mui.min.js"></script>
    <title>Login</title>
  </head>

  <body>
    <img src="/CN_logo.png" class="CNLogo" alt="" />
    <div class="container">
      <div class="box">
        <div class="msg">
          <p>Login here Ninja</p>
          <span>
            <img class="ninjaLogo" src="/NinjaLogo.jpeg" alt="" />
          </span>
        </div>
       <% if (messages.error) { %>
        <p class="err-msg"><%= messages.error %></p>
        <% } %>
        <form action="/login" method="POST">
          <div>
            <label class="label-form" for="email">Email</label>
            <input
              class="input"
              type="email"
              id="email"
              name="email"
              required
            />
          </div>
          <div>
            <label class="label-form" for="password">Password</label>
            <input
              class="input"
              type="password"
              id="password"
              name="password"
              required
            />
          </div>
          <button class="mui-btn mui-btn--primary" type="submit">LOG IN</button>
        </form>
        <p class="msg-s">New user? <a href="/register">Register</a></p>
      </div>
    </div>
  </body>
</html>

register.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/style.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"
      rel="stylesheet"
    />
    <link
      href="//cdn.muicss.com/mui-0.10.3/css/mui.min.css"
      rel="stylesheet"
      type="text/css"
    />
    <script src="//cdn.muicss.com/mui-0.10.3/js/mui.min.js"></script>
    <title>Register</title>
  </head>

  <body>
    <img src="/CN_logo.png" class="CNLogo" alt="" />
    <div class="container">
      <div class="box">
        <div class="msg">
          <p>Register here Ninja</p>
          <span>
            <img class="ninjaLogo" src="/NinjaLogo.jpeg" alt="" />
          </span>
        </div>
        <form action="/register" method="POST">
          <div>
            <label class="label-form" for="name">Name</label>
            <input class="input" type="text" id="name" name="name" required />
          </div>
          <div>
            <label class="label-form" for="email">Email</label>
            <input
              class="input"
              type="email"
              id="email"
              name="email"
              required
            />
          </div>
          <div>
            <label class="label-form" for="password">Password</label>
            <input
              class="input"
              type="password"
              id="password"
              name="password"
              required
            />
          </div>
          <button class="mui-btn mui-btn--primary" type="submit">
            Register
          </button>
        </form>
        <p class="msg-s">Already a user? <a href="/login">Login</a></p>
      </div>
    </div>
  </body>
</html>

 

secret-page.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="/style.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"
      rel="stylesheet"
    />
    <link
      href="//cdn.muicss.com/mui-0.10.3/css/mui.min.css"
      rel="stylesheet"
      type="text/css"
    />
    <script src="//cdn.muicss.com/mui-0.10.3/js/mui.min.js"></script>
    <title>Secret Page</title>
  </head>

  <body>
    <img src="/CN_logo.png" class="CNLogo" alt="" />
    <div class="container">
      <div class="box">
        <p class="msg">Login Sucessful Ninja🤘</p>
        <form action="/logout" method="post">
          <button class="mui-btn mui-btn--primary" type="submit">
            LOG OUT
          </button>
        </form>
      </div>
    </div>
  </body>
</html>

 

We will then create style.css in a new folder named ‘public’.

style.css

* {
  font-family: "Open Sans", sans-serif;
}
body {
 background: linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%) fixed;
  min-width: 100%;
  min-height: 100%;
}

.CNLogo {
  margin-top: 10px;
  margin-left: 10px;
  height: 50px;
  width: 100px;
}
.ninjaLogo {
  display: inline;
  width: 37px;
  height: 37px;
}

.login-box {
  display: flex;
  height: 300px;
  width: 300px;
  justify-content: center;
  align-content: center;
}

.container {
  margin: 15px;
  padding: 15px;
}

.err-msg {
  border-radius: 8px;
  margin-bottom: 12px;
  color: red;
}

.box {
  background-color: white;
  font-size: 1.3rem;
  width: 320px;
  height: 100%;
  border-radius: 4%;
  border: solid 3px black;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin: auto;
  align-items: center;
  padding: 15px;
  transition: all 0.2s ease-in-out;
}

.box:hover {
  box-shadow: 2px 2px 4px 4px black;
  transform: scale(1.02);
}

.msg {
  text-align: center;
  font-size: 26px;
  height: 60px;
  display: flex;
}

.msg-s {
  margin-top: 10px;
  text-align: center;
  font-size: 20px;
}

form label {
  display: block;
  color: #eee;
  margin-bottom: 5px;
}

.label-form {
  font-size: 20px;
  color: #0275d8;
}

.input {
  margin-bottom: 15px;
  padding: 5px;
  width: 10rem;
  height: 1.2rem;
  font-size: 15px;
  border: 2px solid black;
  border-radius: 6px;
  cursor: pointer;
}


After adding CSS, the pages should look like this,

localhost/register

Register screenshot

localhost/login

Login screenshot

localhost/

logout Screenshot

 

Now that the styling is done let us work on the authentication using passport.js.

We will first create an empty array named users to store user data,

const users = [];
You can also try this code with Online Javascript Compiler
Run Code


After the user registers on the website, we would save the data in the user’s array. But for the password field, we will store a hashed password instead of the original password to maintain security. We will use bcrypt to generate a hash of the original password and create a hashed password.

In post request of register,

app.post("/register", async (req, res) => {
 try {
   const hash = await bcrypt.hash(req.body.password, 10);
   users.push({
     name: req.body.name,
     email: req.body.email,
     password: hash,
   });
   res.redirect("/login");
 } catch {
   res.redirect("/register");
 }
});
You can also try this code with Online Javascript Compiler
Run Code


After saving the user information in the user’s array, the user will be redirected to /login or to /register in case of an error.

Once that is done, we will authenticate users with passport.js whenever the user tries to log in to our website.

First we use passport.intialize() middleware to initialize passport and the passport.session() middleware for having persistent login sessions.

app.use(passport.initialize());
app.use(passport.session());
You can also try this code with Online Javascript Compiler
Run Code


In our application, we are using LocalStrategy (email and password), and the method auth() will get called for user authentication. We will first get the user by email and if the user is nulldone() will be invoked with false to indicate authentication failure has occurred. We will also pass a flash message to the user saying, “Incorrect email entered!”

We will then also check the user’s password using bcrypt.compare.

If the password is correct, done() is invoked with (null, user). In case it’s incorrect error message will be shown to the user.

function initialize(passport, getUserByEmail, getUserById) {
 const auth = async (email, password, done) => {
   const user = getUserByEmail(email);
   if (user == null) {
     return done(null, false, { message: "Incorrect email entered!" });
   }
   try {
     if (await bcrypt.compare(password, user.password)) {
       return done(null, user);
     } else {
       return done(null, false, { message: "Incorrect password entered!" });
     }
   } catch (err) {
     return done(err);
   }
 };
 passport.use(new LocalStrategy({ usernameField: "email" }, auth));
You can also try this code with Online Javascript Compiler
Run Code

 

Now comes the serialization and deserialization part,

passport.serializeUser((user, done) => done(null, user.id));
 passport.deserializeUser((id, done) => {
   return done(null, getUserById(id));
 });
You can also try this code with Online Javascript Compiler
Run Code

 

To protect the secret page from getting accessed by non-authenticated users, we will add two methods isLoggedIn() and notLoggedIn().

If the user is isLoggedIn, we will redirect the user to /login, but if a user is not logged in, the user will be redirected to /.

function isLoggedIn(req, res, next) {
 if (req.isAuthenticated()) {
   return next();
 }
 res.redirect("/login");
}
function notLoggedIn(req, res, next) {
 if (req.isAuthenticated()) {
   return res.redirect("/");
 }
 next();
}
You can also try this code with Online Javascript Compiler
Run Code

 

We will update our route section with those two methods,

app.get("/", isLoggedIn, (req, res) => {
 res.render("secret_page.ejs");
});
// Login
app.get("/login", notLoggedIn, (req, res) => {
 res.render("login.ejs");
});
app.post(
 "/login",
 notLoggedIn,
 passport.authenticate("local", {
   successRedirect: "/",
   failureRedirect: "/login",
   failureFlash: true,
 })
);
// Register
app.get("/register", notLoggedIn, (req, res) => {
 res.render("register.ejs");
});
app.post("/register", async (req, res) => {
 try {
   const hash = await bcrypt.hash(req.body.password, 10);
   users.push({
     id: Date.now.toString().toString(),
     name: req.body.name,
     email: req.body.email,
     password: hash,
   });
   res.redirect("/login");
 } catch {
   res.redirect("/register");
 }
 console.log(users);
});
You can also try this code with Online Javascript Compiler
Run Code


For the secret page, we will add a logout route for the logout button in our secret page. User will be redirected to /login after clicking on logout button.

app.post("/logout", function (req, res) {
    req.logout(function (err) {
        if (err) { return next(err); }
        res.redirect('/login');
    });
});
You can also try this code with Online Javascript Compiler
Run Code


Now everything is almost done, so let's quickly wrap up our code and see how the application is working.

App.js

var express = require("express");
const app = express();
const bcrypt = require("bcrypt");
const flash = require("express-flash");
const session = require("express-session");
const methodOverride = require("method-override");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const dotenv = require("dotenv");
dotenv.config({ path: "./config.env" });
const PORT = process.env.PORT;
const users = [];

// *******************************Middleware*******************************
app.use(express.static(__dirname + "/public"));
app.set("view-engine", "ejs");
app.use(express.urlencoded({ extended: false }));
app.use(flash());
app.use(
    session({
        secret: process.env.SESSION_SECRET,
        resave: false,
        saveUninitialized: false,
    })
);
app.use(methodOverride("_method"));

// *******************************Passport.js*******************************
app.use(passport.initialize());
app.use(passport.session());
function initialize(passport, getUserByEmail, getUserById) {
    const auth = async (email, password, done) => {
        const user = getUserByEmail(email);
        if (user == null) {
            return done(null, false, { message: "Incorrect email entered!" });
        }
        try {
            if (await bcrypt.compare(password, user.password)) {
                return done(null, user);
            } else {
                return done(null, false, { message: "Incorrect password entered!" });
            }
        } catch (err) {
            return done(err);
        }
    };
    passport.use(new LocalStrategy({ usernameField: "email" }, auth));
    passport.serializeUser((user, done) => done(null, user.id));
    passport.deserializeUser((id, done) => {
        return done(null, getUserById(id));
    });
}
initialize(
    passport,
    (email) => users.find((user) => user.email === email),
    (id) => users.find((user) => user.id === id)
);

// *******************************Methods*******************************
function isLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return next();
    }
    res.redirect("/login");
}

function notLoggedIn(req, res, next) {
    if (req.isAuthenticated()) {
        return res.redirect("/");
    }
    next();
}

//*******************************Routes*******************************
// Secret Page
app.get("/", isLoggedIn, (req, res) => {
    res.render("secret_page.ejs");
});
// Login
app.get("/login", notLoggedIn, (req, res) => {
    res.render("login.ejs");
});
app.post(
    "/login",
    notLoggedIn,
    passport.authenticate("local", {
        successRedirect: "/",
        failureRedirect: "/login",
        failureFlash: true,
    })
);
// Register
app.get("/register", notLoggedIn, (req, res) => {
    res.render("register.ejs");
});
app.post("/register", async (req, res) => {
    try {
        const hash = await bcrypt.hash(req.body.password, 10);
        users.push({
            id: Date.now.toString().toString(),
            name: req.body.name,
            email: req.body.email,
            password: hash,
        });
        res.redirect("/login");
    } catch {
        res.redirect("/register");
    }
});
// Logout
app.post("/logout", function (req, res) {
    req.logout(function (err) {
        if (err) { return next(err); }
        res.redirect('/login');
    });
});

// Starting the server on PORT
app.listen(PORT, () => {
    console.log(`The application started successfully!`);
});
You can also try this code with Online Javascript Compiler
Run Code

Result

We first register with Name, Email and Password. Then we login using the same credentials, and after successful login we are redirected to the Secret Page, from where we finally logout.

Register

 

If an incorrect email or password is passed during login, an error message is shown.

Login

Frequently Asked Questions

What are serialization and deserialization?

In serialization, serializeUser function is used, and user.id is saved in the session in req.session.passport.user and is used to retrieve the user object by deserializeUser later on.

Incase of deserialization the reverse of serialization happens; the user id obtains the user object via the deserializeUser function.

What is passport.js?

Passport.js is a very flexible and powerful middleware used for authenticating requests in Express applications.

What is passport.initialize() and passport.session()?

Passport.initialize() middleware is used to initialize passport and the passport.session() middleware is used for having persistent login sessions.

What can you use Node.js for?

Node.js is generally used for building real-time applications, which generate a large no of I/O operations. It includes, gaming applications, social networking applications, real time chat applications, IoT devices and apps, etc.

Can we use Node.js to build AI and Machine learning algorithms?

Node.js has libraries and tools for incorporating AI-based elements into the web apps, but it would not be the best use case for this technology, Python and Ruby would be better choice for Machine learning and AI.

Conclusion

In this blog, we learned about the basic authentication flow of passport.js.After that, we created a simple Node.js login application to authenticate users with passport.js. We were able to break down many vague concepts regarding passport.js while coding the application.

We hope that the blog has helped in you learning more about authentication using passport.js. If, you want to lean more about web development then you can check out more blogs here.

We hope this article has helped you in some way and if you liked our article, do upvote our article and help other ninjas grow.  You can refer to our Guided Path on Coding Ninjas Studio to upskill yourself in Data Structures and AlgorithmsCompetitive ProgrammingSystem Design, and many more!

Head over to our practice platform Coding Ninjas Studio to practice top problems, attempt mock tests, read interview experiences and interview bundles, follow guided paths for placement preparations, and much more!

Happy Learning Ninja!

Live masterclass