AuthGuard는 Nestjs에서 제공하는 미들웨어이다. AuthGuard는 Strategy와 사용되는데 보통 Passport 라이브러리를 활용하여 다양한 인증 전략을 구성할 수 있게 해 준다.
아래는 예시 코드이다. 보통 Controller layer에서 데코레이터를 활용해 사용할 수 있다. (라우터에서도 사용 가능함)
여기서 'jwt'는 Passport를 활용해 작성된 Strategy이고, 거기에 decorator로 만든 user.decorator까지 사용하게 되면 http 통신이 올 때 먼저 토큰 검증부터 진행할 수 있다.
// Controller layer
@Controller('example')
export class ExampleController {
constructor(private exampleService: ExampleService) {}
@UseGuards(AuthGuard('jwt'))
@Get('example')
async exampleFn(@User() user): Promise<ResponseOutPut<GetPlatformDataOutput[]>> {
const result = await this.exampleService.getPlatformList();
return ResponseOutPut.OK(result);
}
}
// jwt.stategy.ts
import { Request } from 'express';
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
return request?.cookies?.ACCESS_TOKEN;
},
]),
secretOrKey: process.env.JWT_SECRET,
passReqToCallback: true,
});
}
async validate(request: Request, payload: any) {
console.log(payload);
const accessToken = request.cookies?.ACCESS_TOKEN;
return payload;
}
}
// user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
});
여기서 만약 토큰이 제대로 넘어왔다면 jwt에 담겨있는 정보가 user 객체에 담길 것이고, 만약 토큰이 없거나 만료된 토큰이 넘어온다면 401 에러를 리턴하게 된다.
AuthGuard 이용해 토큰 예외처리하기
api를 만들다 보면 토큰이 있어도 되고 없어도 잘 돌아가는 기능이지만 둘의 리턴값이 다른 값을 리턴해야 할 때가 있다.
예를 들어 토큰이 있는 상태로 콜을 보내면 요청을 보낸 유저와 관계된 글들을 리턴하고, 토큰 없이 요청을 보내면 최신순으로 리턴하는 기능이 있다고 가정해 보자. 해당 기능의 리턴값은 토큰이 있든 없든 동일한 인터페이스를 갖는다. 하지만 토큰이 있을 때의 값과 없을 때의 값을 구하는 로직은 전혀 다를 것이다.
이런 상황에선 AuthGuard를 이용해 토큰을 예외처리해 줄 수 있다. 아래는 예시 코드이다.
// src/guards/nullable.guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class OptionalAuthGuard extends AuthGuard('jwt') {
async canActivate(context: ExecutionContext) {
const result = (await super.canActivate(context)) as boolean;
const request = context.switchToHttp().getRequest();
// JWT 토큰이 없어도 통과하도록 설정
if (!request.headers.authorization) {
return true;
}
return result;
}
handleRequest(err, user, info) {
if (err || !user) {
return false; // 인증 실패시 false 리턴
}
return user;
}
}
// Controller
@Controller('example')
export class ExampleController {
constructor(private exampleService: ExampleService) {}
@UseGuards(OptionalAuthGuard)
@Get('example')
async exampleFn(): Promise<ResponseOutPut<GetPlatformDataOutput[]>> {
const result = await this.exampleService.getPlatformList();
return ResponseOutPut.OK(result);
}
}
먼저 AuthGuard를 상속받아 OptionalAuthGuard를 만들고 내부에 원하는 로직을 구현한다. 해당 예시는 헤더에 토큰이 없어도 401 에러 없이 통과하고 user 객체는 false로 리턴하게 하는 로직이다. 만약 정상적인 토큰이 넘어온다면 원래 토큰에 있던 user 객체를 넘겨주게 된다.
'NestJS' 카테고리의 다른 글
Express에서 DI 프레임워크 만들어보기 (0) | 2023.05.04 |
---|---|
Nest.js에서 모듈간의 의존성 관리하기 (0) | 2023.04.19 |
Nest.js 에서 역직렬화 하기 (0) | 2023.03.14 |
Nest.js custom decorator 사용하기 (0) | 2023.03.07 |
@nestjs/config 사용하는 이유 (0) | 2023.02.09 |