DEV Community

Cover image for Role-Based Access Control in NestJS
Anandhu Prakash
Anandhu Prakash

Posted on

3 1 1 1 1

Role-Based Access Control in NestJS

Role-Based Authorization in NestJS

Authorization is a critical aspect of application security. It ensures that users can access only the resources they’re permitted to. In NestJS, a progressive Node.js framework, implementing role-based authorization is straightforward thanks to its modular design and support for decorators.

What is Role-Based Authorization?

Role-based authorization assigns permissions to users based on their roles. Common roles include “admin,” “user,” and “guest.” Each role has a predefined set of actions it can perform. For example:

  • Admin: Can manage all resources.
  • User: Can read and update their own data.
  • Guest: Can only view public resources.

By implementing role-based authorization, you can easily control access within your application.

Setting Up Role-Based Authorization in NestJS

Here’s how you can implement role-based authorization in NestJS:

1. Install Dependencies

First, ensure you have a NestJS project set up. If not, you can create one using the CLI:

npm i -g @nestjs/cli
nest new my-app
Enter fullscreen mode Exit fullscreen mode

Next, install @nestjs/passport and passport for authentication support:

npm install @nestjs/passport passport passport-jwt
npm install --save-dev @types/passport-jwt
Enter fullscreen mode Exit fullscreen mode

2. Define Roles

Create a roles.enum.ts file to define roles as an enum:

export enum Role {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
}
Enter fullscreen mode Exit fullscreen mode

3. Create a Roles Guard

Guards in NestJS intercept requests before they reach route handlers. Create a custom guard to enforce role-based access control.

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from './roles.enum';

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

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<Role[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return roles.some((role) => user?.roles?.includes(role));
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Create a Roles Decorator

To simplify the process of adding roles to route handlers, create a custom decorator:

import { SetMetadata } from '@nestjs/common';
import { Role } from './roles.enum';

export const Roles = (...roles: Role[]) => SetMetadata('roles', roles);
Enter fullscreen mode Exit fullscreen mode

5. Apply the Roles Guard Globally or Locally

To make the RolesGuard active, you can apply it globally or locally. For global application, add it to your AppModule:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { RolesGuard } from './roles.guard';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

For local application, use it in specific controllers or methods:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles } from './roles.decorator';
import { RolesGuard } from './roles.guard';
import { Role } from './roles.enum';

@Controller('resources')
@UseGuards(RolesGuard)
export class ResourcesController {
  @Get()
  @Roles(Role.Admin, Role.User)
  findAll() {
    return 'This endpoint is accessible to Admins and Users.';
  }
}
Enter fullscreen mode Exit fullscreen mode

6. Authenticate Users

To identify a user’s roles, ensure your authentication process adds the roles to the user object in the request. This often involves decoding a JWT or using a session store.

Example Authentication Middleware

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers['authorization'];
    if (token) {
      const decoded = this.decodeToken(token); // Implement JWT decoding
      req.user = decoded;
    }
    next();
  }

  private decodeToken(token: string): any {
    // Logic to decode JWT and retrieve user data
    return { roles: ['user'] }; // Example decoded payload
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing the Implementation

Start your application and test endpoints with users of different roles. Ensure each user can access only what their role permits.

Example Requests

  • Admin User: Should access all routes.
  • Regular User: Should access user-specific routes.
  • Guest: Should be restricted to public routes.

Conclusion

Role-based authorization in NestJS is clean and efficient thanks to decorators, guards, and the framework’s modularity. By following the steps above, you can secure your application while maintaining flexibility and scalability.

For more complex scenarios, consider integrating with external libraries like Casl or implementing attribute-based access control (ABAC).

Sentry blog image

Identify what makes your TTFB high so you can fix it

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

Read more

Top comments (0)

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay