Skip to content

Commit 21ee379

Browse files
committed
First commit
0 parents  commit 21ee379

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+27839
-0
lines changed

README.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
This project is a very simple app developed using MEAN stack technology: Mongo as database, Express as web application framework (for routing etc), Angular as the front end framework, and Nodejs as backend server. This app demonstrates a simple login + registration feature, with authentication using the passport-js library and persistent sessions using JWT tokens. This app can be used as a starting template when you develop your own apps.
2+
3+
4+
1) Install the latest version of npm and nodejs using below command:
5+
6+
sudo apt install nodejs npm
7+
8+
2) Install mongodb by following the steps from https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
9+
10+
3) Download this code, then change to the downloaded folder, and run below command to download all nodejs dependencies:
11+
12+
npm install
13+
14+
4) Make sure the mongod service is running, if not, start using below command:
15+
16+
sudo service mongod start
17+
18+
5) Login to the mongo shell using command: mongo
19+
20+
6) Create a new database with some name (example: mydb) using below query:
21+
22+
use mydb;
23+
24+
7) Create a database user for our application using below query:
25+
26+
db.createUser({user: "myuser", pwd: "mypassword", roles: ["readWrite"]});
27+
28+
8) Open db.js and change the database name, username and password. Make sure mongod service is running on port 27017. If it is running on a different port, then set that port in this file. Mongod port is defined in /etc/mongod.conf automatically during installation.
29+
30+
9) By default, the app will work on port 8080. If you need to change it, then open app.js and at the bottom of the code, change 8080 to your desired port.
31+
32+
10) Run below command to start the node server:
33+
34+
node .
35+
36+
11) Open your browser and hit http://localhost:8080 (use different port if you edited it in the app.js file)

app.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const express = require('express');
2+
const app = express();
3+
const router = express.Router();
4+
const passport = require('passport');
5+
const cookieParser = require('cookie-parser');
6+
const path = require('path');
7+
8+
app.set('view engine', 'html');
9+
app.use(cookieParser());
10+
app.use(express.urlencoded({ extended: true }));
11+
app.use(express.static(path.join(__dirname, 'public')));
12+
13+
require('./db');
14+
15+
const index = require('./routes/index');
16+
const login = require('./routes/login');
17+
const registration = require('./routes/registration');
18+
19+
// caching disabled for every route
20+
app.use(function(req, res, next) {
21+
res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
22+
next();
23+
});
24+
25+
app.use('/', index);
26+
app.use('/login', login);
27+
app.use('/registration', registration);
28+
29+
// catch 404 and forward to error handler
30+
app.use(function(req, res, next) {
31+
res.status(404);
32+
33+
// respond with html page
34+
if(req.accepts('html')) {
35+
res.sendFile(path.join(__dirname + '/public/404.html'));
36+
return;
37+
}
38+
39+
// respond with json
40+
if(req.accepts('json')) {
41+
res.send({ err: 'Not found' });
42+
return;
43+
}
44+
45+
// default to plain-text. send()
46+
res.type('txt').send('Not found');
47+
});
48+
49+
// error handler
50+
app.use(function(err, req, res, next) {
51+
console.log(err);
52+
var status = err.status || 500;
53+
res.status(status);
54+
55+
// respond with html page
56+
if(req.accepts('html')) {
57+
res.sendFile(path.join(__dirname + '/public/error.html'));
58+
return;
59+
}
60+
61+
// respond with json
62+
if(req.accepts('json')) {
63+
res.send({ err: 'Error occurred' });
64+
return;
65+
}
66+
67+
// default to plain-text. send()
68+
res.type('txt').send('Error occurred');
69+
});
70+
71+
app.listen(8080, function() {
72+
console.log('App listening on port 8080!')
73+
});

config/passport.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const config = {
2+
secret: 'zAq47d!m(f^9ld',
3+
expirationMillis: 3600000
4+
};
5+
6+
module.exports = config;

