This app allows users to create an account, log into the account and sign back out securely. All user data is stored in a mysql database.
As a user who wants to safely log in to a website, I want to know my personal details are safely stored saftly.
- Languages Used
- Instructions
- Installation
- Directory of Files
- Directory of Files Explained
- Example Img
- Repository Link
- Test
- Licence
- GitHub Info
- HTML5/CSS3
- JAVASCRIPT
- BCRYPTJS
- EXPRESS
- EXPRESS-SESSION
- MYSQL2
- PASSPORT
- PASSPORT-LOCAL
- SEQUELIZE
After cloning this repository into your local storage proceed to preform the following steps;
- Create a MySQL database called
passport_demo
- Open
config.js
and insert your personal username and password for MySQL - Open terminal in current root folder and install all node packages Installation
- Run
node server.js
to successfully connect to the server - Open browser and put
http://localhost:8080
in search bar - Begin using the app
Open terminal in the root directory and install the following:
npm install
npm install express
npm install express-session
npm install mysql
npm install passport
npm install passport-local
npm install sequelize
- config ↓
- → middleware ↓
- → passport.js
- → config.json
- models ↓
- node_modules ↓
- → installed node files/folders
- public ↓
- → js ↓
- → login.js
- → members.js
- → signup.js
- → stylesheet ↓
- → login.html
- → members.html
- → signup.html
- → js ↓
- routes ↓
- server.js
- package.json
-
-
-
- This is middleware for restricting routes a user is not allowed to visit. If the user is logged in, continue with the request to the restricted route. If the user isn't logged in, redirect them to the login page
// This is middleware for restricting routes a user is not allowed to visit if not logged in module.exports = function(req, res, next) { // If the user is logged in, continue with the request to the restricted route if (req.user) {return next();} // If the user isn't logged in, redirect them to the login page return res.redirect("/"); };
-
-
- Contains javascript logic that tells passport we want to log in with an email address and password. Tell passwort we want to login using a username and password. Set the Local Strategy to use a an email as the username. Once tan email is given with the matching password then login with given credentials by authenticating the http request by using Sequelize to serialize and deserialize the user and exporting the reuired passport.
// Require passport, passport-local and /models path var passport = require("passport"); var LocalStrategy = require("passport-local").Strategy; var db = require("../models"); // Telling passport we want to use a Local Strategy. In other words, we want login with a username/email and password passport.use( new LocalStrategy( // Our user will sign in using an email, rather than a "username" { usernameField: "email" }, function (email, password, done) { // When a user tries to sign in this code runs db.User.findOne({ where: { email: email, }, }).then(function (dbUser) { // If there's no user with the given email if (!dbUser) { return done(null, false, { message: "Incorrect email.", }); } // If there is a user with the given email, but the password the user gives us is incorrect else if (!dbUser.validPassword(password)) { return done(null, false, { message: "Incorrect password.", }); } // If none of the above, return the user return done(null, dbUser); }); }, ), ); // In order to help keep authentication state across HTTP requests, // Sequelize needs to serialize and deserialize the user passport.serializeUser(function (user, cb) { cb(null, user); }); passport.deserializeUser(function (obj, cb) { cb(null, obj); }); // Exporting our configured passport module.exports = passport;
-
- Connection configuration to connect to server
{ "development": {"username": "root", "password": null, "database": passport_demo", "host": "127.0.0.1", "dialect": "mysql"}, "test": {"username": "root", "password": null, "database": "database_test", "host": "127.0.0.1", "dialect": "mysql"}, "production": {"username": "root", "password": null, "database": "database_production", "host": "127.0.0.1", "dialect": "mysql"} }
-
-
-
- Connects to database and imports users log in data
'use strict'; // Require fs, path, sequelize var fs = require('fs'); var path = require('path'); var Sequelize = require('sequelize'); var basename = path.basename(module.filename); var env = process.env.NODE_ENV || 'development'; var config = require(__dirname + '/../config/config.json')[env]; // set db to an empty array var db = {}; if (config.use_env_variable) { var sequelize = new Sequelize(process.env[config.use_env_variable]); } else { var sequelize = new Sequelize(config.database, config.username, config.password, config); } fs .readdirSync(**dirname) .filter(function(file) { return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); }) .forEach(function(file) { var model = sequelize['import'](path.join(**dirname, file)); db[model.name] = model; }); Object.keys(db).forEach(function(modelName) { if (db[modelName].associate) { db[modelName].associate(db); }}); db.sequelize = sequelize; db.Sequelize = Sequelize; // export to database module.exports = db;
-
- Requires "bcrypt" for password hashing. This makes the database secure even if compromised. JavaScript is used to define what is stored on our database when creating a user model.
// Requiring bcrypt for password hashing. Using the bcryptjs version as the regular bcrypt module sometimes causes errors on Windows machines var bcrypt = require("bcryptjs"); // Creating our User model module.exports = function (sequelize, DataTypes) { var User = sequelize.define("User", { // The email cannot be null, and must be a proper email before creation email: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { isEmail: true, }, }, // The password cannot be null password: { type: DataTypes.STRING, allowNull: false, }, }); // Creating a custom method for our User model. This will check if an unhashed password entered by the user can be compared to the hashed password stored in our database User.prototype.validPassword = function (password) { return bcrypt.compareSync(password, this.password); }; // Hooks are automatic methods that run during various phases of the User Model lifecycle // In this case, before a User is created, we will automatically hash their password User.addHook("beforeCreate", function (user) { user.password = bcrypt.hashSync( user.password, bcrypt.genSaltSync(10), null, ); }); return User; };
-
-
- A collection of files/folders installed when running npm install from package.json and other required applications
-
-
-
- Gets references to the form and inputs. When the form is sumbitted, the email and passowrd entered are validated and then the loginUser function is run and the form cleared. loginUser posts to the api/login route and if successful it redirects to the members page.
$(document).ready(function () { // Getting references to our form and inputs var loginForm = $("form.login"); var emailInput = $("input#email-input"); var passwordInput = $("input#password-input"); // When the form is submitted, we validate there's an email and password entered loginForm.on("submit", function (event) { event.preventDefault(); var userData = { email: emailInput.val().trim(), password: passwordInput.val().trim(), }; if (!userData.email || !userData.password) { return; } // If we have an email and password we run the loginUser function and clear the form loginUser(userData.email, userData.password); emailInput.val(""); passwordInput.val(""); }); // loginUser does a post to our "api/login" route and if successful, redirects us the the members page function loginUser(email, password) { $.post("/api/login", { email: email, password: password, }) .then(function () { window.location.replace("/members"); // If there's an error, log the error }) .catch(function (err) { console.log(err); }); } });
-
- This file just does a GET request to figure out which user is logged in and updates the HTML on the page
$(document).ready(function () { // This file just does a GET request to figure out which user is logged in // and updates the HTML on the page $.get("/api/user_data").then(function (data) { $(".member-name").text(data.email); }); });
-
- Get the refernces from the form and input. When the signup button is clicked, the email and password are checked to make sure they're not blank. If an email and password is inputed then it runs the signUpUser function. Posts to the signup route redirecting to page to the member's page.
$(document).ready(function () { // Getting references to our form and input var signUpForm = $("form.signup"); var emailInput = $("input#email-input"); var passwordInput = $("input#password-input"); // When the signup button is clicked, we validate the email and password are not blank signUpForm.on("submit", function (event) { event.preventDefault(); var userData = { email: emailInput.val().trim(), password: passwordInput.val().trim(), }; if (!userData.email || !userData.password) { return; } // If we have an email and password, run the signUpUser function signUpUser(userData.email, userData.password); emailInput.val(""); passwordInput.val(""); }); // Does a post to the signup route. If successful, we are redirected to the members page // Otherwise we log any errors function signUpUser(email, password) { $.post("/api/signup", { email: email, password: password, }) .then(function (data) { window.location.replace("/members"); // If there's an error, handle it by throwing up a bootstrap alert }) .catch(handleLoginErr); } function handleLoginErr(err) { $("#alert .msg").text(err.responseJSON); $("#alert").fadeIn(500); } });
-
-
-
- A stylesheet for designing custome CSS. This stylesheet only applies a top margin of 50px for the form.signup and form.login html references.
-
-
- This file is used to create the html content for the login page. It includes a Bootswatch style sheet to utilize the Lumen Bootstrap template as well as linking the style.css stylesheet, jQuery's JavaScript library and the login.js script. It renders a navbar and login form.
-
- This file is used to create the html content for the members page. It includes a Bootswatch style sheet to utilize the Lumen Bootstrap template as well as linking the style.css stylesheet, jQuery's JavaScript library and the login.js script. It renders a navbar with a logout option and a member's welcom page.
-
- This file is used to create the html content for the members page. It includes a Bootswatch style sheet to utilize the Lumen Bootstrap template as well as linking the style.css stylesheet, jQuery's JavaScript library and the login.js script. It renders a navbar and a signup page with a form with the option to register a user name and password or to retun to the login page to use already registered credentials.
-
-
-
- Contains routes for signing in, logging out and getting users specific data to be displayed client side. It requirs models and passport as configured.
// Requiring our models and passport as we've configured it var db = require("../models"); var passport = require("../config/passport"); module.exports = function (app) {
/api/login
uses the passport.authenticate middleware with local strategy to validate the users login credentials and proceeds to send them to the members page.
// Using the passport.authenticate middleware with our local strategy. // If the user has valid login credentials, send them to the members page. // Otherwise the user will be sent an error app.post("/api/login", passport.authenticate("local"), function (req, res) { res.json(req.user); });
/api/setup
is used to sign up a user by automatically hashing and storing the users password using the configured Sequelize User Model. Once the user's credentials are created it logs the user in using the/api/login
route.
// Route for signing up a user. The user's password is automatically hashed and stored securely thanks to // how we configured our Sequelize User Model. If the user is created successfully, proceed to log the user in, // otherwise send back an error app.post("/api/signup", function (req, res) { db.User.create({ email: req.body.email, password: req.body.password, }) .then(function () { res.redirect(307, "/api/login"); }) .catch(function (err) { res.status(401).json(err); }); });
/logout
is used for loggin the user out then redirects them back to the main page.
// Route for logging user out app.get("/logout", function (req, res) { req.logout(); res.redirect("/"); });
/api/user_data
is used for getting user data to be used client side
// Route for getting some data about our user to be used client side app.get("/api/user_data", function (req, res) { if (!req.user) { // The user is not logged in, send back an empty object res.json({}); } else { // Otherwise send back the user's email and id // Sending back a password, even a hashed password, isn't a good idea res.json({ email: req.user.email, id: req.user.id, }); } }); };
-
- Routes that check whether user is signed in and if the user already has account then sends them to the correct html page. It requires path to use relative routes to our html files. It requires custom middleware for checking if a user is logged in. If the user already has an account send them to the members page by using isAuthenticated middleware. Else if a user who is not logged in tries to access this route they will be redirected to the signup page.
// Requiring path to so we can use relative routes to our HTML files var path = require("path"); // Requiring our custom middleware for checking if a user is logged in var isAuthenticated = require("../config/middleware/isAuthenticated"); module.exports = function (app) { app.get("/", function (req, res) { // If the user already has an account send them to the members page if (req.user) { res.redirect("/members"); } res.sendFile(path.join(__dirname, "../public/signup.html")); }); app.get("/login", function (req, res) { // If the user already has an account send them to the members page if (req.user) { res.redirect("/members"); } res.sendFile(path.join(__dirname, "../public/login.html")); }); // Here we've add our isAuthenticated middleware to this route. // If a user who is not logged in tries to access this route they will be redirected to the signup page app.get("/members", isAuthenticated, function (req, res) { res.sendFile(path.join(__dirname, "../public/members.html")); }); };
-
-
- Declares required npm packages. Requires passport as configured. Sets up port and requires models for syncing. Creates an express application and configures middleware needed for authentication. Requires html and api routes. Syncs the database and logs a message if successful.
// Requiring necessary npm packages var express = require("express"); var session = require("express-session"); // Requiring passport as we've configured it var passport = require("./config/passport"); // Setting up port and requiring models for syncing var PORT = process.env.PORT || 8080; var db = require("./models"); // Creating express app and configuring middleware needed for authentication var app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(express.static("public")); // We need to use sessions to keep track of our user's login status app.use( session({ secret: "keyboard cat", resave: true, saveUninitialized: true }), ); app.use(passport.initialize()); app.use(passport.session()); // Requiring our routes require("./routes/html-routes.js")(app); require("./routes/api-routes.js")(app); // Syncing our database and logging a message to the user upon success db.sequelize.sync().then(function () { app.listen(PORT, function () { console.log( "==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.", PORT, PORT, ); }); });
-
- Contains all package info, node modules used and version info.
- See Installation information for list of installed dependencies.
{ "name": "1-Passport-Example", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js", "watch": "nodemon server.js"}, "keywords": [], "author": "", "license": "ISC", "dependencies": { "bcryptjs": "2.4.3", "express": "^4.17.1", "express-session": ^1.17.1", "mysql2": "^1.6.5", "passport": "^0.4.1", "passport-local": "^1.0.0", "sequelize": "^5.8.6"} }
- Contains all package info, node modules used and version info.
↑ Return to Directory of Files ↑
E-Mail: [email protected]