RBAC
Workers Auth includes a full RBAC (Role-Based Access Control) system. Define roles with permissions, assign them to users, and gate routes by role or permission. Roles are persisted to D1 via the user_roles table, so they survive Worker restarts and work across replicas.
import { RBACPolicy } from 'workers-auth/authz/rbac';
const authz = RBACPolicy({ roles: { admin: ['*'], // wildcard: all permissions editor: ['read', 'write'], user: ['read'], }, defaultRole: 'user',});Pass the policy to WorkersAuth:
const auth = WorkersAuth({ // ... authz,});Configuration
Section titled “Configuration”| Option | Type | Description |
|---|---|---|
roles | Record<string, string[]> | Map of role names to permission arrays |
defaultRole | string | Role assigned to every new user (must exist in roles) |
Using the authorize middleware
Section titled “Using the authorize middleware”The authorize middleware factory returns Hono middleware that checks whether the authenticated user meets a requirement.
Check by role
Section titled “Check by role”// Only users with the "admin" role can access /admin/*app.use('/admin/*', auth.authorize('role', 'admin'));Check by permission
Section titled “Check by permission”// Only users with the "write" permission can access POST /api/postsapp.post('/api/posts', auth.authorize('permission', 'write'), handler);Important: auth.authenticate must run before auth.authorize. The authenticate middleware sets c.get('user'), which authorize reads.
app.use('/api/*', auth.authenticate);app.use('/api/admin/*', auth.authorize('role', 'admin'));Wildcard permissions
Section titled “Wildcard permissions”A role with ['*'] as its permissions matches any permission check:
roles: { admin: ['*'], // passes any auth.authorize('permission', '...') check user: ['read'], // only passes auth.authorize('permission', 'read')}Wildcard applies to permission checks only. A role check (auth.authorize('role', 'admin')) always checks for the exact role name.
Managing roles
Section titled “Managing roles”Roles are persisted to D1 via the user_roles table. You can manage them through the Admin API:
POST /users/:id/roles— assign a roleDELETE /users/:id/roles/:role— remove a role
The Admin API validates roles against your RBAC policy configuration, so you can only assign roles that are defined in your roles config.
Managing roles programmatically
Section titled “Managing roles programmatically”The AuthorizationPolicy interface also exposes methods for runtime role management. The Admin API is the recommended approach for most use cases, but the programmatic interface is available when you need direct control:
interface AuthorizationPolicy { getUserRoles(userId: string, db: DatabaseAdapter): Promise<string[]>; getUserPermissions(userId: string, db: DatabaseAdapter): Promise<string[]>; assignRole(userId: string, role: string, db: DatabaseAdapter): Promise<void>; removeRole(userId: string, role: string, db: DatabaseAdapter): Promise<void>;}Example: full setup
Section titled “Example: full setup”const auth = WorkersAuth({ database: (binding) => D1Adapter(binding), cache: (binding) => KVAdapter(binding), authn: { strategies: [MagicLinkStrategy({ provider, template })], }, authz: RBACPolicy({ roles: { admin: ['*'], editor: ['read', 'write'], viewer: ['read'], }, defaultRole: 'viewer', }), redirectUrl: '/dashboard',});
app.route('/auth', auth.handler);app.use('/api/*', auth.authenticate);app.use('/api/admin/*', auth.authorize('role', 'admin'));app.post('/api/posts', auth.authorize('permission', 'write'), createPost);app.get('/api/posts', auth.authorize('permission', 'read'), listPosts);