db.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const mongoose = require('mongoose');
2+
3+
const MONGO_USERNAME = 'myuser';
4+
const MONGO_PASSWORD = 'mypassword';
5+
const MONGO_HOSTNAME = 'localhost';
6+
const MONGO_PORT = '27017';
7+
const MONGO_DB = 'mydb';
8+
9+
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}`;
10+
11+
mongoose.connect(url, {
12+
useCreateIndex: true,
13+
useNewUrlParser: true
14+
});

middleware/passport.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
const passport = require('passport');
2+
const LocalStrategy = require('passport-local').Strategy;
3+
const passportJWT = require('passport-jwt');
4+
const jwt = require('jsonwebtoken');
5+
const JWTStrategy = passportJWT.Strategy;
6+
const bcrypt = require('bcrypt');
7+
8+
const UserModel = require('../models/user');
9+
const passportConfig = require('../config/passport');
10+
11+
passport.use('login', new LocalStrategy({
12+
usernameField: 'username',
13+
passwordField: 'password',
14+
session: false
15+
}, async(username, password, done) => {
16+
try {
17+
const userDocument = await UserModel.findOne({username: username}).exec();
18+
if(!userDocument) {
19+
return done('Incorrect username or password');
20+
}
21+
22+
const passwordsMatch = bcrypt.compareSync(password, userDocument.passwordHash);
23+
if(passwordsMatch) {
24+
return done(null, userDocument);
25+
} else {
26+
return done('Incorrect username or password');
27+
}
28+
} catch(error) {
29+
console.log(error);
30+
return done('An error occurred while authentication');
31+
}
32+
}));
33+
34+
passport.use('registration', new LocalStrategy({
35+
usernameField: 'username',
36+
passwordField: 'password',
37+
session: false
38+
}, async(username, password, done) => {
39+
try {
40+
const userDocument = await UserModel.findOne({username: username}).exec();
41+
if(userDocument) {
42+
return done('Username already exists');
43+
}
44+
45+
const hash = bcrypt.hashSync(password, 10);
46+
const newUserDocument = new UserModel({username: username, passwordHash: hash});
47+
try {
48+
const savedUser = await newUserDocument.save();
49+
return done(null, true);
50+
} catch(ex) {
51+
return done('Unable to create user');
52+
}
53+
} catch(error) {
54+
console.log(error);
55+
return done('An error occurred while registration');
56+
}
57+
}));
58+
59+
passport.use('user', new JWTStrategy({
60+
jwtFromRequest: req => req.cookies.jwt,
61+
secretOrKey: passportConfig.secret,
62+
}, async (jwtPayload, done) => {
63+
if(!jwtPayload || !jwtPayload.username) {
64+
return done('Authentication token is invalid');
65+
}
66+
67+
const userDocument = await UserModel.findOne({username: jwtPayload.username}).exec();
68+
if(!userDocument) {
69+
return done('Authentication token is invalid');
70+
}
71+
72+
if(!jwtPayload.expires || Date.now() >= jwtPayload.expires) {
73+
return done('Authentication token expired');
74+
}
75+
76+
return done(null, jwtPayload);
77+
}));
78+
79+
const validateRegistration = (req, res, next) => {
80+
passport.authenticate('registration', { session: false }, (error, isUserCreated) => {
81+
if(error) {
82+
res.status(400).json({ error: error });
83+
return;
84+
}
85+
86+
res.status(200).send({success: true});
87+
})(req, res);
88+
};
89+
90+
const doLoginCheck = (req, res, next) => {
91+
passport.authenticate('user', { session: false }, (error, payload) => {
92+
if(!payload && req.path != '/index.html' && req.path != '/') {
93+
res.redirect('/');
94+
} else if(payload && (req.path == '/' || req.path == '/index.html')) {
95+
res.redirect('/home');
96+
} else {
97+
next();
98+
}
99+
})(req, res);
100+
};
101+
102+
const validateLogin = (req, res, next) => {
103+
passport.authenticate('login', { session: false }, (error, user) => {
104+
if(error) {
105+
res.status(400).json({ error: error });
106+
return;
107+
}
108+
109+
/** This is what ends up in our JWT */
110+
const payload = {
111+
username: user.username,
112+
expires: Date.now() + parseInt(passportConfig.expirationMillis)
113+
};
114+
115+
/** assigns payload to req.user */
116+
req.login(payload, {session: false}, (error) => {
117+
if(error) {
118+
console.log(error);
119+
res.status(400).send({ error: "Unable to login" });
120+
return;
121+
}
122+
123+
/** generate a signed json web token and return it in the response */
124+
const token = jwt.sign(JSON.stringify(payload), passportConfig.secret);
125+
126+
/** assign our jwt to the cookie */
127+
res.cookie('jwt', token, { path: '/', httpOnly: true, maxAge: 604800000 }); //7 days
128+
res.status(200).send({success: true});
129+
});
130+
})(req, res);
131+
};
132+
133+
module.exports = {
134+
doLoginCheck, validateLogin, validateRegistration
135+
};

models/user.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const mongoose = require('mongoose');
2+
const Schema = mongoose.Schema;
3+
4+
const User = new Schema ({
5+
username: { type: String, required: true, index: true, unique: true },
6+
passwordHash: { type: String, required: true }
7+
});
8+
9+
module.exports = mongoose.model('User', User, 'user'); //3rd param is collection name
10+

0 commit comments

Comments
 (0)