RBAC & ABAC Authorization
Role-Based Access Control vs Attribute-Based Access Control: design trade-offs, policy engines, permission hierarchies, and multi-tenancy.
Authentication vs Authorization
Authentication (AuthN) answers 'who are you?' and is handled by OAuth 2.0 / OIDC. Authorization (AuthZ) answers 'what are you allowed to do?' and is the subject of this lesson. These two concerns must be kept separate in your design — do not conflate them. A user can be perfectly authenticated but have zero authorization to access a specific resource.
Role-Based Access Control (RBAC)
In RBAC, permissions are assigned to roles, and roles are assigned to users. A user can have multiple roles, and a role can have multiple permissions. The key benefit is manageability: when you hire a new billing manager, you assign them the `billing-admin` role, and they immediately inherit all 47 billing-related permissions. When regulations change, you update the role once and all role members are updated.
Hierarchical RBAC
Most real systems need role inheritance: a `super-admin` should inherit all permissions of `admin`, which inherits from `editor`, and so on. This creates a directed acyclic graph (DAG) of roles. Implementing this requires careful cycle detection and a permission resolution algorithm that traverses the inheritance graph. Libraries like Casbin (Go, Python, Node) and OPA (Open Policy Agent) handle this for you.
Attribute-Based Access Control (ABAC)
ABAC makes access decisions by evaluating policies that combine attributes of four entities: the subject (user attributes: role, department, clearance level), the resource (sensitivity, owner, classification), the action (read, write, delete), and the environment (time of day, IP address, geo-location). This enables extremely fine-grained policies that RBAC cannot express.
# OPA (Open Policy Agent) — ABAC policy example
# Allow read if user's clearance >= resource sensitivity AND within business hours
package authz
default allow = false
allow {
input.action == "read"
input.user.clearance_level >= input.resource.sensitivity_level
is_business_hours
input.user.department == input.resource.owning_department
}
is_business_hours {
hour := time.clock([time.now_ns(), "America/New_York"])[0]
hour >= 9
hour < 17
}RBAC vs ABAC: Choosing the Right Model
| Dimension | RBAC | ABAC |
|---|---|---|
| Complexity | Low — roles are intuitive | High — policies require careful engineering |
| Flexibility | Low — coarse-grained | High — fine-grained, context-aware |
| Audit ease | High — 'who has role X?' is simple | Medium — must trace policy evaluation |
| Performance | High — simple role lookup | Lower — policy evaluation overhead |
| Multi-tenancy | Requires tenant-scoped roles | Natural — tenant ID is an attribute |
| Dynamic decisions | Not possible without new roles | Native — environment attributes |
| Best for | Enterprise apps, admin panels | SaaS, healthcare, finance, government |
Combining RBAC and ABAC
Most production systems use a hybrid: RBAC for coarse-grained access (the user must be an `editor` to write anything) and ABAC for fine-grained decisions (editors can only modify resources they own, during business hours, from approved IP ranges). This keeps the permission model auditable while enabling dynamic policy enforcement.
Multi-Tenancy Authorization
In a multi-tenant SaaS system, every authorization decision must be scoped to a tenant. The most critical bug class is cross-tenant data leakage — user Alice from TenantA accidentally reading TenantB's data. There are two common approaches: tenant-scoped roles (the user has role `admin` within tenant `T1`, expressed as `T1:admin`) or tenant as an ABAC attribute (every resource has a `tenant_id`, and every policy requires `subject.tenant_id == resource.tenant_id`).
ReBAC: Relationship-Based Access Control
Google Zanzibar (the authorization system behind Google Drive, Docs, and YouTube) introduced Relationship-Based Access Control. Access is determined by a graph of relationships between users and objects: 'User A is an editor of Folder B, which contains Document C, therefore User A can edit Document C.' This is expressive enough to model complex real-world ownership and delegation hierarchies that RBAC and ABAC struggle with. Open-source implementations include Authzed/SpiceDB, OpenFGA, and Ory Keto.
Interview Tip
If an interviewer asks you to design authorization for a Google Drive-like system, name-drop Google Zanzibar and ReBAC. Explain that you need relationship tuples (user: alice, relation: editor, object: folder:marketing) and that authorization queries traverse this graph. This demonstrates awareness of state-of-the-art authorization systems beyond basic RBAC.
Storing and Enforcing Permissions
- Centralized Policy Engine: OPA, Casbin, or SpiceDB runs as a sidecar or microservice. Every service makes an authorization call before handling a request. Adds latency (~1–5ms) but enables consistent policy enforcement.
- JWT Claims: Embed roles/permissions in the JWT. Fast (no extra call), but permissions are stale until token refresh. Only suitable for coarse-grained, infrequently-changing permissions.
- Middleware/Decorator: Authorization logic in API gateway or framework middleware. Request is rejected before reaching business logic. Clean separation of concerns.
- Database Row-Level Security: For data access, PostgreSQL and others support row-level security policies enforced at the database layer — a powerful backstop against application bugs.