# Fine-grained authorization: model access to individual resources

> A practical guide to resource-level authorization with explicit permission rules, inheritance, enforcement, and tests, illustrated with Ory Keto.

Canonical URL: https://www.devobs.io/articles/fine-grained-authorization/
By: Lena Fischer
Published: 2026-10-02T17:28:35.606Z
Updated: 2026-10-02T17:28:35.606Z
Section: Authorization

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](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) 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](https://www.ory.com/docs/network/keto)

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, `read` or `write`.
- **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](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html).

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](https://www.ory.com/docs/network/kratos/intro)
- **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](https://www.ory.com/docs/network/hydra)
- **Application permissions:** Keto models relationships and permission rules, including access to particular documents. [Keto documentation](https://www.ory.com/docs/network/keto)

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](https://www.ory.com/docs/network/keto)

The following pseudocode restates the example’s logic; it is not runnable OPL:

```text
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](https://www.ory.com/docs/network/keto)

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](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html)

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](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/)

Ory describes Keto as implementing Zanzibar’s design principles. Architectural inspiration alone, however, does not establish identical consistency guarantees or performance. [Keto documentation](https://www.ory.com/docs/network/keto)

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](https://www.ory.com/docs/network/keto). 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](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html)

Build tests at three levels:

1. **Policy decisions:** Verify every row, including the subject who is an editor of a different document.
2. **Endpoint enforcement:** Verify that denied reads and writes cannot proceed through the protected endpoint.
3. **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.

## Source references

- <https://www.ory.com/docs/network/keto>
- <https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html>
- <https://www.ory.com/docs/network/kratos/intro>
- <https://www.ory.com/docs/network/hydra>
- <https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/>
