Open In App

Login Authentication using Express.js, Passport.js and BCrypt

Last Updated : 21 Sep, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will create a Login Authentication application. This application basically displays a login/register form interface and authenticate the user. All this logic of login/register authentication is implemented using express, mongodb, passport and bcrypt and frontend is created using ejs.

Preview of Final Output:

gfg

 

Prerequisites and Technologies:

Approach: Our project contains two sections server and client. In client we will take the user inputs such as the username, password and then use the form action property to sent to the server. After that, we will build the required API’s to call the authentication routes in server section. In the server section, we will use mongo express passport and bcrypt to make Authentication routes for client section we will use view engine ejs.

 

Steps to create the application: To create a login authentication we will work on Server Section first then Client section.

Creating the Server Section:

Step 1: Create a folder name LoginAuth using the following command:

mkdir LoginAuth

Step 2: Intialize package.json using the following command

npm init -y 

Step 3: create a file in server.js using following command

touch ./server.js

Step 4: Install the required modules:

npm install ejs express mongoose passport passport-local bcrypt express-session cookie-parser body-parser

concurrently is used to run both the server aand client simultaneouly

Step 5: Create necessary server files controllers.js db.js models.js passp.js routes.js pages.js :

The updated dependencies in package.json will look like:

{
"name": "loginauth",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"server": "nodemon run server.js",
},
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.0",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"ejs": "^3.1.9",
"express": "^4.18.2",
"express-session": "^1.17.3",
"mongoose": "^7.4.0",
"passport": "^0.6.0",
"passport-local": "^1.0.0"
}
}

Creating Client Section:

Step 1: In LoginAuth create a folder name views to store our ejs files

mkdir views

Step 2:  Create necessary files for client:

cd views

Step 3: Inside this folder create files home.ejs register.ejs login.ejs

Step 3: Import Tailwind by addind this line in every ejs file:

<script src="https://cdn.tailwindcss.com"></script>

Project Structure:

gfg

Example: Write the following code in respective files in server and client section

Server section:

  • server.js: main server files contains the code of everything This the base file that connects to the others files and connects at 3000 port:
  • db.js: contains the logic of database connections to the MongoDB.
  • passp.js: contains the logic of user login authentication using passport-local module.
  • controllers.js: contains the logic of register and logic route
  • models.js: contains the models schema of our user collections.
  • pages.js: contains the routes to pages of the website.

Javascript




// Server.js
  
// Imports //
const express = require("express");
const passport = require("passport");
const User = require("./models.js");
const localStrategy = require("./passp.js");
const controllers = require("./controllers.js");
const cookieParser = require("cookie-parser");
const connectDB = require("./db");
const ejs = require("ejs");
const bodyParser = require("body-parser");
const routes = require("./pages.js");
const session = require("express-session");
  
// Main Server //
const app = express();
connectDB();
app.use(
    session({
        secret: "GFGLogin346",
        resave: false,
        saveUninitialized: false,
    })
);
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(passport.initialize());
app.use(passport.session());
app.set("view engine", "ejs");
  
// Serialize and deserialize user objects to maintain user sessions
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser((id, done) => {
    User.findById(id, (err, user) => done(err, user));
});
// Use the routes
app.use("/api/", controllers); // Path to your authentication routes file
app.use("/", routes);
  
// Start the server
const port = 3000; // Replace with your desired port number
app.listen(port, () => {
    console.log(`Server started on port ${port}`);
});


Javascript




// db.js
const mongoose = require("mongoose");
  
// Mongoose Connection
const connectDB = async () => {
    mongoose
        .connect("mongodb://127.0.0.1:27017/LoginAuth", {
            // My works on 127.0.0.1 you can also use localhost
            useNewUrlParser: true,
            useUnifiedTopology: true,
        })
        .then(() => console.log("Connected to MongoDB"))
        .catch((err) => console.error("Error connecting to MongoDB:", err));
};
  
module.exports = connectDB;


Javascript




// passp.js
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const User = require("./models");
const bcrypt = require("bcrypt");
  
passport.use(
    new LocalStrategy(async (username, password, done) => {
        try {
            // Find the user by username in the database
            const user = await User.findOne({ username });
            // If the user does not exist, return an error
            if (!user) {
                return done(null, false, { error: "Incorrect username" });
            }
  
            // Compare the provided password with the 
            // hashed password in the database
            const passwordsMatch = await bcrypt.compare(
                password,
                user.password
            );
  
            // If the passwords match, return the user object
            if (passwordsMatch) {
                return done(null, user);
            } else {
                // If the passwords don't match, return an error
                return done(null, false, { error: "Incorrect password" });
            }
        } catch (err) {
            return done(err);
        }
    })
);


Javascript




//controllers.js
const express = require("express");
const router = express.Router();
const User = require("./models");
const passport = require("passport");
const bcrypt = require("bcrypt");
  
