UNPKG

@point3/logto-module

Version:

포인트3 내부 logto Authentication 모듈입니다

96 lines (77 loc) 3.37 kB
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, InternalServerErrorException, Inject, HttpStatus } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; import { Reflector } from '@nestjs/core'; import { IncomingHttpHeaders } from 'http'; import { errors } from 'jose'; import { p3Values } from 'point3-common-tool'; import { LogtoTokenVerifier, LogtoTokenVerifierToken } from '../token'; import { ConfigService } from '@nestjs/config'; export const LogtoTokenGuardToken = Symbol('LogtoTokenGuard'); @Injectable() export class LogtoTokenGuard implements CanActivate { private reflector: Reflector = new Reflector(); constructor( @Inject(LogtoTokenVerifierToken) private tokenVerifier: LogtoTokenVerifier, private configService: ConfigService ) { } async canActivate(context: ExecutionContext): Promise<boolean> { if (this.configService.get<string>('NODE_ENV') === 'local') { return true; } //매타데이터에서 필요한 스코프와 역할을 가져온다. const requiredScopes = this.reflector.get<string[]>('requiredScopes', context.getHandler()); const requiredRoles = this.reflector.get<string[]>('requiredRoles', context.getHandler()); const request = this.getRequest(context); //헤더에서 베어러 토큰을 추출한다 try { const bearerToken = this.extractBearerTokenFrom(request.headers); const result = await this.tokenVerifier.verifyToken(bearerToken, requiredScopes, requiredRoles); // request.user에 사용자 정보를 추가한다. request.user = { userId: result.sub, managerId: p3Values.Guid.parse(result.managerId), clientId: result.clientId ? p3Values.Guid.parse(result.clientId) : undefined, } return true; } catch (error) { if (error instanceof UnauthorizedException) throw error; if (error instanceof errors.JOSEError) throw new UnauthorizedException(error); if (error instanceof Error) throw new InternalServerErrorException("요청을 처리하지 못하였습니다.", `${HttpStatus.INTERNAL_SERVER_ERROR}`); throw new UnauthorizedException("접근이 허용되지 않습니다."); } } /** * Extracts the Bearer token from the authorization header. * @param headers - The incoming HTTP headers. * @returns The extracted token. * @throws UnauthorizedException if the authorization header is missing or invalid. */ private extractBearerTokenFrom(headers: IncomingHttpHeaders): string { const bearerTokenIdentifier = 'Bearer'; if (!headers.authorization) { throw new UnauthorizedException('Authorization header is missing'); } if (!headers.authorization.startsWith(bearerTokenIdentifier)) { throw new UnauthorizedException('Authorization token type not supported'); } return headers.authorization.slice(bearerTokenIdentifier.length + 1); }; private getRequest(context: ExecutionContext): any { // Works for both REST and GraphQL if (context.getType<'http' | 'graphql'>() === 'graphql') { const gqlCtx = GqlExecutionContext.create(context); // depends on what you return from GraphQLModule context: ({ req }) => ({ req }) return gqlCtx.getContext().req; } return context.switchToHttp().getRequest(); } }