NestJS Auth - Authorization

1yongs_·2021년 2월 14일
2

NestJS Auth

목록 보기
4/4
post-thumbnail

Index

이전까지 AuthGuard를 만들어 @UseGuard() 데코레이터로 적용한 Authentication 과정을 진행하였습니다. 이번 글에서는 Role-based access control 메커니즘을 적용하여 역할과 권한에 대해 정의하여 액세스를 제어해 봅시다.

Role-Based Authorization

Add Role

먼저 User Entity에 Role 이라는 권한(역할) Column을 추가해줍니다.

  • User Entity
// src/user/entity/user.entity.ts

export enum UserRole {
  CLIENT = 'CLIENT',
  ADMIN = 'ADMIN',
}

@Entity({ name: 'users' })
export class User {
  // ....

  @Column({
    type: 'enum',
    enum: UserRole,
    default: UserRole.CLIENT,
  })
  role: UserRole;
  
  // ...
}

Role Guards

@Role() 이라는 Decorator를 만듭니다. 이 데코레이터는 지정한 특정 리소스에 대해 필요한 role(권한)을 허가합니다.
예를 들어 admin이 필요한 리소스에 접근하기 위해 사용자는 role column에 admin으로 지정이 되어야 하죠.

  • Role Guard
// src/auth/role.decorator.ts

import { SetMetadata } from '@nestjs/common';
import { UserRole } from 'src/user/entity/user.entity';

// 'Any' : 모든 role에 대해 허용
export type AllowedRole = keyof typeof UserRole | 'Any';

export const Role = (roles: AllowedRole[]) => SetMetadata('roles', roles);

참고 🔍

  • Auth Guard
    route의 roles custom metadata에 접근하기 위해, framework 밖에서 제공하는 Reflector 헬퍼 클레스를 사용합니다.
// src/auth/auth.guard.ts

@Injectable()
export class AuthGuard implements CanActivate {
  
  constructor(private reflector: Reflector) {} // `roles` custom metadata에 접근하기 위해 reflector 사용
  
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
      // 
    const requiredRoles = this.reflector.get<AllowedRole>(
      'roles',
      context.getHandler(),
    );
      
    if (!requiredRoles) { // role이 필요없는 resource면 그냥 넘어감 
      return true;
    }
      
    const request = context.switchToHttp().getRequest();
    const user: User = request['user'];
    if (!user) 
      return false;
      
    if (requiredRoles.includes('Any')) 
      return true; // `Any` 권한은 로그인된 사용자 중 아무나 접근이 가능하다는 뜻.
    return requiredRoles.includes(user.role); // 사용자의 role이 resource가 필요한 권한에 포함되어있는지 검사!
  }
}
  • User Role Guards
    이제 Controller에 Role Decorator를 적용해봅시다.
// src/user/user.controller.ts

@Controller('user')
export class UserController {
  // ...

  @Get('/me')
  @Role([UserRole.CLIENT])
  @UseGuards(AuthGuard)
  getMe() {
    return 'Get Me!';
  }

  @Get('/admin')
  @Role([UserRole.ADMIN])
  @UseGuards(AuthGuard)
  getAdmin() {
    return `U R Admin`;
  }
}

Custom Decorator

만약 현재 방식으로 swagger나 다른 decorator를 Auth 과정을 함께 쓴다면 controller는 골뱅이 무침이 될 것 같습니다.

그래서 우리는 Auth에 필요한 Decorator를 모~두 묶어 제공해봅시다.

Auth에 필요한 Decorator 묶기

Custom Decorator를 만들어 봅시다.

  • Auth Decorator
    현재는 2개만 적용했지만, 더 많은 데코레이터를 나열하여 적용시킬 수 있습니다.
// src/auth/auth.decorator.ts

import { applyDecorators, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
import { AllowedRole, Role } from './role.guard';

export function Auth(roles: AllowedRole[]) {
  return applyDecorators(Role(roles), UseGuards(AuthGuard));
}

참고 🔍

  • user controller
    Controller에 적용시켜 봅시다.
@Controller('user')
export class UserController {
  
  // ...

  @Get('/me')
  // @Role([UserRole.CLIENT])
  // @UseGuards(AuthGuard) 밑에꺼로 단축 가능
  @Auth([UserRole.CLIENT])
  getMe() {
    return 'Get Me!';
  }

  @Get('/admin')
  @Auth([UserRole.ADMIN])
  getAdmin() {
    return `U R Admin`;
  }
}

참고 🔍

Github Repository

profile
BackEnd Developer

1개의 댓글

comment-user-thumbnail
2021년 7월 7일

골뱅이 무침ㅋㅋㅋㅋ 글 잘 봤습니다.

답글 달기