JWT에 대해 처음 공부하던 중
몇 가지 이해가 가지 않고 궁금한 개념들이 있었다.
이에 대한 공부 기록이다.
개요
내가 궁금했던 것들은 아래와 같다.
- Guards란?
- Passport란?
- Passport 모듈의 역할과 @nest/passport가 하는 역할
- 배운 개념들이 어떻게 유기적으로 연결되는지
JWT에 대해 처음 배우고 기록할 때
어느 정도 이해가 됐지만 어떻게 유기적으로 동작하는지에 대한 궁금증이 남아있다.
각 코드의 의도를 명확히 파악하고,
위의 궁금증을 해결할 수 있도록 공부하며 기록할 예정이다.
Guards
공식문서에서는 Guards의 역할이 클라이언트의 요청에 대한 권한 부여라고 한다.
Guards는 ExecutionContext 인스턴스에 액세스 할 수 있어 각 핸들러에 대해 권한 부여를 할지 판단할 수 있다.
때문에 Guards가 JWT를 이용한 인증 기능을 구현하는데 적합한 것이다.
파이프와 필터와 같이 Guards는 컨트롤러 범위, 메소드 범위, 글로벌 범위로 사용될 수 있다.
CanActive
CanActive 인터페이스는 Guards가 반드시 implements해야 한다.
CanActive 인터페이스에서 가장 중요한 것은
Guards가 지정한 범위 내에서의 요청에 대해 권한 부여를 할 것인지에 대한 판단을 반환해야 한다는 것이다.
정리해보면, Guards는 요청에 대한 권한 부여 역할을 하고,
CanActive 인터페이스를 implements 하는데 해당 인터페이스에서는
반드시 권한 부여에 대한 판단값을 반환하는 메소드를 가져야 한다.
Guards는 모든 middleware가 처리되고 파이프와 인터셉터가 실행하기 이전에 실행된다.
Passport
passport는 잘 알려진 인기 있는 node.js 인증 라이브러리이다.
passport의 도움은 아래와 같다.
- 자격 증명(JWT, 자격 증명 토큰 등)을 확인하여 사용자를 인증
- 인증 상태 관리(JWT나 세션 등을 생성)
- 라우트 핸들러가 사용할 수 있도록 인증된 사용자에 대한 정보를 요청 객체에 첨부
passport는 다양한 인증 메커니즘을 구현하는 strategy 생태계가 있다.
이러한 다양한 메커니즘을 표준 패턴으로 추상화한 라이브러리이다.
@nest/passport 모듈은 이 패턴을 Nest 구조로 래핑하고 표준화한 것이다.
AuthGuard()
AuthGuards는 @nest/passport 모듈에서 제공하는 메소드이다.
export declare const AuthGuard: (type?: string | string[]) => Type<IAuthGuard>;
optional 한 타입을 받고 IAuthGuard타입의 데이터를 반환하는데
IAuthGuard는
export declare type IAuthGuard = CanActivate & {
위에서 봤던 권한 판단값을 반환하는 CanActivate를 포함한다.
내용을 정리해 보면,
passport는 토큰을 생성하고, 생성한 토큰으로 사용자를 인증한다.
passport를 NestJS에서 원활하게 사용할 수 있게 해주는 @nest/passport 모듈은 AuthGuard라는 메소드를 제공한다.
AuthGuard는 핸들러 사용 권한에 대한 판단값을 가지는 CanActive를 포함한 데이터를 반환한다.
AuthGuard에서 반환한 값을 useGuard로 전달하고,
useGuard에서 전달받은 Guard를 통해 지정 범위에 대해 인증을 구현한다.
Strategy
passport는 JWT, local 등의 많은 strategy를 지원한다.
이 strategy string을 AuthGuard 메소드의 인자로 전달한다.
AuthGuard는 인증에 대한 판단값을 반환해야 하기 때문에
strategy 클래스는 validate() 메소드를 가지고 있어야 하는데 이 메소드에서 인증 기능을 수행한다.
jwt strategy 파일을 생성해 보자.
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { IPayload } from './type/PayloadType';
import { User } from './user.entity';
import { UserRepository } from './user.repository';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository,
) {
super({
secretOrKey: 'Secret1234',
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
}
async validate(payload: IPayload) {
const { userName } = payload;
const user: User = await this.userRepository.findOneBy({ userName });
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
strategy 클래스는 PassportStrategy 클래스를 반드시 상속하며
필수로 지정해줘야 하는 값들을 super()를 통해 지정해 준다.
이 값에는 jwt 토큰 생성 시 사용하는 secret text값과 토큰의 타입이 있다.
위에서 언급한 대로 validate 메소드도 구현했다.
유기적 관계
먼저 auth module을 보자.
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmExModule } from 'src/typeorm-ex.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserRepository } from './user.repository';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule.register({
defaultStrategy: 'jwt',
}),
JwtModule.register({
secret: 'Secret1234',
signOptions: {
expiresIn: 3600,
},
}),
TypeOrmExModule.forCustomRepository([UserRepository]),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [JwtStrategy, PassportModule],
})
export class AuthModule {}
passport module에서 strategy를 jwt로 기본 설정한 모듈과
JWT module에서 secret text와 유효 시간을 지정한 모듈을 가져왔다.
JWT module은 JWT service를 지원하는데 여기서 로그인 시 해당 정보를 payload로 갖는 토큰을 생성할 수 있다.
JWT strategy를 provider로 지정함으로써
passport의 기능을 JWT strategy로 사용할 수 있도록 했다.
JWT strategy와 passport module을 export 해서 다른 모듈에서도
JWT startegy에 의한 passport의 인증 기능을 구현할 수 있도록 했다.
정리하면 JWT는 provider를 통해 로그인 시 해당 정보를 담는 토큰을 반환,
passport는 JWT strategy 파일로부터 JWT 토큰 생성 시 이용한 secret text와 토큰 형식을 받아서
요청 헤더의 토큰이 유효한지 판단하고 그 판단값을 useGuards로 반환하는 역할을 한다.
참고
https://www.youtube.com/watch?v=3JminDpCJNE&t=19151s
마치며
잘못된 정보에 대한 피드백은 환영입니다.
감사합니다.
'Backend > NestJS' 카테고리의 다른 글
[NestJS] Nest Config 환경 변수 관리 (0) | 2023.01.17 |
---|---|
[NestJS] Entity간 관계 형성 (1) | 2023.01.08 |
[NestJS] JWT(1) 기본 개념, 구현 (0) | 2023.01.07 |
[NestJS] @EntityRepository 대체 커스텀 데커레이터, 모듈 만들기 (1) | 2023.01.04 |
[NestJS] TypeORM, Entity, Repository 구조(NestJS PostgresSQL 적용) (1) | 2023.01.03 |