Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OV-8: protect routing #33

Merged
merged 30 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8ce8243
OV-8: + empty auth jwt plugin
Sanchousina Aug 20, 2024
44b2532
OV-8: + register plugins
Sanchousina Aug 20, 2024
da1397e
OV-8: + dependencies
Sanchousina Aug 20, 2024
5af5016
OV-8: * merging with OV-5
Sanchousina Aug 20, 2024
9721153
OV-8: + findById method to Repository type
Sanchousina Aug 20, 2024
89ec60b
OV-8: + findById method to Service type
Sanchousina Aug 20, 2024
b707dc3
OV-8: + findById method to user service and repository
Sanchousina Aug 20, 2024
5e46444
OV-8: * userId to number
Sanchousina Aug 20, 2024
50245d6
OV-8: + http codes to http code enum
Sanchousina Aug 20, 2024
ee3a4e5
OV-8: + hook and error messages enums for auth plugin
Sanchousina Aug 20, 2024
508cd31
Merge remote-tracking branch 'origin/task/OV-5-JWT-token' into task/O…
Sanchousina Aug 20, 2024
8eb1aae
OV-8: * export user entity
Sanchousina Aug 20, 2024
3561451
OV-8: * implement auth jwt plugin
Sanchousina Aug 20, 2024
f034695
OV-8: + white routes constant
Sanchousina Aug 20, 2024
36a220e
OV-8: * refactor checking for white route
Sanchousina Aug 20, 2024
9e4004b
OV-8: * use white routes constant
Sanchousina Aug 20, 2024
502aad9
OV-8: * extract fastify module augmentation into file
Sanchousina Aug 20, 2024
a9e0c53
Merge remote-tracking branch 'origin/next' into task/OV-8-protect-rou…
Sanchousina Aug 20, 2024
218665a
OV-8: * modify find user method instead of adding findById
Sanchousina Aug 21, 2024
2f9f951
OV-8: * move dependencies to backend
Sanchousina Aug 21, 2024
9f17f91
OV-8: * white routes constant to include path method
Sanchousina Aug 21, 2024
3aaf539
OV-8: + route type
Sanchousina Aug 21, 2024
dbfec2d
OV-8: * checking if route is in white list with method
Sanchousina Aug 21, 2024
5a24c65
Merge remote-tracking branch 'origin/task/OV-5-JWT-token' into task/O…
Sanchousina Aug 21, 2024
b4fbbde
OV-8: + util function for checking route in white routes
Sanchousina Aug 21, 2024
a88eca1
OV-8: * modify find method in user repository instead of findById
Sanchousina Aug 21, 2024
1ac2392
OV-8: * use id instead of userId in types for repository and service
Sanchousina Aug 21, 2024
0354932
OV-8: - remove commented code
Sanchousina Aug 23, 2024
e9b8425
OV-8: * rename find to findById in service and repository
Sanchousina Aug 27, 2024
9e20e1c
Merge remote-tracking branch origin/task/OV-5-JWT-token into task/OV-…
Sanchousina Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
"convict": "6.2.4",
"dotenv": "16.4.5",
"fastify": "4.28.1",
"fastify-plugin": "4.5.1",
"jose": "5.7.0",
"knex": "3.1.0",
"objection": "3.1.4",
"pg": "8.12.0",
"pino": "9.3.2",
"pino-pretty": "10.3.1",
"shared": "*",
"swagger-jsdoc": "6.2.8",
"jose": "5.7.0"
"swagger-jsdoc": "6.2.8"
}
}
6 changes: 4 additions & 2 deletions backend/src/bundles/users/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ class UserRepository implements Repository {
this.userModel = userModel;
}

public find(): ReturnType<Repository['find']> {
return Promise.resolve(null);
public async find(userId: number): Promise<UserEntity | null> {
const user = await this.userModel.query().findById(userId).execute();

return user ? UserEntity.initialize(user) : null;
}

public async findByEmail(email: string): Promise<UserEntity | null> {
Expand Down
4 changes: 2 additions & 2 deletions backend/src/bundles/users/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class UserService implements Service {
this.userRepository = userRepository;
}

public find(): ReturnType<Service['find']> {
return Promise.resolve(null);
public async find(userId: number): Promise<UserEntity | null> {
return await this.userRepository.find(userId);
}

public async findByEmail(email: string): Promise<UserEntity | null> {
Expand Down
1 change: 1 addition & 0 deletions backend/src/bundles/users/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
type UserSignUpRequestDto,
type UserSignUpResponseDto,
} from './types/types.js';
export { type UserEntity } from './user.entity.js';
export { UserModel } from './user.model.js';
export {
userSignInValidationSchema,
Expand Down
1 change: 1 addition & 0 deletions backend/src/common/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { USER_PASSWORD_SALT_ROUNDS } from './user.constants.js';
export { WHITE_ROUTES } from './white-routes.constants.js';
14 changes: 14 additions & 0 deletions backend/src/common/constants/white-routes.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiPath, AuthApiPath } from 'shared';

const WHITE_ROUTES = [
{
path: `/api/v1${ApiPath.AUTH}${AuthApiPath.SIGN_IN}`,
method: 'POST',
},
{
path: `/api/v1${ApiPath.AUTH}${AuthApiPath.SIGN_UP}`,
method: 'POST',
},
];

export { WHITE_ROUTES };
58 changes: 58 additions & 0 deletions backend/src/common/plugins/auth/auth-jwt.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import fp from 'fastify-plugin';
import { HttpCode, HttpError, HttpHeader } from 'shared';

import { userService } from '~/bundles/users/users.js';
import { tokenService } from '~/common/services/services.js';

import { ErrorMessage, Hook } from './enums/enums.js';
import { type Route } from './types/types.js';
import { isRouteInWhiteList } from './utils/utils.js';

type Options = {
routesWhiteList: Route[];
};

const authenticateJWT = fp<Options>((fastify, { routesWhiteList }, done) => {
fastify.decorateRequest('user', null);

fastify.addHook(Hook.PRE_HANDLER, async (request) => {
if (isRouteInWhiteList(routesWhiteList, request)) {
return;
}

const authHeader = request.headers[HttpHeader.AUTHORIZATION];

if (!authHeader) {
throw new HttpError({
message: ErrorMessage.MISSING_TOKEN,
status: HttpCode.UNAUTHORIZED,
});
}

const [, token] = authHeader.split(' ');

const userId = await tokenService.getUserIdFromToken(token as string);

if (!userId) {
throw new HttpError({
message: ErrorMessage.INVALID_TOKEN,
status: HttpCode.UNAUTHORIZED,
});
}

const user = await userService.find(userId);

if (!user) {
throw new HttpError({
message: ErrorMessage.MISSING_USER,
status: HttpCode.BAD_REQUEST,
});
}

request.user = user;
});

done();
});

export { authenticateJWT };
2 changes: 2 additions & 0 deletions backend/src/common/plugins/auth/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ErrorMessage } from './error-message.enum.js';
export { Hook } from './hook.enum.js';
7 changes: 7 additions & 0 deletions backend/src/common/plugins/auth/enums/error-message.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ErrorMessage = {
MISSING_TOKEN: 'You are not logged in',
INVALID_TOKEN: 'Token is no longer valid. Please log in again.',
MISSING_USER: 'User with this id does not exist.',
} as const;

export { ErrorMessage };
5 changes: 5 additions & 0 deletions backend/src/common/plugins/auth/enums/hook.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Hook = {
PRE_HANDLER: 'preHandler',
} as const;

export { Hook };
6 changes: 6 additions & 0 deletions backend/src/common/plugins/auth/types/route.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type Route = {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
};

export { type Route };
1 change: 1 addition & 0 deletions backend/src/common/plugins/auth/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { type Route } from './route.type.js';
15 changes: 15 additions & 0 deletions backend/src/common/plugins/auth/utils/check-white-routes.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type FastifyRequest } from 'fastify';

import { type Route } from '../types/types.js';

const isRouteInWhiteList = (
routesWhiteList: Route[],
request: FastifyRequest,
): boolean => {
return routesWhiteList.some(
(route) =>
route.path === request.url && route.method === request.method,
);
};

export { isRouteInWhiteList };
1 change: 1 addition & 0 deletions backend/src/common/plugins/auth/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { isRouteInWhiteList } from './check-white-routes.util.js';
1 change: 1 addition & 0 deletions backend/src/common/plugins/plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { authenticateJWT } from './auth/auth-jwt.plugin.js';
10 changes: 10 additions & 0 deletions backend/src/common/server-application/base-server-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
type ValidationSchema,
} from '~/common/types/types.js';

import { WHITE_ROUTES } from '../constants/constants.js';
import { authenticateJWT } from '../plugins/plugins.js';
import {
type ServerApp,
type ServerAppApi,
Expand Down Expand Up @@ -122,6 +124,12 @@ class BaseServerApp implements ServerApp {
);
}

private registerPlugins(): void {
this.app.register(authenticateJWT, {
routesWhiteList: WHITE_ROUTES,
});
}

private initValidationCompiler(): void {
this.app.setValidatorCompiler(
({ schema }: { schema: ValidationSchema }) => {
Expand Down Expand Up @@ -200,6 +208,8 @@ class BaseServerApp implements ServerApp {

await this.initMiddlewares();

this.registerPlugins();

this.initValidationCompiler();

this.initErrorHandler();
Expand Down
9 changes: 9 additions & 0 deletions backend/src/common/types/fastify.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'fastify';

import { type UserEntity } from '~/bundles/users/users.js';

declare module 'fastify' {
interface FastifyRequest {
user: UserEntity;
}
}
2 changes: 1 addition & 1 deletion backend/src/common/types/repository.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type Repository<T = unknown> = {
find(): Promise<T>;
find(id: number): Promise<T | null>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should accept any params here, or we should rename it to findById

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it to findById

findAll(): Promise<T[]>;
create(payload: unknown): Promise<T>;
update(): Promise<T>;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/common/types/service.type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type Service<T = unknown> = {
find(): Promise<T>;
find(id: number): Promise<T | null>;
findAll(): Promise<{
items: T[];
}>;
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions shared/src/framework/http/enums/http-code.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const HttpCode = {
BAD_REQUEST: 400,
UNPROCESSED_ENTITY: 422,
INTERNAL_SERVER_ERROR: 500,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
} as const;

export { HttpCode };
Loading