javascript - NestJS - Como usar RoleGuard com JWT?


Atualmente, consegui usar o AuthGuard com autenticação JWT, mas não consigo obter o user nos Roles.

Tentei seguir o exemplo dos gatos, mas nunca consigo definir o objeto user, podes ver na linha 14.

Este é o meu código:

// auth.controller.ts
@Controller('auth')
@UseGuards(RolesGuard)
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Get('token')
  async createToken(): Promise<any> {
    return await this.authService.createToken();
  }

  @Post()
  @UseGuards(AuthGuard())
  @Roles('admin')
  findAll() {
    // this route is restricted by AuthGuard
    // JWT strategy
    return 'Super important info';
  }
}


// auth.module.ts
@Module({
  imports: [
    SharedModule,
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.registerAsync({
      imports: [SharedModule],
      useFactory: async (configService: ConfigService) => ({
        secretOrPrivateKey: configService.get('SECRET_KEY'),
        signOptions: {
          expiresIn: configService.get('SECRET_KEY_EXPIRES'),
        },
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AuthController],
  providers: [
    AuthService,
    JwtStrategy,
  ],
})
export class AuthModule {}

Todo o resto é exatamente como no exemplo do repositório. Alguma ideia de como conseguir definir os users?

Resposta #1

O AuthGuard() tem de ser executado antes do RolesGuard para que esteja autenticado e a propriedade user esteja definida.

Até onde sei, não há outra maneira de alterar a ordem dos seus guardas senão definindo-os como:

@UseGuards(AuthGuard(), RolesGuard)

Há uma discussão sobre uma API potencial para alterar a hierarquia de execução dos guardas neste problema.

Esta questão menciona uma solução alternativa:

Para este caso de uso, sinto que colocar a lógica de autenticação num middleware seria melhor, pois ele executa antes do guardião.

Resposta #2

Isto é o que funcionou para mim

jwt.guard.ts

import { AuthGuard } from '@nestjs/passport';

export class JwtGuard extends AuthGuard('jwt') {
  constructor() {
    super();
  }
}

app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: JwtGuard,
    },
    {
      provide: APP_GUARD,
      useClass: RoleGuard,
    },
  ],
})
export class AppModule {}

role.decorator.ts

import { SetMetadata } from '@nestjs/common';
import { Role } from 'common/enums';

export const ROLE_KEY = 'role';
export const AllowRole = (role: Role) => SetMetadata(ROLE_KEY, role);

role.guard.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLE_KEY } from 'common/decorators';
import { Role } from 'common/enums';

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

  canActivate(context: ExecutionContext): boolean {
    const requiredRole = this.reflector.getAllAndOverride<Role>(ROLE_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRole) {
      return true;
    }

    const { user } = context.switchToHttp().getRequest();
    console.log({ requiredRole });
    console.log({ user });
    return user.role === requiredRole;
  }
}