user13079998
user13079998

Reputation:

How does RoleGuard works in Nest JS? with JWT

The feature I am working on is that the system should determine a correct type of user and allow appropriate permissions after successful login and what I had in mind is to use RoleGuard feature of nest JS.

But I can't seem to figure out how this RoleGuard really works.And can't seem to make it work.

I wanted to allow user with specific rules to access some endpoints , like only user with admin role is allowed to get all list of users.

What seem to be the issue ? Anyone has an idea ? I have provided snippets below. And upon requesting should I'll just be adding role in the request body ? or it would be good if Ill get the current logged user and determine the role ? Thank you.

Here is my user data which has the role:

"id": 11, {
    "role": "admin",
    "username": "[email protected]",
    "created": "2020-03-18T02:30:04.000Z",
    "updated": "2020-03-18T03:02:12.000Z"
}

SampleCode

 import { JwtAuthGuard } from '../../auth/jwt-auth.guard';
    import { RolesGuard } from '../../common/guards/roles.guard';
    import { Roles } from '../common/decorators/roles.decorator';

    @Controller('user')
    @UseGuards(JwtAuthGuard, RolesGuard)
    export class UserController {
      constructor(private readonly usersService: UsersService) {}

      @Get()
      @Roles('admin')
      findAll(): Promise<UserEntity[]> {
        return this.usersService.findAll();
      }

RoleGuard

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

      canActivate(context: ExecutionContext): boolean {
        const roles = this.reflector.get<string[]>('roles', context.getHandler());
        console.log('roles:', roles);
        if (!roles) {
          return true;
        }
        const request = context.switchToHttp().getRequest();
        const user = request.user;
        const hasRole = () => user.roles.some(role => roles.indexOf(role) > -1);
        console.log('hasRole', hasRole);

        return user && user.roles && hasRole();
      }
    }

Upvotes: 2

Views: 2281

Answers (1)

Thiago Gabriel Braz
Thiago Gabriel Braz

Reputation: 11

To this code works you need to add User obj into request context using an AuthGuard.

First off you don`t need a JwtAuthGuard if you not implement another things the Standard AuthGuard do, Adding JwtAuthGuard into UseGuards decorator mades a overwrite of default AuthGuard and if you not adding the user obj into request obj inside of JwtAuthGuard code, the RolesGuard not will work correctly.

Standard Approach

If you see the source code into the line 48 the attaches the user obj into request obj. After seeing this it`s simply.. just add into @UseGuards decorator the AuthGuard('jwt'),RolesGuards like that

import { AuthGuard } from '@nestjs/passport';
import { RolesGuard } from '../../common/guards/roles.guard';
import { Roles } from '../common/decorators/roles.decorator';

@Controller('user')
@UseGuards(AuthGuard('jwt'), RolesGuard)
export class UserController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  @Roles('admin')
  findAll(): Promise<UserEntity[]> {
    return this.usersService.findAll();
  }

Doing that the Standard Approach to RolesGuards wil runs correctly... Now if you doing a different things and need a custom AuthGuard.. You need to add the User Obj returned of the validate function of JwtStrategy Class. Like that:

import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
    handleRequest(err: any, user: any, info: any, context: ExecutionContext) {
        const request = context.switchToHttp().getRequest();
        request.user = user;
        return user;
    }
}

Upvotes: 1

Related Questions