Overview
Authorization (AuthZ) answers 'what are you allowed to do?'. After a user or service is authenticated, every request must be checked against policy: can this user read this document, refund this order, or delete this project? Authorization failures are the most common serious API vulnerability.
Common models are role-based access control (RBAC: users get roles, roles get permissions), attribute-based access control (ABAC: rules on user, resource, and context attributes), and relationship-based access control (ReBAC: permissions follow relationships like 'owner of' or 'member of the team that owns'). Large systems often centralize policy in a dedicated service.
Your badge proves who you are (authentication). Whether it opens the server room depends on your role, the time of day, and whether you are on the access list for that room (authorization).
When to use it
- Any multi-user or multi-tenant system.
- Admin features, sharing, and team permissions.
- Regulated data requiring least privilege and audit logs.
- APIs where object IDs appear in URLs (/documents/123).
Where it shows up in interviews
Recognize it when: users must only see their organization's data.
- Design a SaaS platform
- Design Slack workspaces
Recognize it when: documents shared with users, groups, and links.
- Design Google Drive
- Design Notion permissions
Where it is used in real software
Google's global authorization system for Drive, YouTube, and Photos uses relationship tuples; open-source versions include SpiceDB and OpenFGA.
Policies with allow and deny statements evaluated on principal, action, resource, and conditions.
Broken object level authorization (BOLA / IDOR) is ranked the number one API security risk.
Key terms
- RBAC
- Permissions attached to roles (admin, editor, viewer).
- ABAC
- Policies evaluate attributes: user.department == resource.department.
- ReBAC
- Permissions derived from relationships in a graph (Zanzibar).
- IDOR / BOLA
- Accessing another user's object by changing an ID in the request.
- Policy decision point
- The service that evaluates authorization policy (for example OPA).
How it works, step by step
- 1Identify the subject
The authenticated user or service, with its roles and attributes.
- 2Identify the action and resource
action = document.edit, resource = document 123 in tenant 7.
- 3Load context
Resource owner, tenant, sharing relationships, time, IP.
- 4Evaluate policy
Deny by default; allow only if a rule grants it.
- 5Enforce and audit
Return 403 (or 404 to hide existence) and log sensitive decisions.
RBAC vs ABAC vs ReBAC
Same question: can Ana edit document 123?
| Model | How the answer is computed | Good for |
|---|---|---|
| RBAC | Ana has role editor, editor has document.edit | Simple apps, admin consoles |
| ABAC | Ana.dept == doc.dept AND doc.status != locked | Fine-grained, context-aware rules |
| ReBAC | Ana is member of team X, team X is editor of folder Y, doc in folder Y | Sharing, hierarchies, collaboration |
NOWModel: RBAC | How the answer is computed: Ana has role editor, editor has document.edit | Good for: Simple apps, admin consoles
RBAC alone cannot express 'only the owner can edit their document'; most real systems combine roles with ownership or relationship checks.
Implementation
type Role = "viewer" | "editor" | "admin";const permissions: Record<Role, string[]> = { viewer: ["doc.read"], editor: ["doc.read", "doc.edit"], admin: ["doc.read", "doc.edit", "doc.delete", "member.manage"],}; function can(user: User, action: string, doc: Doc): boolean { if (user.tenantId !== doc.tenantId) return false; // tenant isolation first if (doc.ownerId === user.id) return true; // ownership const role = doc.members[user.id]; // per-document role return role !== undefined && permissions[role].includes(action);} app.patch("/v1/docs/:id", async (req, res) => { const doc = await docs.get(req.params.id); if (!doc || !can(req.user, "doc.edit", doc)) return res.status(404).end(); // do not reveal existence res.json(await docs.update(doc.id, req.body));});Complexity and performance
Cache policies and relationships.
Bounded with caching.
Trade-offs
A central policy service gives consistency and audits but adds a network call; in-app checks are fast but drift between services.
403 is honest; 404 avoids revealing that a resource exists, which matters for private data.
Variants and related techniques
Open Policy Agent (Rego) or Cedar evaluate policies separately from application code.
PostgreSQL RLS enforces tenant filters in the database itself.
Coarse permissions granted to a client app (read:orders), combined with per-object checks.
Common mistakes
- Checking only that the user is logged in.
Fix: Check that this user may act on this specific object.
- Trusting client-side checks.
Fix: Hiding a button is not authorization; enforce on the server.
- Forgetting list endpoints.
Fix: Filter queries by tenant and permissions, not just single-item fetches.
Interview questions
What is an IDOR vulnerability and how do you prevent it?
Insecure direct object reference: a user changes /orders/123 to /orders/124 and sees someone else's order. Prevent it by checking ownership or permission for every object access, and scoping queries by tenant and user.
How would you design permissions for Google Docs sharing?
Relationship-based: store tuples like (doc:123, editor, user:ana) and (folder:9, viewer, group:eng), inherit permissions through folders, and evaluate with a Zanzibar-style service with caching.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design RBAC for an admin dashboard | Easy | Roles and permissions. |
| Design tenant isolation for a B2B SaaS | Medium | Tenant scoping at every layer. |
| Design Google Drive sharing | Hard | ReBAC and inheritance. |