-
Authentication
- Email & password security
- Cookies
- Tokens (JWT)
-
Authorization
- Roles
- Permissions
-
Encryption
- Hashing
- SALTing
- Loggers
morganknex-logger
| 'use strict'; | |
| const PORT = process.env.PORT || 8080; | |
| const ENV = process.env.ENV || 'development'; | |
| const express = require('express'); | |
| const bodyParser = require('body-parser'); | |
| const session = require('cookie-session'); | |
| const flash = require('connect-flash'); | |
| const app = express(); | |
| const knexConfig = require('./knexfile'); | |
| const knex = require('knex')(knexConfig[ENV]); | |
| // set up dev tools | |
| if (ENV === 'development') { | |
| const morgan = require('morgan'); | |
| const knexLogger = require('knex-logger'); | |
| app.use(morgan('dev')); | |
| app.use(knexLogger(knex)); | |
| } | |
| // set up middleware | |
| app.use(session({ | |
| name: 'session', | |
| keys: 'alphabrain' | |
| })); | |
| app.use(flash()); | |
| app.set('view engine', 'ejs'); | |
| app.use(bodyParser.urlencoded({ extended: true })); | |
| // set up services | |
| const userService = require('./userService')(knex); | |
| // set up routes | |
| app.get('/', function (req, res) { | |
| res.render('index', { | |
| errors: req.flash('errors'), | |
| info: req.flash('info'), | |
| user: req.session.user_email | |
| }); | |
| }); | |
| app.post('/signup', function (req, res) { | |
| let { name, email, password } = req.body; | |
| userService.createUser(name, email, password) | |
| .then(function (user) { | |
| req.session.user_email = user.email; | |
| res.render('index', { | |
| errors: req.flash('errors'), | |
| info: req.flash('info'), | |
| user: req.session.user_email | |
| }); | |
| }) | |
| .catch(function (err) { | |
| req.flash('errors', err.message); | |
| res.redirect('/'); | |
| }); | |
| }); | |
| app.post('/signin', function (req, res) { | |
| const { email, password } = req.body; | |
| userService.authenticate(email, password) | |
| .then(function (user) { | |
| req.session.user_email = user.email; | |
| res.render('index', { | |
| errors: req.flash('errors'), | |
| info: req.flash('info'), | |
| user: req.session.user_email | |
| }); | |
| }) | |
| .catch(function (err) { | |
| req.flash('errors', err.message); | |
| res.render('index', { | |
| errors: req.flash('errors'), | |
| info: req.flash('info'), | |
| user: {} | |
| }); | |
| }); | |
| }); | |
| app.listen(PORT, function () { | |
| console.log('App listening at 8080'); | |
| }); |
| { | |
| "name": "user-auth", | |
| "version": "1.0.0", | |
| "description": "", | |
| "main": "index.js", | |
| "scripts": { | |
| "test": "echo \"Error: no test specified\" && exit 1" | |
| }, | |
| "author": "", | |
| "license": "ISC", | |
| "dependencies": { | |
| "bcrypt": "^1.0.3", | |
| "body-parser": "^1.15.2", | |
| "connect-flash": "^0.1.1", | |
| "cookie-session": "^2.0.0-beta.3", | |
| "ejs": "^2.4.1", | |
| "express": "^4.13.4", | |
| "knex": "^0.11.7", | |
| "pg": "^6.0.2" | |
| }, | |
| "devDependencies": { | |
| "morgan": "^1.7.0", | |
| "knex-logger": "^0.1.0", | |
| "nodemon": "^1.9.2" | |
| } | |
| } |
| exports.up = function (knex, Promise) { | |
| return knex.schema.createTable('users', function (table) { | |
| table.increments(); | |
| table.string('name').notNullable(); | |
| table.string('email').unique().notNullable(); | |
| table.string('password_digest').notNullable(); | |
| }); | |
| }; | |
| exports.down = function (knex, Promise) { | |
| return knex.schema.dropTable('users'); | |
| }; |
| const { hash, compare } = require('bcrypt'); | |
| module.exports = function (knex) { | |
| let service = {}; | |
| service.getUserByEmail = function (email) { | |
| return knex.select() | |
| .where({ email: email }) | |
| .from('users') | |
| .first(); | |
| }; | |
| service.createUser = function (name, email, password) { | |
| return new Promise(function (resolve, reject) { | |
| service.getUserByEmail(email) | |
| .then(function (user) { | |
| if (!user) { | |
| return hash(password, 10); | |
| } | |
| else { | |
| return reject({ message: 'User with email exists' }); | |
| } | |
| }) | |
| .then(function (passwordDigest) { | |
| return knex('users').insert({ | |
| name: name, | |
| email: email, | |
| password_digest: passwordDigest | |
| }); | |
| }) | |
| .then(function () { | |
| return service.getUserByEmail(email); | |
| }) | |
| .then(function (user) { | |
| return resolve(user); | |
| }) | |
| .catch(function () { | |
| return reject({ message: 'Unable to create user' }); | |
| }); | |
| }); | |
| }; | |
| service.authenticate = function (email, password) { | |
| return new Promise(function (resolve, reject) { | |
| service.getUserByEmail(email) | |
| .then(function (user) { | |
| if (!user) { | |
| return reject('No user found'); | |
| } | |
| compare(password, user.password_digest) | |
| .then(function (equal) { | |
| if (equal) { | |
| return resolve(user); | |
| } | |
| else { | |
| return reject({ message: 'Password is incorrect' }); | |
| } | |
| }).catch(function () { | |
| return reject({ message: 'Unable to authenticate' }); | |
| }); | |
| }); | |
| }); | |
| }; | |
| return service; | |
| }; |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <title>Home</title> | |
| <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" | |
| crossorigin="anonymous" /> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <%- include partials/_flash.ejs %> | |
| <div class="jumbotron"> | |
| <h1>Welcome</h1> | |
| <h2> | |
| <small> | |
| <% user %> | |
| </small> | |
| </h2> | |
| </div> | |
| <div class="row"> | |
| <div class="col-sm-6"> | |
| <form class="form" method="POST" action="/signup"> | |
| <h2>Register</h2> | |
| <div class="form-group"> | |
| <label>Name</label> | |
| <input class="form-control" type="text" name="name" /> | |
| </div> | |
| <div class="form-group"> | |
| <label>Email</label> | |
| <input class="form-control" type="email" name="email" /> | |
| </div> | |
| <div class="form-group"> | |
| <label>Password</label> | |
| <input class="form-control" type="password" name="password" /> | |
| </div> | |
| <div class="form-group"> | |
| <label>Confirm Password</label> | |
| <input class="form-control" type="password" name="passwordConfirm" /> | |
| </div> | |
| <div> | |
| <button class="btn btn-primary" type="submit">Register</button> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="col-sm-6"> | |
| <form class="form" method="POST" action="/signin"> | |
| <h2>Log In</h2> | |
| <div class="form-group"> | |
| <label>Email</label> | |
| <input class="form-control" type="email" name="email" /> | |
| </div> | |
| <div class="form-group"> | |
| <label>Password</label> | |
| <input class="form-control" type="password" name="password" /> | |
| </div> | |
| <div> | |
| <button class="btn btn-primary" type="submit">Log in</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
| <% if (errors.length) { %> | |
| <% errors.forEach((error) => { %> | |
| <p class="error">Error: <%= error %></p> | |
| <% }) %> | |
| <% } %> | |
| <% if (info.length) { %> | |
| <% info.forEach((message) => { %> | |
| <p class="info">Info: <%= message %></p> | |
| <% }) %> | |
| <% } %> |