DEV Community

Mofajjal Rasul
Mofajjal Rasul

Posted on • Edited on

1

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.

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

AWS Security LIVE! Stream

Go beyond the firewall

Watch AWS Security LIVE! to uncover how today’s cybersecurity teams secure what matters most.

Learn More

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay