An authorization system can return Allow after a blocking policy fails to evaluate. Cedar documents that possibility: errored policies are skipped, so a successful permit may still determine the result. How would your application handle it?
That question makes a useful senior or staff engineer practice exercise. Drawing an authorization service leaves its most consequential behavior unspecified: how rules combine, which facts they trust, how inherited access works, and what changes when a new model reaches production. A strong explanation makes those choices inspectable.
This guide develops a hypothetical document-sharing product through those decisions. The examples, test cases, and rehearsal schedule are practice tools, not a company's disclosed interview rubric. For session and identity mechanics, start with our authentication interview guide; here, assume the caller has already authenticated.
1. Define the decision before choosing the engine
Write one complete question: may employee Maya export document D in this request? Cedar expresses requests through principal, action, resource, and context. Adopt that vocabulary for the exercise, then identify the authoritative source of each input. The authenticated identity, document owner, and requested operation should have explicit origins.
Separate product decisions that otherwise hide inside the word “access.” Reading a document, changing its sharing settings, and exporting its contents are separate actions in this proposed model. Ask whether a folder editor may grant external access, whether suspended users retain any capabilities, and whether a document owner can override an organization restriction. Write the answers as examples before introducing policy syntax.
Make rule combination visible
For a concrete engine example, Cedar grants access when a permit succeeds and no forbid succeeds. With no successful permit, it denies. Its rules yield this small truth table, assuming no other policies apply:
| Permit evaluates to | Forbid evaluates to | Cedar decision |
|---|---|---|
| True | False | Allow |
| True | True | Deny |
| False | False | Deny |
| True | Error | Allow, with error diagnostics |
Choose the application's response to that last row explicitly. For this exercise, propose rejecting protected operations whenever a required restriction cannot be evaluated. That is an application rule layered over the engine's documented semantics. Explain which diagnostics trigger rejection and how operators distinguish malformed data from a legitimate policy denial. Otherwise, “fail closed” remains an aspiration without an implementation boundary.
Other engines expose different states. SpiceDB caveats can produce a conditional result when required context is missing, including the names of missing fields. Your adapter should require a definite permission result before releasing protected content. Retrieve trusted missing facts or reject the operation; treating a conditional response as a truthy success would change the intended contract.
Give request context a narrow purpose. Cedar recommends context for request-specific information, such as authentication posture, rather than attributes naturally belonging to the principal or resource. For our design, derive a recent-MFA assertion from verified session state. Do not accept an equivalent-looking browser field as evidence. For every condition, name who can change its input.
2. Model relationships that match the product
Use a tiny concrete example. Maya belongs to the infrastructure group; that group edits the Operations folder; document D belongs to Operations. Propose that document viewers include direct viewers, document editors, and viewers inherited from the parent folder. Keep this as conceptual set logic until you choose an implementation, so readers can question the product rule without first learning a language.
OpenFGA's parent-child guide demonstrates this pattern: a user's editor relationship with a folder can confer editor access to its documents through the authorization model. The parent relationship supplies structure; the model determines the inherited permission. Storing “D belongs to Operations” alone does not state which operations Maya may perform.
Ask what should happen when D moves to another folder. In the proposed product, decide whether direct grants survive, inherited grants change, and external sharing requires renewed approval. Write the expected result for each affected user. A move is now a testable permission change with an owner, rather than an opaque update to a parent identifier.
Challenge the shape of the graph
Then introduce nesting: a team can belong to a department, which belongs to a division. What limits will this product enforce? Propose a maximum nesting depth, a policy on multiple parents, and an approach to preventing cycles. Those choices should follow the customer's organizational model and the evaluator's behavior. Avoid casually promising arbitrary graphs when the evaluation path has operational limits.
SpiceDB currently documents a default traversal-depth limit of 50 hops. Excessive depth returns an error; cycles can reach the same limit. A successful check may short-circuit before encountering a cycle, so one successful example does not establish that the relationship data is healthy. Include unsuccessful subjects when testing a nested model.
Keep a business-level permission name at the application boundary: document.export communicates intent more clearly than reusing whichever group relation currently grants it. In this exercise, explain how export could later require a stronger condition than read without changing every caller's interpretation. Prefer the smallest model that expresses the required behavior, with extension points tied to an actual follow-up requirement.
3. Design authorized listing as its own operation
Checking a known document and finding every document someone may see are different queries. OpenFGA distinguishes Check from Read and ListObjects: Check evaluates a relationship, Read retrieves stored tuples, and ListObjects finds related objects. A direct tuple read can miss access inherited through the model. Use the operation that answers the product's actual question.
State the listing contract separately: return documents Maya can read, ordered by modification time, with a continuation token. Suppose a hypothetical page examines twenty database rows and only three pass authorization. Returning those three is a valid sparse-page design if the interface promises it. Promising twenty authorized results requires continuing the scan or a suitable access-aware query strategy, with a bound on work.
In that scan-based design, advance the cursor past the rows examined, including rejected rows. Otherwise the next request can repeatedly inspect the same inaccessible candidates. Bind the cursor to the relevant query and caller context. This is a proposed pagination approach; explain how permission changes during traversal affect its completeness and whether the product offers a snapshot or a changing view.
Inspect API limits before claiming a listing is exhaustive. OpenFGA documents deadline and maximum-result limits for ListObjects. Consequently, absence from a bounded result set cannot alone establish denial. Explain how your application recognizes incomplete enumeration and avoids turning “not returned” into a definitive permission answer.
Also separate model identity from data freshness. OpenFGA's higher-consistency mode skips its cache and queries the database directly; its latency-oriented mode can use cached results when caching is enabled. Neither a model identifier nor a cursor, by itself, establishes a common snapshot across your content database and permission service.
The original Zanzibar paper makes causal ordering between access-control changes and content changes an explicit concern. Keep that lesson in your answer: specify which permission state must protect which content version. Our security design guide develops those revocation and enforcement boundaries further. Here, keep the focus on listing semantics and explain any remaining race.
Practice a permission decision aloud
Use Interview Copilot to prepare a system-design question set, then rehearse how you explain one authorization rule and the counterexample that could break it.
Create a free account4. Change the policy, data, and callers together
Treat a proposed policy update as a behavior change. Begin with a written delta: “External collaborators may read approved documents but may no longer export them.” Identify the policy, relationship data, caller behavior, and user interface involved. Name the team that owns each part and the evidence needed before enabling the change. This makes the rollout reviewable.
Validate the contract on both sides
Cedar policy validation is separate from authorization evaluation. A schema can catch unsupported actions, type mismatches, and unsafe access to optional attributes. The application must also supply requests and entity data consistent with that schema. Validation does not establish that a correctly typed department identifier came from a trustworthy source.
For the hypothetical export restriction, validate the new policy, the schema it expects, and examples of actual request shapes. Include absent and malformed attributes. When the schema changes, revalidate policies that depend on it; Cedar's documentation explicitly warns that previously valid policies can become invalid after a schema change. Keep the earlier error-handling decision in the application adapter throughout this process.
OpenFGA uses immutable authorization models and recommends pinning an explicit model ID. Its documentation describes coordinating relation renames with tuple migration and application changes. Apply that discipline to the exercise: publishing a new model should not silently change which interpretation every production caller uses. List and direct-access endpoints should deliberately target the intended version.
Propose evaluating old and new versions on the same captured practice inputs before switching enforcement. OpenFGA documents shadow checks as a rollout technique. Classify disagreements by direction: a new allow may expose content; a new deny may block legitimate work. Review both against the intended change. Agreement with the old policy is useful comparison evidence, but the written product rule remains the target.
Plan rollback against the data that will exist after rollout. If new callers start writing a renamed relation, an old model may no longer recognize those grants. For this proposed migration, retain compatible relationships until rollback is no longer needed, or provide a verified reverse migration. Merely restoring an earlier model identifier does not demonstrate that its required inputs still exist.
Explain decisions without collecting everything
OPA decision logs include query inputs, results, bundle metadata, and decision identifiers, and support masking sensitive fields. Use that as a concrete operational example. For this design, propose recording the action, safe identifiers, model revision, outcome, and diagnostic category. Avoid copying document contents, credentials, or unrestricted request bodies into an authorization log.
Use the record to answer a practical support question: why could Maya export yesterday but cannot today? Your explanation should distinguish a changed policy, changed membership, missing context, and evaluation failure. Describe the additional retained evidence required to reproduce an older decision. A model revision and a timestamp are helpful references; they cannot reconstruct historical relationship data that you never preserved.
5. Rehearse counterexamples and a safe rollout
Build a small expected-outcome matrix before practicing the architecture diagram. OPA supports parameterized policy tests and coverage reporting, offering one concrete way to execute such cases. The table below is an illustrative product specification for our exercise. Its expected outcomes must be adapted when you choose a different product contract.
| Practice case | Expected behavior under this proposed contract |
|---|---|
| Direct viewer reads a document | Allow |
| Folder editor reads an inherited document | Allow through the parent relationship |
| External reader attempts export | Deny under the new restriction |
| Required restriction cannot evaluate | Application rejects; operator receives diagnostics |
| Caveat context is incomplete | Obtain trusted context or reject |
| Enumeration reaches a configured bound | Report incomplete enumeration; infer no denial from omission |
| Old caller reads newly migrated relations | Verify the declared compatibility or rollback path |
Pair each allowed case with a nearby counterexample. Change the action from read to export; move the document; remove one membership edge; provide the correct attribute name with an incorrect type. Ask which layer should reject the request and which diagnostic should appear. The aim is to expose assumptions at the boundary between the application and the evaluator.
Test disagreements between interfaces too. Under the same declared inputs and freshness assumptions, should an object listed as readable pass a direct read check? What happens if membership changes between those operations? Write the expected behavior rather than declaring the two APIs “consistent” without a definition. Give the listing limit and the graph-depth failure their own cases so operational bounds remain visible.
For a self-directed 35-minute rehearsal, spend five minutes establishing actions and rule precedence, eight tracing the relationship model, seven designing listing, eight explaining migration and diagnostics, and seven challenging the result. These allocations are an illustrative practice schedule. Ask a partner to introduce one new restriction halfway through, then explain which policies, data, tests, and callers must change.
Finish by stating a precise promise: which decision the application enforces, which facts it trusts, and how a reviewer can detect an unintended permission change. That gives the discussion something concrete to challenge. As the hypothetical product gains more roles and sharing rules, keep returning to the same question: can you explain why this particular request is allowed?
Build an authorization answer you can defend
Prepare with Interview Copilot, then practice your policy truth table, relationship walkthrough, and rollout explanation. Use follow-up questions to make each assumption explicit.
Start preparing for freeSources & References
- How Cedar authorization works — request components, rule combination, skip-on-error semantics, and diagnostics.
- Cedar: Using the context — request-specific context and entity attributes.
- SpiceDB caveats — conditional permissions and missing context.
- OpenFGA: Parent-Child Objects — inherited relationships between folders and documents.
- SpiceDB: Cyclical Relationships and Traversal Limits — depth errors, cycles, and short-circuiting.
- OpenFGA: Relationship Queries — Check, Read, ListObjects, and enumeration limits.
- OpenFGA: Query Consistency Modes — cache behavior and higher-consistency queries.
- Zanzibar: Google's Consistent, Global Authorization System — 2019 primary research on access-control and content ordering.
- Cedar policy validation against schema — validation checks and application input obligations.
- OpenFGA: Immutable Authorization Models — model pinning, migrations, and shadow checks.
- OPA: Decision Logs — decision identifiers, revision metadata, and masking.
- OPA: Policy Testing — parameterized tests and coverage reporting.