Skip to content

web-authentication-documentation with [learnwithfair, Learn with fair, Rahatul Rabbi, Md Rahatul Rabbi ,rahatulrabbi]. In this repo I describes about Database Matching, Database Encryption Hashing, Hashing + Salting Password, Cookies and Session with Passport, Google OAuth with passport session based, Passport-jwt (token based)

Notifications You must be signed in to change notification settings

learnwithfair/web-authentication-documentation

Repository files navigation

WEB-AUTHENTICATION-DOCUMENTATION

Thanks for visiting my GitHub account!

Web Authentication is a web standard published by the World Wide Web Consortium. WebAuthn is a core component of the FIDO2 Project under the guidance of the FIDO Alliance see-more

Source Code (Download)

Click Here

Authentication Schema

Schema
schema

Table of Contents

  1. Database Matching
  2. Database Encryption
  3. Hashing Password
  4. Hashing + Salting Password
  5. Cookies & Session with Passport
  6. Google OAuth with passport session based
  7. Passport-jwt (token based)
  8. Follow Me

Level 1: Database matching

  • save(), find({property: value})
  • if hacker can access our database then our data is too much human readable
  • password checker online

Level 2: Database Encryption

  • read mongoose encryption documentation: https://www.npmjs.com/package/mongoose-encryption

  • install mongoose encryption npm install mongoose-encryption

  • create new mongoose Schema

    const mongoose = require("mongoose"); const encrypt = require("mongoose-encryption"); const userSchema = new mongoose.Schema({ name: String, age: Number, // whatever else });
  • create an encryption key inside .env file

    ENCRYPTION_KEY = thisismyencryptionkey;
  • set encryption key with our schema

    const encrypt = require("mongoose-encryption"); const encKey = process.env.ENCRYPTION_KEY; // encrypt age regardless of any other options. name and _id will be left unencrypted userSchema.plugin(encrypt, { secret: encKey, encryptedFields: ["age"], }); User = mongoose.model("User", userSchema);

Level 3: Hashing password

  • no cncryption key; we will use hashing algorithm

  • hackers can not convert to plain text as no encryption key is available

  • md5 package: https://www.npmjs.com/package/md5

  • install md5 npm package: npm install md5

  • usage

    var md5 = require("md5"); console.log(md5("message")); // 78e731027d8fd50ed642340b7c9a63b3 // hash password when create it const newUser = new User({ email: req.body.username, password: md5(req.body.password), }); app.post("/login", async (req, res) => { try { const email = req.body.email; const password = md5(req.body.password); const user = await User.findOne({ email: email }); if (user && user.password === password) { res.status(200).json({ status: "valid user" }); } else { res.status(404).json({ status: "Not valid user" }); } } catch (error) { res.status(500).json(error.message); } });

Level 4: Hashing + salting password

  • we can hash the password with some random number(salting)

  • install bcrypt npm package npm install bcrypt

  • usage

    const bcrypt = require("bcrypt"); const saltRounds = 10; app.post("/register", async (req, res) => { try { bcrypt.hash(req.body.password, saltRounds, async function (err, hash) { const newUser = new User({ email: req.body.email, password: hash, }); await newUser.save(); res.status(201).json(newUser); }); } catch (error) { res.status(500).json(error.message); } }); app.post("/login", async (req, res) => { try { const email = req.body.email; const password = req.body.password; const user = await User.findOne({ email: email }); if (user) { bcrypt.compare(password, user.password, function (err, result) { if (result === true) { res.status(200).json({ status: "valid user" }); } }); } else { res.status(404).json({ status: "Not valid user" }); } } catch (error) { res.status(500).json(error.message); } });

Level 5: Cookies & Session with passport

  • passport local strategy

  • npm install passport passport-local passport-local-mongoose express-session

  • my computer browser -> browse aliexpress (GET Request) -> to aliexpress server -> response the website -> add some items to the cart (post request to the server) -> aliexpress server will response and tell the browser to create a file in my computer for storing my selection -> so when next time we make a get request to the server we send the cookie with the get request -> server will return the cart again

  • cookie is a text file created by server on a user's device when we visit a website

  • that stores limited information such as login credentials - username, password; user preferences, cart contents from a web browser session

  • saving users behaviour

  • read more about cookies - https://www.trendmicro.com/vinfo/us/security/definition/cookies

  • types of cookies -> session cookie, presistent cookie, supercookie

  • login -> save user credentials as cookie for next time authentication -> log out and the session is destroyed

  • salt and hash is automatically generated by passport-local-mongoose

  • express session package create the cookie

  1. passport js framework has 2 separeate libraries
  • Passport JS Library (main) - maintain session information for user authentication
  • strategy library - methodology for authenticate an user - passport-local, passport-facebook, passport-oauth2 etc.
  1. Login process handled by 2 steps: i) session management (Passport.js), ii) authentication (strategy) npm install passport-local npm install passport-facebook

  2. for managing session Passport.js library takes help from express-session library npm install passport express-session

  3. source code

  • bootstrap the project

    • installing & requiring packages npm install express nodemon dotenv mongoose ejs cors

    • creating server

      //app.js const express = require("express"); const cors = require("cors"); const ejs = require("ejs"); const app = express(); app.set("view engine", "ejs"); app.use(cors()); app.use(express.urlencoded({ extended: true })); app.use(express.json()); module.exports = app; //index.js const app = require("./app"); const PORT = 4000; app.listen(PORT, () => { console.log(`app is running at http://localhost:${PORT}`); });
    • creating routes including try,catch

      // base url app.get("/", (req, res) => { res.render("index"); }); // register routes app.get("/register", (req, res) => { res.render("register"); }); app.post("/register", (req, res) => { try { res.status(201).send("user is registered"); } catch (error) { req.status(500).send(error.message); } }); // login routes app.get("/login", (req, res) => { res.render("login"); }); app.post("/login", (req, res) => { try { res.status(201).send("user is logged in"); } catch (error) { req.status(500).send(error.message); } }); // logout routes app.get("/logout", (req, res) => { res.redirect("/"); }); // profile protected routes app.get("/profile", (req, res) => { res.render("profile"); });
    • creating ejs files

      • create layout

        <!-- views/layout/header.ejs --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <header> <nav> <a href="/">Home</a> <a href="/register">Register</a> <a href="/login">Login</a> <a href="/profile">Profile</a> <a href="/logout">Logout</a> </nav> </header> </body> </html> <!-- views/layout/footer.ejs --> <footer> <p>copyright by Rahatul Rabbi</p> </footer> </body> </html>
      • create pages

        <!-- views/index.ejs --> <%-include("layout/header")%> <main> <h1>Home Page</h1> </main> <%-include("layout/footer")%> <!-- views/register.ejs --> <%-include("layout/header")%> <main> <h1>Register Page</h1> <form action="/register" method="post"> <div> <label for="username">username: </label> <input type="text" id="username" name="username" /> </div> <br /> <div> <label for="password">password: </label> <input type="password" id="password" name="password" /> </div> <br /> <button type="submit">Register</button> </form> </main> <%-include("layout/footer")%> <!-- views/login.ejs --> <%-include("layout/header")%> <main> <h1>Login Page</h1> <form action="/register" method="post"> <div> <label for="username">username: </label> <input type="text" id="username" name="username" /> </div> <br /> <div> <label for="password">password: </label> <input type="password" id="password" name="password" /> </div> <br /> <button type="submit">Login</button> </form> </main> <%-include("layout/footer")%> <!-- views/profile.ejs --> <%-include("layout/header")%> <main> <h1>Profile Page</h1> </main> <%-include("layout/footer")%>
  • create model and connect to mongodb

    // models/user.model.js const mongoose = require("mongoose"); const userSchema = mongoose.Schema({ username: { type: String, require: true, unique: true, }, password: { type: String, require: true, }, }); const User = mongoose.model("User", userSchema); module.exports = User; // config/database.js const mongoose = require("mongoose"); mongoose .connect("mongodb://localhost:27017/passportDB") .then(() => { console.log("db is connected"); }) .catch((error) => { console.log(error.message); }); // app.js require("./config/database");
  • register an user

