Local Authentication Using Node.js

Authentication means validating your credentials like username and password to confirm your identity. The system or application confirms that you are the true user for accessing the private or confidential data. Authorization is a process of verifying that you have access to do something.

Authentication means validating your credentials like username and password to confirm your identity. The system or application confirms that you are the true user for accessing the private or confidential data. Authorization is a process of verifying that you have access to do something.

In other words, Authorization is about who somebody is and what they are allowed to do. In this project, we shall work on authentication for now.

NPM modules for this project:

  • bcrypt
  • body-parser
  • connect-flash
  • ejs
  • express
  • express-session
  • mongoose
  • morgan
  • nodemon
  • passport
  • passport-local
Passport 

Passport is an authentication middleware for NodeJS. It is extremely flexible and modular. It can be easily dropped into any NodeJS web-based application. It has a set of strategies that supports authentication using username and password.
 
Why Passport
  • 140+ strategies for different types
  • Easily handle success and failure state
  • Single sign in with OpenId and OAuth
  • Support persistent sessions
  • Dynamic scope and sessions
  • Lightweight
  • Easy to learn and use
Bcrypt

The first rule of the database is to never save your credentials in plain text. If in any case, the database is hacked, then the hacker can use that data very easily. So, by decrypting that data, it will be useless for a hacker.
 
Connect-flash

It's a special area of the session used for storing messages. Messages are written in flash and cleared after being displayed to the user. The flash is typically used in combination with redirects, ensuring that the message is available to the next page being rendered.
 
Body-parser

It is a middleware which is used to obtain information from the body of post request.
 
Morgan

It is optional because it is only used to handle request logger for the request to the server from the user.
 
Mongoose

Mongoose is a package for Nodejs for accessing the MongoDB and perform CRUD operation very easily.
 
Nodemon

To restart the server again and again automatically on the saving file event so that the developer can develop the server more efficiently and save headache from having to shift to command prompt or terminal.
 
Folder Structure 

|--application
   | app.js
   | package.json
   | tree.txt
   +---app
   |     |    routes.js
   |     \---model
   |           user.js
   +---config
   |    database.js
   |    passport.js
   +---node_modules
   +---static
   |     +---css
   |     |     style.css
   |     +---js
   |     |     main.js
   |     \---vendors
   |     +---css
   |     |     bootstrap.min.css
   |     \---js
   |         bootstrap.min.js
   |         jquery.js
   \---views
   |     index.ejs
   |     login.ejs
   |     profile.ejs
   |     signup.ejs
         \---partial
               footer.ejs
               header.ejs
         \---global
               css.ejs
               js.ejs
 
/package.json
  1. {  
  2.   "name""oauthpractice",  
  3.   "version""1.0.0",  
  4.   "description""",  
  5.   "main""app.js",  
  6.   "scripts": {  
  7.     "test""echo \"Error: no test specified\" && exit 1"  
  8.   },  
  9.   "author""Abhishek",  
  10.   "license""ISC",  
  11.   "dependencies": {  
  12.     "bcrypt""^1.0.3",  
  13.     "body-parser""^1.18.2",  
  14.     "connect-flash""^0.1.1",  
  15.     "cookie-parser""^1.4.3",  
  16.     "ejs""^2.5.7",  
  17.     "express""^4.16.2",  
  18.     "express-session""^1.15.6",  
  19.     "mongoose""^5.0.7",  
  20.     "morgan""^1.9.0",  
  21.     "nodemon""^1.15.1",  
  22.     "passport""^0.4.0",  
  23.     "passport-facebook""^2.1.1",  
  24.     "passport-google""^0.3.0",  
  25.     "passport-local""^1.0.0"  
  26.   }  
  27. }  
