1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-11 03:34:35 +00:00

fix: DB names updates + refresh api is added

This commit is contained in:
Saad Jutt
2021-11-03 15:56:58 +05:00
parent 46c5a75ac4
commit 9f17b17e31
18 changed files with 274 additions and 209 deletions

View File

@@ -1,22 +1,22 @@
import Client from '../model/Client' import Client from '../model/Client'
export const createClient = async (data: any) => { 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 // 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.') if (clientExist) throw new Error('Client ID already exists.')
// Create a new client // Create a new client
const client = new Client({ const client = new Client({
clientid, clientId,
clientsecret clientSecret
}) })
const savedClient = await client.save() const savedClient = await client.save()
return { return {
client_id: savedClient.clientid, clientId: savedClient.clientId,
client_secret: savedClient.clientsecret clientSecret: savedClient.clientSecret
} }
} }

View File

@@ -2,7 +2,7 @@ import bcrypt from 'bcryptjs'
import User from '../model/User' import User from '../model/User'
export const createUser = async (data: any) => { 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 // Checking if user is already in the database
const usernameExist = await User.findOne({ username }) const usernameExist = await User.findOne({ username })
@@ -14,19 +14,19 @@ export const createUser = async (data: any) => {
// Create a new user // Create a new user
const user = new User({ const user = new User({
displayname, displayName,
username, username,
password: hashPassword, password: hashPassword,
isadmin, isAdmin,
isactive isActive
}) })
const savedUser = await user.save() const savedUser = await user.save()
return { return {
displayname: savedUser.displayname, displayName: savedUser.displayName,
username: savedUser.username, username: savedUser.username,
isadmin: savedUser.isadmin, isAdmin: savedUser.isAdmin,
isactive: savedUser.isactive isActive: savedUser.isActive
} }
} }

View File

