inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core

모든 Route 기본 Private로 만들고 IsPublic Annotation 작업하기

@IsPublic 어노테이션으로 RefreshTokenGuard에서 RefreshToken 검증을 하기 위한 코드 공유

342

rhkdtjd_12

작성한 질문수 138

0

// is-public.const.ts
export enum IsPublicEnum {
  ISPUBLIC = 'ISPUBLIC',
  ISREFRESHTOKEN = 'ISREFRESHTOKEN',
}
// is-public.decorator.ts
import { SetMetadata } from '@nestjs/common';
import { IsPublicEnum } from '../const/is-public.const';

export const ISPUBLIC_KEY = 'is_public';

export const IsPublic = (status: IsPublicEnum) =>
  SetMetadata(ISPUBLIC_KEY, status);
// bearer-token.guard.ts
@Injectable()
export class BearerTokenGuard implements CanActivate {
  constructor(
    private readonly authService: AuthService,
    private readonly usersService: UsersService,
    public readonly reflector: Reflector,
  ) {}

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

    const requiredPublic = this.reflector.getAllAndOverride(ISPUBLIC_KEY, [
      context.getHandler(),
      context.getClass,
    ]);

    if (requiredPublic) {
      req.requiredPublic = requiredPublic;
    }

    if (requiredPublic === IsPublicEnum.ISPUBLIC) {
      return true;
    }

    const rawToken = req.headers['authorization'];

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

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

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

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

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

    return true;
  }
}
if (requiredPublic) {
  req.requiredPublic = requiredPublic;
}

requiredPublic 있을때만 req에 담아줌.

if (requiredPublic === IsPublicEnum.ISPUBLIC) {
      return true;
    }

IsPublic일때만 return true 즉, ISREFRESHTOKEN 일때는 아래 로직들이 정상적으로 실행됨.

// bearer-token.guard.ts
@Injectable()
export class AccessTokenGuard extends BearerTokenGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    await super.canActivate(context);

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

    if (
      req.requiredPublic === IsPublicEnum.ISPUBLIC ||
      IsPublicEnum.ISREFRESHTOKEN
    ) {
      return true;
    }

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

    return true;
  }
}
if (req.requiredPublic === IsPublicEnum.ISPUBLIC || IsPublicEnum.ISREFRESHTOKEN) {
   return true;
}

req.requiredPublic이 ISPUBLIC이거나 ISREFRESHTOKEN이면 return true로 global accessTokenGuard return true로 빠져나옴.

// bearer-token-guard.ts
@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;
  }
}

변경 사항 없음. 왜냐하면 위에서 정상적으로 refreshToken을 verify하고 req에 값이 담겼기 때문에 RefreshTokenGuard가 정상적으로 실행 되어야함.

 

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @IsPublic(IsPublicEnum.ISREFRESHTOKEN)
  @Post('token/access')
  @UseGuards(RefreshTokenGuard)
  postTokenAccess(@Headers('authorization') rawToken: string) {
  
  ...
  
  }

  @IsPublic(IsPublicEnum.ISREFRESHTOKEN)
  @Post('token/refresh')
  @UseGuards(RefreshTokenGuard)
  postTokenRefresh(@Headers('authorization') rawToken: string) {

  ...

  }

  @IsPublic(IsPublicEnum.ISPUBLIC)
  @Post('login/email')
  @UseGuards(BasicTokenGuard)
  postLoginEmail(@Headers('authorization') rawToken: string) {

  ...

  }

  @IsPublic(IsPublicEnum.ISPUBLIC)
  @Post('register/email')
  postRegisterEmail(@Body() body: RegisterUserDto) {

 ... 

 }
}

javascript typescript rest-api nestjs backend

답변 2

1

코드팩토리

안녕하세요!

공유 감사합니다!

0

rhkdtjd_12

😃

0

준호리

안녕하세요 혹시 AccessTokenGuard를 전역적으로 지정해준 상태에서

@isPublic , @UseGuards(RefreshTokenGuard) 어노테이션이 적용된 api가 리프레쉬가드를 못타는 문제 때문에 코드 수정하신거 맞으신가요?

0

rhkdtjd_12

네 맞습니다. 기존 코드는 RefreshToken의 validation을 하지 않고 return true를 해주기 때문입니다.

cascade 질문

0

40

1

@types/bcrypt 설치과정이 누락된것같습니다.

0

49

1

process.env port key 에러

0

47

1

추상화

0

50

1

[공유] DTO optional 필드가 undefined로 잡혀 TypeORM 조건이 깨지는 현상

0

101

2

where 키워드가 들어가는 메서드와 아닌 메서드

0

57

1

BearerTokenGuard에서 db를 조회해서 유저 정보를 불러오는 이유?

0

72

1

app.controller app.service 는 지워도되나요?

0

70

1

@JoinColumn을 쓰는 경우와 안쓰는 경우의 차이

0

79

1

포트 3000에서 listen하는 곳까지 넘어가지 않습니다.

0

68

1

PickType 사용 시 `as const`를 꼭 사용해야 하나요?

0

102

2

socket connect 오류

0

97

2

강의를 들으면서 궁금한 점

0

97

2

DELETE 요청의 반환값은 어떤 기준으로 결정하는 게 좋을까요?

0

60

2

커리큘럼 질문

0

100

2

put 요청은 언제

0

90

3

typeorm VS prisma

0

351

2

142 강의 > 4:00 > 포스트멘 활용 관련 질문 드립니다.

0

77

2

User 데코레이터 버그 수정 전달드립니다.

0

67

1

git 주소 부탁드립니다.

0

113

2

nest g resource 명령어 에러

0

99

2

로그인 엔드포인트 관련 질문

0

85

2

yarn으로 express 다운 후 node 2_server.js 실행 안되는 경우

0

148

3

"흔히 사용되는 메서드" 강의 관련 질문입니다~

0

95

2