Skip to content

Commit 83b382c

Browse files
committed
Added User Authenticaton using passport js and jwt
1 parent 57afacc commit 83b382c

File tree

14 files changed

+2710
-928
lines changed

14 files changed

+2710
-928
lines changed

.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
secretKey = 12345-67890-09876-54321
2+
3+
TokenSecret = 12345-67890-09876-54321
4+
5+
PORT = 3000
6+
mongoUrl = mongodb://localhost:27017/quiz

Utils/utils.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const crypto = require("crypto");
2+
3+
const genRandomString = function (length) {
4+
return crypto
5+
.randomBytes(Math.ceil(length / 2))
6+
.toString("hex") /** convert to hexadecimal format */
7+
.slice(0, length); /** return required number of characters */
8+
};
9+
10+
const hashPassword = async (password, salt) => {
11+
let config = {
12+
iterations: 1000,
13+
};
14+
return new Promise((resolve, reject) => {
15+
crypto.pbkdf2(
16+
password,
17+
salt,
18+
config.iterations,
19+
32,
20+
"sha512",
21+
(err, derivedKey) =>
22+
err ? reject(err) : resolve(derivedKey.toString("hex"))
23+
);
24+
});
25+
};
26+
27+
const comparePassword = async (userPassword, userSalt, password) => {
28+
return userPassword === (await hashPassword(password, userSalt));
29+
};
30+
31+
module.exports = {
32+
hashPassword,
33+
genRandomString,
34+
comparePassword,
35+
};

app.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const createError = require("http-errors");
22
const express = require("express");
33
const path = require("path");
4-
const cookieParser = require("cookie-parser");
54
const morgan = require("morgan");
65
const winston = require("./winston");
76
const rateLimit = require("express-rate-limit");
@@ -12,20 +11,21 @@ const cors = require("cors");
1211
const compression = require("compression");
1312
const chalk = require("chalk");
1413

14+
const passport = require("passport");
15+
16+
const dotenv = require("dotenv");
17+
dotenv.config();
18+
19+
const authenticate = require("./authenticate");
20+
1521
const indexRouter = require("./routes/index");
1622
const usersRouter = require("./routes/users");
1723
const quizRouter = require("./routes/quizRouter");
18-
const config = require("./config");
1924

2025
const mongoose = require("mongoose");
21-
const Quizes = require("./models/quizes");
22-
23-
const DB = config.mongoUrl;
24-
25-
mongoose.set("autoIndex", true);
2626

