1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-11 08:20:04 +00:00

chore: auth docs also added

This commit is contained in:
Saad Jutt
2021-11-05 15:57:36 +05:00
parent 52c3823f20
commit dbbdaa83f0
9 changed files with 289 additions and 124 deletions

View File

@@ -1,10 +1,9 @@
import express from 'express'
import bcrypt from 'bcryptjs'
import mongoose from 'mongoose'
import jwt from 'jsonwebtoken'
import AuthController from '../../controllers/auth'
import Client from '../../model/Client'
import User from '../../model/User'
import {
authenticateAccessToken,
@@ -22,7 +21,6 @@ import { InfoJWT } from '../../types'
const authRouter = express.Router()
const clientIDs = new Set()
const authCodes: { [key: string]: { [key: string]: string } } = {}
export const populateClients = async () => {
const result = await Client.find()
@@ -46,127 +44,63 @@ export const connectDB = () => {
}
}
export const saveCode = (userId: number, clientId: string, code: string) => {
if (authCodes[userId]) return (authCodes[userId][clientId] = code)
authCodes[userId] = { [clientId]: code }
return authCodes[userId][clientId]
}
export const deleteCode = (userId: number, clientId: string) =>
delete authCodes[userId][clientId]
authRouter.post('/authorize', async (req, res) => {
const { error, value } = authorizeValidation(req.body)
const { error, value: body } = authorizeValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
const { username, password, clientId } = value
const { clientId } = body
// Verify client ID
if (!clientIDs.has(clientId)) {
return res.status(403).send('Invalid clientId.')
}
// Authenticate User
const user = await User.findOne({ username })
if (!user) return res.status(403).send('Username is not found.')
const controller = new AuthController()
try {
const response = await controller.authorize(body)
const validPass = await bcrypt.compare(password, user.password)
if (!validPass) return res.status(403).send('Invalid password.')
// generate authorization code against clientId
const userInfo: InfoJWT = {
clientId,
userId: user.id
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
const code = saveCode(user.id, clientId, generateAuthCode(userInfo))
res.json({ code })
})
authRouter.post('/token', async (req, res) => {
const { error, value } = tokenValidation(req.body)
const { error, value: body } = tokenValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
const { clientId, code } = value
const controller = new AuthController()
try {
const response = await controller.token(body)
const userInfo = await verifyAuthCode(clientId, code)
if (!userInfo) return res.sendStatus(403)
if (authCodes[userInfo.userId][clientId] !== code) return res.sendStatus(403)
deleteCode(userInfo.userId, clientId)
const accessToken = generateAccessToken(userInfo)
const refreshToken = jwt.sign(
userInfo,
process.env.REFRESH_TOKEN_SECRET as string
)
await saveTokensInDB(userInfo.userId, clientId, accessToken, refreshToken)
res.json({ accessToken: accessToken, refreshToken: refreshToken })
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => {
const { userId, clientId } = req.user
const userInfo = {
userId,
clientId
const userInfo: InfoJWT = req.user
const controller = new AuthController()
try {
const response = await controller.refresh(userInfo)
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
const accessToken = generateAccessToken(userInfo)
const refreshToken = jwt.sign(
userInfo,
process.env.REFRESH_TOKEN_SECRET as string
)
await saveTokensInDB(userInfo.userId, clientId, accessToken, refreshToken)
res.json({ accessToken: accessToken, refreshToken: refreshToken })
})
authRouter.delete('/logout', authenticateAccessToken, async (req: any, res) => {
const { user } = req
const userInfo: InfoJWT = req.user
await removeTokensInDB(user.username, user.clientId)
const controller = new AuthController()
try {
await controller.logout(userInfo)
} catch (e) {}
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'
})
export const generateAuthCode = (data: InfoJWT) =>
jwt.sign(data, process.env.AUTH_CODE_SECRET as string, {
expiresIn: '30s'
})
const verifyAuthCode = async (
clientId: string,
code: string
): Promise<InfoJWT | undefined> => {
return new Promise((resolve, reject) => {
jwt.verify(code, process.env.AUTH_CODE_SECRET as string, (err, data) => {
if (err) return resolve(undefined)
const clientInfo: InfoJWT = {
clientId: data?.clientId,
userId: data?.userId
}
if (clientInfo.clientId === clientId) {
return resolve(clientInfo)
}
return resolve(undefined)
})
})
}
export default authRouter

View File

@@ -4,13 +4,12 @@ import request from 'supertest'
import app from '../../../app'
import UserController from '../../../controllers/user'
import ClientController from '../../../controllers/client'
import {
import AuthController, {
generateAccessToken,
generateAuthCode,
generateRefreshToken,
populateClients,
saveCode
} from '../auth'
generateRefreshToken
} from '../../../controllers/auth'
import { populateClients } from '../auth'
import { InfoJWT } from '../../../types'
import { saveTokensInDB, verifyTokenInDB } from '../../../utils'
@@ -115,7 +114,7 @@ describe('auth', () => {
})
.expect(403)
expect(res.text).toEqual('Username is not found.')
expect(res.text).toEqual('Error: Username is not found.')
expect(res.body).toEqual({})
})
@@ -131,7 +130,7 @@ describe('auth', () => {
})
.expect(403)
expect(res.text).toEqual('Invalid password.')
expect(res.text).toEqual('Error: Invalid password.')
expect(res.body).toEqual({})
})
@@ -167,7 +166,7 @@ describe('auth', () => {
})
it('should respond with access and refresh tokens', async () => {
const code = saveCode(
const code = AuthController.saveCode(
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)
@@ -198,7 +197,7 @@ describe('auth', () => {
})
it('should respond with Bad Request if clientId is missing', async () => {
const code = saveCode(
const code = AuthController.saveCode(
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)
@@ -228,7 +227,7 @@ describe('auth', () => {
})
it('should respond with Forbidden if clientId is invalid', async () => {
const code = saveCode(
const code = AuthController.saveCode(
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)

View File

@@ -4,7 +4,7 @@ import request from 'supertest'
import app from '../../../app'
import UserController from '../../../controllers/user'
import ClientController from '../../../controllers/client'
import { generateAccessToken } from '../auth'
import { generateAccessToken } from '../../../controllers/auth'
import { saveTokensInDB } from '../../../utils'
const client = {

View File

@@ -7,7 +7,7 @@ import UserController from '../../../controllers/user'
import { getTmpFilesFolderPath } from '../../../utils/file'
import { folderExists, fileExists, readFile, deleteFolder } from '@sasjs/utils'
import path from 'path'
import { generateAccessToken } from '../auth'
import { generateAccessToken } from '../../../controllers/auth'
import { saveTokensInDB } from '../../../utils'
const clientId = 'someclientID'

View File

@@ -3,7 +3,7 @@ import { MongoMemoryServer } from 'mongodb-memory-server'
import request from 'supertest'
import app from '../../../app'
import UserController from '../../../controllers/user'
import { generateAccessToken } from '../auth'
import { generateAccessToken } from '../../../controllers/auth'
import { saveTokensInDB } from '../../../utils'
const clientId = 'someclientID'