How to Implement ACL with Passport using Node.js ?
Last Updated :
24 Jul, 2024
Nodejs is an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications.
Passportjs: Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more. It is widely used by developers to create secure and scalable web applications.
Steps to create ACL with passport using Node.js:
1: Create a Folder with a project name in any IDE (I'm using VS Code): See the below-mentioned folder structure.
Step 1: Open the terminal and type "npm init". Keep on pressing enter for the default values since this command is used to set up a new npm package. Upon successful installation, it will create a package.json folder.
Step 2: For installing node dependencies, press "npm i express ejs".
Step 3: For installing dev dependencies, press "npm i --save-dev nodemon dotenv". Nodemon allows you to restart your server automatically every time anything changes and dotenv allows us to have environment variables that will store under .env file that we can load into the server.
Step 4: Make file ".env" and ".gitignore" as given in the below directory structure.
Â
Folder Structure.gitignore contains those files that we don't want to commit to our git repository. Here we have stored .env and node_modules since it contains only the dependencies and .env may contain sensitive information we don't want to share with the world.
add these to your .env fileStep 5: The last thing that we need to do is set up package.json so that we can start the server. Edit the script section as follows:
Â
Update the script as follows:Step 6: Now if you press "npm run devStart" in your terminal it will start the server. You should get something like this in your terminal.
Â
No errors in red, all green!2: Set up our basic express application with the below code in server.js.
JavaScript
// STEP 1: Install all the necessary dependencies
// add the dependencies with the help of require
const express = require('express')
const app = express()
// To run our app on port 3000
app.listen(3000)
But if we run our server after writing this piece of code, we will get the below page on localhost:300
Throws this errorIt shows, Cannot GET/ because we haven't set up any routes for our application yet. Let's resolve this in step 3.
3: Add the above-mentioned code in server.js and make a folder named views with index.ejs in it.
Â
Output on localhost:30004: Install another package using "npm i bcrypt": bcrypt will allow us to hash passwords and compare hashed passwords to make sure that our application is completely secured.
server.js
JavaScript
// Requiring all the necessary dependencies
const express = require('express')
const app = express()
const bcrpyt = require('bcrypt')
const users = []
// In order to use ejs set up view-engine
app.set('view-engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
// Setting up home-page route
app.get('/', (req, res) => {
res.render('index.ejs', { name: 'Janhvi' })
})
// Set up login page route
app.get('/login', (req, res) => {
res.render('login.ejs')
})
// Set up register page route
app.get('/register', (req, res) => {
res.render('register.ejs')
})
app.post('/register', async (req, res) => {
// Using try catch block in order to
// resolve errors
try {
// Hashing password with bcrypt
const hashedPassword =
await bcrpyt.hash(req.body.password, 10)
users.push({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
res.redirect('/login')
} catch {
res.redirect('/register')
}
console.log(users)
})
// To run our app on port 3000
app.listen(3000)
login.ejs
JavaScript
<!-- login page -->
<h1>Login</h1>
<!-- Make a simple login form with name, email
and password fields make a submit button
at end -->
<form action="/login" 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">Login</button>
</form>
<a href="/register">Register</a>
register.ejs
JavaScript
<!-- Register page -->
<h1>Register</h1>
<!-- Make a simple register form with
name, email and password fields
make a submit button at end -->
<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>
Desired output on, http://localhost:3000/login
from login.ejshttp://localhost:3000/register
from register.ejsUpon giving an input, the console will look like this with a hashed password
terminal5: Install passportjs using "npm i passport passport-local express-session express-flash"
Make a file "passport-config.js" in which you will store all the passport-related information. Now let's have a look at the final version of all the codes with desired logic.
server.js
JavaScript
// The below code is only suitable for
// development not suitable for production
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config()
}
// Requiring all the necessary dependencies
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')
const initializePassport = require('./passport-config')
initializePassport(
passport,
email => users.find(user => user.email === email),
id => users.find(user => user.id === id)
)
const users = []
// Setting up the view-engine in order
// to use ejs in code further
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(passport.initialize())
app.use(passport.session())
app.use(methodOverride('_method'))
// Setting up route logic for home page
app.get('/', checkAuthenticated, (req, res) => {
res.render('index.ejs', { name: req.user.name })
})
// Setting up route logic for login page
app.get('/login', checkNotAuthenticated, (req, res) => {
res.render('login.ejs')
})
app.post('/login', checkNotAuthenticated,
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}))
app.get('/register', checkNotAuthenticated,
(req, res) => {
res.render('register.ejs')
})
// Hashinhg the passwords for each user
// using bcrypt
app.post('/register', checkNotAuthenticated,
async (req, res) => {
try {
const hashedPassword =
await bcrypt.hash(req.body.password, 10)
users.push({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
res.redirect('/login')
} catch {
res.redirect('/register')
}
})
// To to login page upon pressing the
// logout button
app.delete('/logout', (req, res) => {
req.logOut()
res.redirect('/login')
})
// If user is authenticated redirect to
// next page otherwise redirect to login
// page
function checkAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/')
}
next()
}
// To run our app on port 3000
app.listen(3000)
passport-config.js
JavaScript
// Requiring all the necessary dependencies
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')
// Add all the code related to passportjs to
// the main initialize function
function initialize(passport, getUserByEmail, getUserById) {
const authenticateUser = async (email, password, done) => {
const user = getUserByEmail(email)
// If user is null return output
// "no user with that email"
if (user == null) {
return done(null, false,
{ message: 'No user with that email' })
}
// try-catch block to check for correct password
try {
if (await bcrypt.compare(password, user.password)) {
return done(null, user)
} else {
return done(null, false,
{ message: 'Password incorrect' })
}
} 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))
})
}
// Exporting the initialize function
module.exports = initialize
index.ejs
HTML
<!-- Index page which will display
the Hi user_name -->
<h1>Hi <%= name %></h1>
<!-- Setting up a logout button in
order to exit the page -->
<form action="/logout?_method=DELETE" method="POST">
<button type="submit">Log Out</button>
</form>
login.ejs
HTML
<!-- login page -->
<h1>Login</h1>
<!-- Adding embedded javascript code to
check for errors -->
<% if (messages.error) { %>
<%= messages.error %>
<% } %>
<!-- Make a simple login form with
name, email and password fields
make a submit button at the end -->
<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">Register</a>
register.ejs
HTML
<!-- Register page -->
<h1>Register</h1>
<!-- Make a simple register form with
name, email and password fields
make a submit button at the end -->
<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>
6: Install another package using "npm i method-override" - The rest of the code remains the same.
The first page that will come up after starting the server is the login page.
 If you have already registered, then login by using the same email and password.
 If you haven't registered and you tried to login then you will get this output.
 Register yourself first and remember the email and password and then again try to login.
 After successful registration and login you will get this on your page i.e. "Welcome to GeeksforGeeks your_name".
 Â
