JS/Nest.js

[Nest] Guard => req에 user 넣기, 로그인 과정 복습, 토큰 타입확인

Mini_96 2024. 9. 20. 14:21

* 로그인 과정 복습 (Basic token 이용)

  • 이메일 : 비밀번호를 encoding
  •  

  • Basic Ymfldgkmdfkgdbdfbdf를 요청
  • 뒷부분만 파싱
  • 다시 디코딩
  • email, pw 나옴
  • db에서 조회
  • user 객체 리턴

 

* req에 user 넣기

/**
 * req.header.authorization 의 토큰으로
 * user를 가와서
 * req.user에 넣는 가드
 */
@Injectable()
export class BasicTokenGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {} //런타임에서 DI에서 주입해줌

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();

    // {authorization : Basic fibntbdffg}
    // fibntbdffg
    const rawToken = req.headers.authorization;

    if(!rawToken){
      throw new UnauthorizedException('토큰이 없습니다!');
    }

    const token = this.authService.extractTokenFromHeader(rawToken,false);

    const {email, password} = this.authService.decodeBasicToken(rawToken);

    const user = await this.authService.authenticationWithEmailAndPassword({
      email,
      password,
    });

    req.user = user;

    return true; //가드 통과
  }
}

 

/**
 * Header중 authorization 필드만 가져옴
 */
@Post('/login/email')
@UseGuards(BasicTokenGuard)
postLoginEmail(
  @Headers('authorization') rawToken: string,){
  const token = this.authService.extractTokenFromHeader(rawToken, false);

  const credentials = this.authService.decodeBasicToken(token); // {email, password} 객체

  return this.authService.loginWithEmail(credentials);
}

req
guard

 

* bearer 토큰 가드

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
import { AuthService } from "../auth.service";
import { UsersService } from "../../users/users.service";

/**
 * Bearer sdfjdsivjlivjdlfiv 토큰을 받아서
 * 파싱후
 * req에 user, token, tokenType 넣기
 */
@Injectable()
export class BearerTokenGuard implements CanActivate {
  constructor(private readonly authService: AuthService,
              private readonly usersService : UsersService) {} //런타임에서 DI에서 주입해줌

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();

    // {authorization : Basic fibntbdffg}
    // fibntbdffg
    const rawToken = req.headers.authorization;

    if(!rawToken){
      throw new UnauthorizedException('토큰이 없습니다!');
    }

    const token = this.authService.extractTokenFromHeader(rawToken,true);

    const result = await this.authService.verifyToken(token);

    /**
     * req에 넣을정보
     * 1) 서용자 정보
     * 2) token
     * 3) token type - access | refresh
     */

    const user = await this.usersService.getUserByEmail(result.email);

    req.user = user;
    req.token = token;
    req.tokenType = result.type;

    return true; //가드 통과
  }
}

/**
 * 토큰이 엑세스 토큰인지 확인
 */
@Injectable()
export class AccessTokenGuard extends BearerTokenGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    await super.canActivate(context);

    const req = context.switchToHttp().getRequest();

    if(req.tokenType !== 'access'){
      throw new UnauthorizedException('Access Token이 아닙니다.');
    }

    return true; //다음 미들웨어로
  }
}

/**
 * 토큰이 refresh 토큰인지 확인
 */
@Injectable()
export class RefreshTokenGuard extends BearerTokenGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    await super.canActivate(context);

    const req = context.switchToHttp().getRequest();

    if(req.tokenType !== 'refresh'){
      throw new UnauthorizedException('Refresh Token이 아닙니다.');
    }

    return true; //다음 미들웨어로
  }
}
@Post('token/access')
@UseGuards(RefreshTokenGuard) //리프레시 토큰으로만 엑세스토큰 발급가능
postTokenAccess(
  @Headers('authorization') rawToken: string,){
  const token = this.authService.extractTokenFromHeader(rawToken, true);

  const newToken = this.authService.rotateToken(token,false);

  /**
   * {accessToken : {token}}
   */
  return {
    accessToken: newToken,
  }

}

@Post('token/refresh')
@UseGuards(RefreshTokenGuard)
postTokenRefresh(
  @Headers('authorization') rawToken: string,){
  const token = this.authService.extractTokenFromHeader(rawToken, true);

  const newToken = this.authService.rotateToken(token,true);

  /**
   * {refreshToken : {token}}
   */
  return {
    refreshToken: newToken,
  }

}