Skip to main content

NestJS

Learn how to leverage the Permit API and SDK to seamlessly integrate robust access control into your NestJS application. This step-by-step guide walks you through setting up your environment, creating policies with the Permit CLI with minimal code and maximum flexibility.


In this guide, we will quickly go through the steps to integrate Permit.io with NestJS. We will use Permit's CLI to create a new environment and a policy with a template. Then, we will use the Permit SDK to integrate it with our NestJS application. We will also set up the PDP (Policy Decision Point) server locally using Docker.

Prerequisites

  • NestJS application
  • Docker (for running the PDP server)
  • Node.js (for installing and running the Permit CLI)
  • Permit.io account (Follow this guide to create an account)
1

Installing the Permit CLI

We will first install the Permit CLI. Permit CLI is a command-line tool that allows you to create policies, run the PDP server, and perform other tasks. To install the CLI, execute the command below in your terminal.

npm install -g @permitio/cli

Once the installation is complete, execute permit to confirm the installation.

2

Authenticating the CLI

To use the CLI, we need to authenticate it with our Permit account. To authenticate, execute

permit login

This will open a browser window and ask you to log in with your Permit account. After logging in, the CLI will be authenticated and ready to use. This will also log you into the default environment (a project can have multiple environments). We can change the environment by executing permit env select and then selecting the environment we want to use.

3

Creating a policy with a template

Now, let's create a policy with a template. The benefit of using a template is that we can create a policy with predefined roles and resources using best practices. This will save us time and effort in creating a policy from scratch. To check the available templates, execute permit template list. Based on our needs, we can apply the template to a new policy by executing permit template apply --template <template-name>.

We will be using the blog-rbac template to create a new policy. This template is great for a blog application. It has a Viewer role that can view the blog posts and a Editor role that can edit the blog posts. It also has a Post resource that represents a blog post.....

To apply the template, execute the command below.

permit template apply --template blog-rbac

This will create a new policy with the blog-rbac template

4

Accessing the policy

Now, let's visit the Permit dashboard to check the created policy and also get the API key that we will use to authenticate our application when using the Permit SDK.

As we can see in the image below, the CLI has created Resources, Roles, and Policies. Now, let's add Read permission to the Viewer for the Visit resource. It is very easy to add permissions using the dashboard. We can also add more roles and resources as per our needs.

permit policy

To get the API key, click on Projects from the left sidebar, then click on the three dots on the environment we want to use, and click on Copy API Key and store it in a safe place. We will be using this API key to authenticate our application with Permit.io. If you are having any difficulty getting the API key, follow this guide.

Once we are done, let's move toward the implementation part and integrate Permit.io with our Express.js application.

5

NestJS Integration

Once you have a NestJS application ready (or you can create a new one for the sake of this guide), we can integrate Permit.io with it. First, we need to install the Permit's Node.js SDK. To install the SDK, execute the below command in your terminal.

npm install permitio

Read more about the Node.js SDK here.

Once the installation is complete, now let's create a Permit provider. Create a new file permit.provider.ts in the src/permit directory (it may vary depending on your project structure) and add the following code.

// src/permit/permit.provider.ts
import { Permit } from 'permitio';

export const permit = new Permit({
token: process.env.PERMIT_API_KEY!,
pdp: process.env.PDP_URL!,
});

Here, we are importing the Permit class from the permitio package and creating a new instance of it. It needs two environment variables PERMIT_API_KEY and PDP_URL. The PERMIT_API_KEY is the API key we copied from the Permit dashboard, and the PDP_URL is the URL of the PDP server. We will set this up later.

Now, we will create two endpoints for syncing users and creating tenants. Create a new file app.controller.ts in the src directory and add the following code. Also, create a guard to protect our endpoints.

// src/app.controller.ts
import { Controller, Post, Body, HttpException, HttpStatus, UseGuards, Get } from '@nestjs/common';
import { permit } from './permit/permit.provider';
import { PermitGuard } from './permit/permit.guard';

