Expert standards for implementing Role-Based Access Control (RBAC/RBCA) in Supabase and React. Use this skill when designing permissions, RLS policies, custom JWT claims, or React-based role gating.
This skill provides a standard for implementing secure, scalable, and maintainable Role-Based Access Control (RBAC) within the project's ecosystem, primarily focusing on Supabase (PostgreSQL) and React.
admin, manager, editor, viewer) for simplicity unless complex hierarchies are strictly required.user_roles) and synchronized with Supabase Auth via Custom JWT Claims for performance.Create a dedicated table for role management. Avoid hardcoding roles into app logic.
-- roles table
create table public.roles (
id uuid primary key default gen_random_uuid(),
name text unique not null,
description text
);
-- user_roles junction table
create table public.user_roles (
user_id uuid references auth.users(id) on delete cascade,
role_id uuid references public.roles(id) on delete cascade,
primary key (user_id, role_id)
);
To avoid frequent database joins in RLS policies, use Custom JWT Claims to embed the user's role directly into the token. Use a Supabase database function triggered on user creation or manual role assignment.
[!IMPORTANT] Use the
authhook pattern or a trigger to populate claims.
Implement policies that check for roles stored in the JWT claims.
-- Example: Allow users with 'admin' role to delete any customer
create policy "Admins can delete customers"
on public.customers
for delete
using (
(auth.jwt() ->> 'role')::text = 'admin'
);
-- Example: Allow 'managers' to view customers in their assigned region
create policy "Managers can view regional customers"
on public.customers
for select
using (
((auth.jwt() ->> 'role')::text = 'manager')
AND (region_id = (auth.jwt() ->> 'region_id')::uuid)
);
Use a React Context to manage and expose the user's current role and permissions efficiently across the app.
Create reusable guard components for conditional rendering.
/**
* RoleGuard Component
* @param allowedRoles - Array of roles permitted to view the children.
*/
export const RoleGuard = ({
children,
allowedRoles,
}: {
children: React.ReactNode
allowedRoles: string[]
}) => {
const { userRole } = useAuth()
if (!allowedRoles.includes(userRole)) return null
return <> {children} </>
}
Use custom hooks for granular permission checks within component logic.
/**
* Hook to check if current user has specific permission.
*/
export const useHasPermission = (permission: string) => {
const { permissions } = useAuth()
return permissions.includes(permission)
}
roles table.user_roles exists with proper foreign keys.auth.jwt() claims for role checks.auth.users claims when roles change.RoleGuard or useHasPermission hook is used for UI gating.USING clauses.auth.jwt() might be null or missing role claims gracefully.