OpenFGA
OpenFGA evaluates authorization from a model and relationship data. It is useful when access follows relationships such as a user belonging to a group, a folder containing a document, or an owner being allowed to edit. The application still has to authenticate the caller, identify the correct object, and enforce the answer.
Start with one consequential operation and explain why a particular user should be allowed to perform it. Turn that explanation into relations, then write denied cases before expanding the model. A compact model with meaningful counterexamples is easier to assess than a diagram containing every noun in the company.
TL;DR
- Separate the authorization model from relationship tuples.
- Treat tuple writers as privileged components.
- Resolve user, tenant, and object identity from trusted application context.
- Pin model IDs and plan data compatibility during a rollout.
- Test denied access, inheritance, revocation, stale decisions, and service errors.
Quick Example
This fictional model lets document owners edit, editors view, and folder viewers view documents linked through parent. Direct viewers can also be assigned. It deliberately does not grant document edit rights through the folder relation.
The official OpenFGA syntax-transformer 0.2.2 converted this exact DSL to schema-1.1 JSON with the expected three types and viewer union. Malformed syntax was rejected. This is offline syntax evidence only: no OpenFGA server model validation, tuple writes, Check requests, or consistency tests ran. A valid parse does not establish correct authorization.
Core Concepts
Model and tuples
The model defines object types and relation rules; tuples supply concrete relationships. OpenFGA’s modeling guide starts with a feature, identifies objects and relations, defines the rules, and tests them. Modeling guide
Model selection
Authorization models are immutable. New writes create new model versions, and relevant APIs accept an authorization_model_id. Pinning that ID avoids silently selecting a newly created model. Immutable models
Consistency choice
The documented default MINIMIZE_LATENCY mode can use cached results. HIGHER_CONSISTENCY bypasses that cache for a database query. Select from the operation’s freshness requirements and actual deployment behavior; it is not a promise that every surrounding system becomes strongly consistent. Query consistency
Turn the model into an access test matrix
For each allowed case, ask which tuple and rule justify it. For each denied case, ask whether another path could still grant access. A direct permission removal may not revoke an inherited permission. The test must describe the intended final access, not merely confirm that one tuple disappeared.
Keep operation names specific. “Editor” is a role-like relationship; “can publish” may need additional state or authority. Avoid assuming that a broad relation covers every operation a UI places beside the edit button.
Decide who is authorized to change relationships. If an application lets a caller write an ownership tuple without checking the sharing operation, the policy engine can correctly evaluate an incorrectly granted relationship. Correct evaluation cannot compensate for an untrusted source of authority.
Integrate the decision at the application boundary
Authenticate the caller, resolve the resource, and construct the check from server-owned context. Do not let a request substitute an arbitrary user ID or tenant merely because those fields have the expected string format. Prefix conventions can help organization, but are not a substitute for enforcing tenant scope.
Specify how the application handles denial, timeout, invalid model, and unavailable authorization service. An unavailable answer is not the same as a valid denial or a valid allow. For consequential access, define a safe response and observability without silently converting an error into permission.
Check at the operation that exposes or changes the resource. Hiding a button only changes presentation. Background jobs, exports, search results, and bulk endpoints also need the correct authorization boundary.
Review caching outside OpenFGA as well. An application can keep an old access decision even if a fresh service query would deny it. Define the allowed decision lifetime and revocation expectation across the complete request path. Measure the performance and failure consequences of the consistency policy in a representative environment.
Roll out a model and its data together
Pin the current model before introducing the candidate. Describe which old and new tuple forms each model accepts and which application versions may be running simultaneously. A new relation can be syntactically valid while the data needed to grant intended access has not been populated.
Test the candidate against a representative, approved set of expected decisions. Include inheritance, removed membership, absent relationships, and cross-tenant attempts. Do not copy sensitive production relationships into an unapproved test environment merely to make the fixture realistic.
A migration may require staged compatibility or coordinated writes, but dual writing is not automatically the right design. If used, define reconciliation, partial-write handling, and the source of truth. Specify what rollback means after relationships have changed, not only which model ID to restore.
Accept the rollout when expected grants and denials, error behavior, freshness, and operational ownership are demonstrated. Parser success is an early check; it is a long way from an authorization signoff.
Best Practices
Start from a narrow operation
Write the user, action, object, relevant state, and reason for the expected decision. Keep denied examples beside the model.
Protect relationship changes
Review tuple write authority and reconcile it with the business source of truth. Audit who can grant and remove consequential relations.
Exercise revocation
Test the actual application path after relationship removal, including inherited access and every relevant decision cache.
Common Mistakes
Bad: Valid model syntax proves the access rules are right.
Correct: Validate the model on the target server and run expected allow/deny cases with actual tuples.
Bad: An object prefix isolates tenants.
Correct: Enforce trusted tenant resolution, resource ownership, and appropriate relationships at the application boundary.
Bad: Changing the model ID is the whole migration.
Correct: Plan tuple compatibility, mixed application versions, reconciliation, and rollback together.
FAQ
Does OpenFGA authenticate users?
The application supplies a trusted authenticated identity to its authorization checks. Model and tuple evaluation do not replace that identity boundary.
Does higher consistency solve every revocation delay?
It addresses the documented OpenFGA query-cache choice. Application caches, data propagation, and surrounding transactions still need review.
Which model should a production service use?
An explicitly selected, tested model ID with a rollout policy. Verify compatibility with its relationship data and current application version.
Related Topics
- Authorization Models: RBAC, ABAC, and ReBAC — Model choices and authorization boundaries.
- Identity & Access Management — Identity lifecycle and access governance.
- Authentication vs Authorization — Where relationship checks sit after identity is established.
- Authorization Testing Across Tenant Boundaries — Proving denials hold across tenants.
- Open Policy Agent and Rego — Policy-as-code as an alternative to relationship tuples.