-
Authentication
- Email & password security
- Cookies
- Tokens (JWT)
-
Authorization
- Roles
- Permissions
-
Encryption
- Hashing
- SALTing
- Loggers
morgan
knex-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> | |
<% }) %> | |
<% } %> |