Guard란 무엇일까?
공식문서를 통해 확인해 보면 CanAtivate 인터페이스를 구현하는 @Injectable() 데코레이터가 달린 클래스를 말한다.
guard는 말그대로 guard로서 어플리케이션으로 들어오는 요청에 대해 controller 단으로 요청이 도달하기 전 요청에 대한 인가/인증 등에 대한 validate를 통해 접근을 제어하며 어플리케이션을 보호하는 역할을 한다.
모든 Guard는 canAtivate 함수를 implement하게 되어 있으며, 해당 함수는 boolean값을 반환하며 true가 반환되면 통과, false가 반환되면 불통이다.
또한 canAtivate는 인수로 ExecutionContext 인스턴스를 가지며, 이를 통해 다양한 정보를 가져와 유용하게 활용이 가능하다.
아래는 accessToken이 유효하다면 해당 요청을 컨트롤러로 넘겨주고 유효하지 않다면 권한 없음을 응답으로 넘겨주도록 만든 guard 이며 passport 라이브러리를 활용하여 더 간단하게 구현한 것이다.
유효한 토큰이라면 토큰에서 userId를 추출하여 controller 단으로 넘김
// access-token.strategy.ts
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
@Injectable()
export class AccessTokenStrategy extends PassportStrategy(Strategy, 'accessToken') {
constructor() {
super({
secretOrKey: process.env.ACCESS_TOKEN_SECRET,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
})
}
validate(payload) {
return { userId: payload.userId };
}
}
위와 같이 작성한 Guard를 적용시키기 위해선 아래와 같이 적용이 필요한 controller 단의 핸들러에 붙여도 되며 controller 전체에 적용시키려면 @Controller 데코레이터와 붙여서 적용해도 된다.
// user.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('user')
export class UserController {
@Get('current')
@UseGuards(AuthGuard('accessToken'))
async getMyData(//...) {
//...~~~~~
}
}
유효한 토큰과 함께 서버로 요청 시 guard를 통과하여 contoller 단으로 요청이 전달되어 응답을 보냄
토큰이 없거나 유효하지 않은 토큰과 함께 서버로 요청 시 guard에서 막히며, unauthorized(401)을 반환