Seize the day

[Nest.js] Login Authentication 구현 ( passport + jwt + local strategy ) 본문

개발/Node.js

[Nest.js] Login Authentication 구현 ( passport + jwt + local strategy )

nofunfromdev 2022. 4. 4. 00:37

사용자인증은 대부분의 애플리케이션에서 필수적인 부분이다. 인증을 처리하는 방법은 다양하지만 Nest.js 공식홈페이지에서 passport를 안내하고 있어 필자는 passport와 jwt를 선택했다.  jwt 전략을 사용 하기 전 passport-local을 먼저 구현하였다.

Nest.js로 Login 구현하기 (1) 

패키지 설치 및  모듈 생성

$ npm install --save @nestjs/passport passport passport-local
$ npm install --save-dev @types/passport-local

패키지를 설치 후 모듈과 서비스를 생성한다.

$ nest g module auth 
$ nest g service auth --no-spec

$ nest g module user
$ nest g service user --no-spec

passport local Strategy

// user/user.service.ts 
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'src/entity/user.entity';
import { Repository } from 'typeorm';

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

  async findOne(id: string): Promise<User | undefined> {
    return this.userRepository.findOne({
      where: {
        id: id,
      }
    });
  }
}
// auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { UserService } from '../user/user.service';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(private userService: UserService) {}

  async validateUser(id: string, password: string): Promise<any> {
    const user = await this.userService.findOne(id);
    if (!(await bcrypt.compare(password, user?.password ?? ''))) {
      return null;
    }
    return user;
  }
}

나는 typeorm을 사용하여 데이터베이스에서 사용자 검색을 하였다. 데이터베이스에는 해시된 암호만 저장한 후 데이터를 비교하였다. bcrypt 라이브러리로 단방향해시 알고리즘을 사용하였다. 

// 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({ usernameField: 'id' });
  }

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

super()을 호출하면서 옵션을 전달 할 수 있는데, default로 request는 usernamepassword 속성이 필요하다. 다른 속성이름으로 지정하려면 super({usernameField: 'id'} 라고 옵션을 설정하면 된다.

// auth/guard/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
// app.controller.ts
import { Controller, Get, Post, UseGuards, Request } from '@nestjs/common';
import { AppService } from './app.service';
import { LocalAuthGuard } from './auth/guard/local-auth.guard';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @UseGuards(LocalAuthGuard)
  @Post('auth/login')
  async login(@Request() req) {
    return req.user;
  }
}
$ curl -X POST http://localhost:3000/auth/login -d '{"id": "nofunfromdev", "password": "test"}' -H "Content-Type: application/json"

 

validate()에서 반환되는 값은 자동으로 사용자 개체를 생성하여 req.user로 Request에 할당된다. 

터미널에서 실행하면 정상적으로 사용자 값이 return 된다.

 

 

참고사이트

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

- https://www.passportjs.org/

 

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 Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

Nest.js Authentication

반응형
Comments