Skip to content

Commit 66ffe45

Browse files
authored
th-23: Protected routing [BE] (#157)
* th-23: + group access strategies * th-23: + default strategies to the business controller * th-23: + a user injection strategy
1 parent f1b0cee commit 66ffe45

File tree

7 files changed

+74
-8
lines changed

7 files changed

+74
-8
lines changed

backend/src/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { type UserEntityObjectWithGroupT } from './packages/users/users.js';
33

44
declare module 'fastify' {
55
interface FastifyInstance {
6+
[AuthStrategy.INJECT_USER]: FastifyAuthFunction;
67
[AuthStrategy.VERIFY_JWT]: FastifyAuthFunction;
8+
[AuthStrategy.VERIFY_BUSINESS_GROUP]: FastifyAuthFunction;
9+
[AuthStrategy.VERIFY_DRIVER_GROUP]: FastifyAuthFunction;
710
}
811

912
interface FastifyRequest {

backend/src/libs/packages/controller/controller.package.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { type ILogger } from '~/libs/packages/logger/logger.js';
22
import { type ServerAppRouteParameters } from '~/libs/packages/server-application/server-application.js';
3+
import { type ValueOf } from '~/libs/types/types.js';
4+
import { type AuthStrategy } from '~/packages/auth/auth.js';
35

46
import { type IController } from './libs/interfaces/interface.js';
57
import {
@@ -8,24 +10,37 @@ import {
810
type ControllerRouteParameters,
911
} from './libs/types/types.js';
1012

13+
type DefaultStrategies =
14+
| ValueOf<typeof AuthStrategy>
15+
| ValueOf<typeof AuthStrategy>[]
16+
| undefined;
17+
1118
class Controller implements IController {
1219
private logger: ILogger;
1320

1421
private apiUrl: string;
1522

1623
public routes: ServerAppRouteParameters[];
1724

18-
public constructor(logger: ILogger, apiPath: string) {
25+
public defaultStrategies: DefaultStrategies;
26+
27+
public constructor(
28+
logger: ILogger,
29+
apiPath: string,
30+
strategies?: DefaultStrategies,
31+
) {
1932
this.logger = logger;
2033
this.apiUrl = apiPath;
2134
this.routes = [];
35+
this.defaultStrategies = strategies;
2236
}
2337

2438
public addRoute(options: ControllerRouteParameters): void {
2539
const { handler, path } = options;
2640
const fullPath = this.apiUrl + path;
2741

2842
this.routes.push({
43+
authStrategy: this.defaultStrategies,
2944
...options,
3045
path: fullPath,
3146
handler: (request, reply) => this.mapHandler(handler, request, reply),

backend/src/libs/packages/controller/libs/types/auth-strategy-handler.type.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { type AuthStrategy } from '~/packages/auth/auth.js';
66

77
type AuthStrategyHandler =
88
| ValueOf<typeof AuthStrategy>
9+
| ValueOf<typeof AuthStrategy>[]
910
| ((
1011
fastify: FastifyInstance,
1112
) =>
1213
| FastifyAuthFunction[]
13-
| (FastifyAuthFunction | FastifyAuthFunction[])[]);
14+
| (FastifyAuthFunction | FastifyAuthFunction[])[])
15+
| null;
1416

1517
export { type AuthStrategyHandler };

backend/src/libs/packages/server-application/server-app.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,15 @@ class ServerApp implements IServerApp {
8181
strategy?: AuthStrategyHandler,
8282
): undefined | preHandlerHookHandler {
8383
if (Array.isArray(strategy)) {
84-
return this.app.auth(strategy);
84+
const strategies = [];
85+
86+
for (const it of strategy) {
87+
if (typeof it === 'string' && it in this.app) {
88+
strategies.push(this.app[it]);
89+
}
90+
}
91+
92+
return this.app.auth(strategies, { relation: 'and' });
8593
}
8694

8795
if (typeof strategy === 'string' && strategy in this.app) {

backend/src/packages/auth/auth.app-plugin.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { type FastifyReply, type FastifyRequest } from 'fastify';
22
import fp from 'fastify-plugin';
33

44
import { HttpMessage } from '~/libs/packages/http/http.js';
5+
import { type ValueOf } from '~/libs/types/types.js';
56

67
import { AuthStrategy } from './auth.js';
8+
import { UserGroupKey } from './libs/enums/enums.js';
79
import { createUnauthorizedError } from './libs/helpers/create-unauthorized-error.helper.js';
810
import { type AuthPluginOptions } from './libs/types/auth-plugin-options.type.js';
911
import { jwtPayloadSchema } from './libs/validation-schemas/validation-schemas.js';
@@ -13,8 +15,8 @@ const authPlugin = fp<AuthPluginOptions>((fastify, options, done) => {
1315

1416
fastify.decorateRequest('user', null);
1517

16-
fastify.decorate(
17-
AuthStrategy.VERIFY_JWT,
18+
const verifyJwtStrategy =
19+
(isJwtRequired: boolean) =>
1820
async (
1921
request: FastifyRequest,
2022
_: FastifyReply,
@@ -23,8 +25,10 @@ const authPlugin = fp<AuthPluginOptions>((fastify, options, done) => {
2325
try {
2426
const token = request.headers.authorization?.replace('Bearer ', '');
2527

26-
if (!token) {
28+
if (!token && isJwtRequired) {
2729
return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED));
30+
} else if (!token) {
31+
return;
2832
}
2933

3034
const rawPayload = await jwtService.verifyToken(token);
@@ -48,7 +52,32 @@ const authPlugin = fp<AuthPluginOptions>((fastify, options, done) => {
4852
} catch (error) {
4953
return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED, error));
5054
}
51-
},
55+
};
56+
57+
const verifyGroup =
58+
(group: ValueOf<typeof UserGroupKey>) =>
59+
async (
60+
request: FastifyRequest,
61+
_: FastifyReply,
62+
done: (error?: Error) => void,
63+
): Promise<void> => {
64+
if (request.user.group.key !== group) {
65+
return done(createUnauthorizedError(HttpMessage.UNAUTHORIZED));
66+
}
67+
};
68+
69+
fastify.decorate(AuthStrategy.INJECT_USER, verifyJwtStrategy(false));
70+
71+
fastify.decorate(AuthStrategy.VERIFY_JWT, verifyJwtStrategy(true));
72+
73+
fastify.decorate(
74+
AuthStrategy.VERIFY_BUSINESS_GROUP,
75+
verifyGroup(UserGroupKey.BUSINESS),
76+
);
77+
78+
fastify.decorate(
79+
AuthStrategy.VERIFY_DRIVER_GROUP,
80+
verifyGroup(UserGroupKey.DRIVER),
5281
);
5382

5483
done();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
const AuthStrategy = {
2+
INJECT_USER: 'injectUser',
23
VERIFY_JWT: 'verifyJWT',
4+
VERIFY_BUSINESS_GROUP: 'verifyBusinessGroup',
5+
VERIFY_DRIVER_GROUP: 'verifyDriverGroup',
36
} as const;
47

58
export { AuthStrategy };

backend/src/packages/business/business.controller.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from '~/libs/packages/controller/controller.js';
77
import { HttpCode } from '~/libs/packages/http/http.js';
88
import { type ILogger } from '~/libs/packages/logger/logger.js';
9+
import { AuthStrategy } from '~/packages/auth/libs/enums/enums.js';
910

1011
import { type BusinessService } from './business.service.js';
1112
import { BusinessApiPath } from './libs/enums/enums.js';
@@ -139,7 +140,12 @@ class BusinessController extends Controller {
139140
private businessService: BusinessService;
140141

141142
public constructor(logger: ILogger, businessService: BusinessService) {
142-
super(logger, ApiPath.BUSINESS);
143+
const defaultStrategies = [
144+
AuthStrategy.VERIFY_JWT,
145+
AuthStrategy.VERIFY_BUSINESS_GROUP,
146+
];
147+
148+
super(logger, ApiPath.BUSINESS, defaultStrategies);
143149

144150
this.businessService = businessService;
145151

0 commit comments

Comments
 (0)