---
title: "Developing a Secure API with NestJS: Managing Roles"
description: "Learn how to use NestJS, a Node.js framework powered by TypeScript, to build a secure API."
authors:
  - name: "Dan Arias"
    url: "https://auth0.com/blog/authors/dan-arias/"
date: "Sep 1, 2020"
category: "Developers,Deep Dive,Nest"
tags: ["typescript", "angular", "nest", "auth0", "full-stack", "frontend", "backend"]
url: "https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-role-based-access-control/"
---

# Developing a Secure API with NestJS: Managing Roles



<details>
<summary>Are you starting from this chapter?</summary>

Clone the application repo and check out the `adding-authorization-auth0` branch:

``` bash  
git clone git@github.com:auth0-blog/wab-menu-api-nestjs.git \
nest-restaurant-api \
--branch adding-authorization-auth0
```

Make the project folder your current directory:

``` bash  
cd nest-restaurant-api
```

Then, install the project dependencies:

``` bash  
npm i
```

If you haven't set up any Auth0 applications, follow the steps from these previous sections:

<a href="https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-authorization/#Set-Up-API-Authorization">Set Up API Authorization</a>

<a href="/blog/developing-a-secure-api-with-nestjs-adding-authorization/#Register-a-Client-Application-with-Auth0">Register a Client Application with Auth0</a>

<a href="/blog/developing-a-secure-api-with-nestjs-adding-authorization/#Connect-a-Client-Application-With-Auth0">Connect a Client Application With Auth0</a>

Create a `.env` hidden file:

``` bash  
touch .env
```

Populate `.env` with this:

``` bash  
PORT=7000
AUTH0_ISSUER_URL="Your Auth0 domain"
AUTH0_AUDIENCE="Your Auth0 audience"
```

</details>

