├── .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 | | Name: |
9 | {{ name }} |
10 |
11 |
12 | | GitHub: |
13 | {{ github }} |
14 |
15 |
16 | | Twitter: |
17 | {{ twitter }} |
18 |
19 |
20 | | Facebook: |
21 | {{ facebook }} |
22 |
23 |
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 |
16 |
17 | Or Register an account.
--------------------------------------------------------------------------------
/views/register.hbs:
--------------------------------------------------------------------------------
1 | Register a new user
2 |
3 | {{> messages }}
4 |
5 |
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 |
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 |
--------------------------------------------------------------------------------