System Design Interview: Design a Multi-Tenant Authorization Policy Service
Prompt and context
A B2B platform has many business services, each reimplementing “can this user access this resource?” Build a shared, multi-tenant authorization policy service supporting roles, resource relationships, and attribute conditions. Policy updates need versions, and a brief central-service failure must not create cross-tenant access.
The focus is the authorization decision path and its consistency boundary. Do not draw only a policy database; explain request context, policy publication, cache invalidation, and the effect of deny or timeout on callers.
What the interviewer is testing
They want you to separate a Policy Decision Point (PDP) from the Policy Enforcement Point (PEP) in the business service, and to design tenant isolation, version propagation, and explainable audit. OPA documents decoupling policy decisions from enforcement; the Zanzibar paper shows the trade-offs of causal ordering and low latency at global authorization scale.
Questions to clarify first
Ask about request volume, latency target, policy complexity, tenant count, relationship depth, and consistency requirements. Clarify policy authors, approval workflow, revocation deadline, offline evaluation, sensitive fields in decision logs, and whether callers can safely fail closed.
A 30-second answer structure
I would define the authorization model and the invariant “no cross-tenant access,” then draw PEP, PDP, policy storage, distribution, and audit. Policies are versioned per tenant; PDPs cache approved versions locally or in-zone. Requests carry subject, action, resource, and required context. High-risk revocations require version confirmation; if PDP is unavailable, sensitive actions deny. Measure p99 latency, propagation delay, false denies, and audit integrity.
Step-by-step analysis
Step 1: Define authorization objects and policy input
Use a consistent request shape: subject, action, resource, and context, with unambiguous tenant and resource namespaces. RBAC handles role grants, ABAC handles attributes such as department, device, or time, and relationship-based authorization handles “this user is a document collaborator.” The policy language must express allow, deny, precedence, and conditions without every service composing its own rules.
Step 2: Separate PEP, PDP, and policy management
The PEP stays in an API gateway or business service, gathers trusted identity, and enforces allow or deny. The PDP evaluates and returns a decision, policy version, and optional reason. The management plane edits, validates, approves, publishes, and rolls back policies. OPA decouples decisions from enforcement and supports local or network deployment; choose based on latency and availability.
Step 3: Design tenant storage and versioned publication
Policy, resource relationships, and attributes carry tenant_id, which is checked on every read. Publication creates an immutable version and digest, evaluates it in a sandbox, then distributes it in batches to PDPs. Record author, approver, time, and scope. Revocation or security policies can take priority and require old versions to expire within a bounded window.
Step 4: Handle consistency, caching, and revocation
Cache keys include tenant, subject, action, resource, and policy_version; a user ID alone is unsafe. Cache low-risk allows with a measured TTL, but use short TTL or forced version checks for revocations, admin changes, and sensitive resources. PDPs return the version used; a business service can reject an older version. Multi-region deployments propagate version watermarks instead of assuming simultaneous invalidation.
Step 5: Design failure modes and secure defaults
Define behavior for PDP timeout, delayed policy distribution, unavailable attribute sources, and a congested audit pipeline. Sensitive writes, permission changes, and exports fail closed; low-risk reads may use an approved, time-bounded cache. A caller must never turn a network error into allow. Limit policy size, relationship recursion, and per-tenant QPS so policy evaluation cannot become a denial-of-service vector.
Step 6: Audit, explainability, and observability
Record decision_id, tenant, subject summary, action, resource summary, result, policy version, and latency; redact or hash sensitive input. The log should answer “who was allowed what, when, and under which version” without becoming a new data-leak surface. Monitor p50/p95/p99, cache hits, version propagation, deny ratio, timeouts, and retries; alert on unusual denies or rollbacks.
High-quality sample answer
I would split the system into PEPs in business services, in-zone PDP clusters, and an independent management plane. Requests carry subject, action, resource, and trusted context. The PDP evaluates tenant-isolated RBAC, relationship, and attribute policies and returns allow or deny, a decisionid, and policyversion. Policy edits are validated and tested, become immutable versions, distribute by tenant and region, and support rollback.
Cache keys include tenant, subject, action, resource, and version. Ordinary reads can use a short-TTL allow, while revocations, permission changes, and data exports require version confirmation; a caller rejects a PDP result older than its required watermark. PDP timeout or missing attributes fail closed for sensitive actions.
Each decision records tenant, subject summary, action, resource summary, result, version, and latency with redacted input. Core metrics are p99 latency, hit rate, propagation delay, stale-version denies, errors, and audit integrity. This centralizes policy management while keeping enforcement at the service boundary.
Common mistakes and improvements
- Drawing one “authorization microservice”: add PEP, PDP, management, and distribution paths.
- Mentioning only RBAC: explain how attributes and relationships enter a decision.
- Caching only by user: include tenant, resource, action, and policy version.
- Allowing on timeout: sensitive operations fail closed and low-risk cache use is explicit.
- Logging the full request: redact input while retaining a traceable decision_id and version.
Follow-up questions and responses
How quickly must a revocation take effect?
Classify revocations by risk. For administrator, export, and sensitive-data actions, require a fresh policy version or a short bounded propagation window; lower-risk reads can use a measured TTL. State the target and monitor it.
Should PDP run locally or as a shared service?
Use local or same-zone PDP for latency and failure isolation, with a shared management plane for policy distribution. A central network PDP can simplify updates but adds a dependency to every request; choose per workload and test the failure mode.
How do you prevent one tenant from reading another tenant’s policy?
Put tenant identity in the authenticated request, enforce it in storage and cache keys, and test cross-tenant queries as a release gate. Never trust a tenant identifier supplied only by the caller payload.
How do you debug an unexpected deny?
Return a decision_id and policy version, then inspect redacted inputs, matching rules, attribute freshness, and propagation state. Do not expose sensitive policy internals to end users; give operators a controlled explanation view.