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)  
댓글 남기기