rbac-guard
v1.0.1
Published
A lightweight, framework-agnostic, TypeScript-first role-based access control (RBAC) utility
Downloads
10
Maintainers
Readme
rbac-guard
A lightweight, framework-agnostic, TypeScript-first role-based access control (RBAC) utility.
Why rbac-guard?
Managing role-based access in JavaScript/TypeScript applications often leads to scattered, inconsistent permission checks. rbac-guard provides a clean, predictable API for checking user roles that works across React, Node.js, Express, Next.js, and any other JavaScript environment.
Features:
- 🎯 Simple API - One function, clear behavior
- 📦 Zero dependencies - No bloat
- 🔷 TypeScript-first - Full type safety
- 🔌 Framework-agnostic - Works everywhere
- 🪶 Tiny footprint - Under 1KB minified
Installation
npm install rbac-guardyarn add rbac-guardpnpm add rbac-guardQuick Start
import { canAccess } from 'rbac-guard';
const user = { role: 'ADMIN' };
// Check if user can access
if (canAccess(user, ['ADMIN', 'EDITOR'])) {
// ✅ User has access
}API Reference
canAccess(user, allowedRoles)
Check if a user has access based on their roles.
Parameters:
user- User object withrole: stringorroles: string[], ornull/undefinedallowedRoles- Array of allowed role names, or"*"for any authenticated user
Returns: boolean
// Single role user
canAccess({ role: 'ADMIN' }, ['ADMIN', 'EDITOR']); // true
// Multiple roles user
canAccess({ roles: ['USER', 'EDITOR'] }, ['ADMIN', 'EDITOR']); // true
// Wildcard - any authenticated user
canAccess({ role: 'VIEWER' }, '*'); // true
// Unauthenticated user
canAccess(null, ['ADMIN']); // falseFluent API (Optional)
For more complex or readable permission checks:
import { can } from 'rbac-guard';
const user = { roles: ['ADMIN', 'VERIFIED'] };
// Check single role
can(user).withRole('ADMIN').allow(); // true
// Check any of multiple roles
can(user).withAnyRole(['ADMIN', 'SUPERADMIN']).allow(); // true
// Check all roles required
can(user).withAllRoles(['ADMIN', 'VERIFIED']).allow(); // true
// Chain conditions (all must pass)
can(user)
.withRole('VERIFIED')
.withAnyRole(['ADMIN', 'MODERATOR'])
.allow(); // trueUsage Examples
React
import { canAccess } from 'rbac-guard';
function AdminPanel({ user }) {
if (!canAccess(user, ['ADMIN'])) {
return <p>Access denied</p>;
}
return <div>Admin Panel Content</div>;
}With a custom hook:
import { canAccess, AllowedRoles } from 'rbac-guard';
import { useAuth } from './your-auth-provider';
export function useCanAccess(allowedRoles: AllowedRoles): boolean {
const { user } = useAuth();
return canAccess(user, allowedRoles);
}
// Usage
function Dashboard() {
const canEdit = useCanAccess(['ADMIN', 'EDITOR']);
return (
<div>
<h1>Dashboard</h1>
{canEdit && <button>Edit Settings</button>}
</div>
);
}Express Middleware
import { canAccess, AllowedRoles } from 'rbac-guard';
import { Request, Response, NextFunction } from 'express';
function requireRoles(allowedRoles: AllowedRoles) {
return (req: Request, res: Response, next: NextFunction) => {
if (!canAccess(req.user, allowedRoles)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
// Usage
app.get('/admin', requireRoles(['ADMIN']), (req, res) => {
res.json({ message: 'Welcome, admin!' });
});
app.get('/dashboard', requireRoles('*'), (req, res) => {
res.json({ message: 'Welcome, authenticated user!' });
});Next.js (App Router)
// middleware.ts
import { canAccess } from 'rbac-guard';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const user = getUserFromToken(request); // Your auth logic
if (request.nextUrl.pathname.startsWith('/admin')) {
if (!canAccess(user, ['ADMIN'])) {
return NextResponse.redirect(new URL('/unauthorized', request.url));
}
}
return NextResponse.next();
}Next.js (Server Component)
import { canAccess } from 'rbac-guard';
import { getCurrentUser } from './auth';
export default async function AdminPage() {
const user = await getCurrentUser();
if (!canAccess(user, ['ADMIN'])) {
return <div>Access Denied</div>;
}
return <div>Admin Content</div>;
}TypeScript Support
All types are exported for full TypeScript integration:
import type {
RoleLike,
AllowedRoles,
UserWithRole,
UserWithRoles
} from 'rbac-guard';
// Your user type can extend the base types
interface MyUser extends UserWithRoles {
id: string;
email: string;
roles: string[];
}
const user: MyUser = {
id: '123',
email: '[email protected]',
roles: ['ADMIN', 'USER']
};
canAccess(user, ['ADMIN']); // Full type safetyType Guards
Utility functions for type checking:
import { hasRole, hasRoles, extractRoles } from 'rbac-guard';
hasRole(user); // true if user has { role: string }
hasRoles(user); // true if user has { roles: string[] }
extractRoles(user); // Returns string[] of all user rolesEdge Case Behavior
| Scenario | Result |
|----------|--------|
| user is null or undefined | false |
| user.role is empty string | false (no valid roles) |
| user.roles is empty array [] | false |
| allowedRoles is empty array [] | false |
| allowedRoles is "*" with valid user | true |
| allowedRoles is "*" with null user | false |
| Role comparison | Case-sensitive |
Design Philosophy
- No side effects - Pure functions, predictable behavior
- Fail closed - When in doubt, deny access
- Minimal API - One import, one function for most use cases
- Framework agnostic - No React/Express/Next.js specific code
- Junior-friendly - Code that's easy to read and understand
License
MIT