2727
const connectDB = async () => {
28-
const con = await mongoose.connect(DB, {
28+
const con = await mongoose.connect(process.env.mongoUrl, {
2929
useNewUrlParser: true,
3030
useCreateIndex: true,
3131
useFindAndModify: false,
@@ -56,8 +56,6 @@ app.set("view engine", "jade");
5656

5757
app.use(morgan("combined", { stream: winston.stream }));
5858

59-
app.use(cookieParser("12345-67890"));
60-
6159
// Limit requests from the same API
6260
const limiter = rateLimit({
6361
max: 100,
@@ -77,6 +75,8 @@ app.use(cors());
7775

7876
app.options("*", cors());
7977

78+
app.use(passport.initialize());
79+
8080
app.use(compression());
8181

8282
app.disable("x-powered-by");
@@ -106,7 +106,8 @@ app.use(function (err, req, res, next) {
106106
);
107107
// render the error page
108108
res.status(err.status || 500);
109-
res.render("error");
109+
res.json({ err, message: err.message });
110+
//res.render("error");
110111
});
111112

112113
module.exports = app;

authenticate.js

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,80 @@
1-
exports.verifyUser = (req, res, next) => {
2-
3-
console.log(req.signedCookies);
4-
5-
if(!req.signedCookies.user) {
6-
var authHeader = req.headers.authorization;
7-
8-
if(!authHeader) {
9-
var err = new Error('You are not authosrised');
10-
res.setHeader('WWW-Authenticate', 'Basic');
11-
err.status = 401;
12-
return next(err);
13-
}
14-
15-
var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(":");
16-
var username = auth[0];
17-
var password = auth[1];
18-
19-
if (username === 'admin' && password === 'password') {
20-
res.cookie('user', 'admin', {signed: true})
21-
next();
22-
}
23-
else {
24-
var err = new Error('password is incorrect');
25-
res.setHeader('WWW-Authenticate', 'Basic');
26-
err.status = 401;
27-
return next(err);
28-
}
29-
}
1+
const passport = require("passport");
2+
const LocalStrategy = require("passport-local").Strategy;
3+
const JwtStrategy = require("passport-jwt").Strategy;
4+
const ExtractJwt = require("passport-jwt").ExtractJwt;
5+
const jwt = require("jsonwebtoken");
6+
7+
const User = require("./models/Users");
8+
9+
const { comparePassword } = require("./Utils/utils");
10+
11+
passport.use(
12+
new LocalStrategy(function (username, password, done) {
13+
User.findOne(
14+
{ username: username },
15+
"username isAdmin passwordHash passwordSalt",
16+
async function (err, user) {
17+
if (err) {
18+
return done(err);
19+
}
20+
21+
if (!user) {
22+
return done(null, false, { message: "Incorrect Username." });
23+
}
24+
const verifyPassword = await comparePassword(
25+
user.passwordHash,
26+
user.passwordSalt,
27+
password
28+
);
29+
if (!verifyPassword) {
30+
return done(null, false, { message: "Incorrect password." });
31+
}
32+
33+
return done(null, user);
34+
}
35+
);
36+
})
37+
);
38+
39+
passport.serializeUser(function (user, done) {
40+
done(null, user.id);
41+
});
42+
43+
passport.deserializeUser(function (id, done) {
44+
User.findById(id, function (err, user) {
45+
done(err, user);
46+
});
47+
});
48+
49+
exports.generateToken = function (user) {
50+
return jwt.sign(user, process.env.TokenSecret, { expiresIn: 36000 });
51+
};
52+
53+
let opts = {};
54+
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
55+
opts.secretOrKey = process.env.TokenSecret;
3056

31-
else {
32-
if(req.signedCookies.user === 'admin') {
33-
next();
34-
}
35-
else {
36-
var err = new Error('cookie is invalid');
57+
passport.use(
58+
new JwtStrategy(opts, (jwt_payload, done) => {
59+
User.findOne({ _id: jwt_payload._id }, "username isAdmin", (err, user) => {
60+
if (err) {
61+
return done(err, false);
62+
} else if (user) {
63+
return done(null, user);
64+
} else {
65+
return done(null, false);
66+
}
67+
});
68+
})
69+
);
3770

38-
err.status = 401;
39-
return next(err);
40-
}
71+
exports.verifyUser = passport.authenticate("jwt", { session: false });
72+
exports.verifyAdmin = (req, res, next) => {
73+
if (req.user && req.user.isAdmin) {
74+
return next();
75+
} else {
76+
err = new Error("You are not Authorized to perform this operation");
77+
err.status = 403;
78+
return next(err);
4179
}
42-
}
80+
};

bin/www

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var server = http.createServer(app);
2525
* Listen on provided port, on all network interfaces.
2626
*/
2727

28-
server.listen(port);
28+
server.listen(port,()=> console.log(`Server is running on port ${port}`));
2929
server.on('error', onError);
3030
server.on('listening', onListening);
3131

config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
module.exports = {
22
'secretKey': '12345-67890-09876-54321',
3-
'mongoUrl': 'mongodb://localhost:27017/quiz'
3+
'mongoUrl': 'mongodb://localhost:27017/quiz',
4+
5+
"start": "node ./bin/www",
6+
"test": "mocha --recursive --exit"
47
}
8+
9+
10+

logs/app.log

Lines changed: 107 additions & 0 deletions
Large diffs are not rendered by default.

models/Users.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const mongoose = require("mongoose");
2+
3+
const Schema = mongoose.Schema;
4+
5+
const UserSchema = new Schema(
6+
{
7+
username: {
8+
type: String,
9+
trim: true,
10+
required: true,
11+
max: 64,
12+
min: 3,
13+
},
14+
15+
passwordSalt: {
16+
type: String,
17+
required: true,
18+
max:16
19+
},
20+
21+
passwordHash: {
22+
type: String,
23+
required: true,
24+
},
25+
26+
isAdmin: {
27+
type: Boolean,
28+
required: true,
29+
default: false,
30+
},
31+
},
32+
{
33+
timestamps: true,
34+
}
35+
);
36+
37+
const User = mongoose.model("Users", UserSchema);
38+
39+
module.exports = User;

0 commit comments

Comments
 (0)