devclassic-rbac
v1.0.0
Published
Enterprise Role-Based (RBAC) & Attribute-Based (ABAC) Permission & Route Guard Engine for React, Next.js, and Modern Web Applications by devclassic.
Maintainers
Readme
devclassic-rbac
Enterprise Role-Based (RBAC) & Attribute-Based (ABAC) Permission Engine for Modern Full-Stack Web Applications
Guard your Frontend UI and your Backend APIs with the exact same security rules — zero discrepancy, zero exploits.
📖 Table of Contents
- ✨ Why devclassic-rbac?
- 📦 Installation
- 🚀 60-Second Quickstart
- 🛡️ Part 1: Frontend Guarding
- 🖥️ Part 2: Backend & API Guarding
- 🎯 Dynamic ABAC (Ownership, Spending Limits, State Boundaries)
- 🔍 Audit & Explainability (
explain()) - 🎨 Production
<AccessDenied />Screen - 📚 Complete Guides & Documentation
- ❓ FAQ & Troubleshooting
- 📄 License
✨ Why devclassic-rbac?
Most permission libraries either only run in React (leaving your backend vulnerable to direct Postman/curl requests) or only run in Node.js (leaving your frontend developers to manually build button-hiding logic).
devclassic-rbac solves this by giving you a unified, isomorphic engine:
| Capability | What it gives you |
| :--- | :--- |
| Full-Stack Parity | Share one single role/permission config between your React components and your Backend API. |
| Graceful "Disabled" Mode | Instead of buttons mysteriously vanishing from the page, keep them visible but disabled with an explanatory tooltip (fallback="disabled"). |
| Role Hierarchy & Inheritance | Define parent/child role graphs (e.g. super_admin > admin > manager > viewer) with automatic cycle detection. |
| Wildcard Matcher | Standard globs (users:*, facility:reports:*, *). |
| Dynamic ABAC | Evaluate runtime conditions (resource ownership, geographic boundaries, financial limits). |
| Zero Runtime Dependencies | The core engine is lightweight pure TypeScript — blazingly fast with 0 external dependencies. |
| Dual ESM & CJS | Seamlessly imports in modern Vite/Next.js (import) and legacy Node.js (require). |
📦 Installation
npm install devclassic-rbac
# or
yarn add devclassic-rbac
# or
pnpm add devclassic-rbac🚀 60-Second Quickstart
Step 1: Define your roles once (rbac.config.ts)
// rbac.config.ts
import { RBACConfig, createRBACServer } from "devclassic-rbac";
export const rbacConfig: RBACConfig = {
roles: {
super_admin: ["admin"],
admin: {
inherits: ["manager"],
permissions: ["facilities:*", "users:*"],
},
manager: {
inherits: ["viewer"],
permissions: ["facilities:create", "facilities:edit", "reports:export"],
},
viewer: {
permissions: ["facilities:read", "reports:read"],
},
},
superRole: "super_admin", // Automatically bypasses all checks
};
// Backend server instance:
export const serverRBAC = createRBACServer(rbacConfig);Step 2: Wrap your app (layout.tsx or main.tsx)
"use client";
import { RBACProvider } from "devclassic-rbac";
import { rbacConfig } from "./rbac.config";
export default function RootLayout({ children }: { children: React.ReactNode }) {
// Current user from your auth session (NextAuth, Supabase, JWT, etc.)
const currentUser = {
id: "usr_10",
name: "Amina Lawal",
roles: ["manager"], // inherits 'manager' + 'viewer'
};
return (
<RBACProvider config={rbacConfig} user={currentUser}>
{children}
</RBACProvider>
);
}Step 3: Guard your button
import { Protect } from "devclassic-rbac";
export function ActionButtons() {
return (
<Protect
permission="reports:export"
fallback="disabled"
disabledTooltip="Requires Manager or Admin role to export reports."
>
<button onClick={handleExport}>Export CSV</button>
</Protect>
);
}🛡️ Part 1: Frontend Guarding (React & Next.js)
1. <Protect> Declarative Gate
A. Hide unauthorized buttons entirely:
<Protect permission="facilities:create">
<button className="btn-primary">Add Facility</button>
</Protect>B. Enterprise UX: Keep button visible, but disabled with tooltip:
<Protect
permission="facilities:delete"
fallback="disabled"
disabledTooltip="Only Administrators can delete facilities."
>
<button onClick={handleDelete}>Delete Facility</button>
</Protect>C. Role-based check:
<Protect role={["admin", "super_admin"]} fallback={<span>Admin only</span>}>
<AdminPanel />
</Protect>D. Dynamic ABAC check on a resource:
<Protect permission="facilities:edit" resource={selectedFacility} fallback="disabled">
<button onClick={handleEdit}>Edit Facility</button>
</Protect>2. <Can> Semantic Gate
For developers who prefer natural English syntax:
import { Can } from "devclassic-rbac";
// Verb + Noun syntax:
<Can I="export" a="report" fallback="disabled">
<button>Export Data</button>
</Can>
// Resource-passed check:
<Can I="edit" a="user" pass={targetUser}>
<button>Edit User</button>
</Can>3. <ProtectedRoute> Page Guard
Guards entire pages or layouts. Unauthorized visitors are shown a customizable <AccessDenied /> screen or redirected.
// app/admin/page.tsx
"use client";
import { ProtectedRoute } from "devclassic-rbac";
export default function AdminSettingsPage() {
return (
<ProtectedRoute
role="admin"
redirectTo="/unauthorized" // Optional: redirect unauthorized visitors
loading={<p>Verifying admin privileges...</p>}
>
<AdminSettingsDashboard />
</ProtectedRoute>
);
}4. React Hooks Reference
import { useRBAC, usePermission, useRole, useGuard } from "devclassic-rbac";
function MyComponent() {
// 1. Reactive permission boolean:
const canCreate = usePermission("facilities:create");
// 2. Reactive role boolean:
const isAdmin = useRole("admin");
const isManagerOrAdmin = useRole(["manager", "admin"]); // any
// 3. Full engine access:
const { user, roles, permissions, can, hasRole, explain } = useRBAC();
// 4. Client-side navigation guard:
const { isAuthorized } = useGuard({
role: "admin",
redirectTo: "/403",
});
return <div>{canCreate && <button>Create</button>}</div>;
}🖥️ Part 2: Backend & API Guarding (Node.js, Express, Next.js Route Handlers)
1. Next.js Route Handlers & Server Actions
Use rbac.assert() to reject unauthorized requests with a clean 403 status:
// app/api/facilities/route.ts
import { NextResponse } from "next/server";
import { serverRBAC } from "@/app/rbac.config";
import { getAuthUser } from "@/lib/auth";
export async function POST(req: Request) {
const user = await getAuthUser(req);
// 🛡️ Blocks unauthorized calls from Postman/curl:
try {
serverRBAC.assert(user, "facilities:create");
} catch (err: any) {
return NextResponse.json({ error: err.message }, { status: 403 });
}
const body = await req.json();
const created = await db.facility.create({ data: body });
return NextResponse.json(created, { status: 201 });
}2. Express.js REST API Middleware
Use pre-built middleware rbac.requirePermission and rbac.requireRole:
import express from "express";
import { createRBACServer } from "devclassic-rbac";
const app = express();
app.use(express.json());
const rbac = createRBACServer(rbacConfig);
// Enforce permission:
app.post(
"/api/facilities",
authMiddleware, // sets req.user
rbac.requirePermission("facilities:create"),
async (req, res) => {
res.json({ success: true, message: "Facility created" });
}
);
// Enforce role:
app.delete(
"/api/users/:id",
authMiddleware,
rbac.requireRole(["admin", "super_admin"]),
async (req, res) => {
res.json({ success: true, message: "User deleted" });
}
);3. Next.js Edge Middleware (middleware.ts)
Protect frontend routes at the Edge before the page bundle even downloads:
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createRBACMatcher } from "devclassic-rbac";
const routeMatcher = createRBACMatcher([
{ path: "/admin/**", roles: ["admin", "super_admin"], redirectTo: "/403" },
{ path: "/finance/**", permissions: ["finance:view"], redirectTo: "/403" },
]);
export function middleware(req: NextRequest) {
const user = getUserFromToken(req);
const result = routeMatcher.match(req.nextUrl.pathname, user);
if (!result.authorized && result.redirectTo) {
return NextResponse.redirect(new URL(result.redirectTo, req.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/admin/:path*", "/finance/:path*"],
};🎯 Dynamic ABAC (Attribute-Based Access Control)
Support real-world dynamic rules based on the user, resource, or environment:
const rbacConfig: RBACConfig = {
policies: [
// 1. Author ownership rule
{
name: "author-only",
permission: "articles:edit",
condition: ({ user, resource }) => user?.id === resource?.authorId,
},
// 2. Spending threshold rule
{
name: "spending-limit",
permission: "invoices:approve",
condition: ({ user, resource }) =>
(resource?.amount ?? 0) <= (user?.attributes?.approvalLimit ?? 0),
},
// 3. State-level jurisdiction
{
name: "state-boundary",
permission: "facilities:edit",
condition: ({ user, resource }) =>
user?.attributes?.state?.toLowerCase() === resource?.state?.toLowerCase(),
},
],
};🔍 Audit & Explainability (explain())
Diagnose permissions or stream access decisions to Datadog/Sentry:
const decision = engine.explain("facilities:edit", facility);
console.log(decision.granted); // true or false
console.log(decision.reason); // e.g. "Denied by ABAC policy condition: 'state-boundary'"
console.log(decision.matchedPolicy); // "state-boundary"
console.log(decision.rolesChecked); // ["manager", "viewer"]🎨 Production <AccessDenied /> Screen
Self-contained, responsive 403 page with zero external CSS or icon dependencies:
import { AccessDenied } from "devclassic-rbac";
export default function ForbiddenPage() {
return (
<AccessDenied
title="Restricted Access"
message="You do not have the required permissions to view this audit."
code="403"
homeUrl="/dashboard"
showBackButton={true}
/>
);
}📚 Complete Guides & Documentation
- 🚀 Next.js App Router Step-by-Step Guide
- 🖥️ Backend & REST API Guide (Express, NestJS, Node)
- 🍳 Real-World Enterprise Recipes (State Boundaries, Budgets, Multi-Tenant)
- 📐 TypeScript Autocomplete & Strict Typing Guide
❓ FAQ & Troubleshooting
📄 License
MIT © devclassic
