@nestjs/passport
모듈 사용JWT
) 확인을 통한 사용자 인증Request
객체에 첨부super()
validate()
// auth.service.ts
@Injectable()
export class AuthService {
constructor(private usersService: UsersService) {}
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;
}
}
실제 애플리케이션에서는, 사용자 암호를 일반 텍스트로 저장하거나 노출하지 않음
->bcrypt
와 같은 라이브러리 사용 (솔티드 단방향 해시 알고리즘)
// auth/local.strategy.ts
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
// 옵션 객체 전달 예시
/*super({
secretOrKey: process.env.JWT_SECRET,
ignoreExpiration: true,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});*/
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
@Controller()
export class AppController {
// 자동으로 프로비저닝된 AuthGuard 사용
@UseGuards(AuthGuard('local'))
@Post('auth/login')
async login(@Request() req) {
// Passport의 validate() 메서드에서 반환된 값(user)이 Request 객체에 할당됨
return req.user;
}
}
// 전략이름을 AuthGuard()에 직접 전달하는 것 보다
// 별도의 클래스를 생성하는 것이 좋음
@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
@UseGuards(LocalAuthGuard)
@Post('auth/login')
async login(@Request() req) {
return req.user;
}
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService
) {}
async validateUser(username: string, pass: string): Promise<any> {
// 생략
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
// JWT 발행
access_token: this.jwtService.sign(payload),
};
}
}
export const jwtConstants = {
secret: 'secretKey',
};
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
// 환경 변수 이용
//secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
@Controller()
export class AppController {
constructor(private authService: AuthService) {}
@UseGuards(LocalAuthGuard)
@Post('auth/login')
async login(@Request() req) {
// 로그인할 경우 JWT 반환됨
return this.authService.login(req.user);
}
}
// auth/jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
// Request에서 JWT를 추출하는 방법 제공
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
// JWT가 Passport 모듈에 만료되었는지 확인
// 만료된 JWT가 전달될 경우, '401 Unauthorized' 자동 응답 처리
ignoreExpiration: false,
// 토큰 서명을 위한 대칭 암호 제공
// 공개하면 안됨
secretOrKey: jwtConstants.secret,
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
// auth/auth.module.ts
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
// auth/jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
: 유효한 JWT를 포함하는 요청에만 엑세스할 수 있는 보호 경로 생성
@Controller()
export class AppController {
constructor(private authService: AuthService) {}
@UseGuards(LocalAuthGuard)
@Post('auth/login')
async login(@Request() req) {
return this.authService.login(req.user);
}
// 추가된 부분
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}
}
-> GET/profile
접근 시,
Guards는 자동으로 passport-jwt
로직 호출하여 JWT 검증하고 user
속성을 Request
객체에 할당