// User registration route
router.post("/register", async (req, res) => {
    console.log(req.body);
    const { username, email, password, confirmpassword } = req.body;
    if (!username && !email && !password && !confirmpassword) {
        return res
            .status(403)
            .render("register", { error: "All Fields are required" });
    }
    if (confirmpassword !== password) {
        return res
            .status(403)
            .render("register", { error: "Password do not match" });
    }
    try {
        // Check if user already exists
        const existingUser = await User.findOne({ username });
        if (existingUser) {
            return res
                .status(409)
                .render("register", { error: "Username already exists" });
        }
  
        // Hash the password before saving it to the database
        const salt = await bcrypt.genSalt(15);
        const hashedPassword = await bcrypt.hash(password, salt);
  
        // Create and save the new user
        const newUser = new User({ username, email, password: hashedPassword });
        await newUser.save();
  
        return res.redirect("/login");
    } catch (err) {
        return res.status(500).json({ message: err.message });
    }
});
  
// User login route
router.post(
    "/login",
    passport.authenticate("local", { session: false }),
    (req, res) => {
        req.session.name = req.body.username;
        req.session.save();
  
        return res.redirect("/");
    }
);
  
router.get("/logout", (req, res) => {
    req.session.destroy();
    res.redirect("/");
});
module.exports = router;


Javascript




// models.js
const mongoose = require("mongoose");
  
// Creating User Model
const userSchema = new mongoose.Schema({
    email: { type: String, required: true, unique: true },
    username: { type: String, required: true, unique: true },
    password: { type: String, required: true },
});
  
const User = mongoose.model("User", userSchema);
  
module.exports = User;


Javascript




// pages.js
const express = require("express");
const router = express.Router();
  
router.get("/", (req, res) => {
    if (req.session.name) {
        var name = req.session.name;
        res.render("home", { name: name });
    }
    return res.render("home", { name: null });
});
router.get("/login", (req, res) => {
    if (req.session.name) {
        res.redirect("/");
    }
    return res.render("login", { error: null });
});
router.get("/register", (req, res) => {
    if (req.session.name) {
        res.redirect("/");
    }
    return res.render("register", { error: null });
});
module.exports = router;


Client section:

  • Home.ejs: Home of the project that will display if the user is logged in or not and give a message
  • Register.ejs: Register Forms that will register the user in the database
  • Login.ejs: Login Form that will authenticate and login the user

Javascript




<!-- Home.ejs -->
<!DOCTYPE html>
<html>
  
<head>
    <title>Login Authentication</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
  
<body>
    <div class="overflow-hidden h-screen p-8 bg-slate-200">
        <header class="absolute inset-x-0 top-0 z-50">
            <nav class="flex items-center p-6 lg:px-8" 
                 aria-label="Global">
                <div class="flex lg:flex-1 text-blue-800 text-xl">
                    <a className=" border-collapse text-blue-800 text-xl" 
                       href="/">
                        Home
                    </a>
                </div>
            </nav>
        </header>
        <% if (name) { %>
        <div class="pt-10">
            <h1 class="text-4xl font-bold tracking-tight 
                       text-gray-900 sm:text-6xl">
                Welcome <%= name %>
            </h1>
            <p class="mt-6 text-lg leading-8 text-gray-600">
                You're Successfully Authenticated
            </p>
            <div class="mt-10 flex gap-x-6">
                <a href="/api/logout" 
                   class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm 
                          font-semibold text-white shadow-sm hover:bg-indigo-500 
                          focus-visible:outline focus-visible:outline-2 
                          focus-visible:outline-offset-2 
                          focus-visible:outline-indigo-600">
                    Logout
                </a>
            </div>
  
            <% } else { %>
            <div class="pt-4 mt-2">
                <h1 class="text-sm font-bold tracking-tight 
                           text-gray-900 sm:text-6xl">
                    Invalid User
                </h1>
                <p class="mt-6 text-lg leading-8 text-gray-600">
                    You're Not Authenticated please Login
                </p>
                <div class="mt-10 flex gap-x-6">
                    <a href="/login" 
                       class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm
                              font-semibold text-white shadow-sm 
                              hover:bg-indigo-500 focus-visible:outline 
                              focus-visible:outline-2 focus-visible:outline-offset-2 
                              focus-visible:outline-indigo-600">
                        Login
                    </a>
                </div>
            </div>
            <% } %>
        </div>
    </div>
</body>
  
</html>


Javascript




<!--Login.ejs -->
<script src="https://cdn.tailwindcss.com"></script>
<!DOCTYPE html>
<html lang="en">
  
<head>
    <script src="https://cdn.tailwindcss.com"></script>
    <meta charset="UTF-8">
    <meta name="viewport" 
          content="width=device-width, initial-scale=1.0">
    <title>Login Authentication</title>
  