//app.js const User = require("./models/user.model"); app.post("/register", async (req, res) => { try { const user = await User.findOne({ username: req.body.username }); if (user) return res.status(400).send("User already exist"); const newUser = new User(req.body); await newUser.save(); res.status(201).send(newUser); } catch (error) { req.status(500).send(error.message); } });
  • encrypt the user password using bcrypt hashing+salting npm install bcrypt
//app.js const bcrypt = require("bcrypt"); const saltRounds = 10; app.post("/register", async (req, res) => { try { const user = await User.findOne({ username: req.body.username }); if (user) return res.status(400).send("User already exist"); bcrypt.hash(req.body.password, saltRounds, async (err, hash) => { const newUser = new User({ username: req.body.username, password: hash, }); await newUser.save(); res.redirect("/login"); }); } catch (error) { res.status(500).send(error.message); } });
  • create and add session npm install passport express-session connect-mongo

    //Import the main Passport and Express-Session library const passport = require("passport"); const session = require("express-session"); // for storing session in different collection const MongoStore = require("connect-mongo"); // setting middleware app.set("trust proxy", 1); // trust first proxy app.use( session({ secret: "keyboard cat", resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: "mongodb://localhost:27017/testPassportDB", collectionName: "sessions", }), // cookie: { secure: true }, // cookie: { maxAge: 1000 * 60 * 60 * 24 }, }) ); app.use(passport.initialize()); // init passport on every route call. app.use(passport.session()); // allow passport to use "express-session".
  • set passport-local configuration npm install passport-local

    // passport.js const User = require("../model/user.model"); const passport = require("passport"); const bcrypt = require("bcrypt"); const LocalStrategy = require("passport-local").Strategy; passport.use( new LocalStrategy(async function (username, password, done) { try { const user = await User.findOne({ username: username }); // wrong username if (!user) { return done(null, false, { message: "Incorrect Username" }); } // wrong password if (!bcrypt.compare(password, user.password)) { return done(null, false, { message: "Incorrect Password" }); } // if user found return done(null, user); } catch (error) { return done(error); } }) ); // create session id // whenever we login it creares user id inside session passport.serializeUser((user, done) => { done(null, user.id); }); // find session info using session id passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (error) { done(error, false); } });
  • authenticate user using passport-local