@Controller()
export class AppController {
@Post('sync-user')
async syncUser(@Body() body: { email: string, first_name: string, last_name: string }) {
const { email, first_name, last_name } = body;
if (!email || !first_name || !last_name) {
throw new HttpException('Missing required fields', HttpStatus.BAD_REQUEST);
}
try {
const user = await permit.api.users.sync({
key: email,
email,
first_name,
last_name,
});
return user;
} catch (err) {
throw new HttpException('Failed to sync user', HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@Post('create-tenant')
async createTenant(@Body() body: { key: string, name: string, description?: string }) {
const { key, name, description } = body;
if (!key || !name) {
throw new HttpException('Missing key or name', HttpStatus.BAD_REQUEST);
}
try {
const tenant = await permit.api.tenants.create({
key,
name,
description,
});
return tenant;
} catch (err) {
throw new HttpException('Failed to create tenant', HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@UseGuards(PermitGuard)
@Get('protected/secret')
getSecret() {
return { secret: "You have pass the auth check" };
}
}

In this code, we have created two endpoints: sync-user and create-tenant. The sync-user endpoint is used to sync a user with Permit.io. It expects a unique key, email, first name, and last name. The create-tenant endpoint is used to create a tenant with Permit.io. It expects a unique key, name, and description.

The sync-user endpoint uses the sync method of the Permit class to sync the user with Permit.io. The create-tenant endpoint uses the create method of the Permit class to create a tenant with Permit.io. Both methods return the created user and tenant respectively.

Next, we will create a guard to protect our endpoints. It's similar to middleware in Express.js. Create a new file permit.guard.ts in the src/permit directory and add the following code.

import { CanActivate, ExecutionContext, Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { permit } from './permit.provider';

@Injectable()
export class PermitGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.headers['x-user'];
const action = request.headers['x-action'];
const resource = request.headers['x-resource'];

if (!user || !action || !resource) {
throw new HttpException('Missing permission info', HttpStatus.BAD_REQUEST);
}

try {
const permitted = await permit.check(user, action, resource);
if (!permitted) {
throw new HttpException('User is not permitted', HttpStatus.FORBIDDEN);
}
return true;
} catch (err) {
throw new HttpException('Permission check failed', HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

In this code, we have created a guard PermitGuard which checks if the user has access to the resource. The canActivate method is called before the request is handled. It checks if the user has access to the resource using the check method of the Permit class. If the user is not permitted, it throws an exception with status code 403.

6

Setting up the PDP server

Before we run the application and check the routes and enforcement, we need to set up PDP. To enforce the policy, we use Permit's PDP (Policy Decision Point) to check if the user has access to the resource. So first, let's set up the PDP.

We can leverage Permit's CLI to start a PDP server locally. Otherwise, we would need to run long Docker commands to start the PDP server. The PDP server is a service that evaluates access requests and returns a decision (permit or deny) based on the policies defined in Permit.io. To start it, execute the command below in your terminal. Make sure Docker is running on your machine, as this will start a PDP container locally.

permit pdp run

Once you execute the above command, you will see an output like below with information such as container ID, name, etc. It will start a PDP server locally on your machine. You can check the logs to see if the PDP server is running successfully. The PDP server will be running on port 7766 by default. You can also change the port.

permit pdp run

Once the PDP server is running, update the PDP_URL environment variable in your application.

7

Syncing the user

Now, we have everything ready. Let's run the application and do curl requests to test the endpoint for the sync user route.

curl create user

As we can see in the image above, the user is created successfully and we get a lot of information about the user in the response, such as id, project_id, key, etc.

8

Giving the user access to the resource

Let's now head over to the Permit dashboard and check if the user was created successfully. To do that, select Directory from the left sidebar, and we can see the user we just created. Let's give this user access to the resource we created in the policy. To do that, select the user we just created. Then click on the Add Role button, select Tenant, and then select the Viewer role and save it. This will give the user access to the Visit resource we created in the policy. We will verify this later when we check the access. Also, we can give the access programmatically, it was a demo to show how easy it is to add access using the dashboard.

user access

We can also add more roles and resources as per our needs.

9

Checking the access

Now, let's check the policy enforcement. As we have added the user to the Viewer role, we can check if the user has access to the resource. To do that, we will use curl to hit the /protected/secret endpoint. We will pass the user key, action, and resource in the headers.

curl check access

As we can see in the image above, the user is permitted to access the resource. Let's remove the access from the dashboard and check again.

curl check access

Now this time, we can see that the user is not permitted to access the resource. This is how we can enforce policies using Permit.io with NestJS.

Conclusion

In this guide, we have seen how to integrate Permit.io with NestJS. We have created a policy with a template using the Permit CLI. We have also seen how to sync users and create tenants using the Permit SDK. Finally, we have seen how to enforce policies using the Permit SDK in our NestJS application.

Further Reading