When building web apps, one of the important factors you should consider is how users will access various parts of your app. It is best practice to manage user permissions and access by restricting or granting access to certain application areas. In this post, we'll look at how to use Cerbos PDP (Policy Decision Point) to establish role-based access control (RBAC) in an Express application.
If you're familiar with Node.js, you've probably heard of Express. Many engineers opt to use Express, a Node.js framework, for developing JavaScript web apps. Cerbos PDP can be described as an engine for authorizing access to different parts of your app based on certain rules and policies you set.
RBAC is all about assigning permissions to specific roles rather than individual users. Once roles are defined, users are assigned to these roles based on their responsibilities.
Let's say for example, you are building a learning management system. This will call for properly defined roles in your web app.
Following our LMS example, we are going to build a sample project to demonstrate the implementation of RBAC in Express with Cerbos PDP.
Before we begin, there are a few prerequisites you need to have in place. Please ensure that Node.js, Express, and most importantly, Cerbos PDP are installed on your system.
Install Node.js for your platform (we recommend LTS) and verify the installation by running the following command in your terminal:
node -v
Once Node.js is installed, install Express as well:
npm install -g express
Install the Cerbos PDP for your platform, and ensure that the binary is in your $PATH
(or run it from the extracted location):
Once installed, test it to make sure that it can execute:
cerbos --version
Once you're confident that Node.js, Express, and Cerbos PDP are installed, you can start a new project running these commands
mkdir lms-cerbos
cd lms-cerbos
This will create a new folder called lms-cerbos
and navigate into it.
Next, initialize a new Node.js project:
npm init -y
Install the necessary packages, including TypeScript and Axios:
npm install axios dotenv
npm install --save-dev typescript ts-node @types/node @types/express
Initialize a TypeScript configuration file:
npx tsc --init
Configure the Cerbos PDP by creating a basic policy file that defines roles and permissions. Create a cerbos
directory and add a policies
sub-directory.
Create a policy file resource.courses.yaml
in the policies/
directory:
# policies/policy.yaml
resource: "course"
version: "default"
roles:
- id: "super-admin"
grants:
- actions: ["create", "upload", "delete"]
condition:
match:
resource: "course"
- id: "admin"
grants:
- actions: ["update", "edit"]
condition:
match:
resource: "course"
- id: "student"
grants:
- actions: ["view"]
condition:
match:
resource: "course"
Create a roles.ts
file to define the roles and their permissions:
// src/roles.ts
export const roles = {
"super-admin": ["create", "upload", "delete"],
"admin": ["update", "edit"],
"student": ["view"],
};
Now we can set up routing in Express, where we enforce RBAC using Cerbos.
// src/index.ts
import express, { Request, Response } from "express";
import axios from "axios";
import dotenv from "dotenv";
import { roles } from "./roles";
dotenv.config();
const app = express();
app.use(express.json());
const cerbosURL = process.env.CERBOS_URL || "http://localhost:3592/api/check";
interface User {
id: string;
roles: string[];
}
app.post("/course_action", async (req: Request, res: Response) => {
const { user, action } = req.body as { user: User; action: string };
const cerbosRequest = {
requestId: "1",
actions: [action],
resource: {
kind: "course",
attributes: {
owner: user.id,
},
},
principal: {
id: user.id,
roles: user.roles,
},
};
try {
const response = await axios.post(cerbosURL, cerbosRequest);
const isAllowed = response.data.result[0].actions[action]?.isAllowed;
if (isAllowed) {
res.status(200).send("Action permitted");
} else {
res.status(403).send("Action forbidden");
}
} catch (error) {
console.error("Error communicating with Cerbos PDP:", error);
res.status(500).send("Internal server error");
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Compile and run the application:
npx tsc
node dist/index.js
💡 You can test the application on Postman by sending POST requests to the /course_action
endpoint with different roles and actions.
Sample request:
{
"user": {
"id": "user1",
"roles": ["admin"]
},
"action": "edit"
}
From this article, you've learned the importance of roles and permissions. You can also see in our example how we used Cerbos to enforce RBAC for an LMS. The super-admin, admin, and student roles are defined in the Cerbos policy file with their respective permissions for the course resource. The Express app communicates with Cerbos to check if a user is authorized to perform a specific action on a course, ensuring that only the appropriate roles can create, upload, delete, update, edit, or view courses.
Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team
Join thousands of developers | Features and updates | 1x per month | No spam, just goodies.