A resource-level authorization check asks whether a specific subject may perform a specific action on a specific resource—for example, whether user alice may read document design-42. A successful login does not answer that question: authentication verifies identity, while authorization determines whether the requested action is permitted. OWASP’s Authorization Cheat Sheet makes this distinction explicit.
Ory Keto provides a concrete example of fine-grained permissions: a relationship stating that a particular user belongs to the readers of a particular document. Keto documentation
The practical design task is to describe which relationships grant access, which permissions follow from those relationships, where decisions are enforced, and how the rules behave when access changes.
Start with the subject, action, and resource
For a document application, express each access question in three parts:
- Subject: Who is requesting access? For example,
alice. - Action: What do they want to do? For example,
readorwrite. - Resource: Which object is involved? For example,
design-42.
This framing makes the intended boundary visible: “Alice can edit this document” is a different requirement from “Alice can edit every document.”
Write concrete requirements before choosing their representation. Then decide whether each grant should be direct or follow from a broader relationship, such as membership in a team. Keep the resource boundary explicit in both cases.
Keep identity, OAuth/OIDC, and resource permissions distinct
An authenticated user is not necessarily authorized to access every resource. Conversely, some resources may intentionally be available without authentication, as OWASP notes.
Ory’s documentation for Ory Network illustrates a separation of responsibilities:
- Identity and account management: The Kratos-based identity service covers login, registration, account recovery, and profile management. Kratos documentation
- OAuth 2.0 and OpenID Connect: The Hydra-based service provides these protocols through a headless API that can work with an existing user-management system. Hydra documentation
- Application permissions: Keto models relationships and permission rules, including access to particular documents. Keto documentation
When designing a document endpoint, make “may this subject read this document?” an explicit decision rather than substituting “this user signed in successfully.”
Separate relationships from derived permissions
In Keto’s introductory example, a Document has editors and viewers relationships. Its write and read permissions are functions derived from those relationships. The example uses Ory Permission Language, or OPL, which Ory defines as a subset of TypeScript—not unrestricted TypeScript. Keto OPL documentation and example
The following pseudocode restates the example’s logic; it is not runnable OPL:
can_write(subject, document) =
subject is in document.editors
can_read(subject, document) =
can_write(subject, document)
OR subject is in document.viewers
The read rule explicitly includes the write permission. Under these rules, an editor can both write and read. A subject who is only a viewer can read but cannot write. “Write implies read” is a rule this example defines, not an automatic implication to assume in every permission model.
The modeling lesson is to make implications explicit. If your product requires editors to read documents, encode that connection rather than leaving it as an assumption.
Use inheritance deliberately
Direct user-to-document relationships are not the only option. Ory documents support for permission inheritance through groups, roles, and hierarchies. Keto documentation
For an illustrative team-document design, you might propose that members of a team can read documents assigned to that team. That is an application policy to design, not a ready-made rule supplied by the introductory example above.
Before implementing it, resolve questions such as:
- Does team membership grant reading, editing, or both?
- Can a document also have direct user grants?
- What should happen to access when the document moves to another team?
- Should a former team member retain access through a separate direct grant?
These questions define the intended interaction between direct and inherited access. Answer them before translating the policy into configuration.
Enforce the decision on every request
OWASP recommends denying access by default and validating permissions on every request. It also warns against relying on client-side access checks as the decisive factor in granting or denying access. OWASP authorization guidance
For the document application, apply that guidance by placing an authorization gate in the server-side path for each protected read or write operation. Check the requested action against the actual document being accessed. A denied decision must prevent the operation, not merely hide a button.
Treat the permission model and its enforcement as separate test targets: verify both that the rule returns the intended decision and that the endpoint obeys it.
Treat consistency as part of permission behavior
Permission changes introduce another design dimension: how authorization decisions relate to changes in access rights and object contents.
In their 2019 Zanzibar paper, Google’s authors describe a global authorization system whose decisions respect the causal ordering of user actions, providing external consistency amid changes to access-control lists and object contents. This makes consistency part of the described authorization semantics, not merely a performance concern. Zanzibar paper abstract
Ory describes Keto as implementing Zanzibar’s design principles. Architectural inspiration alone, however, does not establish identical consistency guarantees or performance. Keto documentation
For your application, specify when a revoked grant must stop authorizing requests and how access checks should relate to content updates. Evaluate the chosen implementation against that contract rather than inferring behavior from its architectural inspiration.
Turn the policy into an expected-results matrix
For the document rules above, these outcomes follow from the permission functions in Ory’s OPL example. The table assumes no additional grants or inheritance rules.
| Subject’s relationships to the requested document | Read | Write |
|---|---|---|
| Editor only | Allow | Allow |
| Viewer only | Allow | Deny |
| Both editor and viewer | Allow | Allow |
| Neither | Deny | Deny |
| Editor of another document only | Deny | Deny |
Use this matrix as a starting point for tests. OWASP specifically recommends unit and integration tests for authorization logic. OWASP testing guidance
Build tests at three levels:
- Policy decisions: Verify every row, including the subject who is an editor of a different document.
- Endpoint enforcement: Verify that denied reads and writes cannot proceed through the protected endpoint.
- Access changes: When adding inheritance, test membership changes, resource moves, and overlapping direct grants. Test the consistency contract separately from the static permission rules.
For overlapping grants, write the expected result explicitly. Removing team membership and removing all access are different policy changes if your design also permits independent direct grants.
The essential discipline is to connect every allowed action to an explicit rule for the requested resource, enforce that decision, and make the resulting behavior concrete enough to test.
Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