diff --git a/backend/src/index.d.ts b/backend/src/index.d.ts index edaf4eb3a..e52e23f43 100644 --- a/backend/src/index.d.ts +++ b/backend/src/index.d.ts @@ -3,7 +3,10 @@ import { type UserEntityObjectWithGroupT } from './packages/users/users.js'; declare module 'fastify' { interface FastifyInstance { + [AuthStrategy.INJECT_USER]: FastifyAuthFunction; [AuthStrategy.VERIFY_JWT]: FastifyAuthFunction; + [AuthStrategy.VERIFY_BUSINESS_GROUP]: FastifyAuthFunction; + [AuthStrategy.VERIFY_DRIVER_GROUP]: FastifyAuthFunction; } interface FastifyRequest { diff --git a/backend/src/libs/packages/controller/controller.package.ts b/backend/src/libs/packages/controller/controller.package.ts index 843097c4f..39e4f4465 100644 --- a/backend/src/libs/packages/controller/controller.package.ts +++ b/backend/src/libs/packages/controller/controller.package.ts @@ -1,5 +1,7 @@ import { type ILogger } from '~/libs/packages/logger/logger.js'; import { type ServerAppRouteParameters } from '~/libs/packages/server-application/server-application.js'; +import { type ValueOf } from '~/libs/types/types.js'; +import { type AuthStrategy } from '~/packages/auth/auth.js'; import { type IController } from './libs/interfaces/interface.js'; import { @@ -8,6 +10,11 @@ import { type ControllerRouteParameters, } from './libs/types/types.js'; +type DefaultStrategies = + | ValueOf + | ValueOf[] + | undefined; + class Controller implements IController { private logger: ILogger; @@ -15,10 +22,17 @@ class Controller implements IController { public routes: ServerAppRouteParameters[]; - public constructor(logger: ILogger, apiPath: string) { + public defaultStrategies: DefaultStrategies; + + public constructor( + logger: ILogger, + apiPath: string, + strategies?: DefaultStrategies, + ) { this.logger = logger; this.apiUrl = apiPath; this.routes = []; + this.defaultStrategies = strategies; } public addRoute(options: ControllerRouteParameters): void { @@ -26,6 +40,7 @@ class Controller implements IController { const fullPath = this.apiUrl + path; this.routes.push({ + authStrategy: this.defaultStrategies, ...options, path: fullPath, handler: (request, reply) => this.mapHandler(handler, request, reply), diff --git a/backend/src/libs/packages/controller/libs/types/auth-strategy-handler.type.ts b/backend/src/libs/packages/controller/libs/types/auth-strategy-handler.type.ts index ca9632677..18449f47b 100644 --- a/backend/src/libs/packages/controller/libs/types/auth-strategy-handler.type.ts +++ b/backend/src/libs/packages/controller/libs/types/auth-strategy-handler.type.ts @@ -6,10 +6,12 @@ import { type AuthStrategy } from '~/packages/auth/auth.js'; type AuthStrategyHandler = | ValueOf + | ValueOf[] | (( fastify: FastifyInstance, ) => | FastifyAuthFunction[] - | (FastifyAuthFunction | FastifyAuthFunction[])[]); + | (FastifyAuthFunction | FastifyAuthFunction[])[]) + | null; export { type AuthStrategyHandler }; diff --git a/backend/src/libs/packages/server-application/server-app.ts b/backend/src/libs/packages/server-application/server-app.ts index fe0086cd1..482c4c68f 100644 --- a/backend/src/libs/packages/server-application/server-app.ts +++ b/backend/src/libs/packages/server-application/server-app.ts @@ -81,7 +81,15 @@ class ServerApp implements IServerApp { strategy?: AuthStrategyHandler, ): undefined | preHandlerHookHandler { if (Array.isArray(strategy)) { - return this.app.auth(strategy); + const strategies = []; + + for (const it of strategy) { + if (typeof it === 'string' && it in this.app) { + strategies.push(this.app[it]); + } + } + + return this.app.auth(strategies, { relation: 'and' }); } if (typeof strategy === 'string' && strategy in this.app) { diff --git a/backend/src/packages/auth/auth.app-plugin.ts b/backend/src/packages/auth/auth.app-plugin.ts index c0a3be701..079bf8a4d 100644 --- a/backend/src/packages/auth/auth.app-plugin.ts +++ b/backend/src/packages/auth/auth.app-plugin.ts @@ -2,8 +2,10 @@ import { type FastifyReply, type FastifyRequest } from 'fastify'; import fp from 'fastify-plugin'; import { HttpMessage } from '~/libs/packages/http/http.js'; +import { type ValueOf } from '~/libs/types/types.js'; import { AuthStrategy } from './auth.js'; +import { UserGroupKey } from './libs/enums/enums.js'; import { createUnauthorizedError } from './libs/helpers/create-unauthorized-error.helper.js'; import { type AuthPluginOptions } from './libs/types/auth-plugin-options.type.js'; import { jwtPayloadSchema } from './libs/validation-schemas/validation-schemas.js'; @@ -13,8 +15,8 @@ const authPlugin = fp((fastify, options, done) => { fastify.decorateRequest('user', null); - fastify.decorate( - AuthStrategy.VERIFY_JWT, + const verifyJwtStrategy = + (isJwtRequired: boolean) => async ( request: FastifyRequest, _: FastifyReply, @@ -23,8 +25,10 @@ const authPlugin = fp((fastify, options, done) => { try { const token = request.headers.authorization?.replace('Bearer ', ''); - if (!token) { + if (!token && isJwtRequired) { return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED)); + } else if (!token) { + return; } const rawPayload = await jwtService.verifyToken(token); @@ -48,7 +52,32 @@ const authPlugin = fp((fastify, options, done) => { } catch (error) { return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED, error)); } - }, + }; + + const verifyGroup = + (group: ValueOf) => + async ( + request: FastifyRequest, + _: FastifyReply, + done: (error?: Error) => void, + ): Promise => { + if (request.user.group.key !== group) { + return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED)); + } + }; + + fastify.decorate(AuthStrategy.INJECT_USER, verifyJwtStrategy(false)); + + fastify.decorate(AuthStrategy.VERIFY_JWT, verifyJwtStrategy(true)); + + fastify.decorate( + AuthStrategy.VERIFY_BUSINESS_GROUP, + verifyGroup(UserGroupKey.BUSINESS), + ); + + fastify.decorate( + AuthStrategy.VERIFY_DRIVER_GROUP, + verifyGroup(UserGroupKey.DRIVER), ); done(); diff --git a/backend/src/packages/auth/libs/enums/auth-strategy.enum.ts b/backend/src/packages/auth/libs/enums/auth-strategy.enum.ts index ac6e197be..f8a5301f3 100644 --- a/backend/src/packages/auth/libs/enums/auth-strategy.enum.ts +++ b/backend/src/packages/auth/libs/enums/auth-strategy.enum.ts @@ -1,5 +1,8 @@ const AuthStrategy = { + INJECT_USER: 'injectUser', VERIFY_JWT: 'verifyJWT', + VERIFY_BUSINESS_GROUP: 'verifyBusinessGroup', + VERIFY_DRIVER_GROUP: 'verifyDriverGroup', } as const; export { AuthStrategy }; diff --git a/backend/src/packages/business/business.controller.ts b/backend/src/packages/business/business.controller.ts index 49c621775..1a71c4e29 100644 --- a/backend/src/packages/business/business.controller.ts +++ b/backend/src/packages/business/business.controller.ts @@ -6,6 +6,7 @@ import { } from '~/libs/packages/controller/controller.js'; import { HttpCode } from '~/libs/packages/http/http.js'; import { type ILogger } from '~/libs/packages/logger/logger.js'; +import { AuthStrategy } from '~/packages/auth/libs/enums/enums.js'; import { type BusinessService } from './business.service.js'; import { BusinessApiPath } from './libs/enums/enums.js'; @@ -139,7 +140,12 @@ class BusinessController extends Controller { private businessService: BusinessService; public constructor(logger: ILogger, businessService: BusinessService) { - super(logger, ApiPath.BUSINESS); + const defaultStrategies = [ + AuthStrategy.VERIFY_JWT, + AuthStrategy.VERIFY_BUSINESS_GROUP, + ]; + + super(logger, ApiPath.BUSINESS, defaultStrategies); this.businessService = businessService;