1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-15 18:00:05 +00:00

feat: JWT saved in DB + logout api added

This commit is contained in:
Saad Jutt
2021-11-03 14:56:04 +05:00
parent d6aeb378de
commit 46c5a75ac4
15 changed files with 338 additions and 149 deletions

View File

@@ -1,4 +1,5 @@
import mongoose from 'mongoose' import { string } from 'joi'
import mongoose, { Schema } from 'mongoose'
const userSchema = new mongoose.Schema({ const userSchema = new mongoose.Schema({
displayname: { displayname: {
@@ -20,7 +21,23 @@ const userSchema = new mongoose.Schema({
isactive: { isactive: {
type: Boolean, type: Boolean,
default: true default: true
} },
tokens: [
{
clientid: {
type: String,
required: true
},
accesstoken: {
type: String,
required: true
},
refreshtoken: {
type: String,
required: true
}
}
]
}) })
export default mongoose.model('User', userSchema) export default mongoose.model('User', userSchema)

View File

@@ -1,18 +1,24 @@
import express from 'express' import express from 'express'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import mongoose, { Mongoose } from 'mongoose' import mongoose from 'mongoose'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import Client from '../../model/Client' import Client from '../../model/Client'
import User from '../../model/User' import User from '../../model/User'
import { authorizeValidation, tokenValidation } from '../../utils' import {
authenticateToken,
authorizeValidation,
removeTokensInDB,
saveTokensInDB,
tokenValidation
} from '../../utils'
import { InfoJWT } from '../../types' import { InfoJWT } from '../../types'
const authRouter = express.Router() const authRouter = express.Router()
const clients: { [key: string]: string } = {} const clients: { [key: string]: string } = {}
const clientIDs = new Set() const clientIDs = new Set()
const authCodes: { [key: string]: string } = {} const authCodes: { [key: string]: { [key: string]: string } } = {}
export const populateClients = async () => { export const populateClients = async () => {
const result = await Client.find() const result = await Client.find()
@@ -37,11 +43,14 @@ export const connectDB = () => {
} }
} }
export const saveCode = (client_id: string, code: string) => export const saveCode = (username: string, client_id: string, code: string) => {
(authCodes[client_id] = code) if (authCodes[username]) return (authCodes[username][client_id] = code)
export const deleteCode = (client_id: string) => delete authCodes[client_id]
const refreshTokens: string[] = [] authCodes[username] = { [client_id]: code }
return authCodes[username][client_id]
}
export const deleteCode = (username: string, client_id: string) =>
delete authCodes[username][client_id]
authRouter.post('/authorize', async (req, res) => { authRouter.post('/authorize', async (req, res) => {
const { error, value } = authorizeValidation(req.body) const { error, value } = authorizeValidation(req.body)
@@ -49,6 +58,11 @@ authRouter.post('/authorize', async (req, res) => {
const { username, password, client_id } = value const { username, password, client_id } = value
// Verify client ID
if (!clientIDs.has(client_id)) {
return res.status(403).send('Invalid client_id.')
}
// Authenticate User // Authenticate User
const user = await User.findOne({ username }) const user = await User.findOne({ username })
if (!user) return res.status(403).send('Username is not found.') if (!user) return res.status(403).send('Username is not found.')
@@ -56,20 +70,13 @@ authRouter.post('/authorize', async (req, res) => {
const validPass = await bcrypt.compare(password, user.password) const validPass = await bcrypt.compare(password, user.password)
if (!validPass) return res.status(403).send('Invalid password.') if (!validPass) return res.status(403).send('Invalid password.')
// Verify client ID
if (!clientIDs.has(client_id)) {
return res.status(403).send('Invalid client_id.')
}
// generate authorization code against client_id // generate authorization code against client_id
const userInfo: InfoJWT = { const userInfo: InfoJWT = {
client_id, client_id,
username, username
isadmin: user.isadmin,
isactive: user.isactive
} }
const code = saveCode(client_id, generateAuthCode(userInfo)) const code = saveCode(username, client_id, generateAuthCode(userInfo))
res.json({ code }) res.json({ code })
}) })
@@ -78,22 +85,23 @@ authRouter.post('/token', async (req, res) => {
const { error, value } = tokenValidation(req.body) const { error, value } = tokenValidation(req.body)
if (error) return res.status(400).send(error.details[0].message) if (error) return res.status(400).send(error.details[0].message)
const { client_id, client_secret, code } = value const { client_id, code } = value
const userInfo = await verifyAuthCode(client_id, client_secret, code) const userInfo = await verifyAuthCode(client_id, code)
if (!userInfo) return res.sendStatus(403)
if (!userInfo) { if (authCodes[userInfo.username][client_id] !== code)
return res.sendStatus(403) return res.sendStatus(403)
}
deleteCode(userInfo.username, client_id)
const accessToken = generateAccessToken(userInfo) const accessToken = generateAccessToken(userInfo)
const refreshToken = jwt.sign( const refreshToken = jwt.sign(
userInfo, userInfo,
process.env.REFRESH_TOKEN_SECRET as string process.env.REFRESH_TOKEN_SECRET as string
) )
refreshTokens.push(refreshToken)
deleteCode(client_id) await saveTokensInDB(userInfo.username, client_id, accessToken, refreshToken)
res.json({ accessToken: accessToken, refreshToken: refreshToken }) res.json({ accessToken: accessToken, refreshToken: refreshToken })
}) })
@@ -113,9 +121,10 @@ authRouter.post('/token', async (req, res) => {
// ) // )
// }) // })
authRouter.delete('/logout', (req, res) => { authRouter.delete('/logout', authenticateToken, async (req: any, res) => {
const index = refreshTokens.findIndex(req.body.token) const { user } = req
if (index > -1) refreshTokens.splice(index, 1)
await removeTokensInDB(user.username, user.client_id)
res.sendStatus(204) res.sendStatus(204)
}) })
@@ -132,7 +141,6 @@ export const generateAuthCode = (data: InfoJWT) =>
const verifyAuthCode = async ( const verifyAuthCode = async (
client_id: string, client_id: string,
client_secret: string,
code: string code: string
): Promise<InfoJWT | undefined> => { ): Promise<InfoJWT | undefined> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -141,15 +149,9 @@ const verifyAuthCode = async (
const clientInfo: InfoJWT = { const clientInfo: InfoJWT = {
client_id: data?.client_id, client_id: data?.client_id,
username: data?.username, username: data?.username
isadmin: data?.isadmin,
isactive: data?.isactive
} }
if ( if (clientInfo.client_id === client_id) {
clientInfo.client_id === client_id &&
clients[client_id] === client_secret &&
authCodes[client_id] === code
) {
return resolve(clientInfo) return resolve(clientInfo)
} }
return resolve(undefined) return resolve(undefined)

View File

@@ -1,13 +1,12 @@
import express from 'express' import express from 'express'
import jwt from 'jsonwebtoken'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import { InfoJWT } from '../../types'
import driveRouter from './drive' import driveRouter from './drive'
import stpRouter from './stp' import stpRouter from './stp'
import userRouter from './user' import userRouter from './user'
import clientRouter from './client' import clientRouter from './client'
import authRouter, { connectDB } from './auth' import authRouter, { connectDB } from './auth'
import { authenticateToken } from '../../utils'
dotenv.config() dotenv.config()
connectDB() connectDB()
@@ -20,32 +19,9 @@ router.use('/user', authenticateToken, verifyAdmin, userRouter)
router.use('/client', authenticateToken, verifyAdmin, clientRouter) router.use('/client', authenticateToken, verifyAdmin, clientRouter)
router.use('/auth', authRouter) router.use('/auth', authRouter)
function authenticateToken(req: any, res: any, next: any) {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token == null) return res.sendStatus(401)
jwt.verify(
token,
process.env.ACCESS_TOKEN_SECRET as string,
(err: any, data: any) => {
if (err) return res.sendStatus(403)
const user: InfoJWT = {
client_id: data?.client_id,
username: data?.username,
isadmin: data?.isadmin,
isactive: data?.isactive
}
req.user = user
next()
}
)
}
function verifyAdmin(req: any, res: any, next: any) { function verifyAdmin(req: any, res: any, next: any) {
const { user } = req 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() next()
} }

View File

@@ -4,13 +4,17 @@ import request from 'supertest'
import app from '../../../app' import app from '../../../app'
import { createUser } from '../../../controllers/createUser' import { createUser } from '../../../controllers/createUser'
import { createClient } from '../../../controllers/createClient' import { createClient } from '../../../controllers/createClient'
import { generateAuthCode, populateClients, saveCode } from '../auth' import {
generateAccessToken,
generateAuthCode,
populateClients,
saveCode
} from '../auth'
import { InfoJWT } from '../../../types' import { InfoJWT } from '../../../types'
import { saveTokensInDB, verifyTokenInDB } from '../../../utils'
const client = { const client_id = 'someclientID'
client_id: 'someclientID', const client_secret = 'someclientSecret'
client_secret: 'someclientSecret'
}
const user = { const user = {
displayname: 'Test User', displayname: 'Test User',
username: 'testUsername', username: 'testUsername',
@@ -26,7 +30,7 @@ describe('auth', () => {
beforeAll(async () => { beforeAll(async () => {
mongoServer = await MongoMemoryServer.create() mongoServer = await MongoMemoryServer.create()
con = await mongoose.connect(mongoServer.getUri()) con = await mongoose.connect(mongoServer.getUri())
await createClient(client) await createClient({ client_id, client_secret })
await populateClients() await populateClients()
}) })
@@ -51,7 +55,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: user.password, password: user.password,
client_id: client.client_id client_id
}) })
.expect(200) .expect(200)
@@ -63,7 +67,7 @@ describe('auth', () => {
.post('/SASjsApi/auth/authorize') .post('/SASjsApi/auth/authorize')
.send({ .send({
password: user.password, password: user.password,
client_id: client.client_id client_id
}) })
.expect(400) .expect(400)
@@ -76,7 +80,7 @@ describe('auth', () => {
.post('/SASjsApi/auth/authorize') .post('/SASjsApi/auth/authorize')
.send({ .send({
username: user.username, username: user.username,
client_id: client.client_id client_id
}) })
.expect(400) .expect(400)
@@ -103,7 +107,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: user.password, password: user.password,
client_id: client.client_id client_id
}) })
.expect(403) .expect(403)
@@ -119,7 +123,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: 'WrongPassword', password: 'WrongPassword',
client_id: client.client_id client_id
}) })
.expect(403) .expect(403)
@@ -146,10 +150,8 @@ describe('auth', () => {
describe('token', () => { describe('token', () => {
const userInfo: InfoJWT = { const userInfo: InfoJWT = {
client_id: client.client_id, client_id,
username: user.username, username: user.username
isadmin: user.isadmin,
isactive: user.isactive
} }
beforeAll(async () => { beforeAll(async () => {
await createUser(user) await createUser(user)
@@ -161,13 +163,16 @@ describe('auth', () => {
}) })
it('should respond with access and refresh tokens', async () => { it('should respond with access and refresh tokens', async () => {
const code = saveCode(userInfo.client_id, generateAuthCode(userInfo)) const code = saveCode(
userInfo.username,
userInfo.client_id,
generateAuthCode(userInfo)
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_id: client.client_id, client_id,
client_secret: client.client_secret,
code code
}) })
.expect(200) .expect(200)
@@ -180,8 +185,7 @@ describe('auth', () => {
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_id: client.client_id, client_id
client_secret: client.client_secret
}) })
.expect(400) .expect(400)
@@ -190,12 +194,15 @@ describe('auth', () => {
}) })
it('should respond with Bad Request if client_id is missing', async () => { it('should respond with Bad Request if client_id is missing', async () => {
const code = saveCode(userInfo.client_id, generateAuthCode(userInfo)) const code = saveCode(
userInfo.username,
userInfo.client_id,
generateAuthCode(userInfo)
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_secret: client.client_secret,
code code
}) })
.expect(400) .expect(400)
@@ -204,27 +211,11 @@ describe('auth', () => {
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
it('should respond with Bad Request if client_secret is missing', async () => {
const code = saveCode(userInfo.client_id, generateAuthCode(userInfo))
const res = await request(app)
.post('/SASjsApi/auth/token')
.send({
client_id: client.client_id,
code
})
.expect(400)
expect(res.text).toEqual(`"client_secret" is required`)
expect(res.body).toEqual({})
})
it('should respond with Forbidden if code is invalid', async () => { it('should respond with Forbidden if code is invalid', async () => {
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_id: client.client_id, client_id,
client_secret: client.client_secret,
code: 'InvalidCode' code: 'InvalidCode'
}) })
.expect(403) .expect(403)
@@ -233,28 +224,16 @@ describe('auth', () => {
}) })
it('should respond with Forbidden if client_id is invalid', async () => { it('should respond with Forbidden if client_id is invalid', async () => {
const code = saveCode(userInfo.client_id, generateAuthCode(userInfo)) const code = saveCode(
userInfo.username,
userInfo.client_id,
generateAuthCode(userInfo)
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_id: 'WrongClientID', client_id: 'WrongClientID',
client_secret: client.client_secret,
code
})
.expect(403)
expect(res.body).toEqual({})
})
it('should respond with Forbidden if client_secret is invalid', async () => {
const code = saveCode(userInfo.client_id, generateAuthCode(userInfo))
const res = await request(app)
.post('/SASjsApi/auth/token')
.send({
client_id: client.client_id,
client_secret: 'WrongClientSecret',
code code
}) })
.expect(403) .expect(403)
@@ -262,4 +241,47 @@ describe('auth', () => {
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
}) })
describe('logout', () => {
const accessToken = generateAccessToken({
client_id,
username: user.username
})
beforeEach(async () => {
await createUser(user)
await saveTokensInDB(
user.username,
client_id,
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 no content and remove access/refresh tokens from DB', async () => {
const res = await request(app)
.delete('/SASjsApi/auth/logout')
.auth(accessToken, { type: 'bearer' })
.send()
.expect(204)
expect(res.body).toEqual({})
expect(
await verifyTokenInDB(user.username, client_id, accessToken)
).toBeUndefined()
})
})
}) })

View File

@@ -4,8 +4,21 @@ import request from 'supertest'
import app from '../../../app' import app from '../../../app'
import { createClient } from '../../../controllers/createClient' import { createClient } from '../../../controllers/createClient'
import { generateAccessToken } from '../auth' import { generateAccessToken } from '../auth'
import { createUser } from '../../../controllers/createUser'
import { saveTokensInDB } from '../../../utils'
const client = { const client = {
client_id: 'someclientID',
client_secret: 'someclientSecret'
}
const adminUser = {
displayname: 'Test Admin',
username: 'testAdminUsername',
password: '12345678',
isadmin: true,
isactive: true
}
const newClient = {
client_id: 'newClientID', client_id: 'newClientID',
client_secret: 'newClientSecret' client_secret: 'newClientSecret'
} }
@@ -27,10 +40,18 @@ describe('user', () => {
describe('create', () => { describe('create', () => {
const adminAccessToken = generateAccessToken({ const adminAccessToken = generateAccessToken({
client_id: 'someClientID', client_id: client.client_id,
username: 'someAdminUsername', username: adminUser.username
isadmin: true, })
isactive: true
beforeAll(async () => {
await createUser(adminUser)
await saveTokensInDB(
adminUser.username,
client.client_id,
adminAccessToken,
'refreshToken'
)
}) })
afterEach(async () => { afterEach(async () => {
@@ -43,17 +64,17 @@ describe('user', () => {
const res = await request(app) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send(client) .send(newClient)
.expect(200) .expect(200)
expect(res.body.client_id).toEqual(client.client_id) expect(res.body.client_id).toEqual(newClient.client_id)
expect(res.body.client_secret).toEqual(client.client_secret) expect(res.body.client_secret).toEqual(newClient.client_secret)
}) })
it('should respond with Unauthorized if access token is not present', async () => { it('should respond with Unauthorized if access token is not present', async () => {
const res = await request(app) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.send(client) .send(newClient)
.expect(401) .expect(401)
expect(res.text).toEqual('Unauthorized') expect(res.text).toEqual('Unauthorized')
@@ -61,17 +82,29 @@ describe('user', () => {
}) })
it('should respond with Forbideen if access token is not of an admin account', async () => { it('should respond with Forbideen if access token is not of an admin account', async () => {
const accessToken = generateAccessToken({ const user = {
client_id: 'someClientID', displayname: 'User 1',
username: 'someUsername', username: 'username1',
password: '12345678',
isadmin: false, isadmin: false,
isactive: true isactive: true
}
const accessToken = generateAccessToken({
client_id: client.client_id,
username: user.username
}) })
await createUser(user)
await saveTokensInDB(
user.username,
client.client_id,
accessToken,
'refreshToken'
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(accessToken, { type: 'bearer' }) .auth(accessToken, { type: 'bearer' })
.send(client) .send(newClient)
.expect(403) .expect(403)
expect(res.text).toEqual('Admin account required') expect(res.text).toEqual('Admin account required')
@@ -79,12 +112,12 @@ describe('user', () => {
}) })
it('should respond with Forbidden if client_id is already present', async () => { it('should respond with Forbidden if client_id is already present', async () => {
await createClient(client) await createClient(newClient)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send(client) .send(newClient)
.expect(403) .expect(403)
expect(res.text).toEqual('Error: Client ID already exists.') expect(res.text).toEqual('Error: Client ID already exists.')
@@ -96,7 +129,7 @@ describe('user', () => {
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send({ .send({
...client, ...newClient,
client_id: undefined client_id: undefined
}) })
.expect(400) .expect(400)
@@ -110,7 +143,7 @@ describe('user', () => {
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send({ .send({
...client, ...newClient,
client_secret: undefined client_secret: undefined
}) })
.expect(400) .expect(400)

View File

@@ -1,3 +1,5 @@
import mongoose, { Mongoose } from 'mongoose'
import { MongoMemoryServer } from 'mongodb-memory-server'
import request from 'supertest' import request from 'supertest'
import app from '../../../app' import app from '../../../app'
import { getTreeExample } from '../../../controllers/deploy' import { getTreeExample } from '../../../controllers/deploy'
@@ -5,15 +7,50 @@ import { getTmpFilesFolderPath } from '../../../utils/file'
import { folderExists, fileExists, readFile, deleteFolder } from '@sasjs/utils' import { folderExists, fileExists, readFile, deleteFolder } from '@sasjs/utils'
import path from 'path' import path from 'path'
import { generateAccessToken } from '../auth' import { generateAccessToken } from '../auth'
import { createUser } from '../../../controllers/createUser'
import { saveTokensInDB } from '../../../utils'
const client = {
clientid: 'someclientID',
clientsecret: 'someclientSecret'
}
const user = {
displayname: 'Test User',
username: 'testUsername',
password: '87654321',
isadmin: false,
isactive: true
}
describe('files', () => { describe('files', () => {
const accessToken = generateAccessToken({ let con: Mongoose
client_id: 'someClientID', let mongoServer: MongoMemoryServer
username: 'username',
isadmin: false, beforeAll(async () => {
isactive: true mongoServer = await MongoMemoryServer.create()
con = await mongoose.connect(mongoServer.getUri())
})
afterAll(async () => {
await con.connection.dropDatabase()
await con.connection.close()
await mongoServer.stop()
}) })
describe('deploy', () => { describe('deploy', () => {
const accessToken = generateAccessToken({
client_id: client.clientid,
username: user.username
})
beforeAll(async () => {
await createUser(user)
await saveTokensInDB(
user.username,
client.clientid,
accessToken,
'refreshToken'
)
})
const shouldFailAssertion = async (payload: any) => { const shouldFailAssertion = async (payload: any) => {
const res = await request(app) const res = await request(app)
.post('/SASjsApi/drive/deploy') .post('/SASjsApi/drive/deploy')

View File

@@ -4,6 +4,7 @@ import request from 'supertest'
import app from '../../../app' import app from '../../../app'
import { createUser } from '../../../controllers/createUser' import { createUser } from '../../../controllers/createUser'
import { generateAccessToken } from '../auth' import { generateAccessToken } from '../auth'
import { saveTokensInDB } from '../../../utils'
const client = { const client = {
clientid: 'someclientID', clientid: 'someclientID',
@@ -42,9 +43,17 @@ describe('user', () => {
describe('create', () => { describe('create', () => {
const adminAccessToken = generateAccessToken({ const adminAccessToken = generateAccessToken({
client_id: client.clientid, client_id: client.clientid,
username: adminUser.username, username: adminUser.username
isadmin: adminUser.isadmin, })
isactive: adminUser.isactive
beforeEach(async () => {
await createUser(adminUser)
await saveTokensInDB(
adminUser.username,
client.clientid,
adminAccessToken,
'refreshToken'
)
}) })
afterEach(async () => { afterEach(async () => {
@@ -79,10 +88,15 @@ describe('user', () => {
it('should respond with Forbideen if access token is not of an admin account', async () => { it('should respond with Forbideen if access token is not of an admin account', async () => {
const accessToken = generateAccessToken({ const accessToken = generateAccessToken({
client_id: client.clientid, client_id: client.clientid,
username: user.username, username: user.username
isadmin: user.isadmin,
isactive: user.isactive
}) })
await createUser(user)
await saveTokensInDB(
user.username,
client.clientid,
accessToken,
'refreshToken'
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/user') .post('/SASjsApi/user')

View File

@@ -14,7 +14,8 @@ userRouter.post('/', async (req, res) => {
displayname: savedUser.displayname, displayname: savedUser.displayname,
username: savedUser.username, username: savedUser.username,
isadmin: savedUser.isadmin, isadmin: savedUser.isadmin,
isactive: savedUser.isactive isactive: savedUser.isactive,
tokens: []
}) })
} catch (err: any) { } catch (err: any) {
res.status(403).send(err.toString()) res.status(403).send(err.toString())

View File

@@ -1,6 +1,4 @@
export interface InfoJWT { export interface InfoJWT {
client_id: string client_id: string
username: string username: string
isadmin: boolean
isactive: boolean
} }

View File

@@ -0,0 +1,25 @@
import jwt from 'jsonwebtoken'
import { verifyTokenInDB } from './verifyTokenInDB'
export const authenticateToken = (req: any, res: any, next: any) => {
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)
// verify this valid token's entry in DB
const user = await verifyTokenInDB(data?.username, data?.client_id, token)
if (user) {
req.user = user
return next()
}
return res.sendStatus(403)
}
)
}

View File

@@ -1,4 +1,8 @@
export * from './authenticateToken'
export * from './file' export * from './file'
export * from './removeTokensInDB'
export * from './saveTokensInDB'
export * from './sleep' export * from './sleep'
export * from './upload' export * from './upload'
export * from './validation' export * from './validation'
export * from './verifyTokenInDB'

View File

@@ -0,0 +1,14 @@
import User from '../model/User'
export const removeTokensInDB = async (username: string, client_id: string) => {
const user = await User.findOne({ username })
const tokenObjIndex = user.tokens.findIndex(
(tokenObj: any) => tokenObj.clientid === client_id
)
if (tokenObjIndex > -1) {
user.tokens.splice(tokenObjIndex, 1)
await user.save()
}
}

View File

@@ -0,0 +1,25 @@
import User from '../model/User'
export const saveTokensInDB = async (
username: string,
client_id: string,
accessToken: string,
refreshToken: string
) => {
const user = await User.findOne({ username })
const currentTokenObj = user.tokens.find(
(tokenObj: any) => tokenObj.clientid === client_id
)
if (currentTokenObj) {
currentTokenObj.accesstoken = accessToken
currentTokenObj.refreshtoken = refreshToken
} else {
user.tokens.push({
clientid: client_id,
accesstoken: accessToken,
refreshtoken: refreshToken
})
}
await user.save()
}

View File

@@ -13,7 +13,6 @@ export const authorizeValidation = (data: any): Joi.ValidationResult =>
export const tokenValidation = (data: any): Joi.ValidationResult => export const tokenValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
client_id: Joi.string().required(), client_id: Joi.string().required(),
client_secret: Joi.string().required(),
code: Joi.string().required() code: Joi.string().required()
}).validate(data) }).validate(data)

View File

@@ -0,0 +1,22 @@
import User from '../model/User'
export const verifyTokenInDB = async (
username: string,
client_id: string,
token: string
) => {
const dbUser = await User.findOne({ username })
const currentTokenObj = dbUser.tokens.find(
(tokenObj: any) => tokenObj.clientid === client_id
)
return currentTokenObj?.accesstoken === token
? {
client_id,
username,
isadmin: dbUser.isadmin,
isactive: dbUser.isactive
}
: undefined
}