As it is, any request with a valid [access token](https://auth0.com/docs/tokens/access-tokens) can use your API to _read and write_ data from the store. But not all users are equal: some are customers or employees that only need to read data, while a few others are employees who need to add, delete, or change the data in the store.

As such, you need to further develop your authorization strategy to check if a client application making a request on behalf of an authenticated user is authorized to perform an operation.

## Manage Access with Auth0
  
An easy way to implement this level of authorization is through role-based access control (RBAC), which refers to the idea of assigning permissions to users based on their role within an organization. For this application, you can create permissions to write data to the store and create a `menu-admin` role. You can then assign those write permissions to the `menu-admin` role and assign that role to any user that you want to make an admin.

When users successfully log in, Auth0 issues to the client application an access token that has information on any permissions the users have based on their assigned roles. Since Auth0 issues the access token as a [JSON Web Token (JWT)](https://jwt.io/), that access information can be added to the token as a [**claim**](https://auth0.com/docs/tokens/jwt-claims) named `permissions`. **JWT claims** are essentially key-value pairs encoded as a JSON object.

Consequently, your server application can inspect the access token to compare the values present in its `permissions` claim with the permissions required by the API endpoint. If the server can fully match the permissions required by the endpoint, the client request is authorized.

Implementing RBAC is easily done through the Auth0 Dashboard. Here's the plan of what you'll do:

- Create permissions for the _Menu API_ you created earlier.

- Create a role called `menu-admin`.

- Assign permissions from the _Menu API_ to the `menu-admin` role.

- Create a new user and assign it the `menu-admin` role.

Let's get started.

## Create API Permissions

Head to the [APIs page from the Auth0 Dashboard](https://manage.auth0.com/#/apis) and select the _Menu API_ that  you created earlier. 

In the _Menu API_ page, click on the _Permissions_ tab. Once there, fill in the fields and use the _+ Add_ button to create the following permissions:

- `read:items`: Read menu items

- `create:items`: Create menu items

- `update:items`: Update menu items

- `delete:items`: Delete menu items

Now that you have permissions in place, you need to tell Auth0 that you want to enforce role-based access control (RBAC) authorization for the _Menu API_. Click on the _Settings_ tab and scroll down until you see the _RBAC Settings_ section. 

Use the toggle button next to _Enable RBAC_ to turn it on. This will enforce RBAC authorization policies during the login transaction of a user. Next, enable _Add Permissions in the Access Token_ to add a `permissions` attribute (JWT claim) to the access token created by Auth0. The presence of this claim is critical for the implementation of RBAC in your NestJS app.

Make sure you press `Save` once you're done.

## Create Access Roles

Open the [Roles page from the Auth0 Dashboard](https://manage.auth0.com/#/roles) and click on the _Create Role_ button.

Fill out the form in the pop-up as follows:

- Name: `menu-admin`

- Description: Create, update, and delete menu items.

Once done, click the _Create_ button to complete the creation of the role.

Next, you need to associate permissions with that role. Permissions map a role to areas of an API. Click on the _Permissions_ tab of the role page. Once there, click on the _Add Permissions_ button.

In the dialog that comes up, choose the _Menu API_ from the dropdown box to select permissions from an existing API. A _Scopes_ section shows up. [A scope is a term used by the OAuth 2.0 protocol](https://auth0.com/blog/on-the-nature-of-oauth2-scopes/) to define limitations on the amount of access that is granted to an access token &mdash; in essence, permissions define the scope of an access token.

Select all of the scopes by clicking on them one by one or by using the _All_ link. Once that's done, click on the _Add permissions_ button. You are taken back to the `menu-admin` role page, which now lists all the permissions associated with the role.

> You don't really need to add the `read:items` scope to this role as anyone can read items from the API; however, it's a good practice to add the role in case that your business requirements change and reading data becomes a privileged operation.

## Get User Roles

You learned earlier that when you enable role-based access control (RBAC) for your Auth0 API, you can also attach the permissions created for the user as a claim to the access token. But, what about the user roles? For example, your client application may need to easily determine the roles assigned to a user to render certain parts of your user interface conditionally: if the user has the `menu-admin` role, show a button that lets the user create a new item.

To make the user roles available to your client or server application, you are going to use [Auth0 Rules](https://auth0.com/docs/rules) to enhance the tokens that are sent to the client.

When a user logs in to your application, upon success, the Auth0 authorization server sends two tokens to the client:

#### [**Access Token**](https://auth0.com/docs/tokens/access-tokens)

The access token is a credential that can be used by an application to access an API. The client application receives an access token after a user successfully authenticates and authorizes access, then it passes the access token as a credential when it calls the target API &mdash; in your case, the _Menu API_. The passed token informs the API that the bearer of the token has been authorized to access the API and to perform specific actions specified by the permissions that were granted during authorization.

#### [**ID Token**](https://auth0.com/docs/tokens/id-tokens)

The ID Token, usually referred to as `id_token` in code, is a JSON Web Token (JWT) that contains user profile attributes represented in the form of claims. The ID Token is consumed by the client application to get user information like the user's name, email, and so forth, typically used for UI display.


You'll attach the roles assigned to each authenticated user as a claim to either token using Auth0 Rules.

### What are Auth0 Rules?

[Auth0 Rules](https://auth0.com/docs/rules) are JavaScript functions that execute when a user authenticates to your application. They run once the authentication process is complete and you can use them to customize and extend Auth0's capabilities. For security reasons, your Rules code executes in a sandbox, isolated from the code of other Auth0 tenants. 

You can create Auth0 Rules easily using the Auth0 Dashboard. Follow these steps to create a rule that adds user roles to tokens:

- Open the [Rules page from the Auth0 Dashboard](https://manage.auth0.com/#/rules).

- Click on the _Create Rule_ button.

- Click on the _Empty Rule_ option.

- In the _Edit Rule_ page, provide a _Name_ to your rule, such as _Add user roles to tokens_.

- Next, replace the content of the _Script_ section with the following function:

``` javascript  
function(user, context, callback) {
  const namespace = 'https://menu-api.demo.com';

  if (context.authorization && context.authorization.roles) {
    const assignedRoles = context.authorization.roles;

    if (context.idToken) {
      const idTokenClaims = context.idToken;
      idTokenClaims[`${namespace}/roles`] = assignedRoles;
      context.idToken = idTokenClaims;
    }

    if (context.accessToken) {
      const accessTokenClaims = context.accessToken;
      accessTokenClaims[`${namespace}/roles`] = assignedRoles;
      context.accessToken = accessTokenClaims;
    }
  }

  callback(null, user, context);
}
```

- Click the _Save Changes_ button.


What's this rule doing?

When the user successfully authenticates, this rule function executes and receives [three parameters](https://auth0.com/docs/rules#syntax):

- [`user`](https://auth0.com/docs/rules/references/user-object) is an object that represents the logged in user, returned by the identity provider &mdash; this could be Auth0 or a social provider such as Google.

- [`context`](https://auth0.com/docs/rules/references/context-object) is an object that stores contextual information about the current authentication transaction, such as the user's IP address, application, or location.

- `callback` is a function to send potentially modified tokens back to Auth0 or an error. Because of the async nature of Node.js, it is important to always call the callback function or else the script will timeout.

``` javascript  
function(user, context, callback) {
  // ...
}
```

To keep your custom claims from colliding with any reserved claims or claims from other resources, you must give them a globally unique name using a namespaced format.

> By default, Auth0 always enforces namespacing; any custom claims with non-namespaced identifiers will be silently excluded from tokens.

[Namespaces are arbitrary identifiers](https://auth0.com/docs/tokens/concepts/claims-namespacing), so technically you can call your namespace anything you want. For convenience, the API audience value set in the [WAB Dashboard _Demo Settings_](https://dashboard-v1.whatabyte.app/settings) is used as the namespace.

``` javascript  
function(user, context, callback) {
  const namespace = 'https://menu-api.demo.com';
  
  //...
}
```

You then check if the `context` object has an `authorization` property which in turn has a `roles` property:

``` javascript  
function(user, context, callback) {
  const namespace = 'https://menu-api.demo.com';

  if (context.authorization && context.authorization.roles) {
   // ...
  }
  
  // ...
}
```

`context.authorization` is an object containing information related to the authorization transaction, such as roles. 

`context.authorization.roles` is an array of strings containing the names of a user's assigned roles.

Next, you assigned the `roles` array to the `assignedRoles` constant and check if there is an ID token or access token present in the `context` object:

``` javascript  
function(user, context, callback) {
  const namespace = 'https://menu-api.demo.com';

  if (context.authorization && context.authorization.roles) {
    const assignedRoles = context.authorization.roles;

    if (context.idToken) {
      // ...
    }

    if (context.accessToken) {
      // ...
    }
  }

  // ...
}
```

If any of these token objects is present, you add to it a `<namespace>/roles` property with the roles array, `assignedRoles`, as its value. This effectively creates a custom claim on the token that represents the user roles.


``` javascript  
function(user, context, callback) {
  const namespace = 'https://menu-api.demo.com';

  if (context.authorization && context.authorization.roles) {
    const assignedRoles = context.authorization.roles;

    if (context.idToken) {
      const idTokenClaims = context.idToken;
      idTokenClaims[`${namespace}/roles`] = assignedRoles;
      context.idToken = idTokenClaims;
    }

    if (context.accessToken) {
      const accessTokenClaims = context.accessToken;
      accessTokenClaims[`${namespace}/roles`] = assignedRoles;
      context.accessToken = accessTokenClaims;
    }
  }

  // ...
}
```

Finally, you invoke the `callback` function to send potentially modified tokens back to Auth0 which in turn sends them to the client:

``` javascript  
function(user, context, callback) {
  // ...

  callback(null, user, context);
}
```

That's it for creating an Auth0 rule that lets you add user roles to tokens.

What's left to do is for you to create a user that will have the `menu-admin` role. 

## Create an Admin User

Open the [Users page from the Auth0 Dashboard](https://manage.auth0.com/#/users) and click on _Create User_. Fill the form that pops up with the following:

- Email: `admin@example.com`

- Password and Repeat Password: Any password of your choice

- Connection: `Username-Password-Authentication`

Click on the _Create_ button. A user page for `admin@example.com` loads up. On this page, click on the _Roles_ tab and then click on the _Assign Roles_ button.

From the dropdown, select the `menu-admin` role that was created earlier and click on the _Assign_ button.

> Optionally, verify that the user has the right permissions by clicking on the _Permissions_ tab.

Your admin user is all set up and ready to use.

## Use the Admin Access

Head back to the WAB Dashboard and sign out. Click on the _Sign In_ button again and enter `admin@example.com` in the email box and the password you created for that user. Then, click _Log In_.

Below the _Sign Out_ button you'll now see `admin@example.com` as the username. When you open the [WAB Dashboard Menu tab](https://dashboard-v1.whatabyte.app/menu), you get the three menu items you saw with the non-admin user but, if you look at the upper right corner, you'll also see an _Add Item_ link.

Logged in with a `menu-admin` role, you can now create, edit, and delete menu items using the WAB Dashboard. However, as of now, the same could be done with a non-admin user if the client-side route protections were to be circumvented or a malicious, yet authenticated, user was able to make requests to the API manually using the access token as your API is not enforcing access control just yet.

## Hack Yourself

To see this attack vector in action, follow these steps using Google Chrome (you can use any other browser that offers the same functionality):

- Open the Chrome Dev Tools.

- Click on the _Network_ tab of the console.

- Sign out of the admin account and sign back in with a non-admin account.

- In the Network activity panel look for the `token` entry and click it.

The Chrome Dev Tools will show you an adjacent panel with details on the `token` network request. In the "Preview" tab you can see that the response to this request is an object with an `access_token` property. 

![Chrome Dev Tools using the Network tab to get an access token](https://images.ctfassets.net/23aumh6u8s0i/2FdO8T5hzsKi773JajQCr/3db6926f55a907e3dfe7993c329bde28/chrome-dev-tools-using-the-network-tab-to-get-an-access-token)

To visualize this response better:
 
- Click on the _Response_ tab of the network request details.

![Chrome Dev Tools using the Network tab to get an access token details](https://images.ctfassets.net/23aumh6u8s0i/3fwidY9axOnaZAciGEC5GF/2be6b739db27696943140c6436fd9ba3/chrome-dev-tools-using-the-network-tab-to-get-an-access-token-details)

- Copy the entire response object.

- Using your IDE or text editor, create a `res.json` file and paste the response object there. 

> Some IDEs may format this JSON file nicely for you making it easier to read.

There you have it: the complete access token that proves your identity to the _Menu API_. Copy the value of the `access_token` property and use it in the following command to access an "admin" endpoint as a non-admin user:

``` bash  
curl -X POST  -H 'Authorization: Bearer YOUR-ACCESS-TOKEN-HERE' -H 'Content-Type: application/json' -d '{
  "item": {
    "name": "Salad",
    "price": 4.99,
    "description": "Fresh",
    "image": "https://cdn.auth0.com/blog/whatabyte/salad-sm.png"
  }
}' http://localhost:7000/items -i
```

> Replace `YOUR-ACCESS-TOKEN-HERE` with the value of your `access_token` and execute the command.

You'll get a successful `201 Created` response. [Carlos Mostek, Auth0 Senior Solution Architect](https://auth0.com/blog/meet-the-experts-carlos-mostek/), explains this attack vector nicely:

"To perform authentication from the client, there will be a call from the browser to the `/oauth/token` endpoint and you'll be able to inspect the response of that call using the browser developer tools. Thus, there needs to be server-side validation for your authentication and authorization strategy to be effective. The access token that you give to the client must have the right amount of information in it, such as permissions, so that calls can be restricted on the server.  **The restriction of certain pages in a single-page application is done for user convenience** but it doesn’t actually provide any additional security as anything that lives in the client-side can be hacked."

To mitigate this attack vector, you'll implement role-based access control (RBAC) in your NestJS application by building a guard that granularly grants access to protected endpoints based on permissions associated with an user that are defined in an access token.

## Implement Role-Based Access Control in NestJS

This is the plan: You are going to attach custom metadata to a route handler through a decorator named `@Permissions`. The custom metadata will specify the permissions needed to access that route handler, such as `create:items`, and supply that permission data to a custom guard, `PermissionsGuard`.

The guard will inspect the access token provided by the client request and verify that it has the permissions required by the endpoint it needs to access. Consequently, if the proper permissions are present in the access token, access to the endpoint is granted; otherwise, an unauthorized error is sent to the client.

To start, head back to your NestJS project and run the following command to create the `Permissions` decorator:

``` bash  
nest generate decorator permissions --no-spec
```

The file `permissions.decorator.ts` is created under the `src` directory. Populate this file as follows:

``` typescript  
// src/permissions.decorator.ts

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

export const Permissions = (...permissions: string[]) =>
  SetMetadata('permissions', permissions);
```

The `SetMetadata()` method is used to associate an array of strings representing permissions with a `permissions` key. This key-value pair will be attached to whatever method `@Permissions` decorates. For example, the route handler of `POST items/` would look like this:

``` typescript  
@UseGuards(AuthGuard('jwt'))
@Post()
@Permissions('create:items')
create(@Body('item') item: Item) {
  this.itemsService.create(item);
}
```

Next, you need to tie the permissions metadata with a guard. Issue the following command to create a `PermissionsGuard` file:

``` bash  
nest generate guard permissions --no-spec
```

The file `permissions.guard.ts` is created under the `src` directory. Populate this file as follows:

``` typescript  
// src/permissions.guard.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';

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

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const routePermissions = this.reflector.get<string[]>(
      'permissions',
      context.getHandler(),
    );

    const userPermissions = context.getArgs()[0].user.permissions;

    if (!routePermissions) {
      return true;
    }

    const hasPermission = () =>
      routePermissions.every(routePermission =>
        userPermissions.includes(routePermission),
      );

    return hasPermission();
  }
}
```

Let's go over what's happening in this guard implementation.

In this guard, you want to return a conditional value based on whether or not the authenticated user has the required permissions to access the endpoint. As you can see, there are a lot of moving parts in this authorization flow. At a high-level:

- The `JwtStrategy` Passport strategy gets invoked which validates the client request. 

- Then, control is passed to `ItemsController`.

- `ItemsController` matches the route path with one of its route handlers and invokes it if the route guards return `true`.

These three separate high-level stages need to share data: access tokens and permissions metadata. NestJS facilitates this data sharing across the request handling cycle through an `ExecutionContext` object. This object provides additional details about the current execution process through methods such as `getHandler()` to get a reference to the route handler function and `getArgs()` to get an array with arguments passed natively to the application. By default NestJS uses Express under the hood, so this array has the original list of parameters passed to the original Express handler: `[req, res, next]`.

`PermissionsGuard` uses an instance of the `Reflector` helper class provided by NestJS to easily access the metadata attached to a router handler by the `@Permissions` decorator. To get the route permissions, you use the `reflector.get()` method which takes as arguments the key of the property you want to extract from the metadata, in this case `permissions`, and a reference to the route handler function where the metadata has been attached, `context.getHandler()`.

These are the _local_ permissions that you'll need to compare with the permissions claim of the access token. You can access the access token of the authenticated user through the `user` object returned by the `context.getArgs()` method.

When comparing local and access token permissions, you return `true` from `PermissionsGuard` if and only if:

- The route handler doesn't have any permissions set.

- The route handler has permissions set and each one of them is present in the permissions claim of the access token.

With `PermissionsGuard` and the `@Permission` decorator implemented, it's time to integrate them into the route handlers of `ItemsController`. Open `src/items/items.controller.ts` and update it like so:

``` typescript  
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  UseGuards,
} from '@nestjs/common';
import { ItemsService } from './items.service';
import { Items } from '../items';
import { Item } from '../item';
import { AuthGuard } from '@nestjs/passport';
import { Permissions } from '../permissions.decorator';
import { PermissionsGuard } from '../permissions.guard';

@Controller('items')
export class ItemsController {
  constructor(private readonly itemsService: ItemsService) {}

  @Get()
  async findAll(): Promise<Items> {
    return this.itemsService.findAll();
  }

  @Get(':id')
  async find(@Param('id') id: number): Promise<Item> {
    return this.itemsService.find(id);
  }

  @UseGuards(AuthGuard('jwt'), PermissionsGuard)
  @Post()
  @Permissions('create:items')
  create(@Body('item') item: Item) {
    this.itemsService.create(item);
  }

  @UseGuards(AuthGuard('jwt'), PermissionsGuard)
  @Put()
  @Permissions('update:items')
  update(@Body('item') item: Item) {
    this.itemsService.update(item);
  }

  @UseGuards(AuthGuard('jwt'), PermissionsGuard)
  @Delete(':id')
  @Permissions('delete:items')
  delete(@Param('id') id: number) {
    this.itemsService.delete(id);
  }
}
```

The protection of the write endpoints (`POST items/`, `PUT items/`, `DELETE items/:id`) is enhanced to include role-based access control (RBAC) by adding the `PermissionsGuard` as an additional argument to the `@UseGuards()` decorator. You passed the `PermissionsGuard` type, instead of an instance like `AuthGuard`, to delegate the responsibility of instantiating the guard to the framework and enabling dependency injection.

Try to create a new menu item again using the non-admin access token and you'll see how it fails:

``` bash  
curl -X POST  -H 'Authorization: Bearer YOUR-ACCESS-TOKEN-HERE' -H 'Content-Type: application/json' -d '{
  "item": {
    "name": "Coffee",
    "price": 2.99,
    "description": "Woke",
    "image": "https://cdn.auth0.com/blog/whatabyte/coffee-sm.png"
  }
}' http://localhost:7000/items -i
```

This time, you get a [`403 Forbidden`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) response thanks to the RBAC protection you added to your NestJS API:

``` bash  
{"statusCode":403,"error":"Forbidden","message":"Forbidden resource"}
```

Sign out of the non-admin account and sign back in as the admin user in the WAB Dashboard. Try to add a new item. The _Add Item_ form is pre-loaded with some data to make this process easier for you. If you already created the salad item, try to create a coffee item with this data:

``` bash  
name: Coffee
price: 2.99
description: Woke
image: https://cdn.auth0.com/blog/whatabyte/coffee-sm.png
```

Click on that newly created item and notice that you can either edit or delete it. Try both operations.

![WAB Dashboard showing a newly added menu item, coffee](https://images.ctfassets.net/23aumh6u8s0i/4MKo6ZNM62Oj00d1b8UCP6/6cda36fceb17b48bfd400d7242ae3e6b/wab-dashboard-showing-a-newly-added-menu-item-coffee)


## Security Exercise

As an experiment to test the robustness of your API, go back to [Roles page from the Auth0 Dashboard](https://manage.auth0.com/#/roles) and edit the `menu-admin` role. Remove the `create:items` permission from it.

Refresh the WAB Dashboard to rotate the access token. Under the hood, the WAB Dashboard is using [RxJS](https://auth0.com/blog/whats-new-in-rxjs-6/) and the [Auth0 SPA SDK](https://auth0.com/docs/libraries/auth0-spa-js) to power an authentication service. Anytime you refresh the Dashboard, a new instance of an Auth0 client is created with a new and updated access token.

Then, try to create an item again. You should get a `403 (Forbidden)` error from the server. Open an existing menu item and either delete it or update it. Since those permissions are still associated with the `menu-admin` role and are present in the `permissions` claim of the access token, these two operations (update and delete) should be successful.

Let me know if you were able to complete this exercise!

## What's Next

This concludes the tutorial for now. What other chapters should be added? This is what I have in mind for the future:

- Deploying a NestJS application to Heroku and AWS.

- Connecting a NestJS application to a MongoDB or PostgreSQL store.

- Using GraphQL or gRPC with NestJS.

Let me know through our feedback section if these are chapters you'd like to read or what other content I should cover. Thank you for reading this far and happy engineering!

<include src="tutorial/FeedbackButton" communityTopic="developing-a-secure-api-with-nestjs/33026"/>
