Authentication for a web application is difficult to setup manually.To make it easier, passport came into action.
passport is an authentication middleware for node application. passport provides different set of strategies using a username and password, Facebook and Twitter.
The article is separated as two parts for better understanding of the passport authentication concepts
we can use different ways to login in a web application such as Facebook,Twitter, Google and local(username and password)
In this article, we will use local strategy for the web application and folder structure will looks like:
Firstly, we are going to install all the dependencies needed for an application. Therefore, we install the packages needed for an application
1"dependencies": {2 "bcrypt-nodejs": "0.0.3",3 "body-parser": "^1.18.3",4 "connect-flash": "^0.1.1",5 "cookie-parser": "^1.4.4",6 "cors": "^2.8.5",7 "express": "^4.16.4",8 "express-handlebars": "^3.0.2",9 "express-session": "^1.16.1",10 "method-override": "^3.0.0",11 "mongoose": "^5.5.2",12 "morgan": "^1.9.1",13 "passport": "^0.4.0",14 "passport-local": "^1.0.0"15 },
Most importantly, passport and passport-local are the packages are used for passport authentication.
on the other hand, we need to create models which is nothing but Schema for user. create a file called user.schema.js
1const mongoose = require("mongoose")2const bcrypt = require("bcrypt-nodejs")3const Schema = mongoose.Schema45let userschema = new Schema({6 email: String,7 password: String,8})910userschema.methods.generateHash = function(password) {11 return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null)12}1314// checking if password is valid15userschema.methods.validPassword = function(password) {16 return bcrypt.compareSync(password, this.password)17}1819let User = mongoose.model("User", userschema)2021module.exports = User
we need to create a file called passport where we need to setup the passport strategy. that is to say, create a file called config/passport.js .
Firstly, we will see how to setup the signup using passport authentication.Therefore, add the following code in the config/passport.js
1const LocalStrategy = require("passport-local").Strategy23let User = require("../models/user.schema")45module.exports = function(passport) {6 // used to serialize the user for the session7 passport.serializeUser(function(user, done) {8 done(null, user.id)9 })1011 // used to deserialize the user12 passport.deserializeUser(function(id, done) {13 User.findById(id, function(err, user) {14 done(err, user)15 })16 })1718 passport.use(19 "local-signup",20 new LocalStrategy(21 {22 // by default, local strategy uses username and password, we will override with email23 usernameField: "email",24 passwordField: "password",25 passReqToCallback: true, // allows us to pass back the entire request to the callback26 },27 function(req, email, password, done) {28 // asynchronous29 // User.findOne wont fire unless data is sent back30 process.nextTick(function() {31 // find a user whose email is the same as the forms email32 // we are checking to see if the user trying to login already exists33 User.findOne({ email: email }, function(err, user) {34 // if there are any errors, return the error35 if (err) return done(err)3637 // check to see if theres already a user with that email38 if (user) {39 return done(40 null,41 false,42 req.flash("signupMessage", "That email is already taken.")43 )44 } else {45 // if there is no user with that email46 // create the user47 var newUser = new User()4849 // set the user's local credentials5051 newUser.email = email52 newUser.password = newUser.generateHash(password)5354 // save the user55 newUser.save(function(err) {56 if (err) throw err57 return done(null, newUser)58 })59 }60 })61 })62 }63 )64 )65}
In the above file, we setup the passport local strategy for signup and insert the user into the database.
After that, we need to create a route file where we need to handle the signup url.So,create a file called routes/index.js
1module.exports = function(app, passport) {2 app.get("/", isLoggedIn, (req, res) => {3 console.log("req user", req.user)4 res.render("home", {5 user: req.user,6 })7 })89 app.get("/signup", (req, res) => {10 res.render("signup")11 })1213 app.post(14 "/signup",15 passport.authenticate("local-signup", {16 successRedirect: "/", // redirect to the secure profile section17 failureRedirect: "/signup", // redirect back to the signup page if there is an error18 failureFlash: true, // allow flash messages19 })20 )2122 app.get("/logout", function(req, res) {23 req.logout()24 res.redirect("/")25 })2627 // route middleware to make sure a user is logged in28 function isLoggedIn(req, res, next) {29 // if user is authenticated in the session, carry on30 if (req.isAuthenticated()) return next()3132 // if they aren't redirect them to the home page33 res.redirect("/login")34 }35}
So far, we added the route for signup and after successful signup, we redirect them to home page. So, we need to create view files for signup and home page.
create a folder called views and add the the following files
1- views2 - layouts3 ------ main.handlebars <!-- show our home page with4 ------ home.handlebars <!-- show our home -->5 ------ login.handlebars <!-- show our login form -->
that is to say, main.hanldebars should looks like
1<!DOCTYPE html>2<html>3 <head>4 <meta charset="utf-8" />5 <title>Worker App</title>6 <link rel="icon" href="/images/favicon.png" />7 <link8 rel="stylesheet"9 href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"10 />11 </head>12 <body>13 {{{body}}}14 </body>15</html>
signup.handlebars should looks like
1{{#if message }}2 <div class="ui negative message transition hidden">3 <i class="close icon"></i>4 <div class="header">5 {{message}}6 </div>7 </div>8 {{/if}}9 <div class="ui middle aligned center aligned grid container">10 <div class="column">11 <h2 class="ui teal image header">12 <img src="/images/favicon.png" alt="cloudnweb" class="image"/>13 <div class="content">14 Cloudnweb.dev15 </div>16 </h2>17 <form action="/signup" method="POST" class="ui large form">18 <div class="ui stacked segment">192021 <div class="field">22 <div class="ui left icon input">23 <i class="user icon"></i>24 <input type="text" name="email" placeholder="Enter Email Address"/>25 </div>26 </div>27 <div class="field">28 <div class="ui left icon input">29 <i class="lock icon"></i>30 <input type="password" name="password" placeholder="Enter Password"/>31 </div>32 </div>33 <input type="submit" class="ui fluid large teal submit button" value="Sign Up"> </div>34 </form>3536 </div>37 </div>
home.handlebars should look like
1<div class="ui small menu">2 <a href="" class="active item">3 Home4 </a>5 <div class="right menu">6 <div class="item">7 <h4>{{user.email}}</h4>8 </div>9 <div class="item">10 <a href="/logout" class="ui primary button">Log Out</a>11 </div>12 </div>13</div>
Finally, add the following code in the app.js
1const express = require("express")2const exphbs = require("express-handlebars")3const mongoose = require("mongoose")4const app = express()5const passport = require("passport")6const flash = require("connect-flash")7const morgan = require("morgan")8const cookieParser = require("cookie-parser")9const bodyParser = require("body-parser")10const session = require("express-session")1112require("dotenv").config()1314app.engine("handlebars", exphbs({ defaultLayout: "main" }))15app.set("view engine", "handlebars")1617app.use(morgan("dev"))18app.use(cookieParser())19app.use(bodyParser.urlencoded({ extended: true }))20app.use(bodyParser.json())2122const MONGODB_URI = process.env.MONGODB_URL2324mongoose.connect(MONGODB_URI, { useNewUrlParser: true })2526var db = mongoose.connection27db.on("error", console.error.bind(console, "connection error:"))28db.once("open", function() {29 console.log("connected")30})3132app.use(session({ secret: "ilearnnodejs" }))33app.use(passport.initialize())34app.use(passport.session())35app.use(flash())3637require("./config/passport")(passport)38require("./routes/index")(app, passport)3940const PORT = process.env.PORT4142app.listen(PORT, () => {43 console.log(`app is listening to port ${PORT}`)44})
Now, we can run the application in command as node app.js
we will see how to login using passport authentication in part 2 : https://cloudnweb.dev/2019/04/node-authentication-using-passport-js-part-2/
No spam, ever. Unsubscribe anytime.