Local Authentication Using Passport in Node.js
Hello. This tutorial will explain authentication in the Node.js applications through the passport module.
1. Introduction
Passport.js is an authentication middleware designed for Nodejs. passport-local uses the passport strategy for authenticating with a username and password. The module helps to authenticate using a username and password in the nodejs applications. If you are interested in reading further about this module take a look at this link.
1.1 Setting up Node.js
To set up Node.js on windows you will need to download the installer from this link. Click on the installer (also include the NPM package manager) for your platform and run the installer to start with the Node.js setup wizard. Follow the wizard steps and click on Finish when it is done. If everything goes well you can navigate to the command prompt to verify if the installation was successful as shown in Fig. 1.
2. Local Authentication Using Passport in Node.js
To set up the application, we will need to navigate to a path where our project will reside. For programming stuff, I am using Visual Studio Code as my preferred IDE. You’re free to choose the IDE of your choice.
2.1 Setting up the implementation
Let us write the different files which will be required for practical learning.
2.1.1 Setting up dependencies
Navigate to the project directory and run npm init -y
to create a package.json
file. This file holds the metadata relevant to the project and is used for managing the project dependencies, script, version, etc. Add the following code to the file wherein we will specify the required dependencies.
package.json
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | { "name" : "passport-app" , "version" : "1.0.0" , "description" : "" , "main" : "server.js" , "scripts" : { "start" : "nodemon server.js" }, "keywords" : [], "author" : "" , "license" : "ISC" , "dependencies" : { "bcrypt" : "^5.0.1" , "ejs" : "^3.1.6" , "express" : "^4.17.1" , "express-flash" : "0.0.2" , "express-session" : "^1.17.2" , "method-override" : "^3.0.0" , "passport" : "^0.4.1" , "passport-local" : "^1.0.0" }, "devDependencies" : { "dotenv" : "^10.0.0" , "nodemon" : "^2.0.12" } } |
To download the dependencies navigate to the directory path containing the file and use the npm install
command. If everything goes well the dependencies will be loaded inside the node_modules
folder and you are good to go with the further steps.
2.1.2 View – Creating welcome screen
In the root folder create a folder named views
and add the following content to the index.ejs
. This screen will be responsible to show the welcome page after successful authentication.
index.ejs
1 2 3 4 | <h1>Hi <%= name %></h1> <form action= "/logout?_method=DELETE" method= "POST" > <button type= "submit" >Sign Out</button> </form> |
2.1.3 View – Creating login screen
In the root folder create a folder named views
and add the following content to the login.ejs
. This screen will be responsible for the login.
index.ejs
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | <h1>Login</h1> <% if (messages.error) { %> <%= messages.error %> <% } %> <form action= "/login" method= "POST" > <div> <label for = "email" >Email</label> <input type= "email" id= "email" name= "email" required /> </div> <div> <label for = "password" >Password</label> <input type= "password" id= "password" name= "password" required /> </div> <button type= "submit" >Login</button> </form> <a href= "/register" >Sign up</a> |
2.1.4 View – Creating registration screen
In the root folder create a folder named views
and add the following content to the login.ejs
. This screen will be responsible for the registration of new users.
index.ejs
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | <h1>Register</h1> <form action= "/register" method= "POST" > <div> <label for = "name" >Name</label> <input type= "text" id= "name" name= "name" required /> </div> <div> <label for = "email" >Email</label> <input type= "email" id= "email" name= "email" required /> </div> <div> <label for = "password" >Password</label> <input type= "password" id= "password" name= "password" required /> </div> <button type= "submit" >Register</button> </form> <a href= "/login" >Login</a> |
2.1.5 Creating passport configuration
In the root folder add the following content to the configuration file. The file will be responsible to configure the strategy using a username and password. The strategy will also require a callback that will accept the credentials and calls a done(…)
method to provide the user details.
passport-config.js
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // adding passport related configuration const LocalStrategy = require( "passport-local" ).Strategy; const bcrypt = require( "bcrypt" ); function initialize(passport, getUserByEmail, getUserById) { const authenticateUser = async (email, password, done) => { const user = getUserByEmail(email); if (user == null ) { return done( null , false , { message: "User not found" }); } try { if ( await bcrypt.compare(password, user.password)) { return done( null , user); } else { return done( null , false , { message: "Invalid credentials" }); } } catch (e) { return done(e); } }; passport.use( new LocalStrategy({ usernameField: "email" }, authenticateUser)); passport.serializeUser((user, done) => done( null , user.id)); passport.deserializeUser((id, done) => { return done( null , getUserById(id)); }); } module.exports = initialize; |
2.1.6 Creating controller
In the root folder add the following content to the index file. The file will be responsible to initialize the imports, routes, and specify the passport configuration to authenticate the requests. Remember to create a .env
file at the same location and specify the sensitive information such as session-secret, application port number, etc.
server.js
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | if (process.env.NODE_ENV !== "production" ) { require( "dotenv" ).config(); } // imports const express = require( "express" ); const app = express(); const bcrypt = require( "bcrypt" ); const passport = require( "passport" ); const flash = require( "express-flash" ); const session = require( "express-session" ); const methodOverride = require( "method-override" ); // todo - add external db support const users = []; // configuring and initializing passport const initializePassport = require( "./passport-config" ); initializePassport( passport, (email) => users.find((user) => user.email === email), (id) => users.find((user) => user.id === id) ); app.set( "view-engine" , "ejs" ); app.use(express.urlencoded({ extended: false })); app.use(flash()); app.use( session({ secret: process.env.SESSION_SECRET || "8unto0n4oc7903zm" , resave: false , saveUninitialized: false , }) ); app.use(passport.initialize()); app.use(passport.session()); app.use(methodOverride( "_method" )); // routes // welcome page // display greetings message for the user and logout button app.get( "/" , checkAuthenticated, (req, res) => { res.render( "index.ejs" , { name: req.user.name }); }); // login page app.get( "/login" , checkNotAuthenticated, (req, res) => { res.render( "login.ejs" ); }); app.post( "/login" , checkNotAuthenticated, passport.authenticate( "local" , { successRedirect: "/" , failureRedirect: "/login" , failureFlash: true , }) ); // new user sign-up page app.get( "/register" , checkNotAuthenticated, (req, res) => { res.render( "register.ejs" ); }); app.post( "/register" , checkNotAuthenticated, async (req, res) => { try { const hashedPassword = await bcrypt.hash(req.body.password, 10); users.push({ id: "_" + Math.random().toString(36).slice(2), name: req.body.name, email: req.body.email, password: hashedPassword, }); res.redirect( "/login" ); } catch (e) { // console.log(e); res.redirect( "/redirect" ); } // check if the user is successfully added to array // console.log(users); }); // logout of the application app. delete ( "/logout" , (req, res) => { req.logOut(); res.redirect( "/login" ); }); // util methods // only authenticated user should enter index page function checkAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } else { res.redirect( "/login" ); } } // unauthenticated user should not enter index page function checkNotAuthenticated(req, res, next) { if (req.isAuthenticated()) { return res.redirect( "/" ); } next(); } // start server const port = process.env.APPLICATION_PORT || 6001; app.listen(port, () => { }); |
3. Run the Application
To run the application navigate to the project directory and enter the following command as shown in Fig. 2. If everything goes well the application will be started successfully on a port number read from the .env
file or 6001
.
4. Demo
Once the application is started successfully navigate to the following url to show the login screen. You can click on the signup button for creating a new user and play around with the application after that. In this tutorial, I have also added some of the basic validations like user not found and invalid credentials.
Endpoint
That is all for this tutorial and I hope the article served you with whatever you were looking for. Happy Learning and do not forget to share!
5. Summary
In this tutorial, we learned about the passport module and how to create a simple login application with the help of passport-local dependency. You can download the source code and the postman collection from the Downloads section.
6. Download the Project
This was a tutorial to implement local authentication using a passport module in a nodejs application.
You can download the full source code of this example here: Local Authentication Using Passport in Node.js