Permit-Powered Task Manager: API-First Access Control in Action
Akua Konadu

Akua Konadu @adonaitechnologies

About: Build Things For The Web

Joined:
Jun 19, 2024

Permit-Powered Task Manager: API-First Access Control in Action

Publish Date: May 2
6 0

This is a submission for the Permit.io Authorization Challenge: API-First Authorization Reimagined

What I Built

I built a Task Manager API — think of it like a digital notebook where people can create, update, or delete their to-dos (we call them “tasks”). But here’s the cool part: not everyone is allowed to do everything. Some people (admins) can add and remove tasks, while others (users) can only view them.

What problem does it solve?
In many apps, rules about who can do what (called authorization) are written deep inside the code. That’s like building a house where the doors are nailed shut — hard to open, hard to change. If you want to update who can come in or out, you need to tear the walls apart!

Instead, the app uses a tool called Permit.io to handle those rules outside the code, like having a smart security guard at the door. We tell Permit.io who the admins are, who the users are, and what each can do — and Permit makes sure those rules are followed.

Why is that awesome?
🧩 Easy to change rules without breaking anything

🔐 Super safe — users only see or do what they’re allowed to

🧼 Clean code — no messy “if admin do this” scattered everywhere

Real-life example:
Let’s say this app was used by schools. The headteacher (admin) can create homework, and the students (users) can only view it. Using Permit.io, we can control all this easily — even if we have 1,000 schools with their own teachers and students!

Demo

You can test the live Task Manager API here:
👉 https://task-manager-t318.onrender.com

note: This is a backend API. You’ll need a tool like Postman to interact with it.

🧪 Test Credentials
✅ Admin
Username: admin
Password: 2025DEVChallenge
👤 User
Username: newuser
Password: 2025DEVChallenge

🔌 Available API Routes
Here are the main API routes:

👤 Authentication
POST /api/auth/login – Get a token

🧾 Task Routes (Protected by Permit.io)

*GET * /api/tenants/:tenantId/tasks – Read tasks

POST /api/tenants/:tenantId/tasks – Create task

PUT /api/tenants/:tenantId/tasks/:taskId – Update task

DELETE /api/tenants/:tenantId/tasks/:taskId – Delete task

Use global as the :tenantId when testing.

Project Repo

Repo Link

My Journey

This project was built to demonstrate how externalized authorization (using Permit.io) can be used to control who can do what in a real-world API. Below is a breakdown of the process, the challenges I faced, how I solved them, and the key lessons I learned.

🔨 Step-by-Step Process

1. Set up the project structure
Created a backend folder with routes, middleware, and utils subfolders.

Initialized the project using npm init and installed dependencies like Express, jsonwebtoken, dotenv, and permitio.

2. Built the core API
Created basic routes for authentication (/api/auth/login) and task management (/api/tenants/:tenantId/tasks).

Image description

Enabled JWT-based authentication and stored roles (admin, user) in the token.
Used middleware to validate tokens before accessing protected routes.

  1. Integrated Permit.io Created a Permit.io account and configured resources, roles, and permissions:

Defined the task resource and CRUD actions (read, create, update, delete).

Assigned permissions to roles like admin and user.

Used the permit.check() method in middleware to verify permissions before allowing access.

Image description

  1. Deployed the app Deployed the backend using Render.

Made sure the .env file and node_modules were excluded via .gitignore.

Verified the deployed API worked using Postman.

🚧 Challenges Faced & How I Solved Them
**
🔐 **Permit denying access even with correct token
: Realized I needed to match tenant, user, and role exactly as defined in the Permit.io UI

Image description
🚨
"secretOrPrivateKey must have a value" error: Forgot to set JWT_SECRET in the .env file during deployment — added it and redeployed

🔒 Confusing PDP & API key configuration: Used Permit.io docs and examples to set up pdp: "https://cloudpdp.api.permit.io" and the correct token

🔍 Understanding how roles map to actions: Explored Permit’s visual UI to assign the right permissions to admin and user roles and test them properly

📘 What I Learned

Authorization should not be hardcoded. Using Permit.io made it easy to manage permissions without constantly editing my code.

Separation of concerns is powerful. By keeping permissions outside the core API logic, the system is easier to update and more secure.

API-first means thinking about security upfront. I had to carefully design endpoints with authorization in mind from the very beginning.

Documentation and testing are critical. It was much easier to validate my work because I documented everything clearly and used consistent test users.

API-First Authorization

✅ Step-by-Step Integration with Permit.io
**
**1. Created a Permit.io Project

Image description

I started by creating a free account on Permit.io and setting up a new project. This gave us access to the Policy Decision Point (PDP), where all access decisions would be made.

  1. Defined Resources and Actions

I defined the main resource in our app:
task — the object we wanted to control (e.g., view, create, update, delete).
Then I listed actions users might perform on a task:
read, create, update, delete

3. Set Up Roles

admin — full access to all actions on tasks
Image description

user — limited access (e.g., read-only)
Image description

IF YOU DONT UNDERSTAND ANYTHING REFER TO THE Permit.io Docs from here

  1. Created Declarative Policies Using Permit’s visual editor, I assigned permissions based on roles:

Admins can read, create, update, and delete tasks.

Users can only read tasks.
This gave us fine-grained control over who could do what — without changing our code every time.

  1. Integrated the Permit SDK In our Express.js backend:

I installed Permit’s Node SDK: npm install permitio

Configured the SDK with our API key and PDP URL from the dashboard
Connected Permit inside our middleware to check permissions for every request
const { Permit } = require("permitio");
const permit = new Permit({
token: process.env.PERMIT_API_KEY,
pdp: "https://cloudpdp.api.permit.io",
});

  1. Protected Routes with Middleware We created a custom middleware checkPermission(action, resource) that: Extracts the user's role and tenant from their JWT Calls permit.check() to ask Permit.io if the action is allowed Grants or denies access accordingly

await permit.check({
user: req.user.id,
action: 'read',
resource: 'task',
tenant: req.user.tenant,
});

🚀 What Makes It API-First?
Authorization is treated as part of the API design, not an afterthought.

Each API route was designed with a specific permission in mind.

I used middleware to enforce authorization consistently across routes.

The logic is clean and externalized, so we can change permissions without touching our backend code.

Comments 0 total

    Add comment