Interview Q&A · 30 questions · 5 answered publicly
Authorization Interview Questions
Ordered by how often they're asked. Sign in free to see every answer, rate your confidence and run timed mock interviews.
-
Most asked Compare RBAC, ABAC and ReBAC authorization models.
Summary
RBAC grants permissions to roles assigned to users; ABAC evaluates policies over attributes of the user, resource, action and context; ReBAC (relationship-based) derives access from relationships in a graph, like 'editor of the folder containing this document'.
Longer Answer
RBAC is simple and auditable but leads to role explosion for fine-grained rules. ABAC handles rules like 'managers can approve expenses under $5k in their department during business hours'. ReBAC (Google Zanzibar, OpenFGA, SpiceDB) models sharing and hierarchies (orgs → teams → projects → documents) naturally. Many real systems combine them: roles for coarse access, relationships or attributes for resource-level checks.
Key Points
- RBAC: roles → permissions
- ABAC: attribute policies
- ReBAC: relationship graph
- Combine models in practice
Comparison / Tradeoffs
Model Strength Weakness RBAC Simple, auditable Role explosion ABAC Context-rich rules Harder to audit ReBAC Sharing & hierarchies Needs a relationship store -
Most asked What is IDOR (Insecure Direct Object Reference) and how do you prevent it?
Summary
IDOR (now called Broken Object Level Authorization in APIs) happens when an endpoint trusts a client-supplied ID without checking that the current user may access that object, e.g.,
GET /invoices/1043returning someone else's invoice.Longer Answer
Prevent it by scoping every query to the caller (
WHERE id = :id AND tenant_id = :tenant), or checking ownership/permission after loading, in a central place (policy layer, repository). Random IDs (UUIDs) make guessing harder but are not access control. It's the #1 risk in the OWASP API Security Top 10.Code Example
# FastAPI: scope the lookup to the current user's tenant @router.get("/invoices/{invoice_id}") async def get_invoice(invoice_id: int, user: CurrentUser, db: DbSession): invoice = await db.scalar( select(Invoice).where(Invoice.id == invoice_id, Invoice.tenant_id == user.tenant_id) ) if invoice is None: raise HTTPException(404) # don't reveal that it exists elsewhere authorize(user, "invoice:read", invoice) return invoiceKey Points
- Never trust client-supplied IDs
- Scope queries to the caller/tenant
- Central policy checks
- UUIDs ≠ authorization
-
Most asked What is the principle of least privilege and how do you apply it?
Summary
Least privilege means every user, service and process gets only the permissions it needs, for only as long as it needs them. It limits the blast radius of mistakes and compromises.
Longer Answer
Apply it with narrowly scoped roles, separate read/write permissions, short-lived elevated access (just-in-time admin with approval), scoped API keys and tokens, per-service database users, and regular access reviews that remove unused permissions.
Key Points
- Minimal permissions
- Time-bound elevation
- Scoped credentials
- Regular access reviews
-
Most asked Where should authorization checks live in an application?
Summary
Enforce authorization on the server, as close to the data and business logic as practical, through a central policy layer that every entry point (HTTP, GraphQL resolvers, jobs, admin tools) calls. UI checks only improve the experience; they never protect data.
Longer Answer
Typical layering: the route/middleware checks authentication and coarse permissions; the service/use-case layer checks resource-level permissions with a policy function (
can(user, 'refund', order)); the data layer scopes queries by tenant. Avoid scatteringif user.role == 'admin'across the codebase.Key Points
- Server-side, near the data
- Central policy functions
- Cover every entry point
- UI checks are cosmetic
-
Most asked How do you enforce tenant isolation in a multi-tenant SaaS?
Summary
Resolve the tenant from the authenticated session or token (never from an unchecked request parameter), and scope every query, cache key, file path and message by tenant, ideally enforced centrally (repository base filters or database row-level security).
Longer Answer
Postgres Row-Level Security with a session variable (
SET app.tenant_id) adds a defense-in-depth backstop. Write automated tests that try cross-tenant access. Separate databases per tenant give stronger isolation at higher operational cost.Key Points
- Tenant from trusted identity
- Scope queries, caches, storage
- Row-level security as backstop
- Cross-tenant access tests
- What are policy engines like OPA, Cedar or Casbin, and when would you use one?
- What is Google Zanzibar and how does it inspire systems like OpenFGA and SpiceDB?
- What is role explosion and how do you avoid it?
- Why should code check permissions rather than role names?
- How do you handle authorization in GraphQL APIs?
- What is mass assignment and how is it an authorization problem?
- How do you design privileged admin access safely?
- How do OAuth scopes relate to user permissions in an API?
- Is it enough to enforce authorization at an API gateway?
- How should the frontend handle permissions?
- How do you cache authorization decisions safely?
- How do you audit who has access to what?
- How do you model resource ownership and sharing?
- How does Postgres Row-Level Security work?
- How do you test authorization logic?
- Should an unauthorized request for a resource return 403 or 404?
- How do you implement delegated access (acting on behalf of someone)?
- What's the difference between feature flags and authorization?
- Should you put roles and permissions inside JWTs?
- How do you enforce authorization in background jobs and queues?
- How do you return only the data a user is allowed to see in list endpoints?
- What is the 'new enemy' problem in authorization systems?
- How does cloud IAM (AWS/GCP/Azure) authorization work at a high level?
- How do plan limits (quotas) relate to authorization?
- What is privilege escalation and how do you prevent it?