/app.js
  1. const express = require("express");  
  2. const expressSession = require("express-session");  
  3. const cookieParser = require("cookie-parser");  
  4. const morgan = require("morgan");  
  5. const bodyparser = require("body-parser");  
  6. const mongoose = require("mongoose");  
  7. const passport = require("passport");  
  8. const flash = require("connect-flash");  
  9.   
  10. const dbconfig = require("./config/database");  
  11.   
  12. mongoose.connect(dbconfig.url);  
  13.   
  14. const app = express();  
  15.   
  16. // For Frontend   
  17. app.set("view engine""ejs");  
  18. app.use(bodyparser.urlencoded({extended: false}));  
  19. app.use("/static", express.static("static"));  
  20.   
  21. // For loggers  
  22. app.use(morgan('dev'));  
  23.   
  24. // For Sessions  
  25. app.use(cookieParser());  
  26. app.use(expressSession({  
  27.     secret: "ThisIsOauthProject",  
  28.     saveUninitialized: true,  
  29.     resave: false  
  30. }));  
  31.   
  32. app.use(passport.initialize());  
  33. app.use(passport.session());  
  34. app.use(flash());  
  35. require("./config/passport")(passport);  
  36.   
  37. const port = process.env.port || 4080;  
  38.   
  39. // To see information about session and cookies (Optional)  
  40. // app.use("/", function(req, res){  
  41. //     res.send("This is node js Oauth project running on " + port);  
  42. //     console.log(req.cookies);  
  43. //     console.log("---------------------");  
  44. //     console.log(req.session);  
  45. // });  
  46.   
  47. require("./app/routes")(app, passport);  
  48.   
  49. console.log("Server is running at -> "+ port);  
  50. app.listen(port);  
app/model/user.js
  1. const mongoose = require("mongoose");  
  2. const bcrypt = require("bcrypt");  
  3.   
  4. const userSchema = mongoose.Schema({  
  5.     local: {  
  6.         username: String,  
  7.         password: String  
  8.     }  
  9. });  
  10.   
  11. userSchema.methods.generateHash = function(password){  
  12.     return bcrypt.hashSync(password, bcrypt.genSaltSync(9));  
  13. };  
  14.   
  15. userSchema.methods.validPassword = function(password){  
  16.     return bcrypt.compareSync(password, this.local.password);  
  17. }  
  18.   
  19. var User = mongoose.model("userdetails", userSchema);  
  20. module.exports = User;  
app/routes.js 
  1. const User = require("./model/user");  
  2.   
  3. module.exports = function(app, passport){  
  4.     app.get("/"function(req, res){  
  5.         res.render("index.ejs");  
  6.     });  
  7.   
  8.     app.get("/signup"function(req, res){  
  9.         res.render("signup.ejs", { message: req.flash("signupMsg") });  
  10.     });  
  11.   
  12.     app.post("/signup", passport.authenticate("local-signup", {  
  13.         successRedirect: "/",  
  14.         failureRedirect: "/signup",  
  15.         failureFlash: true  
  16.     }));  
  17.       
  18.     app.get("/login"function(req, res){  
  19.         res.render("login.ejs", { message: req.flash("loginMsg") });  
  20.     });  
  21.       
  22.     app.post("/login", passport.authenticate("local-login", {  
  23.         successRedirect: "/profile",  
  24.         failureRedirect: "/login",  
  25.         failureFlash: true  
  26.     }));  
  27.   
  28.     app.get("/profile", isLoggedIn, function(req, res){  
  29.         res.render("profile.ejs", { user : req.user });  
  30.     });  
  31.   
  32.     app.get("/logout"function(req, res){  
  33.         req.logout();  
  34.         res.redirect("/");  
  35.     });  
  36. };  
  37.   
  38. function isLoggedIn(req, res, next){  
  39.     if(req.isAuthenticated()) return next();  
  40.     res.redirect("/login");  
  41. }  
/config/database.js
  1. module.exports = {  
  2.     url: "mongodb://localhost:27017/oauthpractice"  
  3. };  
/config/passport.js 
  1. const localStrategy = require("passport-local").Strategy;  
  2. const User = require("../app/model/user");  
  3.   
  4. module.exports = function(passport){  
  5.       
  6.     passport.serializeUser(function(user, done){  
  7.         done( null, user.id);  
  8.     });  
  9.   
  10.     passport.deserializeUser(function(id, done){  
  11.         User.findById(id, function(err, user){  
  12.             done(err, user);  
  13.         });  
  14.     });  
  15.   
  16.     passport.use("local-signup"new localStrategy(  
  17.         {   
  18.             usernameField: "email",  
  19.             passwordField: "password",  
  20.             passReqToCallback: true  
  21.         }, function(req, email, password, done){  
  22.             process.nextTick(function(){  
  23.                 User.findOne({"local.username": email }, function(err, user){  
  24.                     if (err) return done(err);  
  25.                     if (user) return done(nullfalse, req.flash("signupMsg""Email already exist"));  
  26.                     else{  
  27.                         var newuser = new User();  
  28.                         newuser.local.username = email;  
  29.                         newuser.local.password = newuser.generateHash(password);  
  30.                         newuser.save(function(err){  
  31.                             if (err) throw err;  
  32.                             return done(null, newuser);  
  33.                         });  
  34.                     }   
  35.                 });  
  36.             });  
  37.         }  
  38.     ));  
  39.   
  40.     passport.use("local-login"new localStrategy(  
  41.         {   
  42.             usernameField: "email",  
  43.             passwordField: "password",  
  44.             passReqToCallback: true  
  45.         }, function(req, email, password, done){  
  46.             process.nextTick(function(){  
  47.                 User.findOne({"local.username": email }, function(err, user){  
  48.                     if (err) return done(err);  
  49.                     if (!user) return done(nullfalse, req.flash("loginMsg""User not found"));  
  50.                     if (!user.validPassword(password)) return done(nullfalse, req.flash("loginMsg""Password invalid"));  
  51.                     return done(null, user);   
  52.                 });  
  53.             });  
  54.         }  
  55.     ));  
  56. };  