// app.js // login using passport-local strategy const checkLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { return res.redirect("/profile"); } next(); }; // login routes app.get("/login", checkLoggedIn, (req, res) => { try { res.render("login"); } catch (error) { req.status(500).send(error.message); } }); // register routes app.get("/register", checkLoggedIn, (req, res) => { res.render("register"); });
  • check user is already logged in or not

    const checkAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return next(); } res.redirect("/login"); }; // profile protected routes app.get("/profile", checkAuthenticated, (req, res) => { res.render("profile"); });
  • logout route setup

    // logout routes app.get("/logout", (req, res) => { try { req.logout((err) => { if (err) { return next(err); } res.redirect("/"); }); } catch (error) { req.status(500).send(error.message); } });
  • finally the entire app.js

    const express = require("express"); const cors = require("cors"); const ejs = require("ejs"); const bcrypt = require("bcrypt"); const saltRounds = 10; const passport = require("passport"); const session = require("express-session"); // for storing session in different collection const MongoStore = require("connect-mongo"); require("./config/database"); require("./config/passport"); const User = require("./model/user.model"); const app = express(); app.set("view engine", "ejs"); app.use(cors()); app.use(express.urlencoded({ extended: true })); app.use(express.json()); // setting middleware app.set("trust proxy", 1); // trust first proxy app.use( session({ secret: "keyboard cat", resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: "mongodb://localhost:27017/passportDB", collectionName: "sessions", }), // cookie: { maxAge: 1000 * 60 * 60 * 24 }, }) ); app.use(passport.initialize()); // init passport on every route call. app.use(passport.session()); // allow passport to use "express-session". const checkLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { return res.redirect("/profile"); } next(); }; // base url app.get("/", (req, res) => { res.render("index"); }); // register routes app.get("/register", checkLoggedIn, (req, res) => { res.render("register"); }); app.post("/register", async (req, res) => { try { const user = await User.findOne({ username: req.body.username }); if (user) return res.status(400).send("User already exist"); bcrypt.hash(req.body.password, saltRounds, async (err, hash) => { const newUser = new User({ username: req.body.username, password: hash, }); await newUser.save(); res.redirect("/login"); }); } catch (error) { res.status(500).send(error.message); } }); // login routes app.get("/login", checkLoggedIn, (req, res) => { res.render("login"); }); app.post( "/login", passport.authenticate("local", { successRedirect: "/profile" }) ); // logout routes app.get("/logout", (req, res) => { try { req.logout((err) => { if (err) { return next(err); } res.redirect("/"); }); } catch (error) { req.status(500).send(error.message); } }); const checkAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return next(); } res.redirect("/login"); }; // profile protected routes app.get("/profile", checkAuthenticated, (req, res) => { res.render("profile"); }); module.exports = app;

Level 6: Google OAuth with passport session based

  • setup database name in db and also for session

  • change in schema

    const mongoose = require("mongoose"); const userSchema = mongoose.Schema({ username: { type: String, require: true, unique: true, }, googleId: { type: String, require: true, }, }); const User = mongoose.model("User", userSchema); module.exports = User;
  • inside login ejs add <a href="/auth/google">Login with Google</a>

  • create dynamic user name in profile page Welcome <%=username%>

  • configure strategy

    • we need client id, client secret
    // passport.js require("dotenv").config(); const User = require("../models/user.model"); const passport = require("passport"); const GoogleStrategy = require("passport-google-oauth20").Strategy; passport.use( new GoogleStrategy( { clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: "http://localhost:5000/auth/google/callback", }, function (accessToken, refreshToken, profile, cb) { User.findOne({ googleId: profile.id }, (err, user) => { if (err) return cb(err, null); // not a user; so create a new user with new google id if (!user) { let newUser = new User({ googleId: profile.id, username: profile.displayName, }); newUser.save(); return cb(null, newUser); } else { // if we find an user just return return user return cb(null, user); } }); } ) ); // create session id // whenever we login it creares user id inside session passport.serializeUser((user, done) => { done(null, user.id); }); // find session info using session id passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (error) { done(error, false); } }); // app.js const express = require("express"); const cors = require("cors"); const ejs = require("ejs"); const app = express(); require("./config/database"); require("dotenv").config(); require("./config/passport"); const User = require("./models/user.model"); const passport = require("passport"); const session = require("express-session"); const MongoStore = require("connect-mongo"); app.set("view engine", "ejs"); app.use(cors()); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.set("trust proxy", 1); // trust first proxy app.use( session({ secret: "keyboard cat", resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: process.env.MONGO_URL, collectionName: "sessions", }), // cookie: { secure: true }, }) ); app.use(passport.initialize()); app.use(passport.session()); // base url app.get("/", (req, res) => { res.render("index"); }); const checkLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { return res.redirect("/profile"); } next(); }; // login : get app.get("/login", checkLoggedIn, (req, res) => { res.render("login"); }); app.get( "/auth/google", passport.authenticate("google", { scope: ["profile"] }) ); app.get( "/auth/google/callback", passport.authenticate("google", { failureRedirect: "/login", successRedirect: "/profile", }), function (req, res) { // Successful authentication, redirect home. res.redirect("/"); } ); const checkAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return next(); } res.redirect("/login"); }; // profile protected route app.get("/profile", checkAuthenticated, (req, res) => { res.render("profile", { username: req.user.username }); }); // logout route app.get("/logout", (req, res) => { try { req.logout((err) => { if (err) { return next(err); } res.redirect("/"); }); } catch (error) { res.status(500).send(error.message); } }); module.exports = app;

Level 7: passport-jwt (token based)

  • how token based works
    • user register using username, password to the server -> server creates a token for the user -> so next time when user make any request server give access by validating the given token
  • folder and file structure
    • server
      • models
        • user.model.js
      • config
      • app.js
      • index.js
      • .env
      • .gitignore
  • initialize npm and install package npm init -y && npm install express nodemon cors dotenv bcrypt mongoose
  • test

Follow Me

Facebook, Youtube, Instagram

About

web-authentication-documentation with [learnwithfair, Learn with fair, Rahatul Rabbi, Md Rahatul Rabbi ,rahatulrabbi]. In this repo I describes about Database Matching, Database Encryption Hashing, Hashing + Salting Password, Cookies and Session with Passport, Google OAuth with passport session based, Passport-jwt (token based)

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published