├── .gitignore ├── app.js ├── bin └── www ├── package-lock.json ├── package.json ├── public └── stylesheets │ └── style.css ├── routes ├── auth.js ├── index.js └── users.js ├── utils └── auth.js └── views ├── account.hbs ├── error.hbs ├── index.hbs ├── layout.hbs ├── login.hbs ├── partials └── messages.hbs ├── public-profile.hbs └── register.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /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 | // New stuff to add 8 | //--------------------------------------------------- 9 | const hbs = require('hbs'); 10 | const MongoClient = require('mongodb').MongoClient; 11 | const passport = require('passport'); 12 | const Strategy = require('passport-local').Strategy; 13 | const authUtils = require('./utils/auth'); 14 | const session = require('express-session'); 15 | const flash = require('connect-flash'); 16 | // -------------------------------------------------- 17 | 18 | var indexRouter = require('./routes/index'); 19 | var usersRouter = require('./routes/users'); 20 | 21 | // Add new routes 22 | // -------------------------------------------------- 23 | const authRouter = require('./routes/auth'); 24 | // -------------------------------------------------- 25 | 26 | var app = express(); 27 | 28 | // Connect to db 29 | // -------------------------------------------------- 30 | MongoClient.connect('mongodb://localhost', (err, client) => { 31 | if (err) { 32 | throw err; 33 | } 34 | 35 | const db = client.db('account-app'); 36 | const users = db.collection('users'); 37 | app.locals.users = users; 38 | }); 39 | // -------------------------------------------------- 40 | 41 | 42 | // Configure passport 43 | // -------------------------------------------------- 44 | passport.use(new Strategy( 45 | (username, password, done) => { 46 | app.locals.users.findOne({ username }, (err, user) => { 47 | if (err) { 48 | return done(err); 49 | } 50 | 51 | if (!user) { 52 | return done(null, false); 53 | } 54 | 55 | if (user.password != authUtils.hashPassword(password)) { 56 | return done(null, false); 57 | } 58 | 59 | return done(null, user); 60 | }); 61 | } 62 | )); 63 | 64 | passport.serializeUser((user, done) => { 65 | done(null, user._id); 66 | }); 67 | 68 | passport.deserializeUser((id, done) => { 69 | done(null, { id }); 70 | }); 71 | // -------------------------------------------------- 72 | 73 | 74 | // view engine setup 75 | app.set('views', path.join(__dirname, 'views')); 76 | app.set('view engine', 'hbs'); 77 | 78 | // Set partials for handlebars 79 | // -------------------------------------------------- 80 | hbs.registerPartials(path.join(__dirname, 'views/partials')); 81 | // -------------------------------------------------- 82 | 83 | app.use(logger('dev')); 84 | app.use(express.json()); 85 | app.use(express.urlencoded({ extended: false })); 86 | app.use(cookieParser()); 87 | app.use(express.static(path.join(__dirname, 'public'))); 88 | 89 | 90 | // Configure session, passport, flash 91 | // -------------------------------------------------- 92 | app.use(session({ 93 | secret: 'session secret', 94 | resave: false, 95 | saveUninitialized: false, 96 | })); 97 | 98 | app.use(passport.initialize()); 99 | app.use(passport.session()); 100 | app.use(flash()); 101 | 102 | app.use((req, res, next) => { 103 | res.locals.loggedIn = req.isAuthenticated(); 104 | next(); 105 | }); 106 | // -------------------------------------------------- 107 | 108 | app.use('/', indexRouter); 109 | app.use('/users', usersRouter); 110 | 111 | // Add new routes 112 | // -------------------------------------------------- 113 | app.use('/auth', authRouter); 114 | // -------------------------------------------------- 115 | 116 | // catch 404 and forward to error handler 117 | app.use(function(req, res, next) { 118 | next(createError(404)); 119 | }); 120 | 121 | // error handler 122 | app.use(function(err, req, res, next) { 123 | // set locals, only providing error in development 124 | res.locals.message = err.message; 125 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 126 | 127 | // render the error page 128 | res.status(err.status || 500); 129 | res.render('error'); 130 | }); 131 | 132 | module.exports = app; 133 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('accounts: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 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "accounts", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "async": { 22 | "version": "2.6.3", 23 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", 24 | "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", 25 | "requires": { 26 | "lodash": "^4.17.14" 27 | } 28 | }, 29 | "basic-auth": { 30 | "version": "2.0.1", 31 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 32 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 33 | "requires": { 34 | "safe-buffer": "5.1.2" 35 | } 36 | }, 37 | "body-parser": { 38 | "version": "1.18.3", 39 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 40 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 41 | "requires": { 42 | "bytes": "3.0.0", 43 | "content-type": "~1.0.4", 44 | "debug": "2.6.9", 45 | "depd": "~1.1.2", 46 | "http-errors": "~1.6.3", 47 | "iconv-lite": "0.4.23", 48 | "on-finished": "~2.3.0", 49 | "qs": "6.5.2", 50 | "raw-body": "2.3.3", 51 | "type-is": "~1.6.16" 52 | } 53 | }, 54 | "bson": { 55 | "version": "1.1.1", 56 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", 57 | "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" 58 | }, 59 | "bytes": { 60 | "version": "3.0.0", 61 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 62 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 63 | }, 64 | "commander": { 65 | "version": "2.20.0", 66 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", 67 | "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", 68 | "optional": true 69 | }, 70 | "connect-flash": { 71 | "version": "0.1.1", 72 | "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", 73 | "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" 74 | }, 75 | "content-disposition": { 76 | "version": "0.5.2", 77 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 78 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 79 | }, 80 | "content-type": { 81 | "version": "1.0.4", 82 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 83 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 84 | }, 85 | "cookie": { 86 | "version": "0.3.1", 87 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 88 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 89 | }, 90 | "cookie-parser": { 91 | "version": "1.4.4", 92 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", 93 | "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", 94 | "requires": { 95 | "cookie": "0.3.1", 96 | "cookie-signature": "1.0.6" 97 | } 98 | }, 99 | "cookie-signature": { 100 | "version": "1.0.6", 101 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 102 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 103 | }, 104 | "debug": { 105 | "version": "2.6.9", 106 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 107 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 108 | "requires": { 109 | "ms": "2.0.0" 110 | } 111 | }, 112 | "depd": { 113 | "version": "1.1.2", 114 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 115 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 116 | }, 117 | "destroy": { 118 | "version": "1.0.4", 119 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 120 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 121 | }, 122 | "ee-first": { 123 | "version": "1.1.1", 124 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 125 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 126 | }, 127 | "encodeurl": { 128 | "version": "1.0.2", 129 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 130 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 131 | }, 132 | "escape-html": { 133 | "version": "1.0.3", 134 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 135 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 136 | }, 137 | "etag": { 138 | "version": "1.8.1", 139 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 140 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 141 | }, 142 | "express": { 143 | "version": "4.16.4", 144 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 145 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 146 | "requires": { 147 | "accepts": "~1.3.5", 148 | "array-flatten": "1.1.1", 149 | "body-parser": "1.18.3", 150 | "content-disposition": "0.5.2", 151 | "content-type": "~1.0.4", 152 | "cookie": "0.3.1", 153 | "cookie-signature": "1.0.6", 154 | "debug": "2.6.9", 155 | "depd": "~1.1.2", 156 | "encodeurl": "~1.0.2", 157 | "escape-html": "~1.0.3", 158 | "etag": "~1.8.1", 159 | "finalhandler": "1.1.1", 160 | "fresh": "0.5.2", 161 | "merge-descriptors": "1.0.1", 162 | "methods": "~1.1.2", 163 | "on-finished": "~2.3.0", 164 | "parseurl": "~1.3.2", 165 | "path-to-regexp": "0.1.7", 166 | "proxy-addr": "~2.0.4", 167 | "qs": "6.5.2", 168 | "range-parser": "~1.2.0", 169 | "safe-buffer": "5.1.2", 170 | "send": "0.16.2", 171 | "serve-static": "1.13.2", 172 | "setprototypeof": "1.1.0", 173 | "statuses": "~1.4.0", 174 | "type-is": "~1.6.16", 175 | "utils-merge": "1.0.1", 176 | "vary": "~1.1.2" 177 | } 178 | }, 179 | "express-flash": { 180 | "version": "0.0.2", 181 | "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz", 182 | "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=", 183 | "requires": { 184 | "connect-flash": "0.1.x" 185 | } 186 | }, 187 | "express-session": { 188 | "version": "1.16.2", 189 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.16.2.tgz", 190 | "integrity": "sha512-oy0sRsdw6n93E9wpCNWKRnSsxYnSDX9Dnr9mhZgqUEEorzcq5nshGYSZ4ZReHFhKQ80WI5iVUUSPW7u3GaKauw==", 191 | "requires": { 192 | "cookie": "0.3.1", 193 | "cookie-signature": "1.0.6", 194 | "debug": "2.6.9", 195 | "depd": "~2.0.0", 196 | "on-headers": "~1.0.2", 197 | "parseurl": "~1.3.3", 198 | "safe-buffer": "5.1.2", 199 | "uid-safe": "~2.1.5" 200 | }, 201 | "dependencies": { 202 | "depd": { 203 | "version": "2.0.0", 204 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 205 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 206 | } 207 | } 208 | }, 209 | "finalhandler": { 210 | "version": "1.1.1", 211 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 212 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 213 | "requires": { 214 | "debug": "2.6.9", 215 | "encodeurl": "~1.0.2", 216 | "escape-html": "~1.0.3", 217 | "on-finished": "~2.3.0", 218 | "parseurl": "~1.3.2", 219 | "statuses": "~1.4.0", 220 | "unpipe": "~1.0.0" 221 | } 222 | }, 223 | "foreachasync": { 224 | "version": "3.0.0", 225 | "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", 226 | "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=" 227 | }, 228 | "forwarded": { 229 | "version": "0.1.2", 230 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 231 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 232 | }, 233 | "fresh": { 234 | "version": "0.5.2", 235 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 236 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 237 | }, 238 | "handlebars": { 239 | "version": "4.0.14", 240 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.14.tgz", 241 | "integrity": "sha512-E7tDoyAA8ilZIV3xDJgl18sX3M8xB9/fMw8+mfW4msLW8jlX97bAnWgT3pmaNXuvzIEgSBMnAHfuXsB2hdzfow==", 242 | "requires": { 243 | "async": "^2.5.0", 244 | "optimist": "^0.6.1", 245 | "source-map": "^0.6.1", 246 | "uglify-js": "^3.1.4" 247 | } 248 | }, 249 | "hbs": { 250 | "version": "4.0.4", 251 | "resolved": "https://registry.npmjs.org/hbs/-/hbs-4.0.4.tgz", 252 | "integrity": "sha512-esVlyV/V59mKkwFai5YmPRSNIWZzhqL5YMN0++ueMxyK1cCfPa5f6JiHtapPKAIVAhQR6rpGxow0troav9WMEg==", 253 | "requires": { 254 | "handlebars": "4.0.14", 255 | "walk": "2.3.9" 256 | } 257 | }, 258 | "http-errors": { 259 | "version": "1.6.3", 260 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 261 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 262 | "requires": { 263 | "depd": "~1.1.2", 264 | "inherits": "2.0.3", 265 | "setprototypeof": "1.1.0", 266 | "statuses": ">= 1.4.0 < 2" 267 | } 268 | }, 269 | "iconv-lite": { 270 | "version": "0.4.23", 271 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 272 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 273 | "requires": { 274 | "safer-buffer": ">= 2.1.2 < 3" 275 | } 276 | }, 277 | "inherits": { 278 | "version": "2.0.3", 279 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 280 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 281 | }, 282 | "ipaddr.js": { 283 | "version": "1.9.0", 284 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 285 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 286 | }, 287 | "lodash": { 288 | "version": "4.17.15", 289 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 290 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 291 | }, 292 | "media-typer": { 293 | "version": "0.3.0", 294 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 295 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 296 | }, 297 | "merge-descriptors": { 298 | "version": "1.0.1", 299 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 300 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 301 | }, 302 | "methods": { 303 | "version": "1.1.2", 304 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 305 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 306 | }, 307 | "mime": { 308 | "version": "1.4.1", 309 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 310 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 311 | }, 312 | "mime-db": { 313 | "version": "1.40.0", 314 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 315 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 316 | }, 317 | "mime-types": { 318 | "version": "2.1.24", 319 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 320 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 321 | "requires": { 322 | "mime-db": "1.40.0" 323 | } 324 | }, 325 | "minimist": { 326 | "version": "0.0.10", 327 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 328 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" 329 | }, 330 | "mongodb": { 331 | "version": "3.3.0-beta2", 332 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.0-beta2.tgz", 333 | "integrity": "sha512-Z8L7VwLx4omRw4twLQ00tPxVrfDi6pELFR4oCDAu8rDUjhtmH7vsBtS9idqBTbvkcx7w9eaN+m2h4aPkXlnpDg==", 334 | "requires": { 335 | "bson": "^1.1.1", 336 | "require_optional": "^1.0.1", 337 | "safe-buffer": "^5.1.2" 338 | } 339 | }, 340 | "morgan": { 341 | "version": "1.9.1", 342 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", 343 | "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", 344 | "requires": { 345 | "basic-auth": "~2.0.0", 346 | "debug": "2.6.9", 347 | "depd": "~1.1.2", 348 | "on-finished": "~2.3.0", 349 | "on-headers": "~1.0.1" 350 | } 351 | }, 352 | "ms": { 353 | "version": "2.0.0", 354 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 355 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 356 | }, 357 | "negotiator": { 358 | "version": "0.6.2", 359 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 360 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 361 | }, 362 | "on-finished": { 363 | "version": "2.3.0", 364 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 365 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 366 | "requires": { 367 | "ee-first": "1.1.1" 368 | } 369 | }, 370 | "on-headers": { 371 | "version": "1.0.2", 372 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 373 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 374 | }, 375 | "optimist": { 376 | "version": "0.6.1", 377 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 378 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 379 | "requires": { 380 | "minimist": "~0.0.1", 381 | "wordwrap": "~0.0.2" 382 | } 383 | }, 384 | "parseurl": { 385 | "version": "1.3.3", 386 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 387 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 388 | }, 389 | "passport": { 390 | "version": "0.4.0", 391 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", 392 | "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", 393 | "requires": { 394 | "passport-strategy": "1.x.x", 395 | "pause": "0.0.1" 396 | } 397 | }, 398 | "passport-local": { 399 | "version": "1.0.0", 400 | "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", 401 | "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", 402 | "requires": { 403 | "passport-strategy": "1.x.x" 404 | } 405 | }, 406 | "passport-strategy": { 407 | "version": "1.0.0", 408 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 409 | "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" 410 | }, 411 | "path-to-regexp": { 412 | "version": "0.1.7", 413 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 414 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 415 | }, 416 | "pause": { 417 | "version": "0.0.1", 418 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 419 | "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" 420 | }, 421 | "proxy-addr": { 422 | "version": "2.0.5", 423 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 424 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 425 | "requires": { 426 | "forwarded": "~0.1.2", 427 | "ipaddr.js": "1.9.0" 428 | } 429 | }, 430 | "qs": { 431 | "version": "6.5.2", 432 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 433 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 434 | }, 435 | "random-bytes": { 436 | "version": "1.0.0", 437 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 438 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" 439 | }, 440 | "range-parser": { 441 | "version": "1.2.1", 442 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 443 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 444 | }, 445 | "raw-body": { 446 | "version": "2.3.3", 447 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 448 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 449 | "requires": { 450 | "bytes": "3.0.0", 451 | "http-errors": "1.6.3", 452 | "iconv-lite": "0.4.23", 453 | "unpipe": "1.0.0" 454 | } 455 | }, 456 | "require_optional": { 457 | "version": "1.0.1", 458 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 459 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 460 | "requires": { 461 | "resolve-from": "^2.0.0", 462 | "semver": "^5.1.0" 463 | } 464 | }, 465 | "resolve-from": { 466 | "version": "2.0.0", 467 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 468 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 469 | }, 470 | "safe-buffer": { 471 | "version": "5.1.2", 472 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 473 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 474 | }, 475 | "safer-buffer": { 476 | "version": "2.1.2", 477 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 478 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 479 | }, 480 | "semver": { 481 | "version": "5.7.0", 482 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 483 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 484 | }, 485 | "send": { 486 | "version": "0.16.2", 487 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 488 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 489 | "requires": { 490 | "debug": "2.6.9", 491 | "depd": "~1.1.2", 492 | "destroy": "~1.0.4", 493 | "encodeurl": "~1.0.2", 494 | "escape-html": "~1.0.3", 495 | "etag": "~1.8.1", 496 | "fresh": "0.5.2", 497 | "http-errors": "~1.6.2", 498 | "mime": "1.4.1", 499 | "ms": "2.0.0", 500 | "on-finished": "~2.3.0", 501 | "range-parser": "~1.2.0", 502 | "statuses": "~1.4.0" 503 | } 504 | }, 505 | "serve-static": { 506 | "version": "1.13.2", 507 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 508 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 509 | "requires": { 510 | "encodeurl": "~1.0.2", 511 | "escape-html": "~1.0.3", 512 | "parseurl": "~1.3.2", 513 | "send": "0.16.2" 514 | } 515 | }, 516 | "setprototypeof": { 517 | "version": "1.1.0", 518 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 519 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 520 | }, 521 | "source-map": { 522 | "version": "0.6.1", 523 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 524 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 525 | }, 526 | "statuses": { 527 | "version": "1.4.0", 528 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 529 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 530 | }, 531 | "type-is": { 532 | "version": "1.6.18", 533 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 534 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 535 | "requires": { 536 | "media-typer": "0.3.0", 537 | "mime-types": "~2.1.24" 538 | } 539 | }, 540 | "uglify-js": { 541 | "version": "3.6.0", 542 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", 543 | "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", 544 | "optional": true, 545 | "requires": { 546 | "commander": "~2.20.0", 547 | "source-map": "~0.6.1" 548 | } 549 | }, 550 | "uid-safe": { 551 | "version": "2.1.5", 552 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 553 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 554 | "requires": { 555 | "random-bytes": "~1.0.0" 556 | } 557 | }, 558 | "unpipe": { 559 | "version": "1.0.0", 560 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 561 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 562 | }, 563 | "utils-merge": { 564 | "version": "1.0.1", 565 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 566 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 567 | }, 568 | "vary": { 569 | "version": "1.1.2", 570 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 571 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 572 | }, 573 | "walk": { 574 | "version": "2.3.9", 575 | "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz", 576 | "integrity": "sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins=", 577 | "requires": { 578 | "foreachasync": "^3.0.0" 579 | } 580 | }, 581 | "wordwrap": { 582 | "version": "0.0.3", 583 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 584 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" 585 | } 586 | } 587 | } 588 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "accounts", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "connect-flash": "^0.1.1", 10 | "cookie-parser": "~1.4.4", 11 | "debug": "~2.6.9", 12 | "express": "~4.16.1", 13 | "express-flash": "0.0.2", 14 | "express-session": "^1.16.2", 15 | "hbs": "~4.0.4", 16 | "http-errors": "~1.6.3", 17 | "mongodb": "^3.3.0-beta2", 18 | "morgan": "~1.9.1", 19 | "passport": "^0.4.0", 20 | "passport-local": "^1.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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/auth.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const authUtils = require('../utils/auth'); 4 | const passport = require('passport'); 5 | const flash = require('connect-flash'); 6 | 7 | // Create login page 8 | // -------------------------------------------------- 9 | router.get('/login', (req, res, next) => { 10 | const messages = req.flash(); 11 | res.render('login', { messages }); 12 | }); 13 | // -------------------------------------------------- 14 | 15 | 16 | // Handle login request 17 | // -------------------------------------------------- 18 | router.post('/login', passport.authenticate('local', 19 | { failureRedirect: '/auth/login', 20 | failureFlash: 'Wrong username or password'}), (req, res, next) => { 21 | res.redirect('/users'); 22 | }); 23 | // -------------------------------------------------- 24 | 25 | 26 | // Create register page 27 | // -------------------------------------------------- 28 | router.get('/register', (req, res, next) => { 29 | const messages = req.flash(); 30 | res.render('register', { messages }); 31 | }); 32 | // -------------------------------------------------- 33 | 34 | 35 | // Handle register request 36 | // -------------------------------------------------- 37 | router.post('/register', (req, res, next) => { 38 | const registrationParams = req.body; 39 | const users = req.app.locals.users; 40 | const payload = { 41 | username: registrationParams.username, 42 | password: authUtils.hashPassword(registrationParams.password), 43 | }; 44 | 45 | users.insertOne(payload, (err) => { 46 | if (err) { 47 | req.flash('error', 'User account already exists.'); 48 | } else { 49 | req.flash('success', 'User account registered successfully.'); 50 | } 51 | 52 | res.redirect('/auth/register'); 53 | }) 54 | }); 55 | // -------------------------------------------------- 56 | 57 | // Logout page 58 | // -------------------------------------------------- 59 | router.get('/logout', (req, res, next) => { 60 | req.session.destroy(); 61 | res.redirect('/'); 62 | }); 63 | 64 | module.exports = router; -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | // Create custom homepage 5 | // -------------------------------------------------- 6 | router.get('/', function(req, res, next) { 7 | const users = req.app.locals.users; 8 | 9 | users.find().limit(3).toArray((err, recent) => { 10 | res.render('index', { recent } ); 11 | }); 12 | }); 13 | // -------------------------------------------------- 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | const ObjectID = require('mongodb').ObjectID; 4 | 5 | 6 | // Configure user account profile edit 7 | // -------------------------------------------------- 8 | router.get('/', function(req, res, next) { 9 | if (!req.isAuthenticated()) { 10 | res.redirect('/auth/login'); 11 | } 12 | const users = req.app.locals.users; 13 | const _id = ObjectID(req.session.passport.user); 14 | 15 | users.findOne({ _id }, (err, results) => { 16 | if (err) { 17 | throw err; 18 | } 19 | 20 | res.render('account', { ...results }); 21 | }); 22 | }); 23 | // -------------------------------------------------- 24 | 25 | 26 | // Get public profile for any user 27 | // -------------------------------------------------- 28 | router.get('/:username', (req, res, next) => { 29 | const users = req.app.locals.users; 30 | const username = req.params.username; 31 | 32 | users.findOne({ username }, (err, results) => { 33 | if (err || !results) { 34 | res.render('public-profile', { messages: { error: ['User not found'] } }); 35 | } 36 | 37 | res.render('public-profile', { ...results, username }); 38 | }); 39 | }) 40 | // -------------------------------------------------- 41 | 42 | 43 | // Handle updating user profile data 44 | // -------------------------------------------------- 45 | router.post('/', (req, res, next) => { 46 | if (!req.isAuthenticated()) { 47 | res.redirect('/auth/login'); 48 | } 49 | 50 | const users = req.app.locals.users; 51 | const { name, github, twitter, facebook } = req.body; 52 | const _id = ObjectID(req.session.passport.user); 53 | 54 | users.updateOne({ _id }, { $set: { name, github, twitter, facebook } }, (err) => { 55 | if (err) { 56 | throw err; 57 | } 58 | 59 | res.redirect('/users'); 60 | }); 61 | }); 62 | // -------------------------------------------------- 63 | 64 | module.exports = router; 65 | -------------------------------------------------------------------------------- /utils/auth.js: -------------------------------------------------------------------------------- 1 | // Create password hash util 2 | // -------------------------------------------------- 3 | const crypto = require('crypto'); 4 | 5 | const hashPassword = (plainText) => { 6 | return crypto.createHmac('sha256', 'secret key') 7 | .update(plainText) 8 | .digest('hex'); 9 | } 10 | // -------------------------------------------------- 11 | 12 | module.exports = { hashPassword }; -------------------------------------------------------------------------------- /views/account.hbs: -------------------------------------------------------------------------------- 1 |
{{error.stack}}4 | -------------------------------------------------------------------------------- /views/index.hbs: -------------------------------------------------------------------------------- 1 |
Login to update your profile
13 | -------------------------------------------------------------------------------- /views/layout.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |11 | Logout 12 |
13 | {{/loggedIn}} 14 |Or Register an account.
-------------------------------------------------------------------------------- /views/partials/messages.hbs: -------------------------------------------------------------------------------- 1 | {{#each messages.error}} 2 |Name: | 9 |{{ name }} | 10 |
GitHub: | 13 |{{ github }} | 14 |
Twitter: | 17 |{{ twitter }} | 18 |
Facebook: | 21 |{{ facebook }} | 22 |
Or Login if you already have an account.
--------------------------------------------------------------------------------