NestJS에서 passport-local 전략을 사용할 때 필요한 패키지를 pnpm으로 설치
pnpm add passport passport-local @nestjs/passport pnpm add -D @types/passport-local
https://www.passportjs.org/packages/passport-local/
패키지용도
passport인증 미들웨어의 코어 라이브러리
passport-local로컬 전략 (아이디/비밀번호 로그인 등)
@nestjs/passportNestJS에서 Passport를 연동하기 위한 래퍼 모듈
@types/passport-localTypeScript에서 타입 지원 (개발용 -D 옵션)
1)/src/auth/strategy/local.strategy.ts
import { Injectable } from "@nestjs/common";
import { AuthGuard, PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-local";
import { AuthService } from "../auth.service";
export class LocalAuthGuard extends AuthGuard('local') { }
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
constructor(private readonly authService: AuthService) {
super();
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.authenticate(email, password);
return user;
}
}
2)/src/auth/auth.controller.ts
import { ClassSerializerInterceptor, Controller, Headers, Post, Request, UseGuards, UseInterceptors } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from '@nestjs/passport';
import { User } from 'src/users/entities/user.entity';
import type{ Request as ExpressRequest } from 'express';
import { LocalAuthGuard } from './strategy/local.strategy';
@Controller('auth')
@UseInterceptors(ClassSerializerInterceptor)
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
registerUser(@Headers('authorization') token: string) {
return this.authService.registerUser(token);
}
@Post('login')
loginUser(@Headers('authorization') token: string) {
return this.authService.login(token);
}
//@UseGuards(AuthGuard('local'))
@UseGuards(LocalAuthGuard)
@Post('login/passport')
loginUserPassport(@Request() req: ExpressRequest): User {
const user = req.user as User;
return user;
}
}
3)/src/auth/auth.service.ts
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
private readonly configService: ConfigService,
private readonly jwtService: JwtService,
) {}
parseBasicToken(rawToken: string) {
//1)토큰을 '' 기준으로 스프릿 한 후 토큰 값만 추출하기
const basicSplit = rawToken.split(' ');
console.log(' basicSplit ', basicSplit);
if (basicSplit.length !== 2) {
throw new BadRequestException('토큰 포멧이 잘못되었습니다.');
}
//const [_, token] = basicSplit;
const token = basicSplit[1];
console.log(' token ', token);
//2) 추출한 토큰을 Base64 decoding 해서 이메일과 비밀번호를 나눈다.
const decoded = Buffer.from(token, 'base64').toString('utf-8');
console.log(' decoded ', decoded);
/// "email:password"
const tokenSplit = decoded.split(':');
console.log(' tokenSplit ', tokenSplit);
if (tokenSplit.length !== 2) {
throw new BadRequestException('토큰 포멧이 잘못되었습니다.');
}
const [email, password] = tokenSplit;
console.log(' email, password ', email, password);
return { email, password };
}
async registerUser(rowToken: string) {
const { email, password } = this.parseBasicToken(rowToken);
const user = await this.usersRepository.findOne({ where: { email } });
if (user) {
throw new BadRequestException('이미 가입한 이메일 입니다.');
}
const hashRounds = this.configService.get<number>('HASH_ROUNDS') || 10;
console.log('????hashRounds ', hashRounds);
const hashedPassword = await bcrypt.hash(password, hashRounds);
await this.usersRepository.save({
username: email,
email,
password: hashedPassword,
});
return this.usersRepository.findOne({ where: { email } });
}
async authenticate(email: string, password: string) {
const user = await this.usersRepository.findOne({ where: { email } });
if (!user) {
throw new BadRequestException('잘못된 로그인 정보입니다.');
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
throw new BadRequestException('잘못된 로그인 정보입니다.');
}
return user;
}
async login(token: string) {
const { email, password } = this.parseBasicToken(token);
const user = await this.authenticate(email, password);
const refreshTokenSecret = this.configService.get<string>(
'REFRESH_TOKEN_SECRET',
);
const accessTokenSecret = this.configService.get<string>(
'ACCESS_TOKEN_SECRET',
);
return {
refreshToken: await this.jwtService.signAsync(
{
sub: user.id,
role: user.role,
type: 'refresh',
},
{
secret: refreshTokenSecret,
expiresIn: '14d',
},
),
accessToken: await this.jwtService.signAsync(
{
sub: user.id,
role: user.role,
type: 'access',
},
{
secret: accessTokenSecret,
expiresIn: 300,
},
),
};
}
}
4)/src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/users/entities/user.entity';
import { JwtModule } from '@nestjs/jwt';
import { LocalStrategy } from './strategy/local.strategy';
@Module({
imports: [
TypeOrmModule.forFeature([
User
]),
JwtModule.register({}),
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy],
exports: [AuthService],
})
export class AuthModule {}













댓글 ( 0)
댓글 남기기