DEV Community

Cover image for Custom Role-Based Access Control in NestJS Using Custom Guards
Mofajjal Rasul
Mofajjal Rasul

Posted on • Edited on

Custom Role-Based Access Control in NestJS Using Custom Guards

This technical walkthrough demonstrates the implementation of a production-ready Role-Based Access Control (RBAC) system in NestJS, leveraging custom guards and metadata reflection to enforce granular endpoint security. The architecture adheres to modular design principles while maintaining compatibility with NestJS's authentication ecosystem.


Architectural Components

  1. Role Enumeration Definition Establishes standardized application roles with type safety:
export enum ApplicationUserRoleEnum {
  ADMIN = 'ADMIN',
  OWNER = 'OWNER',
  USER = 'USER',
}
Enter fullscreen mode Exit fullscreen mode
  1. Role Requirement Decorator Enables declarative endpoint security configuration:
import { SetMetadata } from '@nestjs/common';
import { ApplicationUserRoleEnum } from '../enum/application-user-role.enum';

export const RequiredRoles = (...roles: ApplicationUserRoleEnum[]) => SetMetadata('roles', roles);
Enter fullscreen mode Exit fullscreen mode
  1. Access Control Guard Implements request interception logic with contextual authorization:
import {
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Injectable,
  Logger,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { RequiredRoles } from '../decorator/roles.decorator';
import { ApplicationUserRoleEnum } from '../enum/application-user-role.enum';

@Injectable()
export class ApplicationUserRolesGuard implements CanActivate {
  private readonly logger = new Logger(ApplicationUserRolesGuard.name);

  constructor(private reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const roles = this.reflector.get<ApplicationUserRoleEnum[]>(
      RequiredRoles,
      context.getHandler(),
    );

    if (!roles || roles.length === 0) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    if (
      !user ||
      !roles.some((role) => user.role === role)
    ) {
      this.logger.error(`Unauthorized role: required roles are ${roles.join(', ')}`);
      throw new ForbiddenException('You do not have access to perform this action');
    }

    return true;
  }
}
Enter fullscreen mode Exit fullscreen mode

System Integration

1. Global Guard Registration

Enables application-wide security policy enforcement:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { ApplicationUserRolesGuard } from './guards/application-user-roles.guard';
import { ApplicationUserController } from './controllers/application-user.controller';
import { ApplicationUserService } from './services/application-user.service';

@Module({
  controllers: [ApplicationUserController],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ApplicationUserRolesGuard,
    },
    ApplicationUserService,
  ],
})
export class ApplicationUserModule {}
Enter fullscreen mode Exit fullscreen mode

2. Endpoint Security Configuration

Demonstrates tiered access control implementation:

import { Controller, Get, Patch, Param, Delete, Body } from '@nestjs/common';
import { RequiredRoles } from '../decorator/roles.decorator';
import { ApplicationUserRoleEnum } from '../enum/application-user-role.enum';
import { UpdateUserDto } from '../dto/update-user.dto';

@Controller('users')
export class ApplicationUserController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    return `User ${id} details`;
  }

  @Patch(':id')
  @RequiredRoles(ApplicationUserRoleEnum.ADMIN, ApplicationUserRoleEnum.OWNER)
  update(
    @Param('id') id: string,
    @Body() updateUserDto: UpdateUserDto,
  ) {
    return `Updated user ${id} with data ${JSON.stringify(updateUserDto)}`;
  }

  @Delete(':id')
  @RequiredRoles(ApplicationUserRoleEnum.ADMIN)
  remove(@Param('id') id: string) {
    return `Deleted user ${id}`;
  }
}
Enter fullscreen mode Exit fullscreen mode

Security Workflow Analysis

  1. Request Interception

    • Guard intercepts incoming HTTP requests
    • Extracts role requirements from endpoint metadata
  2. Authorization Validation

    • Compares authenticated user's roles with endpoint requirements
    • Implements logical OR permission evaluation
  3. Access Decision

    • Grants access for matching role profiles
    • Throws ForbiddenException for unauthorized requests
  4. Audit Logging

    • Records authorization failures with required role context
    • Maintains security incident trail

Production Considerations

  1. Performance Optimization

    • Metadata caching for rapid role requirement retrieval
    • Lean validation logic for minimal request overhead
  2. Security Enhancements

    • JWT claim validation integration
    • Session management compatibility
  3. Extensibility Features

    • Dynamic role configuration support
    • Multi-factor authentication integration points
  4. Monitoring Capabilities

    • Metrics collection for authorization attempts
    • Alerting mechanisms for repeated failures

Enterprise Implementation Patterns

  1. Hierarchical RBAC

    • Implement role inheritance structures
    • Develop tiered permission escalation protocols
  2. Attribute-Based Control

    • Combine role and resource attributes
    • Implement contextual access policies
  3. Distributed Systems Support

    • Microservices permission propagation
    • Cross-service role validation

This implementation establishes a robust foundation for enterprise authorization requirements, offering:

  • Declarative Security Configuration: Simplified endpoint protection through decorators
  • Centralized Policy Management: Unified guard implementation across modules
  • Audit-Compliant Logging: Detailed authorization attempt records
  • Scalable Architecture: Support for complex organizational structures

The pattern serves as a critical component in modern application security stacks, providing granular access control while maintaining developer productivity and system performance. Future enhancements could incorporate time-based restrictions or geolocation validation for heightened security postures.

Top comments (0)