Role-Based Access Control (RBAC) in React Applications
<h2>What is RBAC?</h2>
<p>Role-Based Access Control (RBAC) is a method of regulating access to resources based on the roles of individual users. Instead of assigning permissions to each user, you assign them to roles, and users inherit permissions from their roles.</p>
<h2>RBAC Concepts</h2>
<ul>
<li><strong>Users:</strong> Individual people using your application</li>
<li><strong>Roles:</strong> Job functions (admin, editor, viewer)</li>
<li><strong>Permissions:</strong> Actions users can perform (read, write, delete)</li>
<li><strong>Resources:</strong> What permissions apply to (articles, users, settings)</li>
</ul>
<h2>Designing Your Permission System</h2>
<p>Start by mapping out your application's roles and permissions:</p>
<pre><code>// Define roles and permissions
export const ROLES = { ADMIN: 'admin', EDITOR: 'editor', AUTHOR: 'author', VIEWER: 'viewer', } as const;
export const PERMISSIONS = { // Article permissions ARTICLE_CREATE: 'article:create', ARTICLE_READ: 'article:read', ARTICLE_UPDATE: 'article:update', ARTICLE_DELETE: 'article:delete', ARTICLE_PUBLISH: 'article:publish',
// User permissions USER_CREATE: 'user:create', USER_READ: 'user:read', USER_UPDATE: 'user:update', USER_DELETE: 'user:delete', } as const;
// Map roles to permissions export const ROLE_PERMISSIONS = { [ROLES.ADMIN]: Object.values(PERMISSIONS), [ROLES.EDITOR]: [ PERMISSIONS.ARTICLE_CREATE, PERMISSIONS.ARTICLE_READ, PERMISSIONS.ARTICLE_UPDATE, PERMISSIONS.ARTICLE_PUBLISH, PERMISSIONS.USER_READ, ], [ROLES.AUTHOR]: [ PERMISSIONS.ARTICLE_CREATE, PERMISSIONS.ARTICLE_READ, PERMISSIONS.ARTICLE_UPDATE, ], [ROLES.VIEWER]: [ PERMISSIONS.ARTICLE_READ, ], };
<h2>Implementing RBAC in React</h2>
<h3>Permission Hook</h3>
<pre><code>// hooks/usePermissions.ts
import { useSession } from 'next-auth/react';
export function usePermissions() { const { data: session } = useSession(); const userRole = session?.user?.role; const permissions = ROLE_PERMISSIONS[userRole] || [];
const hasPermission = (permission: string) => { return permissions.includes(permission); };
const hasAnyPermission = (requiredPermissions: string[]) => { return requiredPermissions.some(p => permissions.includes(p)); };
const hasAllPermissions = (requiredPermissions: string[]) => { return requiredPermissions.every(p => permissions.includes(p)); };
return { hasPermission, hasAnyPermission, hasAllPermissions, permissions }; }
<h3>Protected Component</h3>
<pre><code>// components/ProtectedComponent.tsx
import { usePermissions } from '@/hooks/usePermissions';
export function DeleteButton({ articleId }: { articleId: string }) { const { hasPermission } = usePermissions();
if (!hasPermission(PERMISSIONS.ARTICLE_DELETE)) { return null; // Hide button if no permission }
return ( <button onClick={() => deleteArticle(articleId)}> Delete Article ); }
<h2>Route Protection</h2>
<p>Protect entire routes based on permissions:</p>
<pre><code>// middleware.ts
import { getToken } from 'next-auth/jwt'; import { NextResponse } from 'next/server';
export async function middleware(req: NextRequest) { const token = await getToken({ req }); const path = req.nextUrl.pathname;
// Admin routes if (path.startsWith('/admin')) { if (!token || token.role !== 'admin') { return NextResponse.redirect(new URL('/unauthorized', req.url)); } }
// Editor routes if (path.startsWith('/editor')) { if (!token || !['admin', 'editor'].includes(token.role)) { return NextResponse.redirect(new URL('/unauthorized', req.url)); } }
return NextResponse.next(); }
export const config = { matcher: ['/admin/:path*', '/editor/:path*'], };
<h2>API Route Protection</h2>
<pre><code>// app/api/articles/[id]/route.ts
export async function DELETE(req: Request, { params }: { params: { id: string } }) { const session = await getServerSession(authOptions);
if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); }
const permissions = ROLE_PERMISSIONS[session.user.role] || []; if (!permissions.includes(PERMISSIONS.ARTICLE_DELETE)) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); }
// User has permission, proceed with deletion await Article.findByIdAndDelete(params.id); return NextResponse.json({ message: 'Article deleted' }); }
<h2>Testing RBAC</h2>
<p>Test all permission combinations:</p>
<ul>
<li>Test each role can access what they should</li>
<li>Test each role is blocked from what they shouldn't</li>
<li>Test edge cases (no role, invalid role)</li>
<li>Test permission changes take effect immediately</li>
</ul>
<h2>Conclusion</h2>
<p>RBAC provides a scalable way to manage permissions. By defining clear roles and permissions, you can build secure applications that scale from 10 to 10,000 users. The key is starting with a well-designed permission system and enforcing it consistently across your application.</p>
About the Author
Marcus Weber
Lead QA Engineer
Built testing frameworks used by 50+ companies, 12+ years in QAQA Engineer and testing advocate. Built testing frameworks for Fortune 500 companies. Passionate about making testing accessible and effective for all developers.
Get notified of updates
Subscribe to receive an email when this article is updated with new information.
We'll only email you about updates to this specific article. Unsubscribe anytime.Tagged with
Related Articles
View all AuthZero Trust Architecture for Modern Web Applications
Implement zero trust security principles in your web application. Learn continuous verification, least privilege access, and micro-segmentation.
API Key Management and Rotation Strategies
Learn how to securely manage, rotate, and protect API keys in production. Includes key generation, storage, and automated rotation strategies.
Secure Session Management in Next.js with Redis
Best practices for managing user sessions securely in Next.js using Redis. Covers session storage, rotation, and security hardening.
Passwordless Authentication: The Future of Login
Explore passwordless authentication with magic links, WebAuthn, and biometrics. Learn how to implement secure, user-friendly login without passwords.
Implementing Multi-Factor Authentication in Next.js 14
Step-by-step guide to adding MFA to your Next.js application with SMS, email, and authenticator app support. Includes code examples and security best practices.
JWT vs Session Tokens: A Production Comparison
Deep dive into JWT and session-based authentication with real-world performance benchmarks, security analysis, and recommendations for EU applications.