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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user