Open In App

How to Implement ACL with Passport using Node.js ?

Last Updated : 06 Jun, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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 file

Step 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 error

It 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:3000

4: 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.ejs

http://localhost:3000/register

from register.ejs

Upon giving an input, the console will look like this with a hashed password

terminal

5: 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:

 



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads