Roles & Permissions
Control who can do what inside your product — from simple role toggles to fine-grained, resource-scoped authorization.
Options
Authorization Model*
Permission Scope*
Custom Role Management
Decision Points
Do users need different levels of access in different parts of the product?
If yes
Move beyond simple roles — at minimum, adopt RBAC.
If no
A two-value role enum on the user table is plenty.
Do users share individual items (documents, projects) with specific other users?
If yes
You need per-resource scope. Plan for ABAC or ReBAC now, not later.
If no
Workspace-level scope is usually sufficient.
Are enterprise customers asking to configure roles themselves?
If yes
Expose a role editor on the built-in permission primitives; price it.
If no
Ship a fixed role set and iterate based on feedback.
Do you need per-field permission granularity?
If yes
Move to ABAC or a policy engine (OpenFGA, Cerbos) — field-level rules are unmaintainable in RBAC.
If no
Row/resource-level checks are enough; keep the model coarse.
Do permissions need to inherit via hierarchical groups (folder → subfolder, org → team)?
If yes
ReBAC is the natural fit — Zanzibar-style graph traversal handles inheritance cleanly.
If no
Flat role-to-resource assignments are simpler and easier to debug.
Is deny-by-default the required posture?
If yes
Default every permission check to false; require an explicit grant. Standard for compliance-regulated products.
If no
Allow-by-default with blocklist rules is risky — only acceptable for internal tools.
Must users delegate access (X grants Y access to Z) without an admin?
If yes
Per-resource scope is required. Build a share action with grantor tracking in the audit log.
If no
Admin-mediated grants keep the authorization surface auditable and small.
Do you need policy-as-code (OPA, Cedar) managed alongside application code?
If yes
Adopt a policy engine — policies get versioned, reviewed, and tested like any source file.
If no
Keep authorization as a central module in app code until policy churn justifies the engine.
Do grants need to expire automatically (temporary access, contractor windows)?
If yes
Add an expires_at on every grant and a scheduled job that revokes on expiry. Critical for least-privilege compliance.
If no
Permanent grants with manual revocation are simpler but audit-unfriendly.
Do you need a break-glass / super-admin role for incident response?
If yes
Define it explicitly, require MFA to assume it, and audit-log every action taken under it. Keep the member list tiny.
If no
Regular admin + vendor support access covers most cases; avoid god-mode accounts.
Are sensitive role grants (e.g. billing-admin) subject to approval workflow?
If yes
Build a request + approve flow with a second approver on the granting side. Common in SOC 2 environments.
If no
Direct admin grants are faster — add audit logging instead.
Do enterprise customers need SCIM-driven group membership from their IdP?
If yes
Map SCIM groups to roles; treat the IdP as source of truth and avoid manual role edits for SCIM-managed users.
If no
In-app role management is simpler for SMB customers.
Do permission changes need a dedicated audit log (separate from general audit log)?
If yes
Emit a specialized authz-change stream — compliance reviewers need to query grants without sifting through all activity.
If no
Fold permission changes into the general audit log; tag them for easy filtering.
Do API keys need permissions separate from the user who minted them?
If yes
Give keys their own scoped permission set (typically a subset of the user's). Prevents accidental privilege inheritance.
If no
Mirror the minting user's permissions — simpler but revoking a user breaks their keys.
Can you push authorization into the database with row-level security?
If yes
Postgres RLS (or Supabase) centralizes enforcement at the data layer — out-of-band queries can't bypass it. Set auth context on every connection.
If no
Central policy module in app code is easier to debug and port across databases.
Are permission checks on your hot path (every list render)?
If yes
Cache lookups with a short TTL (30–60s) plus a revocation list checked per request — pure DB lookups will bottleneck.
If no
Uncached checks are fine; add caching only when profiler data demands it.
Tradeoffs
Fast to build but every 'special case' access rule becomes bespoke code that's hard to audit
Requires a policy engine and relationship store kept in sync with primary data
Every list/read query must filter by ACL — expect query-plan work and caching investment
Support load increases substantially — each customer now has a unique permission configuration
Go Deeper
Where and how to evaluate policy
Centralize authorization decisions so every entry point enforces the same rules.
Caching and invalidation of permission checks
Permission checks run on nearly every request — caching is inevitable and dangerous.