While building any web app people get confused or feel difficulty in authentication process. Creating a registration form and sign in process is a hectic process if we don't follow proper method. In this tutorial we will see how we can register and login using passport-local. Setting Up Our Application: To set up our base Node application, we'll need a few things. We'll set up our npm packages, node application, configuration files, models, and routes. Application Structure: - app ------ models ---------- user.js <!-- our user model --> ------ routes.js <!-- all the routes our application --> - config ------ auth.js <!-- will hold all our client secret keys (facebook, twitter, google) --> ------ database.js <!-- will hold our database connection settings --> ------ passport.js <!-- configuring the strategies passport --> - views ------ index.ejs <!-- show our home page login links --> ------ login.ejs <!-- show our login form --> ------ signup.ejs <!-- show our signup form --> ------ profile.ejs <!-- after a user logs , they will see their profile --> - package.json <!-- handle our npm packages --> - server.js <!-- setup our application --> for for with in Create all those files and folders and we'll fill them in as we go along. Packages / package.json: We are going to install all the packages needed for the entire tutorial series. This means we'll install all the packages needed for passport local and the other things we need. : { : , : , : , : , : , : , : , : , : , : , : , : , } "dependencies" "bcrypt-nodejs" "0.0.3" "body-parser" "^1.19.0" "connect-flash" "^0.1.1" "cookie-parser" "^1.4.5" "ejs" "^3.0.1" "express" "^4.17.1" "express-session" "^1.17.0" "method-override" "^3.0.0" "mongoose" "^5.9.6" "morgan" "^1.10.0" "passport" "^0.4.1" "passport-local" "^1.0.0" Most of the packages are self-explanatory. is the framework. Express is the templating engine. Ejs is object modeling for our MongoDB database. Mongoose stuff will help us authenticating with different methods. Passport allows for passing session flashdata messages. Connect-flash gives us the ability to hash the password. Bcrypt-nodejs With all of our packages ready to go, let's set up our application in . npm i server.js Setting up server.js: Let's make all our packages work together nicely. Our goal is to set up this file and try to have it bootstrap our entire application. We'd like to not go back into this file if it can be helped. This file will be the glue for our entire application. express = ( ); app = express(); port = process.env.PORT || ; mongoose = ( ); passport = ( ); flash = ( ); morgan = ( ); cookieParser = ( ); bodyParser = ( ); session = ( ); mongoose.connect( , { : , : , : }) app.use(morgan( )); app.use(cookieParser()); app.use(bodyParser()); app.set( , ); app.use(session({ : })); app.use(passport.initialize()); app.use(passport.session()); app.use(flash()); ( )(app, passport); ( )(passport); app.listen(port); var require 'express' var var 8000 var require 'mongoose' var require 'passport' var require 'connect-flash' var require 'morgan' var require 'cookie-parser' var require 'body-parser' var require 'express-session' // connect to our database 'mongodb://localhost/blog' useNewUrlParser true useUnifiedTopology true useCreateIndex true // pass passport for configuration // set up our express application 'dev' // log every request to the console // read cookies (needed for auth) // get information from html forms 'view engine' 'ejs' // set up ejs // required for passport secret 'ilovenodejs' // session secret // persistent login sessions // use connect-flash for flash messages stored in session // routes require './app/routes.js' require './config/passport' The path of our object is important to note here. We will create it at the very beginning of the file with var passport = require('passport');. Then we pass it into our config/passport.js file for it to be configured. Then we pass it to the app/routes.js file for it to be used in our routes. passport Now with this file, we have our application listening on . All we have to do to start up our server is: port 8000 npm i nodemon : { : , : } "scripts" "on" "nodemon server.js" "start" "node server.js" In json file edit as above. To start the node server,In terminal: nodemon Our server will start.Read about nodemon here Routes app/routes.js: We will have the following routes: Home Page (/) Login Page (/login) Signup Page (/signup) Profile Page (/profile) .exports = { app.get( , { res.render( ); }); app.get( , { res.render( , { : req.flash( ) }); }); app.get( , { res.render( , { : req.flash( ) }); }); app.get( , isLoggedIn, { res.render( , { : req.user }); }); app.get( , { req.logout(); res.redirect( ); }); app.post( , passport.authenticate( , { : , failureRedirect : , failureFlash : })); app.post( , passport.authenticate( ,{ : , failureRedirect : , failureFlash : })); }; { (req.isAuthenticated()) next(); res.redirect( ); } module ( ) function app, passport // =======================HOME-PAGE =============================== '/' ( ) function req, res 'index.ejs' // load the index.ejs file // ==========================LOGIN =============================== '/login' ( ) function req, res 'login.ejs' message 'loginMessage' // =====================SIGNUP ============================== '/signup' ( ) function req, res 'signup.ejs' message 'signupMessage' //==========================profile ===================== '/profile' ( ) function req, res 'profile.ejs' user // LOGOUT '/logout' ( ) function req, res '/' // process the signup form '/signup' 'local-signup' successRedirect '/profile' //redirect to the secure profile section '/signup' // redirect back to the signup page if there is an error true // allow flash messages // process the login form '/login' 'local-login' successRedirect '/profile' //redirect to the secure profile section '/login' // redirect back to the signup page if there is an error true // allow flash messages // makes sure a user is logged in ( ) function isLoggedIn req, res, next // if user is authenticated in the session, carry on if return // if they aren't redirect them to the home page '/' This is our app/routes.js file. Home Page views/index.ejs: Our home page will just show links to all our forms of authentication. Authentication Login or Register with: Local Login Local Signup <!doctype html> < > html < > head < > title </ > title < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" <!-- load bootstrap css --> < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" <!-- load fontawesome --> < > style { : ; } body padding-top 80px </ > style </ > head < > body < = > div class "container" < = > div class "jumbotron text-center" < > p </ > p < = = > a href "/login" class "btn btn-default" < = > span class "fa fa-user" </ > span </ > a < = = > a href "/signup" class "btn btn-default" < = > span class "fa fa-user" </ > span </ > a </ > div </ > div </ > body </ > html This is our /view/index.ejs file. Login Form views/login.ejs: <% %> <%= %> <% %> Node Authentication Login <!doctype html> < > html < > head < > title </ > title < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" <!-- load bootstrap css --> < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" <!-- load fontawesome --> < > style { : ; } body padding-top 80px </ > style </ > head < > body < = > div class "container" < = > div class "col-sm-6 col-sm-offset-3" < > h1 < = > span class "fa fa-sign-in" </ > span </ > h1 <!-- show any messages that come back with authentication --> (message.length > ) { if 0 < = > div class "alert alert-danger" message </ > div } Email Password Login Need an account? Signup Or go home . <!-- LOGIN FORM --> < = = > form action "/login" method "post" < = > div class "form-group" < > label </ > label < = = = > input required type "text" class "form-control" name "email" </ > div < = > div class "form-group" < > label </ > label < = = = > input required type "password" class "form-control" name "password" </ > div < = = > button type "submit" class "btn btn-warning btn-lg" </ > button </ > form < > hr < > p < = > a href "/signup" </ > a </ > p < > p < = > a href "/" </ > a </ > p </ > div </ > div </ > body </ > html This is our /view/login.ejs file. Signup Form views/signup.ejs: <% %> <%= %> <% %> Signup Signup <!doctype html> < > html < > head < > title </ > title < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" <!-- load bootstrap css --> < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" <!-- load fontawesome --> < > style { : ; } body padding-top 80px </ > style </ > head < > body < = > div class "container" < = > div class "col-sm-6 col-sm-offset-3" < > h1 < = > span class "fa fa-sign-in" </ > span </ > h1 <!-- show any messages that come back with authentication --> (message.length > ) { if 0 < = > div class "alert alert-danger" message </ > div } Email Password Signup Already have an account? Login Or go home . <!-- LOGIN FORM --> < = = > form action "/signup" method "post" < = > div class "form-group" < > label </ > label < = = = > input required type "text" class "form-control" name "email" </ > div < = > div class "form-group" < > label </ > label < = = = > input required type "password" class "form-control" name "password" </ > div < = = > button type "submit" class "btn btn-warning btn-lg" </ > button </ > form < > hr < > p < = > a href "/login" </ > a </ > p < > p < = > a href "/" </ > a </ > p </ > div </ > div </ > body </ > html This is our /view/signup.ejs file. Authenticating With Passport Locally: Finally! We have finally set up our application and have gotten to the authentication part. So far we have installed our packages, set up our application, connected to our database, created our routes, and created our views. Now we will create our user model, configure passport for local authentication, and use our configured passport to process our login/signup forms. User Model: For local accounts, we will be keeping and . You can change these fields out to be whatever you want. You can authenticate locally using username and password (passport-local actually uses username by default but we'll change that to email). email password mongoose = ( ); bcrypt = ( ); userSchema = mongoose.Schema({ : { : , : , }, : { : , : , : , : }, : { : , : , : , : }, : { : , : , : , : } }); userSchema.methods.generateHash = { bcrypt.hashSync(password, bcrypt.genSaltSync( ), ); }; userSchema.methods.validPassword = { bcrypt.compareSync(password, .local.password); }; .exports = mongoose.model( , userSchema); var require 'mongoose' var require 'bcrypt-nodejs' // define the schema for our user model var local email String password String facebook id String token String name String email String twitter id String token String displayName String username String google id String token String email String name String // generating a hash ( ) function password return 8 null // checks if password is valid ( ) function password return this // create the model for users and expose it to our app module 'User' This is our app/models/user.js file. Our model is done. We will be hashing our password within our user model before it saves to the database. This means we don't have to deal with generating the hash ourselves. It is all handled nicely and neatly inside our user model. Configuring Passport for Local Accounts: All the configuration for passport will be handled in config/passport.js. We want to keep this code in its own file away from our other main files like routes or the server file. I have seen some implementations where passport will be configured in random places. I believe having it in this config file will keep your overall application clean and concise. LocalStrategy = ( ).Strategy; User= ( ); .exports = { passport.serializeUser( { done( , user.id); }); passport.deserializeUser( { User.findById(id, { done(err, user); }); }); passport.use( , LocalStrategy({ usernameField : , : , : }, { process.nextTick( { User.findOne({ : email }, { (err) done(err); (user) { done( , , req.flash( , )); } { newUser = User(); newUser.local.email = email; newUser.local.password = newUser.generateHash(password); newUser.save( { (err) err; done( , newUser); }); } }); }); })); passport.use( , LocalStrategy({ : , : , : }, { User.findOne({ : email }, { (err) done(err); (!user) done( , , req.flash( , )); (!user.validPassword(password)) done( , , req.flash( , )); done( , user); }); })); }; var require 'passport-local' var require '../app/models/user' module ( ) function passport //passport serialize and unserialize users out of session ( ) function user, done null ( ) function id, done ( ) function err, user // ======================SIGNUP =========================== 'local-signup' new // by default, local strategy uses username and password, we will override with email 'email' passwordField 'password' passReqToCallback true // allows us to pass back the entire request to the callback ( ) function req, email, password, done // User.findOne won't fire unless data is sent back ( ) function // find a user whose email is the same as the forms email 'local.email' ( ) function err, user if return if return null false 'signupMessage' 'That email is already taken.' else // if there is no user with that email -create the user var new // set the user's local credentials // save the user ( ) function err if throw return null // =================LOCAL LOGIN ====================================== 'local-login' new usernameField 'email' passwordField 'password' passReqToCallback true ( ) function req, email, password, done // find a user whose email is the same as the forms email 'local.email' ( ) function err, user if return if return null false 'loginMessage' 'No user found.' // req.flash is the way to set flashdata using connect-flash if return null false 'loginMessage' 'Oops! Wrong password.' // create the loginMessage and save it to session as flashdata // all is well, return successful user return null So far, we created our passport object in server.js, and then we pass it to our config/passport.js file. This is where we configure our for local. Strategy This is also the file where we create the serializeUser and deserializeUser functions to store our user in session. We have now provided a strategy to passport called . We will use this strategy to process our signup form. Let's open up our app/routes.js and handle the POST for our signup form. local-signup ... app.post( , passport.authenticate( , { : , failureRedirect : , failureFlash : })); ... // app/routes.js // process the signup form '/signup' 'local-signup' successRedirect '/profile' // redirect to the secure profile section '/signup' // redirect back to the signup page if there is an error true // allow flash messages With users able to sign up, let's give them a way to login.We have provided a strategy to passport called . We will use this strategy to process our login form. We can check if a user exists, if the password is wrong, and set flash data to show error messages. Let's open up our app/routes.js and handle the POST for our login form. local-login ... app.post( , passport.authenticate( , { : , failureRedirect : , failureFlash : })); ... // app/routes.js // process the login form '/login' 'local-login' successRedirect '/profile' // redirect to the secure profile section '/login' // redirect back to the signup page if there is an error true // allow flash messages If you try to login with a user email that doesn't exist in our database, you will see the error. Same goes for if your password is wrong. Displaying User and Secure Profile Page views/profile: Now we have functional signup and login forms. If a user is successful in authenticating they will be redirected to the profile page. If they are not successful, they will go home. The last thing we need to do is make our profile page. Profile Profile Page Logout Local id : email : password : <!doctype html> < > html < > head < > title </ > title < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" < = = > link rel "stylesheet" href "//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" < > style { : ; :break-word; } body padding-top 80px word-wrap </ > style </ > head < > body < = > div class "container" < = > div class "page-header text-center" < > h1 < = > span class "fa fa-anchor" </ > span </ > h1 < = = > a href "/logout" class "btn btn-default btn-sm" </ > a </ > div < = > div class "row" <!-- INFORMATION --> < = > div class "col-sm-6" < = > div class "well" < > h3 < = > span class "fa fa-user" </ > span </ > h3 < > p < > strong </ > strong < %> %= user._id < > br < > strong </ > strong < %> %= user.local.email < > br < > strong </ > strong < %> %= user.local.password </ > p </ > div </ > div </ > div </ > div </ > body </ > html Conclusion: We've built a brand new application from scratch and have the ability to let users signup/register and login. We even have support for flash messages, hashing passwords, and requiring login for some sections of our site using route middleware. You can refer to my for the complete setup. github repo