Prompt
How do I protect API routes based on user roles or permissions?
Latest observation
Oct 6, 2026
OpenAI APIWeb search: off
To protect API routes by roles or permissions, you generally do this in two steps:
- Authenticate the user (confirm who they are)
- Authorize the request (confirm they’re allowed to do this action)
Common approaches
1) Role-based access control (RBAC)
Assign users roles like:
admineditorviewer
Then restrict routes based on role.
Example logic:
GET /reports→viewer,editor,adminPOST /reports→editor,adminDELETE /reports/:id→admin
2) Permission-based access control
Instead of checking roles directly, check specific permissions like:
report:readreport:createreport:delete
Roles map to permissions:
admin→ all permissionseditor→ read/createviewer→ read only
This is usually more flexible than roles alone.
Typical implementation pattern
A. Authenticate with middleware
Use something like:
- JWT middleware
- session middleware
- OAuth access token validation
This middleware should attach the user info to the request object, e.g.:
req.user.idreq.user.rolesreq.user.permissions
B. Add authorization middleware
Create reusable middleware that checks:
- required role(s), or
- required permission(s)
Example in Express.js
Role-based middleware
function requireRole(...allowedRoles) {
return (req, res, next) => {
const userRoles = req.user?.roles || [];
const hasRole = userRoles.some(role => allowedRoles.includes(role));
if (!hasRole) {
return res.status(403).json({ message: 'Forbidden' });
}
next();
};
}
// usage
app.get('/admin', authenticate, requireRole('admin'), (req, res) => {
res.json({ message: 'Welcome admin' });
});
Permission-based middleware
function requirePermission(...requiredPermissions) {
return (req, res, next) => {
const userPermissions = req.user?.permissions || [];
const hasPermission = requiredPermissions.every(permission =>
userPermissions.includes(permission)
);
if (!hasPermission) {
return res.status(403).json({ message: 'Forbidden' });
}
next();
};
}
// usage
app.delete('/reports/:id', authenticate, requirePermission('report:delete'), (req, res) => {
res.json({ message: 'Report deleted' });
});
Best practices
- Never trust the client to tell you their role/permission.
- Store role/permission data in your backend or in signed tokens you verify.
- Return:
401 Unauthorizedif the user is not authenticated403 Forbiddenif the user is authenticated but not allowed
- Prefer permissions for fine-grained control.
- Keep auth checks centralized in middleware or guards.
- Log denied access attempts for auditing.
If you use JWTs
You can include roles/permissions in the JWT claims, but:
- sign the token securely
- validate its signature and expiration
- remember that permission changes won’t take effect until the token is refreshed unless you re-check server-side
For sensitive apps, many teams:
- use JWT for identity
- fetch roles/permissions from the database on each request or from a cache
Example route matrix
| Route | Required |
|---|---|
GET /users/me | authenticated |
GET /users | user:read |
POST /users | user:create |
PATCH /users/:id | user:update |
DELETE /users/:id | user:delete |
If you want, I can show you how to do this in Express, FastAPI, Django, Spring Boot, or NestJS.