인증(Authentication)은 대부분의 애플리케이션에서 필수적인 부분입니다. Passport는 커뮤니티에서 잘 알려져 있고 많은 프로덕션 애플리케이션에서 성공적으로 사용되는 가장 인기 있는 Node.js 인증 라이브러리입니다. @nestjs/passport
모듈을 사용하여 이 라이브러리를 Nest.js 애플리케이션과 통합하는 것은 간단합니다.
비밀번호는 반드시 해싱하여 저장해야 합니다. 이를 위해 Bcrypt를 사용합니다.
Bcrypt는 비밀번호 해싱에 사용되는 오픈소스 알고리즘으로, 단방향 암호화 알고리즘입니다. 암호화 시 고유한 Salt를 생성하여 해시에 포함함으로써 레인보우 테이블 공격을 방어합니다.
npm install bcrypt
import * as bcrypt from 'bcrypt';
export class AuthService {
async hashPassword(password: string): Promise<string> {
const salt = await bcrypt.genSalt();
return bcrypt.hash(password, salt);
}
async comparePasswords(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
}
JWT(Json Web Token)는 Json 형식의 토큰에 대한 표준 규격으로, 인증 및 권한 정보를 주고받기 위해 사용됩니다. Base64로 인코딩된 형식이며, 주로 클라이언트에서 Authorization
HTTP 헤더에 Bearer <토큰>
으로 설정하여 서버로 전송합니다.
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
설치:
npm install @nestjs/jwt passport-jwt
auth.module.ts
설정:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: 'secretKey',
signOptions: { expiresIn: '60m' },
}),
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
auth.service.ts
:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}
jwt.strategy.ts
:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'secretKey',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
Passport는 다양한 인증 전략을 지원하는 인증 미들웨어로, Local, Google, Facebook, Apple, Kakao, Naver, Twitter 등 무수히 많은 인증 전략을 지원합니다.
npm install @nestjs/passport passport passport-local
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;
}
}
auth.service.ts
에 유저 검증 추가:
@Injectable()
export class AuthService {
async validateUser(username: string, pass: string): Promise<any> {
const user = await this.usersService.findOne(username);
if (user && user.password === pass) {
const { password, ...result } = user;
return result;
}
return null;
}
}
auth.controller.ts
:
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { LocalAuthGuard } from './local-auth.guard';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@UseGuards(LocalAuthGuard)
@Post('login')
async login(@Request() req) {
return this.authService.login(req.user);
}
}
local-auth.guard.ts
:
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
객체 직렬화는 데이터를 원하는 형태로 변환하는 작업입니다. Nest.js에서는 ClassSerializerInterceptor
를 사용하여 자동으로 데이터를 직렬화합니다.
user.entity.ts
:
import { Exclude }
from 'class-transformer';
export class User {
id: number;
username: string;
@Exclude()
password: string;
}
user.controller.ts
:
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ClassSerializerInterceptor } from '@nestjs/common';
@Controller('user')
@UseInterceptors(ClassSerializerInterceptor)
export class UserController {
@Get()
getProfile() {
return new User();
}
}
인증 기능을 도입함으로써 애플리케이션의 보안 수준이 향상되었습니다. 이 블로그를 통해 Nest.js에서 Passport와 JWT를 활용하여 인증 기능을 구축하는 기본 방법을 배웠습니다.
좋은 글 감사합니다. 혹시 PassportStrategy 버젼이 어떻게 되나요?
JwtStrategy의 생성자의 super의 인자가 올바르지 않다고 떠서요 ㅠㅠ