How to Implement ACL with Passport using Node.js ?
Last Updated :
06 Jun, 2023
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
const express = require( 'express' )
const app = express()
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
const express = require( 'express' )
const app = express()
const bcrpyt = require( 'bcrypt' )
const users = []
app.set( 'view-engine' , 'ejs' )
app.use(express.urlencoded({ extended: false }))
app.get( '/' , (req, res) => {
res.render( 'index.ejs' , { name: 'Janhvi' })
})
app.get( '/login' , (req, res) => {
res.render( 'login.ejs' )
})
app.get( '/register' , (req, res) => {
res.render( 'register.ejs' )
})
app.post( '/register' , async (req, res) => {
try {
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)
})
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
if (process.env.NODE_ENV !== 'production' ) {
require( 'dotenv' ).config()
}
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 = []
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' ))
app.get( '/' , checkAuthenticated, (req, res) => {
res.render( 'index.ejs' , { name: req.user.name })
})
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' )
})
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' )
}
})
app. delete ( '/logout' , (req, res) => {
req.logOut()
res.redirect( '/login' )
})
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()
}
app.listen(3000)
|
passport-config.js
Javascript
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: 'No user with that email' })
}
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))
})
}
module.exports = initialize
|
index.ejs
HTML
< h1 >Hi <%= name %></ h1 >
< form action = "/logout?_method=DELETE" method = "POST" >
< button type = "submit" >Log Out</ button >
</ form >
|
login.ejs
HTML
< 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" >Register</ a >
|
register.ejs
HTML
< 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 >
|
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:
Share your thoughts in the comments
Please Login to comment...