├── .gitignore ├── .sequelizerc ├── LICENSE ├── README.md ├── app.js ├── bin └── www ├── config ├── config.json └── passport.js ├── migrations ├── 20181117071610-create-product.js └── 20181117071617-create-user.js ├── models ├── index.js ├── product.js └── user.js ├── package-lock.json ├── package.json ├── public └── stylesheets │ └── style.css ├── routes ├── api.js ├── index.js └── users.js └── views ├── error.ejs └── index.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "config": path.resolve('./config', 'config.json'), 5 | "models-path": path.resolve('./models'), 6 | "seeders-path": path.resolve('./seeders'), 7 | "migrations-path": path.resolve('./migrations') 8 | }; 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Didin Jamaludin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Secure Node.js, Express.js and PostgreSQL API using Passport.js 2 | 3 | This source code is part of [Secure Node.js, Express.js and PostgreSQL API using Passport.js](https://www.djamware.com/post/5bf94d9a80aca747f4b9ce9f/secure-nodejs-expressjs-and-postgresql-api-using-passportjs) tutorial. 4 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | var apiRouter = require('./routes/api'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | app.use(logger('dev')); 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: false })); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, 'public'))); 22 | 23 | app.use('/', indexRouter); 24 | app.use('/users', usersRouter); 25 | app.use('/api', apiRouter); 26 | 27 | // catch 404 and forward to error handler 28 | app.use(function(req, res, next) { 29 | next(createError(404)); 30 | }); 31 | 32 | // error handler 33 | app.use(function(err, req, res, next) { 34 | // set locals, only providing error in development 35 | res.locals.message = err.message; 36 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 37 | 38 | // render the error page 39 | res.status(err.status || 500); 40 | res.render('error'); 41 | }); 42 | 43 | module.exports = app; 44 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('secure-node:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "djamware", 4 | "password": "dj@mw@r3", 5 | "database": "secure_node", 6 | "host": "127.0.0.1", 7 | "dialect": "postgres" 8 | }, 9 | "test": { 10 | "username": "root", 11 | "password": "dj@mw@r3", 12 | "database": "secure_node", 13 | "host": "127.0.0.1", 14 | "dialect": "postgres" 15 | }, 16 | "production": { 17 | "username": "root", 18 | "password": "dj@mw@r3", 19 | "database": "secure_node", 20 | "host": "127.0.0.1", 21 | "dialect": "postgres" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/passport.js: -------------------------------------------------------------------------------- 1 | const JwtStrategy = require('passport-jwt').Strategy, 2 | ExtractJwt = require('passport-jwt').ExtractJwt; 3 | 4 | // load up the user model 5 | const User = require('../models').User; 6 | 7 | module.exports = function(passport) { 8 | const opts = { 9 | jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'), 10 | secretOrKey: 'nodeauthsecret', 11 | }; 12 | passport.use('jwt', new JwtStrategy(opts, function(jwt_payload, done) { 13 | User 14 | .findByPk(jwt_payload.id) 15 | .then((user) => { return done(null, user); }) 16 | .catch((error) => { return done(error, false); }); 17 | })); 18 | }; 19 | -------------------------------------------------------------------------------- /migrations/20181117071610-create-product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Products', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | prod_name: { 12 | type: Sequelize.STRING 13 | }, 14 | prod_desc: { 15 | type: Sequelize.STRING 16 | }, 17 | prod_price: { 18 | type: Sequelize.FLOAT 19 | }, 20 | createdAt: { 21 | allowNull: false, 22 | type: Sequelize.DATE 23 | }, 24 | updatedAt: { 25 | allowNull: false, 26 | type: Sequelize.DATE 27 | } 28 | }); 29 | }, 30 | down: (queryInterface, Sequelize) => { 31 | return queryInterface.dropTable('Products'); 32 | } 33 | }; -------------------------------------------------------------------------------- /migrations/20181117071617-create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('Users', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | username: { 12 | type: Sequelize.STRING 13 | }, 14 | password: { 15 | type: Sequelize.STRING 16 | }, 17 | createdAt: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | updatedAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | } 25 | }); 26 | }, 27 | down: (queryInterface, Sequelize) => { 28 | return queryInterface.dropTable('Users'); 29 | } 30 | }; -------------------------------------------------------------------------------- /models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Sequelize = require('sequelize'); 6 | const basename = path.basename(__filename); 7 | const env = process.env.NODE_ENV || 'development'; 8 | const config = require(__dirname + '/../config/config.json')[env]; 9 | const db = {}; 10 | 11 | let sequelize; 12 | if (config.use_env_variable) { 13 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 14 | } else { 15 | sequelize = new Sequelize(config.database, config.username, config.password, config); 16 | } 17 | 18 | fs 19 | .readdirSync(__dirname) 20 | .filter(file => { 21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 22 | }) 23 | .forEach(file => { 24 | const model = sequelize['import'](path.join(__dirname, file)); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /models/product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Product = sequelize.define('Product', { 4 | prod_name: DataTypes.STRING, 5 | prod_desc: DataTypes.STRING, 6 | prod_price: DataTypes.FLOAT 7 | }, {}); 8 | Product.associate = function(models) { 9 | // associations can be defined here 10 | }; 11 | return Product; 12 | }; -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bcrypt = require('bcrypt-nodejs'); 4 | 5 | module.exports = (sequelize, DataTypes) => { 6 | const User = sequelize.define('User', { 7 | username: DataTypes.STRING, 8 | password: DataTypes.STRING 9 | }, {}); 10 | User.beforeSave((user, options) => { 11 | if (user.changed('password')) { 12 | user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null); 13 | } 14 | }); 15 | User.prototype.comparePassword = function (passw, cb) { 16 | bcrypt.compare(passw, this.password, function (err, isMatch) { 17 | if (err) { 18 | return cb(err); 19 | } 20 | cb(null, isMatch); 21 | }); 22 | }; 23 | User.associate = function(models) { 24 | // associations can be defined here 25 | }; 26 | return User; 27 | }; 28 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-node", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/geojson": { 8 | "version": "1.0.6", 9 | "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", 10 | "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==" 11 | }, 12 | "@types/node": { 13 | "version": "10.12.9", 14 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.9.tgz", 15 | "integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==" 16 | }, 17 | "accepts": { 18 | "version": "1.3.5", 19 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 20 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 21 | "requires": { 22 | "mime-types": "~2.1.18", 23 | "negotiator": "0.6.1" 24 | } 25 | }, 26 | "array-flatten": { 27 | "version": "1.1.1", 28 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 29 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 30 | }, 31 | "basic-auth": { 32 | "version": "2.0.1", 33 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 34 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 35 | "requires": { 36 | "safe-buffer": "5.1.2" 37 | } 38 | }, 39 | "bcrypt-nodejs": { 40 | "version": "0.0.3", 41 | "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", 42 | "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=" 43 | }, 44 | "bluebird": { 45 | "version": "3.5.3", 46 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", 47 | "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" 48 | }, 49 | "body-parser": { 50 | "version": "1.18.3", 51 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 52 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 53 | "requires": { 54 | "bytes": "3.0.0", 55 | "content-type": "~1.0.4", 56 | "debug": "2.6.9", 57 | "depd": "~1.1.2", 58 | "http-errors": "~1.6.3", 59 | "iconv-lite": "0.4.23", 60 | "on-finished": "~2.3.0", 61 | "qs": "6.5.2", 62 | "raw-body": "2.3.3", 63 | "type-is": "~1.6.16" 64 | } 65 | }, 66 | "buffer-equal-constant-time": { 67 | "version": "1.0.1", 68 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 69 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 70 | }, 71 | "buffer-writer": { 72 | "version": "2.0.0", 73 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 74 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 75 | }, 76 | "bytes": { 77 | "version": "3.0.0", 78 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 79 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 80 | }, 81 | "cls-bluebird": { 82 | "version": "2.1.0", 83 | "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", 84 | "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=", 85 | "requires": { 86 | "is-bluebird": "^1.0.2", 87 | "shimmer": "^1.1.0" 88 | } 89 | }, 90 | "content-disposition": { 91 | "version": "0.5.2", 92 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 93 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 94 | }, 95 | "content-type": { 96 | "version": "1.0.4", 97 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 98 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 99 | }, 100 | "cookie": { 101 | "version": "0.3.1", 102 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 103 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 104 | }, 105 | "cookie-parser": { 106 | "version": "1.4.3", 107 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", 108 | "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", 109 | "requires": { 110 | "cookie": "0.3.1", 111 | "cookie-signature": "1.0.6" 112 | } 113 | }, 114 | "cookie-signature": { 115 | "version": "1.0.6", 116 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 117 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 118 | }, 119 | "debug": { 120 | "version": "2.6.9", 121 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 122 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 123 | "requires": { 124 | "ms": "2.0.0" 125 | } 126 | }, 127 | "depd": { 128 | "version": "1.1.2", 129 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 130 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 131 | }, 132 | "destroy": { 133 | "version": "1.0.4", 134 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 135 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 136 | }, 137 | "dottie": { 138 | "version": "2.0.1", 139 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.1.tgz", 140 | "integrity": "sha512-ch5OQgvGDK2u8pSZeSYAQaV/lczImd7pMJ7BcEPXmnFVjy4yJIzP6CsODJUTH8mg1tyH1Z2abOiuJO3DjZ/GBw==" 141 | }, 142 | "ecdsa-sig-formatter": { 143 | "version": "1.0.10", 144 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", 145 | "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", 146 | "requires": { 147 | "safe-buffer": "^5.0.1" 148 | } 149 | }, 150 | "ee-first": { 151 | "version": "1.1.1", 152 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 153 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 154 | }, 155 | "ejs": { 156 | "version": "2.5.9", 157 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", 158 | "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==" 159 | }, 160 | "encodeurl": { 161 | "version": "1.0.2", 162 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 163 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 164 | }, 165 | "escape-html": { 166 | "version": "1.0.3", 167 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 168 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 169 | }, 170 | "etag": { 171 | "version": "1.8.1", 172 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 173 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 174 | }, 175 | "express": { 176 | "version": "4.16.4", 177 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 178 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 179 | "requires": { 180 | "accepts": "~1.3.5", 181 | "array-flatten": "1.1.1", 182 | "body-parser": "1.18.3", 183 | "content-disposition": "0.5.2", 184 | "content-type": "~1.0.4", 185 | "cookie": "0.3.1", 186 | "cookie-signature": "1.0.6", 187 | "debug": "2.6.9", 188 | "depd": "~1.1.2", 189 | "encodeurl": "~1.0.2", 190 | "escape-html": "~1.0.3", 191 | "etag": "~1.8.1", 192 | "finalhandler": "1.1.1", 193 | "fresh": "0.5.2", 194 | "merge-descriptors": "1.0.1", 195 | "methods": "~1.1.2", 196 | "on-finished": "~2.3.0", 197 | "parseurl": "~1.3.2", 198 | "path-to-regexp": "0.1.7", 199 | "proxy-addr": "~2.0.4", 200 | "qs": "6.5.2", 201 | "range-parser": "~1.2.0", 202 | "safe-buffer": "5.1.2", 203 | "send": "0.16.2", 204 | "serve-static": "1.13.2", 205 | "setprototypeof": "1.1.0", 206 | "statuses": "~1.4.0", 207 | "type-is": "~1.6.16", 208 | "utils-merge": "1.0.1", 209 | "vary": "~1.1.2" 210 | } 211 | }, 212 | "finalhandler": { 213 | "version": "1.1.1", 214 | "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 215 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 216 | "requires": { 217 | "debug": "2.6.9", 218 | "encodeurl": "~1.0.2", 219 | "escape-html": "~1.0.3", 220 | "on-finished": "~2.3.0", 221 | "parseurl": "~1.3.2", 222 | "statuses": "~1.4.0", 223 | "unpipe": "~1.0.0" 224 | } 225 | }, 226 | "forwarded": { 227 | "version": "0.1.2", 228 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 229 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 230 | }, 231 | "fresh": { 232 | "version": "0.5.2", 233 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 234 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 235 | }, 236 | "generic-pool": { 237 | "version": "3.4.2", 238 | "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.4.2.tgz", 239 | "integrity": "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag==" 240 | }, 241 | "http-errors": { 242 | "version": "1.6.3", 243 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 244 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 245 | "requires": { 246 | "depd": "~1.1.2", 247 | "inherits": "2.0.3", 248 | "setprototypeof": "1.1.0", 249 | "statuses": ">= 1.4.0 < 2" 250 | } 251 | }, 252 | "iconv-lite": { 253 | "version": "0.4.23", 254 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 255 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 256 | "requires": { 257 | "safer-buffer": ">= 2.1.2 < 3" 258 | } 259 | }, 260 | "inflection": { 261 | "version": "1.12.0", 262 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", 263 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" 264 | }, 265 | "inherits": { 266 | "version": "2.0.3", 267 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 268 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 269 | }, 270 | "ipaddr.js": { 271 | "version": "1.8.0", 272 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 273 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 274 | }, 275 | "is-bluebird": { 276 | "version": "1.0.2", 277 | "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", 278 | "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" 279 | }, 280 | "jsonwebtoken": { 281 | "version": "8.4.0", 282 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz", 283 | "integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==", 284 | "requires": { 285 | "jws": "^3.1.5", 286 | "lodash.includes": "^4.3.0", 287 | "lodash.isboolean": "^3.0.3", 288 | "lodash.isinteger": "^4.0.4", 289 | "lodash.isnumber": "^3.0.3", 290 | "lodash.isplainobject": "^4.0.6", 291 | "lodash.isstring": "^4.0.1", 292 | "lodash.once": "^4.0.0", 293 | "ms": "^2.1.1" 294 | }, 295 | "dependencies": { 296 | "ms": { 297 | "version": "2.1.1", 298 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 299 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 300 | } 301 | } 302 | }, 303 | "jwa": { 304 | "version": "1.1.6", 305 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", 306 | "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", 307 | "requires": { 308 | "buffer-equal-constant-time": "1.0.1", 309 | "ecdsa-sig-formatter": "1.0.10", 310 | "safe-buffer": "^5.0.1" 311 | } 312 | }, 313 | "jws": { 314 | "version": "3.1.5", 315 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", 316 | "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", 317 | "requires": { 318 | "jwa": "^1.1.5", 319 | "safe-buffer": "^5.0.1" 320 | } 321 | }, 322 | "lodash": { 323 | "version": "4.17.11", 324 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 325 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 326 | }, 327 | "lodash.includes": { 328 | "version": "4.3.0", 329 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 330 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 331 | }, 332 | "lodash.isboolean": { 333 | "version": "3.0.3", 334 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 335 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 336 | }, 337 | "lodash.isinteger": { 338 | "version": "4.0.4", 339 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 340 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 341 | }, 342 | "lodash.isnumber": { 343 | "version": "3.0.3", 344 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 345 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 346 | }, 347 | "lodash.isplainobject": { 348 | "version": "4.0.6", 349 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 350 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 351 | }, 352 | "lodash.isstring": { 353 | "version": "4.0.1", 354 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 355 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 356 | }, 357 | "lodash.once": { 358 | "version": "4.1.1", 359 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 360 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 361 | }, 362 | "media-typer": { 363 | "version": "0.3.0", 364 | "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 365 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 366 | }, 367 | "merge-descriptors": { 368 | "version": "1.0.1", 369 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 370 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 371 | }, 372 | "methods": { 373 | "version": "1.1.2", 374 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 375 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 376 | }, 377 | "mime": { 378 | "version": "1.4.1", 379 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 380 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 381 | }, 382 | "mime-db": { 383 | "version": "1.37.0", 384 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 385 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 386 | }, 387 | "mime-types": { 388 | "version": "2.1.21", 389 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 390 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 391 | "requires": { 392 | "mime-db": "~1.37.0" 393 | } 394 | }, 395 | "moment": { 396 | "version": "2.22.2", 397 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", 398 | "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" 399 | }, 400 | "moment-timezone": { 401 | "version": "0.5.23", 402 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz", 403 | "integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==", 404 | "requires": { 405 | "moment": ">= 2.9.0" 406 | } 407 | }, 408 | "morgan": { 409 | "version": "1.9.1", 410 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", 411 | "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", 412 | "requires": { 413 | "basic-auth": "~2.0.0", 414 | "debug": "2.6.9", 415 | "depd": "~1.1.2", 416 | "on-finished": "~2.3.0", 417 | "on-headers": "~1.0.1" 418 | } 419 | }, 420 | "ms": { 421 | "version": "2.0.0", 422 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 423 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 424 | }, 425 | "negotiator": { 426 | "version": "0.6.1", 427 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 428 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 429 | }, 430 | "on-finished": { 431 | "version": "2.3.0", 432 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 433 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 434 | "requires": { 435 | "ee-first": "1.1.1" 436 | } 437 | }, 438 | "on-headers": { 439 | "version": "1.0.1", 440 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", 441 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" 442 | }, 443 | "packet-reader": { 444 | "version": "0.3.1", 445 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", 446 | "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=" 447 | }, 448 | "parseurl": { 449 | "version": "1.3.2", 450 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 451 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 452 | }, 453 | "passport": { 454 | "version": "0.4.0", 455 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", 456 | "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", 457 | "requires": { 458 | "passport-strategy": "1.x.x", 459 | "pause": "0.0.1" 460 | } 461 | }, 462 | "passport-jwt": { 463 | "version": "4.0.0", 464 | "resolved": "http://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", 465 | "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", 466 | "requires": { 467 | "jsonwebtoken": "^8.2.0", 468 | "passport-strategy": "^1.0.0" 469 | } 470 | }, 471 | "passport-strategy": { 472 | "version": "1.0.0", 473 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 474 | "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" 475 | }, 476 | "path-to-regexp": { 477 | "version": "0.1.7", 478 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 479 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 480 | }, 481 | "pause": { 482 | "version": "0.0.1", 483 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 484 | "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" 485 | }, 486 | "pg": { 487 | "version": "7.6.1", 488 | "resolved": "https://registry.npmjs.org/pg/-/pg-7.6.1.tgz", 489 | "integrity": "sha512-rAItIkYrRaNGinZN/Hs8F9R5mQjQSPlnzxPF+eCimSl92qnuNGR42gkpOQKP1bnvTwkSjRTBL+VNC5EcFhtCuQ==", 490 | "requires": { 491 | "buffer-writer": "2.0.0", 492 | "packet-reader": "0.3.1", 493 | "pg-connection-string": "0.1.3", 494 | "pg-pool": "~2.0.3", 495 | "pg-types": "~1.12.1", 496 | "pgpass": "1.x", 497 | "semver": "4.3.2" 498 | }, 499 | "dependencies": { 500 | "semver": { 501 | "version": "4.3.2", 502 | "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.2.tgz", 503 | "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" 504 | } 505 | } 506 | }, 507 | "pg-connection-string": { 508 | "version": "0.1.3", 509 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", 510 | "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" 511 | }, 512 | "pg-hstore": { 513 | "version": "2.3.2", 514 | "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.2.tgz", 515 | "integrity": "sha1-9+8FPnubiSrphq8vfL6GQy388k8=", 516 | "requires": { 517 | "underscore": ">=1.12.1" 518 | } 519 | }, 520 | "pg-pool": { 521 | "version": "2.0.4", 522 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.4.tgz", 523 | "integrity": "sha512-Mi2AsmlFkVMpI28NreaDkz5DkfxLOG16C/HNwi091LDlOiDiQACtAroLxSd1vIS2imBqxdjjO9cQZg2CwsOPbw==" 524 | }, 525 | "pg-types": { 526 | "version": "1.12.1", 527 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz", 528 | "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=", 529 | "requires": { 530 | "postgres-array": "~1.0.0", 531 | "postgres-bytea": "~1.0.0", 532 | "postgres-date": "~1.0.0", 533 | "postgres-interval": "^1.1.0" 534 | } 535 | }, 536 | "pgpass": { 537 | "version": "1.0.2", 538 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", 539 | "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", 540 | "requires": { 541 | "split": "^1.0.0" 542 | } 543 | }, 544 | "postgres-array": { 545 | "version": "1.0.3", 546 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz", 547 | "integrity": "sha512-5wClXrAP0+78mcsNX3/ithQ5exKvCyK5lr5NEEEeGwwM6NJdQgzIJBVxLvRW+huFpX92F2QnZ5CcokH0VhK2qQ==" 548 | }, 549 | "postgres-bytea": { 550 | "version": "1.0.0", 551 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 552 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 553 | }, 554 | "postgres-date": { 555 | "version": "1.0.3", 556 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", 557 | "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=" 558 | }, 559 | "postgres-interval": { 560 | "version": "1.1.2", 561 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.2.tgz", 562 | "integrity": "sha512-fC3xNHeTskCxL1dC8KOtxXt7YeFmlbTYtn7ul8MkVERuTmf7pI4DrkAxcw3kh1fQ9uz4wQmd03a1mRiXUZChfQ==", 563 | "requires": { 564 | "xtend": "^4.0.0" 565 | } 566 | }, 567 | "proxy-addr": { 568 | "version": "2.0.4", 569 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 570 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 571 | "requires": { 572 | "forwarded": "~0.1.2", 573 | "ipaddr.js": "1.8.0" 574 | } 575 | }, 576 | "qs": { 577 | "version": "6.5.2", 578 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 579 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 580 | }, 581 | "range-parser": { 582 | "version": "1.2.0", 583 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 584 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 585 | }, 586 | "raw-body": { 587 | "version": "2.3.3", 588 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 589 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 590 | "requires": { 591 | "bytes": "3.0.0", 592 | "http-errors": "1.6.3", 593 | "iconv-lite": "0.4.23", 594 | "unpipe": "1.0.0" 595 | } 596 | }, 597 | "retry-as-promised": { 598 | "version": "2.3.2", 599 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", 600 | "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", 601 | "requires": { 602 | "bluebird": "^3.4.6", 603 | "debug": "^2.6.9" 604 | } 605 | }, 606 | "safe-buffer": { 607 | "version": "5.1.2", 608 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 609 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 610 | }, 611 | "safer-buffer": { 612 | "version": "2.1.2", 613 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 614 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 615 | }, 616 | "semver": { 617 | "version": "5.6.0", 618 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 619 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 620 | }, 621 | "send": { 622 | "version": "0.16.2", 623 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 624 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 625 | "requires": { 626 | "debug": "2.6.9", 627 | "depd": "~1.1.2", 628 | "destroy": "~1.0.4", 629 | "encodeurl": "~1.0.2", 630 | "escape-html": "~1.0.3", 631 | "etag": "~1.8.1", 632 | "fresh": "0.5.2", 633 | "http-errors": "~1.6.2", 634 | "mime": "1.4.1", 635 | "ms": "2.0.0", 636 | "on-finished": "~2.3.0", 637 | "range-parser": "~1.2.0", 638 | "statuses": "~1.4.0" 639 | } 640 | }, 641 | "sequelize": { 642 | "version": "4.41.2", 643 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.41.2.tgz", 644 | "integrity": "sha512-8vPf2R0o9iEmtzkqNzwFdblO+0Mu+RNxOdLeYGGqWGlp3cushLpQucAeSGPQgf2hQVZP5yOCM1ouZKTQ5FTlvA==", 645 | "requires": { 646 | "bluebird": "^3.5.0", 647 | "cls-bluebird": "^2.1.0", 648 | "debug": "^3.1.0", 649 | "depd": "^1.1.0", 650 | "dottie": "^2.0.0", 651 | "generic-pool": "^3.4.0", 652 | "inflection": "1.12.0", 653 | "lodash": "^4.17.1", 654 | "moment": "^2.20.0", 655 | "moment-timezone": "^0.5.14", 656 | "retry-as-promised": "^2.3.2", 657 | "semver": "^5.5.0", 658 | "terraformer-wkt-parser": "^1.1.2", 659 | "toposort-class": "^1.0.1", 660 | "uuid": "^3.2.1", 661 | "validator": "^10.4.0", 662 | "wkx": "^0.4.1" 663 | }, 664 | "dependencies": { 665 | "debug": { 666 | "version": "3.2.6", 667 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 668 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 669 | "requires": { 670 | "ms": "^2.1.1" 671 | } 672 | }, 673 | "ms": { 674 | "version": "2.1.1", 675 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 676 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 677 | } 678 | } 679 | }, 680 | "serve-static": { 681 | "version": "1.13.2", 682 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 683 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 684 | "requires": { 685 | "encodeurl": "~1.0.2", 686 | "escape-html": "~1.0.3", 687 | "parseurl": "~1.3.2", 688 | "send": "0.16.2" 689 | } 690 | }, 691 | "setprototypeof": { 692 | "version": "1.1.0", 693 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 694 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 695 | }, 696 | "shimmer": { 697 | "version": "1.2.0", 698 | "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz", 699 | "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag==" 700 | }, 701 | "split": { 702 | "version": "1.0.1", 703 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 704 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 705 | "requires": { 706 | "through": "2" 707 | } 708 | }, 709 | "statuses": { 710 | "version": "1.4.0", 711 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 712 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 713 | }, 714 | "terraformer": { 715 | "version": "1.0.9", 716 | "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.9.tgz", 717 | "integrity": "sha512-YlmQ1fsMWTkKGDGibCRWgmLzrpDRUr63Q025LJ/taYQ6j1Yb8q9McKF7NBi6ACAyUXO6F/bl9w6v4MY307y5Ag==", 718 | "requires": { 719 | "@types/geojson": "^1.0.0" 720 | } 721 | }, 722 | "terraformer-wkt-parser": { 723 | "version": "1.2.0", 724 | "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.0.tgz", 725 | "integrity": "sha512-QU3iA54St5lF8Za1jg1oj4NYc8sn5tCZ08aNSWDeGzrsaV48eZk1iAVWasxhNspYBoCqdHuoot1pUTUrE1AJ4w==", 726 | "requires": { 727 | "@types/geojson": "^1.0.0", 728 | "terraformer": "~1.0.5" 729 | } 730 | }, 731 | "through": { 732 | "version": "2.3.8", 733 | "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", 734 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 735 | }, 736 | "toposort-class": { 737 | "version": "1.0.1", 738 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", 739 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" 740 | }, 741 | "type-is": { 742 | "version": "1.6.16", 743 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 744 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 745 | "requires": { 746 | "media-typer": "0.3.0", 747 | "mime-types": "~2.1.18" 748 | } 749 | }, 750 | "underscore": { 751 | "version": ">=1.12.1", 752 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", 753 | "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" 754 | }, 755 | "unpipe": { 756 | "version": "1.0.0", 757 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 758 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 759 | }, 760 | "utils-merge": { 761 | "version": "1.0.1", 762 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 763 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 764 | }, 765 | "uuid": { 766 | "version": "3.3.2", 767 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 768 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 769 | }, 770 | "validator": { 771 | "version": "10.9.0", 772 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.9.0.tgz", 773 | "integrity": "sha512-hZJcZSWz9poXBlAkjjcsNAdrZ6JbjD3kWlNjq/+vE7RLLS/+8PAj3qVVwrwsOz/WL8jPmZ1hYkRvtlUeZAm4ug==" 774 | }, 775 | "vary": { 776 | "version": "1.1.2", 777 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 778 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 779 | }, 780 | "wkx": { 781 | "version": "0.4.5", 782 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.5.tgz", 783 | "integrity": "sha512-01dloEcJZAJabLO5XdcRgqdKpmnxS0zIT02LhkdWOZX2Zs2tPM6hlZ4XG9tWaWur1Qd1OO4kJxUbe2+5BofvnA==", 784 | "requires": { 785 | "@types/node": "*" 786 | } 787 | }, 788 | "xtend": { 789 | "version": "4.0.1", 790 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 791 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 792 | } 793 | } 794 | } 795 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-node", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt-nodejs": "0.0.3", 10 | "cookie-parser": "~1.4.3", 11 | "debug": "~2.6.9", 12 | "ejs": "~2.5.7", 13 | "express": "~4.16.0", 14 | "http-errors": "~1.6.2", 15 | "jsonwebtoken": "^8.4.0", 16 | "morgan": "^1.9.1", 17 | "passport": "^0.4.0", 18 | "passport-jwt": "^4.0.0", 19 | "pg": "^7.6.1", 20 | "pg-hstore": "^2.3.2", 21 | "sequelize": "^4.41.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const jwt = require('jsonwebtoken'); 3 | const passport = require('passport'); 4 | const router = express.Router(); 5 | require('../config/passport')(passport); 6 | const Product = require('../models').Product; 7 | const User = require('../models').User; 8 | 9 | router.post('/signup', function(req, res) { 10 | console.log(req.body); 11 | if (!req.body.username || !req.body.password) { 12 | res.status(400).send({msg: 'Please pass username and password.'}) 13 | } else { 14 | User 15 | .create({ 16 | username: req.body.username, 17 | password: req.body.password 18 | }) 19 | .then((user) => res.status(201).send(user)) 20 | .catch((error) => { 21 | console.log(error); 22 | res.status(400).send(error); 23 | }); 24 | } 25 | }); 26 | 27 | router.post('/signin', function(req, res) { 28 | User 29 | .find({ 30 | where: { 31 | username: req.body.username 32 | } 33 | }) 34 | .then((user) => { 35 | if (!user) { 36 | return res.status(401).send({ 37 | message: 'Authentication failed. User not found.', 38 | }); 39 | } 40 | user.comparePassword(req.body.password, (err, isMatch) => { 41 | if(isMatch && !err) { 42 | var token = jwt.sign(JSON.parse(JSON.stringify(user)), 'nodeauthsecret', {expiresIn: 86400 * 30}); 43 | jwt.verify(token, 'nodeauthsecret', function(err, data){ 44 | console.log(err, data); 45 | }) 46 | res.json({success: true, token: 'JWT ' + token}); 47 | } else { 48 | res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'}); 49 | } 50 | }) 51 | }) 52 | .catch((error) => res.status(400).send(error)); 53 | }); 54 | 55 | router.get('/product', passport.authenticate('jwt', { session: false}), function(req, res) { 56 | var token = getToken(req.headers); 57 | if (token) { 58 | Product 59 | .findAll() 60 | .then((products) => res.status(200).send(products)) 61 | .catch((error) => { res.status(400).send(error); }); 62 | } else { 63 | return res.status(403).send({success: false, msg: 'Unauthorized.'}); 64 | } 65 | }); 66 | 67 | router.post('/product', passport.authenticate('jwt', { session: false}), function(req, res) { 68 | var token = getToken(req.headers); 69 | if (token) { 70 | Product 71 | .create({ 72 | prod_name: req.body.prod_name, 73 | prod_desc: req.body.prod_desc, 74 | prod_price: req.body.prod_price 75 | }) 76 | .then((product) => res.status(201).send(product)) 77 | .catch((error) => res.status(400).send(error)); 78 | } else { 79 | return res.status(403).send({success: false, msg: 'Unauthorized.'}); 80 | } 81 | }); 82 | 83 | getToken = function (headers) { 84 | if (headers && headers.authorization) { 85 | var parted = headers.authorization.split(' '); 86 | if (parted.length === 2) { 87 | return parted[1]; 88 | } else { 89 | return null; 90 | } 91 | } else { 92 | return null; 93 | } 94 | }; 95 | 96 | module.exports = router; 97 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |
<%= error.stack %>4 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Welcome to <%= title %>
10 | 11 | 12 | --------------------------------------------------------------------------------