Output:
Similar Reads
Non-linear Components In electrical circuits, Non-linear Components are electronic devices that need an external power source to operate actively. Non-Linear Components are those that are changed with respect to the voltage and current. Elements that do not follow ohm's law are called Non-linear Components. Non-linear Co
11 min read
JavaScript Tutorial JavaScript is a programming language used to create dynamic content for websites. It is a lightweight, cross-platform, and single-threaded programming language. It's an interpreted language that executes code line by line, providing more flexibility.JavaScript on Client Side: On the client side, Jav
11 min read
Web Development Web development is the process of creating, building, and maintaining websites and web applications. It involves everything from web design to programming and database management. Web development is generally divided into three core areas: Frontend Development, Backend Development, and Full Stack De
5 min read
Spring Boot Tutorial Spring Boot is a Java framework that makes it easier to create and run Java applications. It simplifies the configuration and setup process, allowing developers to focus more on writing code for their applications. This Spring Boot Tutorial is a comprehensive guide that covers both basic and advance
10 min read
React Interview Questions and Answers React is an efficient, flexible, and open-source JavaScript library that allows developers to create simple, fast, and scalable web applications. Jordan Walke, a software engineer who was working for Facebook, created React. Developers with a JavaScript background can easily develop web applications
15+ min read
React Tutorial React is a powerful JavaScript library for building fast, scalable front-end applications. Created by Facebook, it's known for its component-based structure, single-page applications (SPAs), and virtual DOM,enabling efficient UI updates and a seamless user experience.Note: The latest stable version
7 min read
JavaScript Interview Questions and Answers JavaScript is the most used programming language for developing websites, web servers, mobile applications, and many other platforms. In Both Front-end and Back-end Interviews, JavaScript was asked, and its difficulty depends upon the on your profile and company. Here, we compiled 70+ JS Interview q
15+ min read
Class Diagram | Unified Modeling Language (UML) A UML class diagram is a visual tool that represents the structure of a system by showing its classes, attributes, methods, and the relationships between them. It helps everyone involved in a projectâlike developers and designersâunderstand how the system is organized and how its components interact
12 min read
Backpropagation in Neural Network Back Propagation is also known as "Backward Propagation of Errors" is a method used to train neural network . Its goal is to reduce the difference between the modelâs predicted output and the actual output by adjusting the weights and biases in the network.It works iteratively to adjust weights and
9 min read
3-Phase Inverter An inverter is a fundamental electrical device designed primarily for the conversion of direct current into alternating current . This versatile device , also known as a variable frequency drive , plays a vital role in a wide range of applications , including variable frequency drives and high power
13 min read