본문 바로가기

카테고리 없음

Authentication 2/2 - Passport package

ref. https://docs.nestjs.com/recipes/passport#implementing-passport-jwt

 

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

 

https://docs.nestjs.com/security/authentication -> 이 문서에서는 AuthGuard 라는 이름의 커스텀 Guard 를 작성하여 사용했으나

@nestJs/passport 패키지에서 제공하는 AuthGuard('jwt') 를 가져와서 Guard 로 사용한다는 차이가 있다.

 

0. 패키지 설치

npm install --save @nestjs/passport passport passport-jwt
npm install --save-dev @types/passport-jwt

 

 (공식문서 내용)
For **any** Passport strategy you choose, you'll always need the `@nestjs/passport` and `passport` packages. Then, you'll need to install the strategy-specific package (e.g., `passport-jwt` or `passport-local`) that implements the particular authentication strategy you are building. In addition, you can also install the type definitions for any Passport strategy, as shown above with `@types/passport-local`, which provides assistance while writing TypeScript code.

 

1. auth.service.ts

(Authentication 1/2 내용과 동일)

login 시에 JWT 토큰을 발급하여 리턴한다.

@Injectable()
export class AuthService {
  constructor(
    private datasource: DataSource,
    private jwtService: JwtService,

    @InjectRepository(Authentication)
    private authRepository: Repository<Authentication>,
  ) {}

  async login(signInDto: signInDto): Promise<{ access_token: string }> {
    const auth = await this.authRepository.findOneBy({
      email: signInDto.email,
    });
    if (auth?.password !== signInDto.password) {
      throw new UnauthorizedException();
    }
    const payload = { sub: auth.id, authEmail: auth.email };
    return {
      access_token: await this.jwtService.signAsync(payload),
    };
  }

2. jwt.strategy.ts 구현, auth.module.ts 일부 수정

jwt.strategy.ts

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: process.env.JWT_CONSTANTS,
    });
  }

  async validate(payload: any) {
    // Passport will build a `user` object based on the return value of our `validate()` method,
    // and attach it as a property on the `Request` object.

    // auth.service.ts
    //     const payload = { sub: auth.id, authEmail: auth.email };
    return { authId: payload.sub, authEmail: payload.authEmail };
  }
}

 

auth.module.ts 에 JwtStrategy 추가

@Module({
  imports: [
    TypeOrmModule.forFeature([Authentication]),
    JwtModule.register({
      // registering the JwtModule as global to make things easier for us.
      // This means that we don't need to import the JwtModule anywhere else in our application.
      global: true,
      secret: process.env.JWT_CONSTANTS,
      signOptions: {
        expiresIn: '600s', // token expiration time
      },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}

3. jwt-auth.guard.ts

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

4. auth.controller.ts

  @UseGuards(JwtAuthGuard)
  @Patch('password')
  async updateAccount(
    @Body() updateAccountDto: UpdateAccountDto,
    @Request() req,
  ): Promise<UpdateResult> {
    console.log(req.user);
    return await this.authService.changePassword(updateAccountDto);
  }

 

원하는 범위에 구현한 JwtAuthGuard 를 추가

 

JwtGuard 는 이전 포스팅인 Authentication 1/2 에서 구현한 AuthGuard 와 동일하게

request 의 헤더에서 jwt 토큰값을 읽어와서 유효성을 검사하고, validation 함수를 실행하여 validation 함수의 반환값을

request 객체의 user 프로퍼티에 추가한다. (request.user)

 

##

auth.controller.ts

console.log(req.user) 실행결과 예시

{
  authId: '61b2dc24-9d07-4730-a5ab-9ab2df9a811b',
  authEmail: 'O^^O@naver.com'
}