visit
We’ll start by setting up a basic Todo application in Next.js and implementing JWTs for user authentication. Next, we’ll configure an RBAC policy using
Make sure you have Node.js and npm installed on your machine. You can download them from the official
git clone //github.com/SagarSingh2003/rbac-permit-starter-next.js
This will clone a Next.js project with the default settings containing the starter code.
cd rbac-permit-starter-next.js
npm install
Now that we’ve successfully set up our Next.js project locally, we can go ahead and implement a basic RBAC model.
Before starting with
The dummy user details are as follows :
User 1 (Admin):
Key: 1
Email: [email protected]
First Name: Project
Last Name: Admin
Tenant: Todo-tenant (or your created tenant)
Role: Admin
User 2 (Regular User):
Key: 2
Email: [email protected]
First Name: Project
Last Name: User
Tenant: Todo-tenant (or your created tenant)
Role: User
const sampleData = [
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6IlByb2plY3QgQWRtaW4iLCJlbWFpbCI6ImFkbWluQGdtYWlsLmNvbSJ9.KtQpee_bZF_Sx0t87trx8-ljuE3SwJ7SZYeYzZO-694",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwibmFtZSI6IlByb2plY3QgVXNlciIsImVtYWlsIjoidXNlckBnbWFpbC5jb20ifQ.FVG2dcFVsOy1cmzImHqtbeJ0mnT1h4aCSN7aSPq9Xew",
];
export default sampleData;
This contains JWTs for both users with the above-written credentials. We will decode these JWTs to obtain user information.
The deleteUser function checks if the current user’s role is ‘admin’ before allowing him to delete the user.
The updatePost function checks if the user role has been updated.
Hard-coding authorization rules using imperative if
statements creates several issues:
if
statements become complex and harder to maintain, making the code prone to bugs and difficult to read.
To begin configuring permissions, log in to
Follow these steps:
Let’s create a resource named Todo
Create: Add a new task to the To-Do list.
Read: View the details of a task.
Update: Mark a task as done or not done.
Edit: Change the task’s name, deadline, or priority.
Delete: Remove a task from the To-Do list.
Now that we have created an RBAC configuration let’s add the dummy users to our Permit directory. Since we’re using dummy JWTs, we’ll manually add users to the directory. If you have an authentication provider, you can utilize the syncUser
API to import all your existing users into Permit.
To get started:
Settings > API Keys > Developmental secret key
PERMIT_TOKEN="Copied API KEY"
src > lib > permitProvider.js
import { Permit } from 'permitio';
const permit = new Permit({
// The token used for authenticating with the Permit.io API
token: process.env.NEXT_PUBLIC_PERMIT_TOKEN,
// The Policy Decision Point (PDP) URL that Permit.io uses to evaluate policies
pdp: "//cloudpdp.api.permit.io",
});
export default permit;
Whenever a user wants to perform an operation on the front end, the front end sends the user_id
and operation name to the backend API.
src > api > check-permission > route.js
import permit from "@/lib/permitProvider";
export async function GET(req) {
const { searchParams } = new URL(req.url)
const id = searchParams.get('id');
const operation = searchParams.get('operation')
try {
const permitted = await permit.check(String(id), String(operation), {
type: 'TodoTasks',
tenant: 'todo-tenant',
});
if (permitted) {
return Response.json({
success: true,
message: "permitted"
}, { status: 200 })
}
} catch (err) {
console.error("Error checking permissions:", err);
}
return Response.json({
success: false,
message: "not permitted"
}, { status: 403 })
}
Here’s a step-by-step breakdown:
Extract Query Parameters:
GET
function is triggered by an HTTP GET request to this API route.id
and operation
parameters from the request's URL.
Check Permission with Permit.io:
permit.check()
method is called to verify if the user identified by id
is authorized to perform the specified operation (e.g., create, update, delete) on the TodoTasks
resource within the todo-tenant
.
Return the Result:
Success (200): If the user has the necessary permissions, a success response with a status code of 200 is returned.
Not Authorized (403): If the user does not have the required permissions, a failure response with a status code of 403 is returned.
const checkPermission = async (operation) => {
const response = await fetch(`/api/check-permission?id=${identifier}&operation=${operation}`);
const data = await response.json();
return data.success;
};
Create: Function to check if the individual has “edit” permission and updates the specified to-do item with new values.
const handleSaveEdit = async () => {
if (!await checkPermission("edit")) {
alert("You do not have permission to edit a to-do.");
return;
}
const updatedTodos = todos.map((todo, i) =>
i === editingIndex
? { ...todo, content: editingContent, deadline: editingDeadline, priority: editingPriority }
: todo
);
setTodos(updatedTodos);
setEditingIndex(null);
setEditingContent("");
setEditingDeadline("");
setEditingPriority("low");
};
Edit: Function to check if the individual has “edit” permission to update the specified to-do item with new details.
const handleSaveEdit = async () => {
if (!await checkPermission("edit")) {
alert("You do not have permission to edit a to-do.");
return;
}
const updatedTodos = todos.map((todo, index) =>
index === editingIndex
? { ...todo, content: editingContent, deadline: editingDeadline, priority: editingPriority }
: todo
);
setTodos(updatedTodos);
setEditingIndex(null);
setEditingContent("");
setEditingDeadline("");
setEditingPriority("low");
};
Update: Function to check if the individual has “update” permission, toggles the “done” status of the specified to-do item.
const handleToggleDone = async (index) => {
if (!await checkPermission("update")) {
alert("You do not have permission to update a to-do.");
return;
}
const updatedTodos = todos.map((todo, i) =>
i === index ? { ...todo, done: !todo.done } : todo
);
setTodos(updatedTodos);
};
Delete: Function to check if the individual has “delete” permission and removes the specified to-do item from the list.
const handleDeleteTodo = async (index) => {
if (!await checkPermission("delete")) {
alert("You do not have permission to delete a to-do.");
return;
}
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
};
npm run dev
This will spin up our Next.js application on
Demo of an individual with “User” permissions
Updating our “User” to “Admin”
To change the user’s role from User to Admin, navigate to the
Demo of Admin “User” with all the Permissions
Update Todos: Modify existing Todos.
Delete Todos: Remove Todos from the list.
Read Todos: Read existing Todos.
What if you want a more granular level of control than user roles but user-detailed identity? For that and more, I recommend you continue reading Permit’s learning materials:
such as
, and .