From 6b92f26f68980c6cf0582c1ba9662f967a70cae7 Mon Sep 17 00:00:00 2001 From: kj1352 Date: Wed, 29 Sep 2021 14:01:19 +0530 Subject: [PATCH] user authentication --- package-lock.json | 130 +++++++++++++++++++++++++++++++++++ package.json | 6 +- src/authentication/auth.ts | 53 ++++++++++++++ src/authentication/routes.ts | 64 ++++++++++++++++- 4 files changed, 249 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 904615c..c43f4e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,6 +134,15 @@ "@types/range-parser": "*" } }, + "@types/jsonwebtoken": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.5.tgz", + "integrity": "sha512-OGqtHQ7N5/Ap/TUwO6IgHDuLiAoTmHhGpNvgkCm/F4N6pKzx/RBSfr2OXZSwC6vkfnsEdb6+7DNZVtiXiwdwFw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -154,6 +163,27 @@ "@types/express": "*" } }, + "@types/passport-jwt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.6.tgz", + "integrity": "sha512-cmAAMIRTaEwpqxlrZyiEY9kdibk94gP5KTF8AT1Ra4rWNZYHNMreqhKUEeC5WJtuN5SJZjPQmV+XO2P5PlnvNQ==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/passport": "*" + } + }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -441,6 +471,11 @@ "ieee754": "^1.1.13" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -696,6 +731,14 @@ "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1178,6 +1221,49 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -1194,6 +1280,41 @@ "package-json": "^6.3.0" } }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -1493,6 +1614,15 @@ "pause": "0.0.1" } }, + "passport-jwt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", + "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", + "requires": { + "jsonwebtoken": "^8.2.0", + "passport-strategy": "^1.0.0" + } + }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", diff --git a/package.json b/package.json index 06f0cdc..da2583e 100644 --- a/package.json +++ b/package.json @@ -19,16 +19,20 @@ "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "mongodb": "^4.1.2", "nodemon": "^2.0.12", - "passport": "^0.5.0" + "passport": "^0.5.0", + "passport-jwt": "^4.0.0" }, "devDependencies": { "@types/bcrypt": "^5.0.0", "@types/cors": "^2.8.12", "@types/express": "^4.17.13", + "@types/jsonwebtoken": "^8.5.5", "@types/node": "^16.9.4", "@types/passport": "^1.0.7", + "@types/passport-jwt": "^3.0.6", "tslint": "^6.1.3", "typescript": "^4.4.3" } diff --git a/src/authentication/auth.ts b/src/authentication/auth.ts index e69de29..bae3a30 100644 --- a/src/authentication/auth.ts +++ b/src/authentication/auth.ts @@ -0,0 +1,53 @@ +import bcrypt from 'bcrypt'; +import passport from 'passport'; +import passportJWT, { StrategyOptions } from 'passport-jwt'; +import jwt from 'jsonwebtoken'; +import { getDatabaseClient, DB_NAME } from '../db-utils'; + +const DEFAULT_SECRET = process.env.SECRET_KEY || '02faf720-e46c-4af8-b4f8-8cdc8ba1aaf5'; +export const SALT_ROUNDS = 12; + +const JwtStrategy = passportJWT.Strategy; +const ExtractJwt = passportJWT.ExtractJwt; +const strategyOptions: StrategyOptions = { + jwtFromRequest: ExtractJwt.fromExtractors([ + ExtractJwt.fromAuthHeaderAsBearerToken(), + ExtractJwt.fromUrlQueryParameter('token') + ]), + secretOrKey: DEFAULT_SECRET, +}; + +passport.use(new JwtStrategy(strategyOptions, async (jwtPayload, done) => { + const usersCollection = getDatabaseClient().db(DB_NAME).collection('users'); + try { + const user = await usersCollection.findOne({ + _id: jwtPayload.sub + }); + + if (user && !user.archived) { + return done(null, user); + } else { + return done(null, false); + } + + } catch (error) { + return done(error, false); + } +})); + +export function generateJWT(id: string) { + const today = new Date(); + const expirationDate = new Date(today); + expirationDate.setDate(today.getDate() + 60); + expirationDate.setHours(0); + expirationDate.setMinutes(0); + expirationDate.setSeconds(0); + expirationDate.setMilliseconds(0); + + const signedToken = jwt.sign({ + sub: id, + exp: expirationDate.getTime() / 1000, + }, DEFAULT_SECRET); + + return signedToken; +} \ No newline at end of file diff --git a/src/authentication/routes.ts b/src/authentication/routes.ts index abcb28a..6162ac0 100644 --- a/src/authentication/routes.ts +++ b/src/authentication/routes.ts @@ -1,11 +1,10 @@ -import express, { response, urlencoded } from 'express'; +import express from 'express'; import { DB_NAME, getDatabaseClient } from '../db-utils'; import bcrypt from 'bcrypt'; +import { generateJWT, SALT_ROUNDS } from './auth'; const authRoutes = express.Router(); -export const SALT_ROUNDS = 12; - authRoutes.get('/users/', async (request, response) => { console.log(request); response.send("List of users will be displayed"); @@ -67,4 +66,63 @@ authRoutes.post('/register-applicant/', async (request, response) => { }); +authRoutes.post('/api-auth/', async (request, response) => { + const email = request.body.email; + const password = request.body.password; + const userType = request.body.userType; + + if (!email || !password || !userType) { + response.status(400); + response.send("Please field the required fields"); + return; + } + + const userCollection = getDatabaseClient().db(DB_NAME).collection('users'); + + let users = await userCollection.find({ + email, + userType, + }).toArray(); + + const matchedUser = users[0]; + + if (!matchedUser) { + response.status(400); + response.send('Wrong credentials'); + return; + } else if (!matchedUser.isVerified) { + response.status(400); + response.send('Please complete user verification'); + return; + } + + let passwordComparisonResult: boolean; + + try { + passwordComparisonResult = await bcrypt.compare(password, matchedUser.password); + + if (passwordComparisonResult) { + response.send({ + id: matchedUser._id, + token: generateJWT(matchedUser._id), + }); + } else { + response.status(400); + response.send('Wrong credentials'); + } + + } catch(e) { + response.sendStatus(500); + response.json(e); + } + + return; + + // let passwordComparisonResult: boolean; + + // try { + // passwordComparisonResult = await bcrypt.compare(password, user.password); + // } +}); + export default authRoutes; \ No newline at end of file