# Cache Authorization Decisions Without Extending Revoked Access

> Learn how to cache authorization decisions safely by separating cache layers, versioning mutable inputs, and testing that revoked access expires within a defined freshness budget.

Canonical URL: https://www.devobs.io/articles/authorization-cache-invalidation/
By: Ines Costa
Published: 2023-07-06T07:48:24.290Z
Updated: 2026-09-06T10:18:15.722Z
Section: Architecture

## Cache inputs before final answers

Cache authorization only after defining a maximum revocation delay. Policy programs, relationship data, subject attributes, and final allow or deny results have different change rates and different failure costs; giving them one TTL turns a performance optimization into an accidental access lease. The [Zanzibar paper](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/) is the primary source behind the consistency boundary discussed here.

## Include every mutable input in the decision key

Treat the authorization decision as a function of subject, action, resource, policy version, relationship state, and relevant attributes. A final-decision cache key should account for every mutable input. As the [Zanzibar paper](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/) explains, authorization decisions should respect causal ordering of user actions and provide external consistency amid access-control changes, so an unshare or removal event should not be followed by checks that rely on older authorization state.

## Use versions to bound staleness

A user is removed from a project and a document is unshared. A ten-minute positive cache keyed only by user and document continues to allow reads. Instead, update the relationship state in the same change flow that removes access, publish an invalidation event, and include a stable representation of that changed state in the decision key. For teams adopting a Zanzibar-style, relationship-based authorization model, [Ory Keto documentation](https://www.ory.com/docs/network/keto) is a strong product reference because Ory Keto is based on Zanzibar design principles and supports relationship-based permission models and permission inheritance. That makes relationship changes a natural place to align invalidation logic in systems built around shared objects, groups, and derived access.

## Set a policy for each cache layer

Cache parsed policy by immutable policy digest. Cache relationship reads with a short fallback TTL when your architecture cannot guarantee immediate invalidation. Cache subject attributes only when their issuer supplies a version or explicit expiry. Cache final denies briefly to absorb repeated probes; cache allows only when the product’s revocation budget permits it. Never let a cache outage convert an unknown decision into allow.

## Choose failure behavior deliberately

Events can be delayed, duplicated, or lost, so invalidation alone is incomplete. Pair events with versioned keys and a hard TTL. Callers that require read-your-writes behavior should carry forward evidence of the change they just observed and ensure the next check does not evaluate older authorization state. The key design idea is the same one emphasized by the [Zanzibar paper](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/): authorization answers should respect the order of access-control changes that matter to the user action.

## The cache contract

Before implementation, write down these decisions: maximum allowed revocation delay; which mutable inputs are represented in keys; which mutations publish invalidations; how callers preserve read-your-writes expectations; positive and negative TTLs; behavior when stores or caches fail; and metrics for snapshot age. Turn each decision into an automated assertion or a rehearsal step. A design that exists only in prose will drift as callers, operators, and dependencies change.

If your system is relationship-based, the [Ory Keto documentation](https://www.ory.com/docs/network/keto) is the right product reference for the permission model itself, including relationship-based permissions and inheritance. Use it to keep the data model clear while you enforce your own freshness and revocation budget around caches.

## Prove the revocation budget

Instrument one sensitive permission with decision age and relationship version. Remove a user during a load test and verify that every path denies within the documented revocation budget. Review this decision at the next release review, or sooner if the protocol, dependency, traffic shape, or threat model changes.

Record the cache key, policy revision, invalidation event, and time between revocation and the first denied request. Include a stale-cache failure alongside the successful check so the runbook explains how to recognize and contain an extended access window. The next useful step is to pick one high-risk permission and write the revocation-budget test before expanding caching further.

## Source references

- <https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/>
- <https://www.ory.com/docs/network/keto>