/static/css/style.css
  1. .block-center > div{  
  2.     floatnone;  
  3.     margin0 auto;  
  4. }  
  5.   
  6. body{  
  7.     display: flex;  
  8.     flex-direction: column;  
  9.     min-height100vh;  
  10.     justify-content: space-between;  
  11. }  
  12.   
  13. footer{      
  14.     text-aligncenter;  
  15.     background-color#555;  
  16.     padding20px;  
  17.     colorwhite;  
  18. }  
/views/partial/global/css.
  1. <link rel="stylesheet" type="text/css" href="/static/vendors/css/bootstrap.min.css">  
  2. <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">  
/views/partial/global/js.ejs 
  1. <script src="/static/vendors/js/jquery.js"></script>  
  2. <script src="/static/vendors/js/bootstrap.min.js"></script>  
  3. <script src="/static/js/main.js"></script>  
/views/partial/header.ejs
  1. <nav class="navbar navbar-default" role="navigation">  
  2.     <!-- Brand and toggle get grouped for better mobile display -->  
  3.     <div class="navbar-header">  
  4.         <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">  
  5.             <span class="sr-only">Toggle navigation</span>  
  6.             <span class="icon-bar"></span>  
  7.             <span class="icon-bar"></span>  
  8.             <span class="icon-bar"></span>  
  9.         </button>  
  10.         <a class="navbar-brand" href="#">OauthProject</a>  
  11.     </div>  
  12.   
  13.     <!-- Collect the nav links, forms, and other content for toggling -->  
  14.     <div class="collapse navbar-collapse navbar-ex1-collapse">  
  15.         <ul class="nav navbar-nav">  
  16.             <li class="active"><a href="/">home</a></li>  
  17.             <li class="active"><a href="/login">Login</a></li>  
  18.             <li class="active"><a href="/signup">Local Signup</a></li>  
  19.         </ul>  
  20.     </div><!-- /.navbar-collapse -->  
  21. </nav>  
/views/partial/footer.ejs 
  1. <footer>  
  2.     <div>  
  3.         <h5>Copyright@ footer</h5>  
  4.     </div>  
  5. </footer>  
/views/index.ejs
  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <meta http-equiv="X-UA-Compatible" content="ie=edge">  
  7.     <title>Home</title>  
  8.     <% include ./partial/header.ejs%>  
  9. </head>  
  10. <body>  
  11.     <% include ./partial/global/css.ejs%>  
  12.       
  13.     <div class="container">  
  14.           
  15.         <div class="jumbotron">  
  16.             <div class="container text-center">  
  17.                 <h1><span class="fa fa-lock"></span>Node Authentication</h1>  
  18.                 <p> Register or Signup with</p>  
  19.                 <p>  
  20.                     <a href="/signup"><button type="button" class="btn btn-primary btn-sm">Local Register</button></a>  
  21.                     <a href="/login"><button type="button" class="btn btn-primary btn-sm">Local Login</button></a>  
  22.                 </p>  
  23.             </div>  
  24.         </div>  
  25.           
  26.     </div>  
  27.   
  28.     <% include ./partial/footer.ejs%>  
  29.     <% include ./partial/global/js.ejs%>  
  30. </body>  
  31. </html>  
