Fine-Grained Authorization with OpenFGA: Zanzibar-style ReBAC, Consistency, and the AuthZEN Standard
A practical guide to pulling authorization out of the three hundred conditionals scattered across your code and into a decision engine with a model, tests and its own operational story: why RBAC breaks the moment a permission depends on the specific object, the three Zanzibar ideas (relationship tuples, a schema that composes them, and a check as a graph search), a complete organization/folder/document model in the OpenFGA DSL with usersets, the public wildcard and tuple-to-userset, writing tuples with a pinned authorization_model_id — the detail that turns publishing a schema into a behavior change with no rollback —, a FastAPI dependency that removes permission logic from every handler and why it returns 404 rather than 403, the classic mistake of using ListObjects to build an IN (...) and three healthy strategies for listings, consistency with MINIMIZE_LATENCY versus HIGHER_CONSISTENCY applied surgically after each write plus the new enemy problem on revocations, ABAC without leaving ReBAC using CEL conditions and contextual tuples, a latency budget and the authorization N+1 solved with BatchCheck and correlation_id, model tests in CI with .fga.yaml, a shadow-mode migration with disagreement metrics and the moment to start failing closed, and the AuthZEN Authorization API 1.0 standard approved in January 2026 as a portable boundary between PEP and PDP. With eight recurring mistakes, a production checklist, FAQ and glossary. Production-ready code in Python, YAML, JSON and the OpenFGA DSL. Expanded edition of 12 September 2026: the model as a deployment artifact, with schema 1.2 modular models, fga.mod and the rule of pinning authorization_model_id so that publishing is not a fleet-wide behaviour change without rollback; the three graph limits (resolveNodeLimit, the ListObjects deadline and the 1,000-result cap that returns incomplete lists with a 200 and no signal at all) and the pattern of filtering in your database and authorizing only the page with BatchCheck; the check query cache and the cache controller invalidating from the changelog, and why your own Redis cache is a window of improper access; the changelog as the basis for historical audit, with a projector that advances the cursor in the same transaction as the rows; server operation with Postgres pool sizing, separate read and write datastores, Prometheus metrics and alerts; advanced modeling with exclusion, intersection and access that expires through conditions rather than nightly jobs; the map of alternatives (SpiceDB, Cerbos, OPA, Cedar) with the criterion that separates relationship engines from policy engines; and a complete migration case from 412 decision points down to 30, with week-by-week figures and the eleven-minute incident that was not the model but the pool.
Verificando acceso...