In this article, we will focus on Role Based Access Control (RBAC) using the Guards in a nestJS application.

Guard

A guard is a class annotated with the @Injectable() decorator. Guards should implement the CanActivate interface.

image

Guards have a single responsibility. They determine whether a given request will be handled by the route handler or not, depending on certain conditions (like permissions, roles, ACLs, etc.) present at run-time. This is often referred to as authorization. Authorization (and its cousin, authentication, with which it usually collaborates) has typically been handled by middleware in traditional Express applications.

Authorization guard

Authorization is a great use case for Guards because specific routes should be available only when the caller (usually a specific authenticated user) has sufficient permissions.

Role Based Authentication

Role-based access control (RBAC) restricts network access based on a person’s role within an organization and has become one of the main methods for advanced access control.

The roles in RBAC refer to the levels of access that employees have to the network. Employees are only allowed to access the information necessary to effectively perform their job duties. Access can be based on several factors, such as authority, responsibility, and job competency.

image

As a result, lower-level employees usually do not have access to sensitive data if they do not need it to fulfill their responsibilities.

This is especially helpful if you have many employees and use third-parties and contractors that make it difficult to closely monitor network access. Using RBAC helps in securing your company’s sensitive data and important applications.

Creating application

Lets create a new application from scratch using : nest new nest-authorization

Creating initial boilerplate for our app:

To do authorization we need to know who the user is and what roles do they have.

To do this we also need some kind of example api to allow some and protect some depending on user roles.

Lets create a new resource users using : nest g resource users

Screenshot 2022-01-04 at 11 21 23 AM

Defining user entities and roles enum:

To get started our user needs to have roles for us to be able to do role based authorization. We need to have a representation of different roles. We can start with a simple enum file.

export enum Role {
  USER = "user",
  ADMIN = "admin",
}

Testing the initial build

Lets first run the application and see how it behaves originally using npm run start:dev

Screenshot 2022-01-04 at 9 56 11 PM

Now we have sort of a mock api which is open to everyone right now.

Screenshot 2022-01-04 at 9 58 47 PM

Screenshot 2022-01-04 at 9 58 47 PM

Implementation and integration

Now we need to check what role does a user have and are they allowd to access these requests.

We can do this by decorating the methods in users.controllers.ts by using setMetadata() Decorator.

There is also another way that is by using a custom decorator that represents the same thing that setMetadata( ) can do.

In users folder create a roles.decorator.ts

import { SetMetadata } from "@nestjs/common";
import { Role } from "./entities/role.enum";

export const Roles = (...roles: Role[]) => SetMetadata("roles", roles);

Now in users.controller.ts add this decorator .

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  SetMetadata,
} from "@nestjs/common";
import { UsersService } from "./users.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { Role } from "./entities/role.enum";
import { Roles } from "./roles.decorator";
@Controller("users")
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  @Roles(Role.ADMIN)
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(":id")
  findOne(@Param("id") id: string) {
    return this.usersService.findOne(+id);
  }

  @Patch(":id")
  @Roles(Role.ADMIN)
  update(@Param("id") id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(":id")
  @Roles(Role.ADMIN)
  remove(@Param("id") id: string) {
    return this.usersService.remove(+id);
  }
}

This doesn’t protect the method yet we are just providing metadata.

Now lets add the piece that adds the actual security i.e. lets create a guard.

Creating a guard

Inside users create a new Roles guard roles.guard.ts

Here we need to check if the user has the required roles to allow the user.

Because we do not have authentication in place which you should to identify the user here we will keep it really simple and mock a user considering we did the authentication.

import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { Observable } from "rxjs";
import { Role } from "./entities/role.enum";
import { User } from "./entities/user.entity";
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}
  canActivate(context: ExecutionContext): boolean {
    const requireRoles = this.reflector.getAllAndOverride<Role[]>("roles", [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requireRoles) {
      return true;
    }
    //const {user}=context.switchToHttp().getRequest();
    const user: User = {
      name: "Nishant",
      roles: [Role.USER],
    };
    return requireRoles.some((role) => user.roles.includes(role));
  }
}

Testing

Generic User

If a user is a generic. Then

Screenshot 2022-01-08 at 3 21 55 PM

It will only be able to perform get request. Because here generic user is only allowed get request by the guard.

Screenshot 2022-01-04 at 9 58 47 PM

For a post request by the user it will give a 403 forbidden error.

Screenshot 2022-01-04 at 9 58 47 PM

Admin User

If a user has admin role.

Screenshot 2022-01-08 at 3 21 55 PM

it will be able to perform get request, post, Patch, delete. Screenshot 2022-01-04 at 9 58 47 PM

Screenshot 2022-01-04 at 9 58 47 PM

References

  1. https://docs.nestjs.com/guards
  2. https://digitalguardian.com/blog/what-role-based-access-control-rbac-examples-benefits-and-more

This was all about using guards in a nestJS application to authorize. You can find the code example here

Categories:

Updated:

Nishant Gharat
WRITTEN BY

Nishant Gharat

Someone passionate about learning, and function effectively in a team. Someone who can develop a scalable working solution from an idea on a piece of paper. I like coding long rides and am a big foodie.

Leave a comment