From 5bbe9b2970751b030da9807963f3a62d1f7dcfc6 Mon Sep 17 00:00:00 2001 From: shancheas Date: Thu, 9 Feb 2023 17:28:20 +0700 Subject: [PATCH] feat: implement JWT guard --- src/core/guards/constants.ts | 1 + .../domain/decorators/unprotected.guard.ts | 13 ++++ src/core/guards/domain/jwt.guard.ts | 70 +++++++++++++++++++ src/core/guards/domain/roles.guard.ts | 23 ++++++ src/core/guards/index.ts | 3 + 5 files changed, 110 insertions(+) create mode 100644 src/core/guards/constants.ts create mode 100644 src/core/guards/domain/decorators/unprotected.guard.ts create mode 100644 src/core/guards/domain/jwt.guard.ts create mode 100644 src/core/guards/domain/roles.guard.ts create mode 100644 src/core/guards/index.ts diff --git a/src/core/guards/constants.ts b/src/core/guards/constants.ts new file mode 100644 index 0000000..602cdd9 --- /dev/null +++ b/src/core/guards/constants.ts @@ -0,0 +1 @@ +export const UNPROTECTED_URL = 'unprotected_url'; diff --git a/src/core/guards/domain/decorators/unprotected.guard.ts b/src/core/guards/domain/decorators/unprotected.guard.ts new file mode 100644 index 0000000..621e28c --- /dev/null +++ b/src/core/guards/domain/decorators/unprotected.guard.ts @@ -0,0 +1,13 @@ +import { SetMetadata } from '@nestjs/common'; +import { UNPROTECTED_URL } from '../../constants'; + +/** + * This decorator will exclude the request from token check + * + * NOTE: + * Because the request does'nt provide token, + * the side effect are, function can't access `UserProvider` + * or `UserProvider` will return null + */ +export const Unprotected = (isUnprotected = true) => + SetMetadata(UNPROTECTED_URL, isUnprotected); diff --git a/src/core/guards/domain/jwt.guard.ts b/src/core/guards/domain/jwt.guard.ts new file mode 100644 index 0000000..8d98851 --- /dev/null +++ b/src/core/guards/domain/jwt.guard.ts @@ -0,0 +1,70 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + UnauthorizedException, + Scope, + Logger, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Observable } from 'rxjs'; +import { SessionService, UsersSession } from 'src/core/sessions'; +import { UNPROTECTED_URL } from '../constants'; + +@Injectable({ scope: Scope.REQUEST }) +export class JWTGuard implements CanActivate { + constructor( + protected readonly session: SessionService, + protected readonly reflector: Reflector, + ) {} + + protected userSession: UsersSession; + + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + /** + * Check if access url is protected or not + * By default `isUnprotected` equals `false` + */ + const isUnprotected = this.reflector.getAllAndOverride( + UNPROTECTED_URL, + [context.getHandler(), context.getClass()], + ); + if (isUnprotected) return true; + + /** + * Check if request give the token or not + * If there is not token in request, the transaction is failed + * and return 401 `Unauthorize` + */ + const request = context.switchToHttp().getRequest(); + const authorization = request.headers['authorization']; + if (!authorization) + throw new UnauthorizedException({ + code: 10002, + message: 'Access denied, please login first!', + error: 'TOKEN_NOT_PROVIDE', + }); + + const [, token] = authorization.split(' '); + + /** + * Verify if token is valid token from login + * if the token is'nt valid token, the transaction is failed + * and return 401 + */ + try { + this.userSession = this.session.verifyToken(token); + Logger.log(`Access from ${this.userSession.name}`, 'AuthGuard'); + return true; + } catch (error) { + throw new UnauthorizedException({ + code: 10001, + message: + 'You cant access this endpoint, because authentication inst valid', + error: 'INVALID_TOKEN', + }); + } + } +} diff --git a/src/core/guards/domain/roles.guard.ts b/src/core/guards/domain/roles.guard.ts new file mode 100644 index 0000000..28e3993 --- /dev/null +++ b/src/core/guards/domain/roles.guard.ts @@ -0,0 +1,23 @@ +import { Injectable, ExecutionContext } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { JWTGuard } from './jwt.guard'; + +@Injectable() +export class RolesGuard extends JWTGuard { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + super.canActivate(context); + + /** + * Create function to check if `this.userSession` have access + * to Read / Create / Update / and Other Action + */ + + /** + * Assign rules to session, So Query can take the rules and give + * the data base on user request + */ + return true; + } +} diff --git a/src/core/guards/index.ts b/src/core/guards/index.ts new file mode 100644 index 0000000..023d968 --- /dev/null +++ b/src/core/guards/index.ts @@ -0,0 +1,3 @@ +export * from './domain/jwt.guard'; +export * from './constants'; +export * from './domain/decorators/unprotected.guard';