/views/signup.ejs
  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <meta http-equiv="X-UA-Compatible" content="ie=edge">  
  7.     <title>Signup</title>  
  8.     <% include ./partial/header.ejs%>  
  9. </head>  
  10. <body>  
  11.     <% include ./partial/global/css.ejs%>  
  12.       
  13.     <div class="container">  
  14.         <div class="row">  
  15.             <div class="col-xs-12 col-sm-7 col-md-7 col-lg-7">  
  16.                 <div class="jumbotron">  
  17.                     <form action="/signup" method="POST" role="form">  
  18.                         <legend>Signup</legend>  
  19.                         <div class="form-group">  
  20.                             <label for="email">Email</label>  
  21.                             <input type="text" class="form-control" name="email" placeholder="Enter name">  
  22.                         </div>  
  23.                         <div class="form-group">  
  24.                             <label for="pass">Password</label>  
  25.                             <input type="password" class="form-control" name="password" placeholder="Enter name">  
  26.                         </div>  
  27.                         <button type="submit" class="btn btn-primary">Submit</button>  
  28.                         <a href="/"><button type="button" class="btn btn-primary">Back</button></a>  
  29.                         <% if( message.length > 0 ){ %>  
  30.                             <br />  
  31.                             <br />  
  32.                             <div class="alert alert-info">  
  33.                                 <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>  
  34.                                 <strong><%= message %></strong>  
  35.                                 <br> Please click on x button to close that bar  
  36.                             </div>                  
  37.                             <% } %>  
  38.                     </form>  
  39.                 </div>  
  40.             </div>  
  41.         </div>  
  42.     </div>  
  43.       
  44.     <% include ./partial/footer.ejs%>  
  45.     <% include ./partial/global/js.ejs%>  
  46. </body>  
  47. </html>  
/views/login.ejs
  1. <!DOCTYPE html>  
  2. <html lang="en">  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  6.     <meta http-equiv="X-UA-Compatible" content="ie=edge">  
  7.     <title>Login</title>  
  8.     <% include ./partial/header.ejs%>  
  9. </head>  
  10. <body>  
  11.     <% include ./partial/global/css.ejs%>  
  12.       
  13.     <div class="container">  
  14.         <div class="row">  
  15.             <div class="col-xs-12 col-sm-7 col-md-7 col-lg-7">  
  16.                 <div class="jumbotron">  
  17.                     <form action="/login" method="POST" role="form">  
  18.                         <legend>Login</legend>  
  19.                         <div class="form-group">  
  20.                             <label for="email">Email</label>  
  21.                             <input type="text" class="form-control" name="email" placeholder="Enter name">  
  22.                         </div>  
  23.                         <div class="form-group">  
  24.                             <label for="pass">Password</label>  
  25.                             <input type="password" class="form-control" name="password" placeholder="Enter name">  
  26.                         </div>  
  27.                         <button type="submit" class="btn btn-primary">Submit</button>  
  28.                         <a href="/"><button type="button" class="btn btn-primary">Back</button></a>  
  29.                         <% if( message.length > 0 ){ %>  
  30.                             <br />  
  31.                             <br />  
  32.                             <div class="alert alert-info">  
  33.                                 <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>  
  34.                                 <strong><%= message %></strong>  
  35.                             </div>                  
  36.                         <% } %>  
  37.                     </form>  
  38.                 </div>  
  39.             </div>  
  40.         </div>  
  41.     </div>  
  42.   
  43.     <% include ./partial/footer.ejs%>  
  44.     <% include ./partial/global/js.ejs%>  
  45. </body>  
  46. </html>   
/views/profile.ejs 
  1. <!DOCTYPE html>    
  2. <html lang="en">    
  3. <head>    
  4.     <meta charset="UTF-8">    
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">    
  6.     <meta http-equiv="X-UA-Compatible" content="ie=edge">    
  7.     <title>Signup</title>    
  8.     <% include ./partial/header.ejs%>    
  9. </head>    
  10. <body>    
  11.     <% include ./partial/global/css.ejs%>    
  12.         
  13.     <div class="container">    
  14.         <div class="row">    
  15.             <div class="col-md-12 col-sm-12">    
  16.                 <h2>Profile Page</h2>    
  17.             </div>    
  18.             <div class="col-md-6 col-sm-6">    
  19.                 <div class="well">    
  20.                     <h3><span class="fa fa-user"></span> Local</h3>    
  21.                     <h5>Username = <%= user.local.username %></h5>                
  22.                     <h5>Password = <%= user.local.password %></h5>                
  23.                     <h5>UserId = <%= user._id %></h5>                
  24.                     <a type="button" href="/logout" class="btn btn-danger">Logout</a>    
  25.                 </div>    
  26.             </div>    
  27.         </div>    
  28.     </div>    
  29.     
  30.     <% include ./partial/footer.ejs%>    
  31.     <% include ./partial/global/js.ejs%>    
  32. </body>    
  33. </html>