nestjs passport-local

송인성·2021년 9월 19일
2

안녕하세요 이번 주제는, 로그인을 위한 passport-local을 nestjs상에 붙이는 작업에 대한 이야기를 다루어볼까 합니다.

이번 포스트에서 삽질한 부분은
🧗‍♀️

@UseGuards(AuthGuard('local'))

에 따른 적용문제였습니다.

nestjs문서 - passport관련 부분을 볼 수 있습니다.(참고:https://docs.nestjs.com/security/authentication)

nestjs안에서 passport-local을 사용하기위해선 몇가지의 절차가 필요합니다.

1단계,

$ nest g module auth
$ nest g service auth

를 통해서 해당하는 파일들을 만들어냅니다.
만들어진 파일들에 대해 소개하겠습니다.

auth.module.ts

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Users } from 'src/entities/Users.entity';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [PassportModule],
  providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

와 같이 생겼습니다.

auth.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Users } from 'src/entities/Users.entity';
import { Repository } from 'typeorm';
import bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(Users) private userRepository: Repository<Users>,
  ) {}

  async validateUser(email: string, password: string): Promise<any> {
    const user = await this.userRepository.findOne({
      where: { email },
      select: ['email', 'password', 'nickname'],
    });

    if (!user) {
      return null;
    }

    const result = await bcrypt.compare(password, user.password);
    console.log('reuslt: ', result);

    if (result) {
      const { password, ...userWithoutPassword } = user;
      return userWithoutPassword;
    }
    return null;
  }
}

와같이 생성되어있습니다.
이렇게 두가지에 대한 파일을 정리하였고

nestjs공식문서에서 만들라고 제시해준 local.strategy파일을 밑에와 같이 생성했습니다.
auth/local.strategy.ts

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

해당 파일을 생성하여 해당 코드를 입력하면 passportStrategy를 사용할 수 있는 준비가 완료됩니다.

그리고 나서 이제 passport를 사용하기만 하면됩니다.

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller()
export class AppController {
  @UseGuards(AuthGuard('local'))
  @Post('auth/login')
  async login(@Request() req) {
    return req.user;
  }
}

위의 코드와 같이 사용하면 됩니다. 그러나 저에겐 하나의 물음과 문제 존재했습니다.

🤔하나의 물음:
어떻게

@UseGuards(AuthGuard('local'))

만을 작성해준 것 만으로도, authGuard가 작동하는 것일까? 당연히 nestjs에서 제공하는 데코레이션 기능을 사용하기때문이지만
AuthGuard('local')를 어떻게 인식할 수 있을까?에대한 고민이 들었습니다.
그 해답은
@UseGuards(AuthGuard('local'))를 입력해주면 nestjs의 @UseGuards에 ()안에 작성된 AuthGuards는 @nestjs/passport에서 가져온 정보이고, nestjs에서 local.strategy.ts파일 안에

export class LocalStrategy extends PassportStrategy(Strategy)

로 선언된 것을 확인하고 해당 코드를 불러서 사용하는 것입니다.

🤔하나의 문제:
왜 코드를 완성했음에도 불과하고 인증에러가 났을까?
그이유는
http://www.passportjs.org/docs/username-password/
에서 찾아볼 수 있었습니다.

기본적으로 및 LocalStrategy라는 매개변수에서 자격 증명을 찾을 것으로 예상합니다 . 사이트에서 이러한 필드의 이름을 다르게 지정하려는 경우 옵션을 사용하여 기본값을 변경할 수 있습니다.username password로 되어있다는 것입니다.
따라서 저의 경우 username-> email을 매개변수로 사용해야하기때문에 바꿔주어야했습니다.

local.strategy.ts


@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({ <- PassportStrategy의 객체의 매개변수를 아래와 같이 변경해야합니다.
      usernameField: 'email',
      password: 'password',
    });
  }

profile
코드 한줄에 의미를 생각할 수 있는 개발자가 되어 가는중... 🧑🏻‍💻

0개의 댓글