@@ -1,11 +1,11 @@
import mongoose from 'mongoose' import mongoose from 'mongoose'
const clientSchema = new mongoose.Schema({ const clientSchema = new mongoose.Schema({
clientid: { clientId: {
type: String, type: String,
required: true required: true
}, },
clientsecret: { clientSecret: {
type: String, type: String,
required: true required: true
} }

View File

@@ -1,8 +1,7 @@
import { string } from 'joi' import mongoose from 'mongoose'
import mongoose, { Schema } from 'mongoose'
const userSchema = new mongoose.Schema({ const userSchema = new mongoose.Schema({
displayname: { displayName: {
type: String, type: String,
required: true required: true
}, },
@@ -14,25 +13,25 @@ const userSchema = new mongoose.Schema({
type: String, type: String,
required: true required: true
}, },
isadmin: { isAdmin: {
type: Boolean, type: Boolean,
default: false default: false
}, },
isactive: { isActive: {
type: Boolean, type: Boolean,
default: true default: true
}, },
tokens: [ tokens: [
{ {
clientid: { clientId: {
type: String, type: String,
required: true required: true
}, },
accesstoken: { accessToken: {
type: String, type: String,
required: true required: true
}, },
refreshtoken: { refreshToken: {
type: String, type: String,
required: true required: true
} }

View File

@@ -6,7 +6,8 @@ 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 { import {
authenticateToken, authenticateAccessToken,
authenticateRefreshToken,
authorizeValidation, authorizeValidation,
removeTokensInDB, removeTokensInDB,
saveTokensInDB, saveTokensInDB,
@@ -16,7 +17,6 @@ import { InfoJWT } from '../../types'
const authRouter = express.Router() const authRouter = express.Router()
const clients: { [key: string]: string } = {}
const clientIDs = new Set() const clientIDs = new Set()
const authCodes: { [key: string]: { [key: string]: string } } = {} const authCodes: { [key: string]: { [key: string]: string } } = {}
@@ -24,8 +24,7 @@ export const populateClients = async () => {
const result = await Client.find() const result = await Client.find()
clientIDs.clear() clientIDs.clear()
result.forEach((r) => { 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) => { export const saveCode = (username: string, clientId: string, code: string) => {
if (authCodes[username]) return (authCodes[username][client_id] = code) if (authCodes[username]) return (authCodes[username][clientId] = code)
authCodes[username] = { [client_id]: code } authCodes[username] = { [clientId]: code }
return authCodes[username][client_id] return authCodes[username][clientId]
} }
export const deleteCode = (username: string, client_id: string) => export const deleteCode = (username: string, clientId: string) =>
delete authCodes[username][client_id] delete authCodes[username][clientId]
authRouter.post('/authorize', async (req, res) => { authRouter.post('/authorize', async (req, res) => {
const { error, value } = authorizeValidation(req.body) const { error, value } = authorizeValidation(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 { username, password, client_id } = value const { username, password, clientId } = value
// Verify client ID // Verify client ID
if (!clientIDs.has(client_id)) { if (!clientIDs.has(clientId)) {
return res.status(403).send('Invalid client_id.') return res.status(403).send('Invalid clientId.')
} }
// Authenticate User // Authenticate User
@@ -70,13 +69,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.')
// generate authorization code against client_id // generate authorization code against clientId
const userInfo: InfoJWT = { const userInfo: InfoJWT = {
client_id, clientId,
username username
} }
const code = saveCode(username, client_id, generateAuthCode(userInfo)) const code = saveCode(username, clientId, generateAuthCode(userInfo))
res.json({ code }) res.json({ code })
}) })
@@ -85,15 +84,15 @@ 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, 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 (!userInfo) return res.sendStatus(403)
if (authCodes[userInfo.username][client_id] !== code) if (authCodes[userInfo.username][clientId] !== code)
return res.sendStatus(403) return res.sendStatus(403)
deleteCode(userInfo.username, client_id) deleteCode(userInfo.username, clientId)
const accessToken = generateAccessToken(userInfo) const accessToken = generateAccessToken(userInfo)
const refreshToken = jwt.sign( const refreshToken = jwt.sign(
@@ -101,36 +100,44 @@ authRouter.post('/token', async (req, res) => {
process.env.REFRESH_TOKEN_SECRET as string 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 }) res.json({ accessToken: accessToken, refreshToken: refreshToken })
}) })
// authRouter.post('/refresh', (req, res) => { authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => {
// const refreshToken = req.body.token const { username, clientId } = req.user
// if (refreshToken == null) return res.sendStatus(401) const userInfo = {
// if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403) username,
// jwt.verify( clientId
// 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.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 const { user } = req
await removeTokensInDB(user.username, user.client_id) await removeTokensInDB(user.username, user.clientId)
res.sendStatus(204) res.sendStatus(204)
}) })
export const generateAccessToken = (data: InfoJWT) => export const generateAccessToken = (data: InfoJWT) =>
jwt.sign(data, process.env.ACCESS_TOKEN_SECRET as string, { 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' expiresIn: '1day'
}) })
@@ -140,7 +147,7 @@ export const generateAuthCode = (data: InfoJWT) =>
}) })
const verifyAuthCode = async ( const verifyAuthCode = async (
client_id: string, clientId: string,
code: string code: string
): Promise<InfoJWT | undefined> => { ): Promise<InfoJWT | undefined> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -148,10 +155,10 @@ const verifyAuthCode = async (
if (err) return resolve(undefined) if (err) return resolve(undefined)
const clientInfo: InfoJWT = { const clientInfo: InfoJWT = {
client_id: data?.client_id, clientId: data?.clientId,
username: data?.username username: data?.username
} }
if (clientInfo.client_id === client_id) { if (clientInfo.clientId === clientId) {
return resolve(clientInfo) return resolve(clientInfo)
} }
return resolve(undefined) return resolve(undefined)

View File

@@ -11,8 +11,8 @@ clientRouter.post('/', async (req, res) => {
try { try {
const savedClient = await createClient(data) const savedClient = await createClient(data)
res.send({ res.send({
client_id: savedClient.client_id, clientId: savedClient.clientId,
client_secret: savedClient.client_secret clientSecret: savedClient.clientSecret
}) })
} catch (err: any) { } catch (err: any) {
res.status(403).send(err.toString()) res.status(403).send(err.toString())

View File

@@ -6,22 +6,22 @@ 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' import { authenticateAccessToken } from '../../utils'
dotenv.config() dotenv.config()
connectDB() connectDB()
const router = express.Router() const router = express.Router()
router.use('/drive', authenticateToken, driveRouter) router.use('/drive', authenticateAccessToken, driveRouter)
router.use('/stp', authenticateToken, stpRouter) router.use('/stp', authenticateAccessToken, stpRouter)
router.use('/user', authenticateToken, verifyAdmin, userRouter) router.use('/user', authenticateAccessToken, verifyAdmin, userRouter)
router.use('/client', authenticateToken, verifyAdmin, clientRouter) router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter)
router.use('/auth', authRouter) router.use('/auth', authRouter)
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

@@ -7,20 +7,21 @@ import { createClient } from '../../../controllers/createClient'
import { import {
generateAccessToken, generateAccessToken,
generateAuthCode, generateAuthCode,
generateRefreshToken,
populateClients, populateClients,
saveCode saveCode
} from '../auth' } from '../auth'
import { InfoJWT } from '../../../types' import { InfoJWT } from '../../../types'
import { saveTokensInDB, verifyTokenInDB } from '../../../utils' import { saveTokensInDB, verifyTokenInDB } from '../../../utils'
const client_id = 'someclientID' const clientId = 'someclientID'
const client_secret = 'someclientSecret' const clientSecret = 'someclientSecret'
const user = { const user = {
displayname: 'Test User', displayName: 'Test User',
username: 'testUsername', username: 'testUsername',
password: '87654321', password: '87654321',
isadmin: false, isAdmin: false,
isactive: true isActive: true
} }
describe('auth', () => { describe('auth', () => {
@@ -30,7 +31,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_id, client_secret }) await createClient({ clientId, clientSecret })
await populateClients() await populateClients()
}) })
@@ -55,7 +56,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: user.password, password: user.password,
client_id clientId
}) })
.expect(200) .expect(200)
@@ -67,7 +68,7 @@ describe('auth', () => {
.post('/SASjsApi/auth/authorize') .post('/SASjsApi/auth/authorize')
.send({ .send({
password: user.password, password: user.password,
client_id clientId
}) })
.expect(400) .expect(400)
@@ -80,7 +81,7 @@ describe('auth', () => {
.post('/SASjsApi/auth/authorize') .post('/SASjsApi/auth/authorize')
.send({ .send({
username: user.username, username: user.username,
client_id clientId
}) })
.expect(400) .expect(400)
@@ -88,7 +89,7 @@ describe('auth', () => {
expect(res.body).toEqual({}) 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) const res = await request(app)
.post('/SASjsApi/auth/authorize') .post('/SASjsApi/auth/authorize')
.send({ .send({
@@ -97,7 +98,7 @@ describe('auth', () => {
}) })
.expect(400) .expect(400)
expect(res.text).toEqual(`"client_id" is required`) expect(res.text).toEqual(`"clientId" is required`)
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
@@ -107,7 +108,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: user.password, password: user.password,
client_id clientId
}) })
.expect(403) .expect(403)
@@ -123,7 +124,7 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: 'WrongPassword', password: 'WrongPassword',
client_id clientId
}) })
.expect(403) .expect(403)
@@ -131,7 +132,7 @@ describe('auth', () => {
expect(res.body).toEqual({}) 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) await createUser(user)
const res = await request(app) const res = await request(app)
@@ -139,18 +140,18 @@ describe('auth', () => {
.send({ .send({
username: user.username, username: user.username,
password: user.password, password: user.password,
client_id: 'WrongClientID' clientId: 'WrongClientID'
}) })
.expect(403) .expect(403)
expect(res.text).toEqual('Invalid client_id.') expect(res.text).toEqual('Invalid clientId.')
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
}) })
describe('token', () => { describe('token', () => {
const userInfo: InfoJWT = { const userInfo: InfoJWT = {
client_id, clientId,
username: user.username username: user.username
} }
beforeAll(async () => { beforeAll(async () => {
@@ -165,14 +166,14 @@ describe('auth', () => {
it('should respond with access and refresh tokens', async () => { it('should respond with access and refresh tokens', async () => {
const code = saveCode( const code = saveCode(
userInfo.username, userInfo.username,
userInfo.client_id, userInfo.clientId,
generateAuthCode(userInfo) generateAuthCode(userInfo)
) )
const res = await request(app) const res = await request(app)
.post('/SASjsApi/auth/token') .post('/SASjsApi/auth/token')
.send({ .send({
client_id, clientId,
code code
}) })
.expect(200) .expect(200)
@@ -185,7 +186,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 clientId
}) })
.expect(400) .expect(400)
@@ -193,10 +194,10 @@ describe('auth', () => {
expect(res.body).toEqual({}) 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( const code = saveCode(
userInfo.username, userInfo.username,
userInfo.client_id, userInfo.clientId,
generateAuthCode(userInfo) generateAuthCode(userInfo)
) )
@@ -207,7 +208,7 @@ describe('auth', () => {
}) })
.expect(400) .expect(400)
expect(res.text).toEqual(`"client_id" is required`) expect(res.text).toEqual(`"clientId" is required`)
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
@@ -215,7 +216,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, clientId,
code: 'InvalidCode' code: 'InvalidCode'
}) })
.expect(403) .expect(403)
@@ -223,17 +224,17 @@ describe('auth', () => {
expect(res.body).toEqual({}) 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( const code = saveCode(
userInfo.username, userInfo.username,
userInfo.client_id, userInfo.clientId,
generateAuthCode(userInfo) 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', clientId: 'WrongClientID',
code code
}) })
.expect(403) .expect(403)
@@ -242,20 +243,59 @@ describe('auth', () => {
}) })
}) })
describe('logout', () => { describe('refresh', () => {
const accessToken = generateAccessToken({ const refreshToken = generateRefreshToken({
client_id, clientId,
username: user.username username: user.username
}) })
beforeEach(async () => { beforeEach(async () => {
await createUser(user) await createUser(user)
await saveTokensInDB( await saveTokensInDB(user.username, clientId, 'accessToken', refreshToken)
user.username, })
client_id,
accessToken, afterEach(async () => {
'refreshToken' 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 () => { afterEach(async () => {
@@ -280,7 +320,12 @@ describe('auth', () => {
expect(res.body).toEqual({}) expect(res.body).toEqual({})
expect( expect(
await verifyTokenInDB(user.username, client_id, accessToken) await verifyTokenInDB(
user.username,
clientId,
accessToken,
'accessToken'
)
).toBeUndefined() ).toBeUndefined()
}) })
}) })

View File

@@ -8,19 +8,19 @@ import { createUser } from '../../../controllers/createUser'
import { saveTokensInDB } from '../../../utils' import { saveTokensInDB } from '../../../utils'
const client = { const client = {
client_id: 'someclientID', clientId: 'someclientID',
client_secret: 'someclientSecret' clientSecret: 'someclientSecret'
} }
const adminUser = { const adminUser = {
displayname: 'Test Admin', displayName: 'Test Admin',
username: 'testAdminUsername', username: 'testAdminUsername',
password: '12345678', password: '12345678',
isadmin: true, isAdmin: true,
isactive: true isActive: true
} }
const newClient = { const newClient = {
client_id: 'newClientID', clientId: 'newClientID',
client_secret: 'newClientSecret' clientSecret: 'newClientSecret'
} }
describe('user', () => { describe('user', () => {
@@ -40,7 +40,7 @@ describe('user', () => {
describe('create', () => { describe('create', () => {
const adminAccessToken = generateAccessToken({ const adminAccessToken = generateAccessToken({
client_id: client.client_id, clientId: client.clientId,
username: adminUser.username username: adminUser.username
}) })
@@ -48,7 +48,7 @@ describe('user', () => {
await createUser(adminUser) await createUser(adminUser)
await saveTokensInDB( await saveTokensInDB(
adminUser.username, adminUser.username,
client.client_id, client.clientId,
adminAccessToken, adminAccessToken,
'refreshToken' 'refreshToken'
) )
@@ -67,8 +67,8 @@ describe('user', () => {
.send(newClient) .send(newClient)
.expect(200) .expect(200)
expect(res.body.client_id).toEqual(newClient.client_id) expect(res.body.clientId).toEqual(newClient.clientId)
expect(res.body.client_secret).toEqual(newClient.client_secret) expect(res.body.clientSecret).toEqual(newClient.clientSecret)
}) })
it('should respond with Unauthorized if access token is not present', async () => { 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 () => { it('should respond with Forbideen if access token is not of an admin account', async () => {
const user = { const user = {
displayname: 'User 1', displayName: 'User 1',
username: 'username1', username: 'username1',
password: '12345678', password: '12345678',
isadmin: false, isAdmin: false,
isactive: true isActive: true
} }
const accessToken = generateAccessToken({ const accessToken = generateAccessToken({
client_id: client.client_id, clientId: client.clientId,
username: user.username username: user.username
}) })
await createUser(user) await createUser(user)
await saveTokensInDB( await saveTokensInDB(
user.username, user.username,
client.client_id, client.clientId,
accessToken, accessToken,
'refreshToken' 'refreshToken'
) )
@@ -111,7 +111,7 @@ describe('user', () => {
expect(res.body).toEqual({}) 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) await createClient(newClient)
const res = await request(app) const res = await request(app)
@@ -124,31 +124,31 @@ describe('user', () => {
expect(res.body).toEqual({}) 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) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send({ .send({
...newClient, ...newClient,
client_id: undefined clientId: undefined
}) })
.expect(400) .expect(400)
expect(res.text).toEqual(`"client_id" is required`) expect(res.text).toEqual(`"clientId" is required`)
expect(res.body).toEqual({}) 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) const res = await request(app)
.post('/SASjsApi/client') .post('/SASjsApi/client')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send({ .send({
...newClient, ...newClient,
client_secret: undefined clientSecret: undefined
}) })
.expect(400) .expect(400)
expect(res.text).toEqual(`"client_secret" is required`) expect(res.text).toEqual(`"clientSecret" is required`)
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
}) })

View File

@@ -10,16 +10,13 @@ import { generateAccessToken } from '../auth'
import { createUser } from '../../../controllers/createUser' import { createUser } from '../../../controllers/createUser'
import { saveTokensInDB } from '../../../utils' import { saveTokensInDB } from '../../../utils'
const client = { const clientId = 'someclientID'
clientid: 'someclientID',
clientsecret: 'someclientSecret'
}
const user = { const user = {
displayname: 'Test User', displayName: 'Test User',
username: 'testUsername', username: 'testUsername',
password: '87654321', password: '87654321',
isadmin: false, isAdmin: false,
isactive: true isActive: true
} }
describe('files', () => { describe('files', () => {
@@ -38,18 +35,13 @@ describe('files', () => {
}) })
describe('deploy', () => { describe('deploy', () => {
const accessToken = generateAccessToken({ const accessToken = generateAccessToken({
client_id: client.clientid, clientId,
username: user.username username: user.username
}) })
beforeAll(async () => { beforeAll(async () => {
await createUser(user) await createUser(user)
await saveTokensInDB( await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
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)

View File

@@ -6,23 +6,20 @@ import { createUser } from '../../../controllers/createUser'
import { generateAccessToken } from '../auth' import { generateAccessToken } from '../auth'
import { saveTokensInDB } from '../../../utils' import { saveTokensInDB } from '../../../utils'
const client = { const clientId = 'someclientID'
clientid: 'someclientID',
clientsecret: 'someclientSecret'
}
const adminUser = { const adminUser = {
displayname: 'Test Admin', displayName: 'Test Admin',
username: 'testAdminUsername', username: 'testAdminUsername',
password: '12345678', password: '12345678',
isadmin: true, isAdmin: true,
isactive: true isActive: true
} }
const user = { const user = {
displayname: 'Test User', displayName: 'Test User',
username: 'testUsername', username: 'testUsername',
password: '87654321', password: '87654321',
isadmin: false, isAdmin: false,
isactive: true isActive: true
} }
describe('user', () => { describe('user', () => {
@@ -42,7 +39,7 @@ describe('user', () => {
describe('create', () => { describe('create', () => {
const adminAccessToken = generateAccessToken({ const adminAccessToken = generateAccessToken({
client_id: client.clientid, clientId,
username: adminUser.username username: adminUser.username
}) })
@@ -50,7 +47,7 @@ describe('user', () => {
await createUser(adminUser) await createUser(adminUser)
await saveTokensInDB( await saveTokensInDB(
adminUser.username, adminUser.username,
client.clientid, clientId,
adminAccessToken, adminAccessToken,
'refreshToken' 'refreshToken'
) )
@@ -70,9 +67,9 @@ describe('user', () => {
.expect(200) .expect(200)
expect(res.body.username).toEqual(user.username) expect(res.body.username).toEqual(user.username)
expect(res.body.displayname).toEqual(user.displayname) expect(res.body.displayName).toEqual(user.displayName)
expect(res.body.isadmin).toEqual(user.isadmin) expect(res.body.isAdmin).toEqual(user.isAdmin)
expect(res.body.isactive).toEqual(user.isactive) expect(res.body.isActive).toEqual(user.isActive)
}) })
it('should respond with Unauthorized if access token is not present', async () => { 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 () => { 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, clientId,
username: user.username username: user.username
}) })
await createUser(user) await createUser(user)
await saveTokensInDB( await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
user.username,
client.clientid,
accessToken,
'refreshToken'
)
const res = await request(app) const res = await request(app)
.post('/SASjsApi/user') .post('/SASjsApi/user')
@@ -149,17 +141,17 @@ describe('user', () => {
expect(res.body).toEqual({}) 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) const res = await request(app)
.post('/SASjsApi/user') .post('/SASjsApi/user')
.auth(adminAccessToken, { type: 'bearer' }) .auth(adminAccessToken, { type: 'bearer' })
.send({ .send({
...user, ...user,
displayname: undefined displayName: undefined
}) })
.expect(400) .expect(400)
expect(res.text).toEqual(`"displayname" is required`) expect(res.text).toEqual(`"displayName" is required`)
expect(res.body).toEqual({}) expect(res.body).toEqual({})
}) })
}) })

View File

@@ -11,10 +11,10 @@ userRouter.post('/', async (req, res) => {
try { try {
const savedUser = await createUser(data) const savedUser = await createUser(data)
res.send({ res.send({
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: [] tokens: []
}) })
} catch (err: any) { } catch (err: any) {

View File

@@ -1,4 +1,4 @@
export interface InfoJWT { export interface InfoJWT {
client_id: string clientId: string
username: string username: string
} }

View File

@@ -1,25 +1,54 @@
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import { verifyTokenInDB } from './verifyTokenInDB' 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 authHeader = req.headers['authorization']
const token = authHeader?.split(' ')[1] const token = authHeader?.split(' ')[1]
if (!token) return res.sendStatus(401) if (!token) return res.sendStatus(401)
jwt.verify( jwt.verify(token, key, async (err: any, data: any) => {
token, if (err) return res.sendStatus(401)
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 // verify this valid token's entry in DB
const user = await verifyTokenInDB(data?.username, data?.client_id, token) const user = await verifyTokenInDB(
data?.username,
data?.clientId,
token,
tokenType
)
if (user) { if (user) {
if (user.isActive) {
req.user = user req.user = user
return next() return next()
} } else return res.sendStatus(401)
return res.sendStatus(403)
} }
) return res.sendStatus(401)
})
} }

View File

@@ -1,10 +1,10 @@
import User from '../model/User' 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 user = await User.findOne({ username })
const tokenObjIndex = user.tokens.findIndex( const tokenObjIndex = user.tokens.findIndex(
(tokenObj: any) => tokenObj.clientid === client_id (tokenObj: any) => tokenObj.clientId === clientId
) )
if (tokenObjIndex > -1) { if (tokenObjIndex > -1) {

View File

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

View File

@@ -7,26 +7,26 @@ export const authorizeValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
username: usernameSchema, username: usernameSchema,
password: passwordSchema, password: passwordSchema,
client_id: Joi.string().required() clientId: Joi.string().required()
}).validate(data) }).validate(data)
export const tokenValidation = (data: any): Joi.ValidationResult => export const tokenValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
client_id: Joi.string().required(), clientId: Joi.string().required(),
code: Joi.string().required() code: Joi.string().required()
}).validate(data) }).validate(data)
export const registerUserValidation = (data: any): Joi.ValidationResult => export const registerUserValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
displayname: Joi.string().min(6).required(), displayName: Joi.string().min(6).required(),
username: usernameSchema, username: usernameSchema,
password: passwordSchema, password: passwordSchema,
isadmin: Joi.boolean(), isAdmin: Joi.boolean(),
isactive: Joi.boolean() isActive: Joi.boolean()
}).validate(data) }).validate(data)
export const registerClientValidation = (data: any): Joi.ValidationResult => export const registerClientValidation = (data: any): Joi.ValidationResult =>
Joi.object({ Joi.object({
client_id: Joi.string().required(), clientId: Joi.string().required(),
client_secret: Joi.string().required() clientSecret: Joi.string().required()
}).validate(data) }).validate(data)

View File

@@ -2,21 +2,22 @@ import User from '../model/User'
export const verifyTokenInDB = async ( export const verifyTokenInDB = async (
username: string, username: string,
client_id: string, clientId: string,
token: string token: string,
tokenType: 'accessToken' | 'refreshToken'
) => { ) => {
const dbUser = await User.findOne({ username }) const dbUser = await User.findOne({ username })
const currentTokenObj = dbUser.tokens.find( 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, username,
isadmin: dbUser.isadmin, isAdmin: dbUser.isAdmin,
isactive: dbUser.isactive isActive: dbUser.isActive
} }
: undefined : undefined
} }