군붕이의 메모장

[Nest.js] Nest.js에서 사용자 인증 처리하기 with Passport 본문

웹 개발/백엔드

[Nest.js] Nest.js에서 사용자 인증 처리하기 with Passport

초보군붕이 2023. 3. 12. 19:36
반응형

● Nest.js 에서 로그인 구현하기

Nest.js의 경우 일반적으로 인증을 처리할 때 Passport 미들웨어를 사용하여 구현한다.

 

 

● Passport 란?

Passport는 Local, Oauth, Jwt 등을 다양하게 지원해주는 Node.js용 인증 미들웨어 이다.

보통 로그인 전략(Strategy)과 가드(Guard)를 생성하여 처리한다.

 

Passport : https://www.passportjs.org/

 

Passport.js

Simple, unobtrusive authentication for Node.js

www.passportjs.org

 

 

● 필요한 패키지

우선 필요한 패키지는 아래와 같다.

  • @nestjs/passport : nest.js에서 passport를 사용하기 위한 패키지
  • passport : 인증을 구현할 때 사용할 passport의미들웨어 패키지
  • passport-local : 이메일, 비밀번호와 같은 로컬기반 인증을 위한 패키지
  • @types/passport-local : 타입스크립트를 위한 타입이 정의된 패키지
npm install @nestjs/passport passport passport-local
npm install -D @types/passport-local

 

● 동작방식

다이어그램으로 표시하면 아래같은 방식이다.

인증방식 다이어그램

 

1. 클라이언트에서 서버로 /auth/login 요청을 하면 LocalAuthGuard가 해당 요청을 가로챈다.

 

2. LocalAuthGuard는 상속받은 AuthGuard의 canActivate() 메소드를 호출한다.

canActivate 메소드는 기본적으로 실행컨텍스트(Execution Context) 객체를 인자로 받는데, 이는 Nestj.js에서 실행중인 요청의 컨텍스트 정보를 캡슐화하는 인터페이스이다. 주로 HTTP 요청과 같은 정보를 제공해준다.

 

3. Passport.authenticate()를 호출하면 LocalStrategy의 validate() 메소드가 호출되는데 여기서 authService에 구현된 validateUser 메소드를 통해 사용자 검증을 시도한다.

 

4. 사용자 검증여부를 LocalAuthGuard로 다시 반환해주고 가드는 검증이 성공되었을시 라우트 핸들러로 작업을 이관한다. 만약 검증이 실패했다면 클라이언트로 401/Unauthorized 에러를 반환한다.

 

● Nest.js의 Guard?

  • 주어진 역할에 따라 Client의 요청이 Server(Route)에 닿기 전에 역할을 처리하는 미들웨어
  • 가드의 경우 미들웨어보다 늦게 실행된다. 하지만  Interceptor와 Pipe보다는 이전에 실행된다.
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

우선 가드의 경우 @nestjs/passport에 정의되어있는 AuthGuard를 상속받아 구현한다.

이메일, 비밀번호 기반의 로컬로그인을 처리하는 가드이므로 AuthGuard의 타입은 'local'로 지정한다.

 

해당 가드의 경우 라우트에 도달하기 전에 먼저 실행되어 AuthGuard.canActivate()를 호출한다.

 

● 전략 구성

위 LocalAuthGuard가 실행되면 AuthGuard.canActivate()는 Strategy를 호출하게 된다.

이떄 동일하게 LocalStrategy는 구현한다.

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    /**
     * Local 전략의 경우 기본값은 username이다.
     * 만약 @Body에 email 필드가 없다면 unauthorized 에러가 발생하므로 필드명을 커스텀해줘야 한다.
     * https://docs.nestjs.com/security/authentication#customize-passport
     */
    super({
      usernameField: 'email',
    });
  }

  async validate(email: string, password: string): Promise<any> {
    /**
     * 서비스에서 구현한 validateUser 메소드로 이메일, 비밀번호를 사용하여 유저를 검증해준다.
     */
    const user = await this.authService.validateUser(email, password);

    if (!user) {
      throw new BadRequestException('invalid_email_or_password');
    }

    return user;
  }
}

우선 PassportStrategy의 경우 Passport에서 제공하는 Strategy 클래스를 Nest.js에서 통합하여 사용하기 위해 제공하는 클래스다. 실질적인 인증처리 로직은 Strategy가 처리하게 된다.

 

이메일, 비밀번호를 기반으로 하는 로컬 인증 전략을 구성할때 Strategy 클래스를 확장하여 진행되는것이다.

 

이후 validate() 메소드에서 실제 검증로직을 처리하게되는데 이 떄 유저의 존재여부 및 비밀번호 일치여부는 인증관련 서비스 레이어에서 처리해준다.

 

 

auth.service.ts

  async validateUser(email: string, password: string) {
    const user = await this.usersService.findUserByEmail(email);
    if (!user) return false;

    const isMatchPassword = await this.passwordService.compare(password, user.password);
    if (!isMatchPassword) return false;

    return true;
  }

 

만약 유저 검증에 실패할경우 401(Unauthorized)에러를 반환하고 검증에 성공할경우는 실제 라우터로 넘어가게 된다.

 

● 컨트롤러 정의

컨트롤러에서는 사용할 가드를 지정해줘야 한다.

  /**
   * [POST] /auth/login - 로그인
   * @param loginDto - 로그인시 사용되는 유저의 데이터
   * @returns 토큰 반환
   */
  @HttpCode(200)
  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Body() loginDto: LoginDto) {
    return await this.authService.login(loginDto);
  }
  • @HttpCode: Nest.js에서 @Post 요청의 대한 기본 반환값은 201이다. 하지만 로그인의 경우 데이터를 변동이 없으므로 200 Success를 지정해준다.
  • @UseGuards : 핵심 데코레이터이다. 특정 가드를 사용한다고 지정해준다.

 

이후에는 authService에서 사용자 데이터를 검색하여 이후 JWT 인증에서 활용될 토큰을 발급해준다.

 

 

● 전체 코드

https://github.com/imkdw/hello_developer/tree/main/_server/src/auth

 

GitHub - imkdw/hello_developer: 개발자를 위한 커뮤니티, 헬로 디벨로퍼 입니다!

개발자를 위한 커뮤니티, 헬로 디벨로퍼 입니다! Contribute to imkdw/hello_developer development by creating an account on GitHub.

github.com

 

 

● 실행결과

 

● 참고자료

https://docs.nestjs.com/security/authentication

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

 

반응형