techstack
techstack

Reputation: 1477

How to add multiple Nestjs RoleGuards in controllers

I have role guard for ADMIN, SUPERADMIN, USER, MODERATORS,

This is an example of one of the guards. An Admin guard in the case. They are working as I expected but I can't add multiple guards in the controller

import { Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';

@/Injectable()
export class AdminGuard implements CanActivate {
 constructor() { }

 canActivate(context: ExecutionContext) {
 const request = context.switchToHttp().getRequest();
 const user = request.user;

 if (user.usertype == 'Admin') {
 return true;
        }
 throw new HttpException('Unauthorized access', HttpStatus.BAD_REQUEST);
    }
}

in my controllers, I have this decorator

 
@UseGuards(AuthGuard('jwt'), AdminGuard)

I want to be able to do something like this

@UseGuards(AuthGuard('jwt'), AdminGuard, SuperAdminGuard)

or

@UseGuards(AuthGuard('jwt'), [AdminGuard, SuperAdminGuard, UserGuard])

or


@UseGuards(AuthGuard('jwt'), AdminGuard || SuperAdminGuard || UserGuard])

None of the above implementations worked. Is there a better way to go about it? Maybe there is something I am not doing right. I have checked the docs but I can't seem to make it work

Upvotes: 7

Views: 9416

Answers (2)

Kim Kern
Kim Kern

Reputation: 60547

I suggest to create one generic RolesGuard and define the required roles per controller or route handler with custom metadata:

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const userType = request.user.userType;
    return roles.some(r => r === userType);

  }
}

Custom decorator to set the required roles:

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

How to use it?

Now you can define the required roles as follows:

// For this route you need either Superadmin or Admin privileges
@Roles('Superadmin', 'Admin')
@UseGuards(AuthGuard('jwt'), RolesGuard)

Upvotes: 8

Chau Tran
Chau Tran

Reputation: 5108

Another approach rather than what Kim has answered is to use Mixin. The mixin concept is used by the AuthGuard itself.

export const UserTypeGuard: (...types: string[]) => CanActivate = createUserTypeGuard;

function createUserTypeGuard(...types: string[]) {
    class MixinUserTypeGuard implements CanActivate {
        canActivate(context: ExecutionContext) {
            const user = context.switchToHttp().getRequest().user;
            return types.some(type => user.userType === type);
        }
    }
}

Usage:

@UseGuards(AuthGuard('jwt'), UserTypeGuard('SuperAdmin', 'Admin'))

Upvotes: 1

Related Questions