</head>
  
<body>
  
  
    <div class="overflow-hidden h-screen p-8 bg-slate-200">
        <div class="overflow-hidden h-screen ">
            <header class="absolute inset-x-0 top-0 z-50">
                <nav class="flex items-center p-6 lg:px-8" 
                     aria-label="Global">
                    <div class="flex lg:flex-1 text-blue-800 text-xl ml-2 ">
                        <a className=" border-collapse text-blue-800 text-xl" 
                           href="/">
                            Home
                        </a>
                    </div>
  
                </nav>
            </header>
            <div class="container py-8">
                <h1 class="text-2xl font-bold ml-2 mb-4">Login Form</h1>
                <form class="w-full max-w-sm bg-white
                     p-4 rounded-md shadow-md" method="post" , action="/api/login">
                    <% if(error) { %>
                    <p class="text-red-500"><%= error %></p>
                    <% } %>
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2"
                               for="username">
                            Username
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md 
                                      focus:outline-none focus:border-indigo-500" 
                               type="text" id="username" name="username" 
                               placeholder="John_01"/>
                    </div>
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2"
                               for="password">
                            Password
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md 
                                      focus:outline-none focus:border-indigo-500"
                               type="password" id="password" name="password"
                               placeholder="********" />
                    </div>
  
                    <button class="w-full bg-indigo-500 text-white text-sm font-bold
                                   py-2 px-4 rounded-md hover:bg-indigo-600
                                   transition duration-300" 
                            type="submit" onClick={submit}>
                        Login
                    </button>
                    <p class="pt-5">
                        Don't have a account
                        <a class=" text-cyan-600 cursor-pointer" href="/register">
                            Register here
                        </a>
                    </p>
                </form>
            </div>
        </div>
</body>
  
</html>


Javascript




<!-- Register.ejs -->
<!DOCTYPE html>
<html lang="en">
  
<head>
    <script src="https://cdn.tailwindcss.com"></script>
    <meta charset="UTF-8">
    <meta name="viewport" 
          content="width=device-width, initial-scale=1.0">
    <title>Login Authentication</title>
</head>
  
<body>
    <div class="overflow-hidden h-screen bg-slate-200 ">
        <div class="overflow-hidden h-screen ">
            <header class="absolute inset-x-0 top-0 z-50">
                <nav class="flex items-center  p-6 lg:px-8" 
                     aria-label="Global">
                    <div class="flex lg:flex-1 text-blue-800 text-xl  ">
                        <a className="border-collapse " href="/">
                            Home
                        </a>
                    </div>
                </nav>
            </header>
            <div class="container mt-8 p-8">
                <h1 class="text-2xl font-bold mb-6 ">Registration Form</h1>
                <form class="w-full max-w-sm  bg-white p-8 
                     rounded-md shadow-md" method="post" action="/api/register">
                    <% if(error) { %>
                    <p class="text-red-500"><%= error %></p>
                    <% } %>
                    <div class="mb-4">
                        <label class="block text-gray-700 
                   text-sm font-bold mb-2" for="username">
                            UserName
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md 
                                      focus:outline-none focus:border-indigo-500"
                               type="text" id="username" name="username"
                               placeholder="John_01" />
                    </div>
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2" 
                               for="email">
                            Email
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md 
                                      focus:outline-none focus:border-indigo-500"
                               type="email" id="email" name="email" 
                               placeholder="john@example.com" />
                    </div>
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2" 
                               for="password">
                            Password
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md 
                                      focus:outline-none focus:border-indigo-500" 
                               type="password" id="password" name="password" 
                               placeholder="********" />
                    </div>
                    <div class="mb-4">
                        <label class="block text-gray-700 text-sm font-bold mb-2"
                               for="confirm-password">
                            Confirm Password
                        </label>
                        <input class="w-full px-3 py-2 border border-gray-300 rounded-md
                                      focus:outline-none focus:border-indigo-500" 
                               type="password" id="confirm-password" name="confirmpassword" 
                               placeholder="********" />
                    </div>
                    <button class="w-full bg-indigo-500 text-white 
                   text-sm font-bold py-2 px-4 
                 rounded-md hover:bg-indigo-600 
                 transition duration-300" type="submit" onClick={submit}>
                        Register
                    </button>
                    <p class="pt-5">
                        Don't have a account
                        <a href="/login" class=" text-cyan-600 cursor-pointer">
                            Register here
                        </a>
                    </p>
                </form>
            </div>
        </div>
</body>
  
</html>


Steps to run the application:

Step 1 : Start the mongodb server by typing the following command in terminal

mongod

Step 2: Inside the loginAuth folder.

npm run server

Step 3: Both the server and client will run simultaneously open the url

http://localhost:3000/

Output:

gfg

loginAuthDBgif

Project Database



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads