From 3569e689a92d80e9aff51b9d0f7349eb88dd4622 Mon Sep 17 00:00:00 2001 From: "Jyotirmoy Bandyopadhyaya [Bravo68]" Date: Fri, 21 Apr 2023 20:40:23 +0530 Subject: [PATCH] Added Auth Services --- packages/api/auth/{verify.ts => callback.ts} | 4 +-- packages/api/auth/index.ts | 6 ++++ packages/api/auth/introspect.ts | 32 ++++++++++++++++++++ packages/api/auth/middleware.ts | 15 ++++++--- packages/api/auth/refresh.ts | 31 +++++++++++++++++++ packages/api/auth/revoke.ts | 27 +++++++++++++++++ packages/api/auth/{check.ts => signin.ts} | 4 +-- packages/api/controllers/auth.controller.ts | 31 +++++++++++++++---- packages/api/routes/auth.routes.ts | 8 ++++- 9 files changed, 143 insertions(+), 15 deletions(-) rename packages/api/auth/{verify.ts => callback.ts} (67%) create mode 100644 packages/api/auth/index.ts create mode 100644 packages/api/auth/introspect.ts create mode 100644 packages/api/auth/refresh.ts create mode 100644 packages/api/auth/revoke.ts rename packages/api/auth/{check.ts => signin.ts} (87%) diff --git a/packages/api/auth/verify.ts b/packages/api/auth/callback.ts similarity index 67% rename from packages/api/auth/verify.ts rename to packages/api/auth/callback.ts index fe09cbf..ab7faac 100644 --- a/packages/api/auth/verify.ts +++ b/packages/api/auth/callback.ts @@ -1,7 +1,7 @@ import { authClient } from '../helpers/auth_client' -import { code_verifier } from './check' +import { code_verifier } from './signin' -export default async (session_state: string, code: string) => { +export const callback = async (session_state: string, code: string) => { return authClient.callback( 'http://localhost:4038/auth/signin/callback', { code_verifier, code, session_state, expires_in: '1d' }, diff --git a/packages/api/auth/index.ts b/packages/api/auth/index.ts new file mode 100644 index 0000000..c4ec5be --- /dev/null +++ b/packages/api/auth/index.ts @@ -0,0 +1,6 @@ +export * from './callback' +export * from './refresh' +export * from './revoke' +export * from './signin' +export * from './introspect' +export * from './middleware' diff --git a/packages/api/auth/introspect.ts b/packages/api/auth/introspect.ts new file mode 100644 index 0000000..55d9984 --- /dev/null +++ b/packages/api/auth/introspect.ts @@ -0,0 +1,32 @@ +import { NextFunction, Response } from 'express' +import { authClient } from '../helpers/auth_client' +import { ModRequest } from '../types' +import { CustomError } from '../libs/error' + +export const introspect = async ( + req: ModRequest | any, + res: Response, + next: NextFunction +) => { + try { + const authHeader = req.headers?.authorization?.split(' ') + if (!authHeader) { + throw new Error('No authorization header') + } + const token: string = authHeader[1] + const decoded = await authClient.introspect(token) + + if (!decoded) { + throw new Error('No user') + } + + return decoded + } catch (err) { + next( + new CustomError({ + message: 'Invalid token', + statusCode: 401, + }) + ) + } +} diff --git a/packages/api/auth/middleware.ts b/packages/api/auth/middleware.ts index 749e8e3..814355c 100644 --- a/packages/api/auth/middleware.ts +++ b/packages/api/auth/middleware.ts @@ -3,7 +3,7 @@ import { authClient } from '../helpers/auth_client' import { ModRequest } from '../types' import { CustomError } from '../libs/error' -const middleware = async ( +export const middleware = async ( req: ModRequest | any, res: Response, next: NextFunction @@ -14,6 +14,7 @@ const middleware = async ( throw new Error('No authorization header') } const token: string = authHeader[1] + const decoded = await authClient.introspect(token) const user = await authClient.userinfo(token, { method: 'GET', tokenType: 'Bearer', @@ -22,7 +23,15 @@ const middleware = async ( }, via: 'header', }) - req.user = user + + if (!user) { + throw new Error('No user') + } + + req.user = { + userData: user, + tokenData: decoded, + } next() } catch (err) { next( @@ -33,5 +42,3 @@ const middleware = async ( ) } } - -export default middleware diff --git a/packages/api/auth/refresh.ts b/packages/api/auth/refresh.ts new file mode 100644 index 0000000..21e234c --- /dev/null +++ b/packages/api/auth/refresh.ts @@ -0,0 +1,31 @@ +import { NextFunction, Response } from 'express' +import { authClient } from '../helpers/auth_client' +import { ModRequest } from '../types' +import { CustomError } from '../libs/error' + +export const refresh = async ( + req: ModRequest | any, + res: Response, + next: NextFunction +) => { + try { + const refreshToken: string = req.body?.refresh_token + if (!refreshToken) { + throw new Error('No refresh token provided !!') + } + const tokenData = await authClient.refresh(refreshToken) + + if (!tokenData) { + throw new Error('No user') + } + + return tokenData + } catch (err) { + next( + new CustomError({ + message: 'Invalid refresh token', + statusCode: 401, + }) + ) + } +} diff --git a/packages/api/auth/revoke.ts b/packages/api/auth/revoke.ts new file mode 100644 index 0000000..0574d19 --- /dev/null +++ b/packages/api/auth/revoke.ts @@ -0,0 +1,27 @@ +import { NextFunction, Response } from 'express' +import { authClient } from '../helpers/auth_client' +import { ModRequest } from '../types' +import { CustomError } from '../libs/error' + +export const revoke = async ( + req: ModRequest | any, + res: Response, + next: NextFunction +) => { + try { + const authHeader = req.headers?.authorization?.split(' ') + if (!authHeader) { + throw new Error('No authorization header') + } + const token: string = authHeader[1] + await authClient.revoke(token) + return 'Token revoked! Logged out!' + } catch (err) { + next( + new CustomError({ + message: 'Invalid token', + statusCode: 401, + }) + ) + } +} diff --git a/packages/api/auth/check.ts b/packages/api/auth/signin.ts similarity index 87% rename from packages/api/auth/check.ts rename to packages/api/auth/signin.ts index 270f0f6..6d3d0fe 100644 --- a/packages/api/auth/check.ts +++ b/packages/api/auth/signin.ts @@ -5,9 +5,9 @@ import { configKeys } from '..' const code_verifier = generators.codeVerifier() const code_challenge = generators.codeChallenge(code_verifier) -export default () => { +export const signon = () => { const authurl = authClient.authorizationUrl({ - scope: 'email profile openid', + scope: 'email profile openid roles', code_challenge, code_challenge_method: 'S256', client_id: configKeys.KEYCLOAK_CLIENT_ID, diff --git a/packages/api/controllers/auth.controller.ts b/packages/api/controllers/auth.controller.ts index bfc6c4a..f6dac0e 100644 --- a/packages/api/controllers/auth.controller.ts +++ b/packages/api/controllers/auth.controller.ts @@ -1,12 +1,11 @@ -import { Request, Response } from 'express' +import { NextFunction, Request, Response } from 'express' import { makeResponse } from '../libs' -import check from '../auth/check' -import verify from '../auth/verify' import { ModRequest } from '../types' +import { callback, introspect, refresh, signon, revoke } from '../auth/index' export default class AuthController { public signin = (req: Request, res: Response) => { - const { authurl } = check() + const { authurl } = signon() res.redirect(authurl) } @@ -15,10 +14,30 @@ export default class AuthController { session_state: string code: string } - res.send(makeResponse(await verify(session_state, code))) + res.send(makeResponse(await callback(session_state, code))) } public me = (req: ModRequest, res: Response) => { - res.send(makeResponse(req.user)) + res.send(makeResponse(req.user.userData)) + } + + public logout = async (req: Request, res: Response, next: NextFunction) => { + res.send(makeResponse(await revoke(req, res, next))) + } + + public refresh = async ( + req: Request, + res: Response, + next: NextFunction + ) => { + res.send(makeResponse(await refresh(req, res, next))) + } + + public introspect = async ( + req: Request, + res: Response, + next: NextFunction + ) => { + res.send(makeResponse(await introspect(req, res, next))) } } diff --git a/packages/api/routes/auth.routes.ts b/packages/api/routes/auth.routes.ts index ffab378..da14894 100644 --- a/packages/api/routes/auth.routes.ts +++ b/packages/api/routes/auth.routes.ts @@ -1,6 +1,6 @@ import { Router } from 'express' import AuthController from '../controllers/auth.controller' -import middleware from '../auth/middleware' +import { middleware } from '../auth' const router = Router() const authController = new AuthController() @@ -11,6 +11,12 @@ router.get('/signin/callback', authController.callback) router.get('/me', middleware, authController.me as any) +router.get('/logout', middleware, authController.logout as any) + +router.get('/refresh', middleware, authController.refresh as any) + +router.get('/introspect', middleware, authController.introspect as any) + router.get('/', function (req, res) { res.render('pages/auth') })