Nodejs

 

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 {}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

Everybody's business is nobody's business. (공동 책임은 무 책임

댓글 ( 0)

댓글 남기기

작성

Nodejs 목록    more