├── .gitignore ├── views ├── error.hbs ├── partials │ └── messages.hbs ├── index.hbs ├── public-profile.hbs ├── login.hbs ├── register.hbs ├── layout.hbs └── account.hbs ├── public └── stylesheets │ └── style.css ├── utils └── auth.js ├── routes ├── index.js ├── users.js └── auth.js ├── package.json ├── bin └── www └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /views/error.hbs: -------------------------------------------------------------------------------- 1 |

{{message}}

2 |

{{error.status}}

3 |
{{error.stack}}
4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /views/partials/messages.hbs: -------------------------------------------------------------------------------- 1 | {{#each messages.error}} 2 |
{{ this }}
3 | {{/each}} 4 | 5 | {{#each messages.success}} 6 |
{{ this }}
7 | {{/each}} 8 | -------------------------------------------------------------------------------- /views/index.hbs: -------------------------------------------------------------------------------- 1 |

User Profiles

2 | 3 |

Recent User Profiles:

4 | 5 | 10 | 11 | 12 |

Login to update your profile

13 |

Register an account

-------------------------------------------------------------------------------- /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 }; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /views/public-profile.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{> messages }} 4 | {{#if username}} 5 |

{{ username }}'s profile

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
Name:{{ name }}
GitHub:{{ github }}
Twitter:{{ twitter }}
Facebook:{{ facebook }}
24 | {{/if}} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /views/login.hbs: -------------------------------------------------------------------------------- 1 |

Login

2 | 3 | {{> messages }} 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |

Or Register an account.

-------------------------------------------------------------------------------- /views/register.hbs: -------------------------------------------------------------------------------- 1 |

Register a new user

2 | 3 | {{> messages }} 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 |

Or Login if you already have an account.

-------------------------------------------------------------------------------- /views/layout.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{title}} 5 | 6 | 7 | 8 | 9 | {{#loggedIn}} 10 |

11 | Logout 12 |

13 | {{/loggedIn}} 14 |
15 | {{{body}}} 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /views/account.hbs: -------------------------------------------------------------------------------- 1 |

Edit your profile

2 |
3 |
4 | 5 | 6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |

View your profile

-------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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; -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------