From 9f17b17e3138ce49f24447cd5ae457e3e90ad4da Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Wed, 3 Nov 2021 15:56:58 +0500 Subject: [PATCH] fix: DB names updates + refresh api is added --- src/controllers/createClient.ts | 12 +-- src/controllers/createUser.ts | 14 ++-- src/model/Client.ts | 4 +- src/model/User.ts | 15 ++-- src/routes/api/auth.ts | 87 +++++++++++---------- src/routes/api/client.ts | 4 +- src/routes/api/index.ts | 12 +-- src/routes/api/spec/auth.spec.ts | 119 ++++++++++++++++++++--------- src/routes/api/spec/client.spec.ts | 46 +++++------ src/routes/api/spec/drive.spec.ts | 20 ++--- src/routes/api/spec/user.spec.ts | 42 +++++----- src/routes/api/user.ts | 6 +- src/types/InfoJWT.ts | 2 +- src/utils/authenticateToken.ts | 53 ++++++++++--- src/utils/removeTokensInDB.ts | 4 +- src/utils/saveTokensInDB.ts | 14 ++-- src/utils/validation.ts | 14 ++-- src/utils/verifyTokenInDB.ts | 15 ++-- 18 files changed, 274 insertions(+), 209 deletions(-) diff --git a/src/controllers/createClient.ts b/src/controllers/createClient.ts index 4d8f0d3..ab85e09 100644 --- a/src/controllers/createClient.ts +++ b/src/controllers/createClient.ts @@ -1,22 +1,22 @@ import Client from '../model/Client' export const createClient = async (data: any) => { - const { client_id: clientid, client_secret: clientsecret } = data + const { clientId, clientSecret } = data // Checking if client is already in the database - const clientExist = await Client.findOne({ clientid }) + const clientExist = await Client.findOne({ clientId }) if (clientExist) throw new Error('Client ID already exists.') // Create a new client const client = new Client({ - clientid, - clientsecret + clientId, + clientSecret }) const savedClient = await client.save() return { - client_id: savedClient.clientid, - client_secret: savedClient.clientsecret + clientId: savedClient.clientId, + clientSecret: savedClient.clientSecret } } diff --git a/src/controllers/createUser.ts b/src/controllers/createUser.ts index 1586e58..5b58cda 100644 --- a/src/controllers/createUser.ts +++ b/src/controllers/createUser.ts @@ -2,7 +2,7 @@ import bcrypt from 'bcryptjs' import User from '../model/User' export const createUser = async (data: any) => { - const { displayname, username, password, isadmin, isactive } = data + const { displayName, username, password, isAdmin, isActive } = data // Checking if user is already in the database const usernameExist = await User.findOne({ username }) @@ -14,19 +14,19 @@ export const createUser = async (data: any) => { // Create a new user const user = new User({ - displayname, + displayName, username, password: hashPassword, - isadmin, - isactive + isAdmin, + isActive }) const savedUser = await user.save() return { - displayname: savedUser.displayname, + displayName: savedUser.displayName, username: savedUser.username, - isadmin: savedUser.isadmin, - isactive: savedUser.isactive + isAdmin: savedUser.isAdmin, + isActive: savedUser.isActive } } diff --git a/src/model/Client.ts b/src/model/Client.ts index 0c7c636..8bbf38a 100644 --- a/src/model/Client.ts +++ b/src/model/Client.ts @@ -1,11 +1,11 @@ import mongoose from 'mongoose' const clientSchema = new mongoose.Schema({ - clientid: { + clientId: { type: String, required: true }, - clientsecret: { + clientSecret: { type: String, required: true } diff --git a/src/model/User.ts b/src/model/User.ts index db3be92..d271ac3 100644 --- a/src/model/User.ts +++ b/src/model/User.ts @@ -1,8 +1,7 @@ -import { string } from 'joi' -import mongoose, { Schema } from 'mongoose' +import mongoose from 'mongoose' const userSchema = new mongoose.Schema({ - displayname: { + displayName: { type: String, required: true }, @@ -14,25 +13,25 @@ const userSchema = new mongoose.Schema({ type: String, required: true }, - isadmin: { + isAdmin: { type: Boolean, default: false }, - isactive: { + isActive: { type: Boolean, default: true }, tokens: [ { - clientid: { + clientId: { type: String, required: true }, - accesstoken: { + accessToken: { type: String, required: true }, - refreshtoken: { + refreshToken: { type: String, required: true } diff --git a/src/routes/api/auth.ts b/src/routes/api/auth.ts index 63ff9e1..5d11a56 100644 --- a/src/routes/api/auth.ts +++ b/src/routes/api/auth.ts @@ -6,7 +6,8 @@ import jwt from 'jsonwebtoken' import Client from '../../model/Client' import User from '../../model/User' import { - authenticateToken, + authenticateAccessToken, + authenticateRefreshToken, authorizeValidation, removeTokensInDB, saveTokensInDB, @@ -16,7 +17,6 @@ import { InfoJWT } from '../../types' const authRouter = express.Router() -const clients: { [key: string]: string } = {} const clientIDs = new Set() const authCodes: { [key: string]: { [key: string]: string } } = {} @@ -24,8 +24,7 @@ export const populateClients = async () => { const result = await Client.find() clientIDs.clear() result.forEach((r) => { - clients[r.clientid] = r.clientsecret - clientIDs.add(r.clientid) + clientIDs.add(r.clientId) }) } @@ -43,24 +42,24 @@ export const connectDB = () => { } } -export const saveCode = (username: string, client_id: string, code: string) => { - if (authCodes[username]) return (authCodes[username][client_id] = code) +export const saveCode = (username: string, clientId: string, code: string) => { + if (authCodes[username]) return (authCodes[username][clientId] = code) - authCodes[username] = { [client_id]: code } - return authCodes[username][client_id] + authCodes[username] = { [clientId]: code } + return authCodes[username][clientId] } -export const deleteCode = (username: string, client_id: string) => - delete authCodes[username][client_id] +export const deleteCode = (username: string, clientId: string) => + delete authCodes[username][clientId] authRouter.post('/authorize', async (req, res) => { const { error, value } = authorizeValidation(req.body) if (error) return res.status(400).send(error.details[0].message) - const { username, password, client_id } = value + const { username, password, clientId } = value // Verify client ID - if (!clientIDs.has(client_id)) { - return res.status(403).send('Invalid client_id.') + if (!clientIDs.has(clientId)) { + return res.status(403).send('Invalid clientId.') } // Authenticate User @@ -70,13 +69,13 @@ authRouter.post('/authorize', async (req, res) => { const validPass = await bcrypt.compare(password, user.password) if (!validPass) return res.status(403).send('Invalid password.') - // generate authorization code against client_id + // generate authorization code against clientId const userInfo: InfoJWT = { - client_id, + clientId, username } - const code = saveCode(username, client_id, generateAuthCode(userInfo)) + const code = saveCode(username, clientId, generateAuthCode(userInfo)) res.json({ code }) }) @@ -85,15 +84,15 @@ authRouter.post('/token', async (req, res) => { const { error, value } = tokenValidation(req.body) if (error) return res.status(400).send(error.details[0].message) - const { client_id, code } = value + const { clientId, code } = value - const userInfo = await verifyAuthCode(client_id, code) + const userInfo = await verifyAuthCode(clientId, code) if (!userInfo) return res.sendStatus(403) - if (authCodes[userInfo.username][client_id] !== code) + if (authCodes[userInfo.username][clientId] !== code) return res.sendStatus(403) - deleteCode(userInfo.username, client_id) + deleteCode(userInfo.username, clientId) const accessToken = generateAccessToken(userInfo) const refreshToken = jwt.sign( @@ -101,36 +100,44 @@ authRouter.post('/token', async (req, res) => { process.env.REFRESH_TOKEN_SECRET as string ) - await saveTokensInDB(userInfo.username, client_id, accessToken, refreshToken) + await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken) res.json({ accessToken: accessToken, refreshToken: refreshToken }) }) -// authRouter.post('/refresh', (req, res) => { -// const refreshToken = req.body.token -// if (refreshToken == null) return res.sendStatus(401) -// if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403) -// jwt.verify( -// refreshToken, -// process.env.REFRESH_TOKEN_SECRET as string, -// (err: any, user: any) => { -// if (err) return res.sendStatus(403) -// const accessToken = generateAccessToken({ name: user.name }) -// res.json({ accessToken: accessToken }) -// } -// ) -// }) +authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => { + const { username, clientId } = req.user + const userInfo = { + username, + clientId + } -authRouter.delete('/logout', authenticateToken, async (req: any, res) => { + const accessToken = generateAccessToken(userInfo) + const refreshToken = jwt.sign( + userInfo, + process.env.REFRESH_TOKEN_SECRET as string + ) + + await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken) + + res.json({ accessToken: accessToken, refreshToken: refreshToken }) +}) + +authRouter.delete('/logout', authenticateAccessToken, async (req: any, res) => { const { user } = req - await removeTokensInDB(user.username, user.client_id) + await removeTokensInDB(user.username, user.clientId) res.sendStatus(204) }) export const generateAccessToken = (data: InfoJWT) => jwt.sign(data, process.env.ACCESS_TOKEN_SECRET as string, { + expiresIn: '1h' + }) + +export const generateRefreshToken = (data: InfoJWT) => + jwt.sign(data, process.env.REFRESH_TOKEN_SECRET as string, { expiresIn: '1day' }) @@ -140,7 +147,7 @@ export const generateAuthCode = (data: InfoJWT) => }) const verifyAuthCode = async ( - client_id: string, + clientId: string, code: string ): Promise => { return new Promise((resolve, reject) => { @@ -148,10 +155,10 @@ const verifyAuthCode = async ( if (err) return resolve(undefined) const clientInfo: InfoJWT = { - client_id: data?.client_id, + clientId: data?.clientId, username: data?.username } - if (clientInfo.client_id === client_id) { + if (clientInfo.clientId === clientId) { return resolve(clientInfo) } return resolve(undefined) diff --git a/src/routes/api/client.ts b/src/routes/api/client.ts index 875e769..f594573 100644 --- a/src/routes/api/client.ts +++ b/src/routes/api/client.ts @@ -11,8 +11,8 @@ clientRouter.post('/', async (req, res) => { try { const savedClient = await createClient(data) res.send({ - client_id: savedClient.client_id, - client_secret: savedClient.client_secret + clientId: savedClient.clientId, + clientSecret: savedClient.clientSecret }) } catch (err: any) { res.status(403).send(err.toString()) diff --git a/src/routes/api/index.ts b/src/routes/api/index.ts index 42f99e2..3ce25da 100644 --- a/src/routes/api/index.ts +++ b/src/routes/api/index.ts @@ -6,22 +6,22 @@ import stpRouter from './stp' import userRouter from './user' import clientRouter from './client' import authRouter, { connectDB } from './auth' -import { authenticateToken } from '../../utils' +import { authenticateAccessToken } from '../../utils' dotenv.config() connectDB() const router = express.Router() -router.use('/drive', authenticateToken, driveRouter) -router.use('/stp', authenticateToken, stpRouter) -router.use('/user', authenticateToken, verifyAdmin, userRouter) -router.use('/client', authenticateToken, verifyAdmin, clientRouter) +router.use('/drive', authenticateAccessToken, driveRouter) +router.use('/stp', authenticateAccessToken, stpRouter) +router.use('/user', authenticateAccessToken, verifyAdmin, userRouter) +router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter) router.use('/auth', authRouter) function verifyAdmin(req: any, res: any, next: any) { const { user } = req - if (!user?.isadmin) return res.status(403).send('Admin account required') + if (!user?.isAdmin) return res.status(403).send('Admin account required') next() } diff --git a/src/routes/api/spec/auth.spec.ts b/src/routes/api/spec/auth.spec.ts index de6d6dc..d79ff4a 100644 --- a/src/routes/api/spec/auth.spec.ts +++ b/src/routes/api/spec/auth.spec.ts @@ -7,20 +7,21 @@ import { createClient } from '../../../controllers/createClient' import { generateAccessToken, generateAuthCode, + generateRefreshToken, populateClients, saveCode } from '../auth' import { InfoJWT } from '../../../types' import { saveTokensInDB, verifyTokenInDB } from '../../../utils' -const client_id = 'someclientID' -const client_secret = 'someclientSecret' +const clientId = 'someclientID' +const clientSecret = 'someclientSecret' const user = { - displayname: 'Test User', + displayName: 'Test User', username: 'testUsername', password: '87654321', - isadmin: false, - isactive: true + isAdmin: false, + isActive: true } describe('auth', () => { @@ -30,7 +31,7 @@ describe('auth', () => { beforeAll(async () => { mongoServer = await MongoMemoryServer.create() con = await mongoose.connect(mongoServer.getUri()) - await createClient({ client_id, client_secret }) + await createClient({ clientId, clientSecret }) await populateClients() }) @@ -55,7 +56,7 @@ describe('auth', () => { .send({ username: user.username, password: user.password, - client_id + clientId }) .expect(200) @@ -67,7 +68,7 @@ describe('auth', () => { .post('/SASjsApi/auth/authorize') .send({ password: user.password, - client_id + clientId }) .expect(400) @@ -80,7 +81,7 @@ describe('auth', () => { .post('/SASjsApi/auth/authorize') .send({ username: user.username, - client_id + clientId }) .expect(400) @@ -88,7 +89,7 @@ describe('auth', () => { expect(res.body).toEqual({}) }) - it('should respond with Bad Request if client_id is missing', async () => { + it('should respond with Bad Request if clientId is missing', async () => { const res = await request(app) .post('/SASjsApi/auth/authorize') .send({ @@ -97,7 +98,7 @@ describe('auth', () => { }) .expect(400) - expect(res.text).toEqual(`"client_id" is required`) + expect(res.text).toEqual(`"clientId" is required`) expect(res.body).toEqual({}) }) @@ -107,7 +108,7 @@ describe('auth', () => { .send({ username: user.username, password: user.password, - client_id + clientId }) .expect(403) @@ -123,7 +124,7 @@ describe('auth', () => { .send({ username: user.username, password: 'WrongPassword', - client_id + clientId }) .expect(403) @@ -131,7 +132,7 @@ describe('auth', () => { expect(res.body).toEqual({}) }) - it('should respond with Forbidden if client_id is incorrect', async () => { + it('should respond with Forbidden if clientId is incorrect', async () => { await createUser(user) const res = await request(app) @@ -139,18 +140,18 @@ describe('auth', () => { .send({ username: user.username, password: user.password, - client_id: 'WrongClientID' + clientId: 'WrongClientID' }) .expect(403) - expect(res.text).toEqual('Invalid client_id.') + expect(res.text).toEqual('Invalid clientId.') expect(res.body).toEqual({}) }) }) describe('token', () => { const userInfo: InfoJWT = { - client_id, + clientId, username: user.username } beforeAll(async () => { @@ -165,14 +166,14 @@ describe('auth', () => { it('should respond with access and refresh tokens', async () => { const code = saveCode( userInfo.username, - userInfo.client_id, + userInfo.clientId, generateAuthCode(userInfo) ) const res = await request(app) .post('/SASjsApi/auth/token') .send({ - client_id, + clientId, code }) .expect(200) @@ -185,7 +186,7 @@ describe('auth', () => { const res = await request(app) .post('/SASjsApi/auth/token') .send({ - client_id + clientId }) .expect(400) @@ -193,10 +194,10 @@ describe('auth', () => { expect(res.body).toEqual({}) }) - it('should respond with Bad Request if client_id is missing', async () => { + it('should respond with Bad Request if clientId is missing', async () => { const code = saveCode( userInfo.username, - userInfo.client_id, + userInfo.clientId, generateAuthCode(userInfo) ) @@ -207,7 +208,7 @@ describe('auth', () => { }) .expect(400) - expect(res.text).toEqual(`"client_id" is required`) + expect(res.text).toEqual(`"clientId" is required`) expect(res.body).toEqual({}) }) @@ -215,7 +216,7 @@ describe('auth', () => { const res = await request(app) .post('/SASjsApi/auth/token') .send({ - client_id, + clientId, code: 'InvalidCode' }) .expect(403) @@ -223,17 +224,17 @@ describe('auth', () => { expect(res.body).toEqual({}) }) - it('should respond with Forbidden if client_id is invalid', async () => { + it('should respond with Forbidden if clientId is invalid', async () => { const code = saveCode( userInfo.username, - userInfo.client_id, + userInfo.clientId, generateAuthCode(userInfo) ) const res = await request(app) .post('/SASjsApi/auth/token') .send({ - client_id: 'WrongClientID', + clientId: 'WrongClientID', code }) .expect(403) @@ -242,20 +243,59 @@ describe('auth', () => { }) }) - describe('logout', () => { - const accessToken = generateAccessToken({ - client_id, + describe('refresh', () => { + const refreshToken = generateRefreshToken({ + clientId, username: user.username }) beforeEach(async () => { await createUser(user) - await saveTokensInDB( - user.username, - client_id, - accessToken, - 'refreshToken' - ) + await saveTokensInDB(user.username, clientId, 'accessToken', refreshToken) + }) + + afterEach(async () => { + const collections = mongoose.connection.collections + const collection = collections['users'] + await collection.deleteMany({}) + }) + + afterAll(async () => { + const collections = mongoose.connection.collections + const collection = collections['users'] + await collection.deleteMany({}) + }) + + it('should respond with new access and refresh tokens', async () => { + const res = await request(app) + .post('/SASjsApi/auth/refresh') + .auth(refreshToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body).toHaveProperty('accessToken') + expect(res.body).toHaveProperty('refreshToken') + + // cannot use same refresh again + const resWithError = await request(app) + .post('/SASjsApi/auth/refresh') + .auth(refreshToken, { type: 'bearer' }) + .send() + .expect(401) + + expect(resWithError.body).toEqual({}) + }) + }) + + describe('logout', () => { + const accessToken = generateAccessToken({ + clientId, + username: user.username + }) + + beforeEach(async () => { + await createUser(user) + await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken') }) afterEach(async () => { @@ -280,7 +320,12 @@ describe('auth', () => { expect(res.body).toEqual({}) expect( - await verifyTokenInDB(user.username, client_id, accessToken) + await verifyTokenInDB( + user.username, + clientId, + accessToken, + 'accessToken' + ) ).toBeUndefined() }) }) diff --git a/src/routes/api/spec/client.spec.ts b/src/routes/api/spec/client.spec.ts index 91ce4fd..ab11084 100644 --- a/src/routes/api/spec/client.spec.ts +++ b/src/routes/api/spec/client.spec.ts @@ -8,19 +8,19 @@ import { createUser } from '../../../controllers/createUser' import { saveTokensInDB } from '../../../utils' const client = { - client_id: 'someclientID', - client_secret: 'someclientSecret' + clientId: 'someclientID', + clientSecret: 'someclientSecret' } const adminUser = { - displayname: 'Test Admin', + displayName: 'Test Admin', username: 'testAdminUsername', password: '12345678', - isadmin: true, - isactive: true + isAdmin: true, + isActive: true } const newClient = { - client_id: 'newClientID', - client_secret: 'newClientSecret' + clientId: 'newClientID', + clientSecret: 'newClientSecret' } describe('user', () => { @@ -40,7 +40,7 @@ describe('user', () => { describe('create', () => { const adminAccessToken = generateAccessToken({ - client_id: client.client_id, + clientId: client.clientId, username: adminUser.username }) @@ -48,7 +48,7 @@ describe('user', () => { await createUser(adminUser) await saveTokensInDB( adminUser.username, - client.client_id, + client.clientId, adminAccessToken, 'refreshToken' ) @@ -67,8 +67,8 @@ describe('user', () => { .send(newClient) .expect(200) - expect(res.body.client_id).toEqual(newClient.client_id) - expect(res.body.client_secret).toEqual(newClient.client_secret) + expect(res.body.clientId).toEqual(newClient.clientId) + expect(res.body.clientSecret).toEqual(newClient.clientSecret) }) it('should respond with Unauthorized if access token is not present', async () => { @@ -83,20 +83,20 @@ describe('user', () => { it('should respond with Forbideen if access token is not of an admin account', async () => { const user = { - displayname: 'User 1', + displayName: 'User 1', username: 'username1', password: '12345678', - isadmin: false, - isactive: true + isAdmin: false, + isActive: true } const accessToken = generateAccessToken({ - client_id: client.client_id, + clientId: client.clientId, username: user.username }) await createUser(user) await saveTokensInDB( user.username, - client.client_id, + client.clientId, accessToken, 'refreshToken' ) @@ -111,7 +111,7 @@ describe('user', () => { expect(res.body).toEqual({}) }) - it('should respond with Forbidden if client_id is already present', async () => { + it('should respond with Forbidden if clientId is already present', async () => { await createClient(newClient) const res = await request(app) @@ -124,31 +124,31 @@ describe('user', () => { expect(res.body).toEqual({}) }) - it('should respond with Bad Request if client_id is missing', async () => { + it('should respond with Bad Request if clientId is missing', async () => { const res = await request(app) .post('/SASjsApi/client') .auth(adminAccessToken, { type: 'bearer' }) .send({ ...newClient, - client_id: undefined + clientId: undefined }) .expect(400) - expect(res.text).toEqual(`"client_id" is required`) + expect(res.text).toEqual(`"clientId" is required`) expect(res.body).toEqual({}) }) - it('should respond with Bad Request if client_secret is missing', async () => { + it('should respond with Bad Request if clientSecret is missing', async () => { const res = await request(app) .post('/SASjsApi/client') .auth(adminAccessToken, { type: 'bearer' }) .send({ ...newClient, - client_secret: undefined + clientSecret: undefined }) .expect(400) - expect(res.text).toEqual(`"client_secret" is required`) + expect(res.text).toEqual(`"clientSecret" is required`) expect(res.body).toEqual({}) }) }) diff --git a/src/routes/api/spec/drive.spec.ts b/src/routes/api/spec/drive.spec.ts index 39e528f..e2c6339 100644 --- a/src/routes/api/spec/drive.spec.ts +++ b/src/routes/api/spec/drive.spec.ts @@ -10,16 +10,13 @@ import { generateAccessToken } from '../auth' import { createUser } from '../../../controllers/createUser' import { saveTokensInDB } from '../../../utils' -const client = { - clientid: 'someclientID', - clientsecret: 'someclientSecret' -} +const clientId = 'someclientID' const user = { - displayname: 'Test User', + displayName: 'Test User', username: 'testUsername', password: '87654321', - isadmin: false, - isactive: true + isAdmin: false, + isActive: true } describe('files', () => { @@ -38,18 +35,13 @@ describe('files', () => { }) describe('deploy', () => { const accessToken = generateAccessToken({ - client_id: client.clientid, + clientId, username: user.username }) beforeAll(async () => { await createUser(user) - await saveTokensInDB( - user.username, - client.clientid, - accessToken, - 'refreshToken' - ) + await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken') }) const shouldFailAssertion = async (payload: any) => { const res = await request(app) diff --git a/src/routes/api/spec/user.spec.ts b/src/routes/api/spec/user.spec.ts index 19ea780..9adb8e1 100644 --- a/src/routes/api/spec/user.spec.ts +++ b/src/routes/api/spec/user.spec.ts @@ -6,23 +6,20 @@ import { createUser } from '../../../controllers/createUser' import { generateAccessToken } from '../auth' import { saveTokensInDB } from '../../../utils' -const client = { - clientid: 'someclientID', - clientsecret: 'someclientSecret' -} +const clientId = 'someclientID' const adminUser = { - displayname: 'Test Admin', + displayName: 'Test Admin', username: 'testAdminUsername', password: '12345678', - isadmin: true, - isactive: true + isAdmin: true, + isActive: true } const user = { - displayname: 'Test User', + displayName: 'Test User', username: 'testUsername', password: '87654321', - isadmin: false, - isactive: true + isAdmin: false, + isActive: true } describe('user', () => { @@ -42,7 +39,7 @@ describe('user', () => { describe('create', () => { const adminAccessToken = generateAccessToken({ - client_id: client.clientid, + clientId, username: adminUser.username }) @@ -50,7 +47,7 @@ describe('user', () => { await createUser(adminUser) await saveTokensInDB( adminUser.username, - client.clientid, + clientId, adminAccessToken, 'refreshToken' ) @@ -70,9 +67,9 @@ describe('user', () => { .expect(200) expect(res.body.username).toEqual(user.username) - expect(res.body.displayname).toEqual(user.displayname) - expect(res.body.isadmin).toEqual(user.isadmin) - expect(res.body.isactive).toEqual(user.isactive) + expect(res.body.displayName).toEqual(user.displayName) + expect(res.body.isAdmin).toEqual(user.isAdmin) + expect(res.body.isActive).toEqual(user.isActive) }) it('should respond with Unauthorized if access token is not present', async () => { @@ -87,16 +84,11 @@ describe('user', () => { it('should respond with Forbideen if access token is not of an admin account', async () => { const accessToken = generateAccessToken({ - client_id: client.clientid, + clientId, username: user.username }) await createUser(user) - await saveTokensInDB( - user.username, - client.clientid, - accessToken, - 'refreshToken' - ) + await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken') const res = await request(app) .post('/SASjsApi/user') @@ -149,17 +141,17 @@ describe('user', () => { expect(res.body).toEqual({}) }) - it('should respond with Bad Request if displayname is missing', async () => { + it('should respond with Bad Request if displayName is missing', async () => { const res = await request(app) .post('/SASjsApi/user') .auth(adminAccessToken, { type: 'bearer' }) .send({ ...user, - displayname: undefined + displayName: undefined }) .expect(400) - expect(res.text).toEqual(`"displayname" is required`) + expect(res.text).toEqual(`"displayName" is required`) expect(res.body).toEqual({}) }) }) diff --git a/src/routes/api/user.ts b/src/routes/api/user.ts index 4f362b7..c3b4dad 100644 --- a/src/routes/api/user.ts +++ b/src/routes/api/user.ts @@ -11,10 +11,10 @@ userRouter.post('/', async (req, res) => { try { const savedUser = await createUser(data) res.send({ - displayname: savedUser.displayname, + displayName: savedUser.displayName, username: savedUser.username, - isadmin: savedUser.isadmin, - isactive: savedUser.isactive, + isAdmin: savedUser.isAdmin, + isActive: savedUser.isActive, tokens: [] }) } catch (err: any) { diff --git a/src/types/InfoJWT.ts b/src/types/InfoJWT.ts index 4d5b546..25f0c53 100644 --- a/src/types/InfoJWT.ts +++ b/src/types/InfoJWT.ts @@ -1,4 +1,4 @@ export interface InfoJWT { - client_id: string + clientId: string username: string } diff --git a/src/utils/authenticateToken.ts b/src/utils/authenticateToken.ts index f572f05..a148539 100644 --- a/src/utils/authenticateToken.ts +++ b/src/utils/authenticateToken.ts @@ -1,25 +1,54 @@ import jwt from 'jsonwebtoken' import { verifyTokenInDB } from './verifyTokenInDB' -export const authenticateToken = (req: any, res: any, next: any) => { +export const authenticateAccessToken = (req: any, res: any, next: any) => { + authenticateToken( + req, + res, + next, + process.env.ACCESS_TOKEN_SECRET as string, + 'accessToken' + ) +} + +export const authenticateRefreshToken = (req: any, res: any, next: any) => { + authenticateToken( + req, + res, + next, + process.env.REFRESH_TOKEN_SECRET as string, + 'refreshToken' + ) +} + +const authenticateToken = ( + req: any, + res: any, + next: any, + key: string, + tokenType: 'accessToken' | 'refreshToken' = 'accessToken' +) => { const authHeader = req.headers['authorization'] const token = authHeader?.split(' ')[1] if (!token) return res.sendStatus(401) - jwt.verify( - token, - process.env.ACCESS_TOKEN_SECRET as string, - async (err: any, data: any) => { - if (err) return res.sendStatus(403) + jwt.verify(token, key, async (err: any, data: any) => { + if (err) return res.sendStatus(401) - // verify this valid token's entry in DB - const user = await verifyTokenInDB(data?.username, data?.client_id, token) + // verify this valid token's entry in DB + const user = await verifyTokenInDB( + data?.username, + data?.clientId, + token, + tokenType + ) - if (user) { + if (user) { + if (user.isActive) { req.user = user return next() - } - return res.sendStatus(403) + } else return res.sendStatus(401) } - ) + return res.sendStatus(401) + }) } diff --git a/src/utils/removeTokensInDB.ts b/src/utils/removeTokensInDB.ts index b1c6a02..22f4667 100644 --- a/src/utils/removeTokensInDB.ts +++ b/src/utils/removeTokensInDB.ts @@ -1,10 +1,10 @@ import User from '../model/User' -export const removeTokensInDB = async (username: string, client_id: string) => { +export const removeTokensInDB = async (username: string, clientId: string) => { const user = await User.findOne({ username }) const tokenObjIndex = user.tokens.findIndex( - (tokenObj: any) => tokenObj.clientid === client_id + (tokenObj: any) => tokenObj.clientId === clientId ) if (tokenObjIndex > -1) { diff --git a/src/utils/saveTokensInDB.ts b/src/utils/saveTokensInDB.ts index 002c730..bdc35f3 100644 --- a/src/utils/saveTokensInDB.ts +++ b/src/utils/saveTokensInDB.ts @@ -2,23 +2,23 @@ import User from '../model/User' export const saveTokensInDB = async ( username: string, - client_id: string, + clientId: string, accessToken: string, refreshToken: string ) => { const user = await User.findOne({ username }) const currentTokenObj = user.tokens.find( - (tokenObj: any) => tokenObj.clientid === client_id + (tokenObj: any) => tokenObj.clientId === clientId ) if (currentTokenObj) { - currentTokenObj.accesstoken = accessToken - currentTokenObj.refreshtoken = refreshToken + currentTokenObj.accessToken = accessToken + currentTokenObj.refreshToken = refreshToken } else { user.tokens.push({ - clientid: client_id, - accesstoken: accessToken, - refreshtoken: refreshToken + clientId: clientId, + accessToken: accessToken, + refreshToken: refreshToken }) } await user.save() diff --git a/src/utils/validation.ts b/src/utils/validation.ts index b97f8ff..233d6a6 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -7,26 +7,26 @@ export const authorizeValidation = (data: any): Joi.ValidationResult => Joi.object({ username: usernameSchema, password: passwordSchema, - client_id: Joi.string().required() + clientId: Joi.string().required() }).validate(data) export const tokenValidation = (data: any): Joi.ValidationResult => Joi.object({ - client_id: Joi.string().required(), + clientId: Joi.string().required(), code: Joi.string().required() }).validate(data) export const registerUserValidation = (data: any): Joi.ValidationResult => Joi.object({ - displayname: Joi.string().min(6).required(), + displayName: Joi.string().min(6).required(), username: usernameSchema, password: passwordSchema, - isadmin: Joi.boolean(), - isactive: Joi.boolean() + isAdmin: Joi.boolean(), + isActive: Joi.boolean() }).validate(data) export const registerClientValidation = (data: any): Joi.ValidationResult => Joi.object({ - client_id: Joi.string().required(), - client_secret: Joi.string().required() + clientId: Joi.string().required(), + clientSecret: Joi.string().required() }).validate(data) diff --git a/src/utils/verifyTokenInDB.ts b/src/utils/verifyTokenInDB.ts index 083f021..9c9f899 100644 --- a/src/utils/verifyTokenInDB.ts +++ b/src/utils/verifyTokenInDB.ts @@ -2,21 +2,22 @@ import User from '../model/User' export const verifyTokenInDB = async ( username: string, - client_id: string, - token: string + clientId: string, + token: string, + tokenType: 'accessToken' | 'refreshToken' ) => { const dbUser = await User.findOne({ username }) const currentTokenObj = dbUser.tokens.find( - (tokenObj: any) => tokenObj.clientid === client_id + (tokenObj: any) => tokenObj.clientId === clientId ) - return currentTokenObj?.accesstoken === token + return currentTokenObj?.[tokenType] === token ? { - client_id, + clientId, username, - isadmin: dbUser.isadmin, - isactive: dbUser.isactive + isAdmin: dbUser.isAdmin, + isActive: dbUser.isActive } : undefined }