Role-Based Access Control (RBAC)
Secure your pages, APIs, and UI components using JopiJS's file-system based permission system.
Overview
In JopiJS, security is declarative. Instead of writing complex middleware logic, you protect your routes by simply placing Marker Files in your folder structure. This makes your security rules immediately visible and easy to manage.
This guide covers:
- Defining and using user roles.
- Protecting routes (Pages & APIs) via File-System Annotations.
- Granular protection for specific HTTP methods.
- Conditional UI rendering based on roles.
- Customizing the "Unauthorized" (401) experience.
1. What are Roles?
A role is a simple string that represents a set of permissions (e.g., admin, editor, billing.manager).
Roles are usually assigned during the Authentication process. You can choose any naming convention:
- Functional:
ecommerce.product.manager - Granular:
can_edit_users
2. Route Protection (Marker Files)
JopiJS uses empty files inside your @routes/ folders to act as security annotations. This is the fastest way to see "who can access what" without opening a single code file.
A. Global Protection (needRole_)
To protect an entire route (Page + all HTTP methods), add a file starting with needRole_ followed by the protected role name.
Project Structure:
src/ mod_catalog/
└── @routes/
└── admin/
└── add-product/ # URL: /admin/add-product
├── page.tsx
├── onPOST.ts
└── needRole_admin.cond # <--- ONLY 'admin' can access this folderB. Multiple Roles (OR Logic)
If you add multiple marker files, JopiJS applies an OR logic. A user having any of the required roles will be granted access.
src/ mod_catalog/ @routes/ admin/ add-product/
├── needRole_admin.cond
└── needRole_manager.cond # Access granted if user is 'admin' OR 'manager'3. Granular Method Protection
Sometimes you want everyone to see a page (GET) but only certain roles to modify it (POST). JopiJS provides specialized prefixes for this:
| Prefix | Targeted Element |
|---|---|
needRole_ | Entire Folder (Page + all APIs). |
pageNeedRole_ | Only the React page.tsx. |
getNeedRole_ | Only the onGET.ts API. |
postNeedRole_ | Only the onPOST.ts API. |
putNeedRole_ | Only the onPUT.ts API. |
Example: Read-only for editors, full access for admins.
src/ mod_catalog/ @routes/ admin/ add-product/
├── page.tsx
├── onPOST.ts
├── needRole_admin.cond # Admins can do everything
└── pageNeedRole_editor.cond # Editors can see the page, but NOT call onPOST4. Isomorphic UI Protection
Route protection prevents the server from serving data, but you also need to hide UI elements (buttons, sidebars) for a better user experience.
A. The <CheckRoles> Component
Use this component to wrap parts of your JSX that should only be visible to specific roles.
import CheckRoles from "@/ui/jopijs.user.CheckRoles";
export default function ProductActions() {
return (
<div>
<button>View Orders</button>
{/* Action buttons reserved for managers */}
<CheckRoles roles={["admin", "manager"]}>
<button className="btn-danger">Delete Product</button>
</CheckRoles>
</div>
);
}B. The useUserHasOneOfThisRoles Hook
For more complex logic, use the isomorphic hook.
import useUserHasOneOfThisRoles from "@/hooks/jopijs.user.useHasOneOfThisRoles";
export default function CustomLogic() {
const isPowerful = useUserHasOneOfThisRoles(["admin", "ceo"]);
return (
<p>{isPowerful ? "Welcome, Boss" : "Hello, User"}</p>
);
}5. Handling Unauthorized Access (401)
When a user tries to access a protected route without the required roles:
- API Call: JopiJS returns a raw
401 Unauthorizedstatus code. - Page Load: JopiJS renders the 401 error page.
Customizing the 401 Page
To create a branded "Access Denied" page, simply create a route at:
src/mod_your_module/@routes/error401/page.tsx.
Manual 401 Redirection
You can manually trigger the 401 behavior from within a local component or route:
import {error401} from "@/routes";
import useUserHasOneOfThisRoles from "@/hooks/jopijs.user.useHasOneOfThisRoles";
export default function SensitiveComponent() {
const hasAccess = useUserHasOneOfThisRoles(["admin"]);
if (!hasAccess) {
return error401(); // Renders the 401 UI
}
return <div>Sensitive Admin Data</div>;
}6. Permissions & Overrides
When a module overrides a route folder from another module, JopiJS follows the route merging principle where permissions are determined solely by the marker files in the overriding folder:
- Marker files are not merged across modules; the files in the overriding folder completely replace those from the original module for that specific route items.
- If the overriding folder contains no marker files, the route becomes public, even if the original module had defined protections.
- This override only applies to the specific route items being replaced during the merge process.
Performance Tip: Marker files are extremely fast. JopiJS reads these files during the build/boot phase and generates highly optimized routing tables, ensuring zero performance overhead for role checks at runtime.
Summary
- Use Marker Files (
needRole_*.cond) inside@routes/for instant security. - Combine global and method-specific prefixes (
postNeedRole_, etc.) for fine-grained control. - Secure your UI with
<CheckRoles>anduseUserHasOneOfThisRoles. - Customize your
/error401page to keep your branding consistent.