# OpenFGA Relationship-based access control in the style of Google Zanzibar. No extra dependency: requests use the standard library. ```python from needtoknow.authorizers.openfga import OpenFGAAuthorizer authorizer = OpenFGAAuthorizer("http://localhost:8080", store_id, resource_field="doc_id") ``` ## The model The authorizer asks whether `user:` has a relation (by default `viewer` for the action `read`) on `document:`. A model such as this one fits: ```text model schema 1.1 type user type group relations define member: [user] type document relations define viewer: [user, user:*, group#member] ``` Chunks of one source document share its id in metadata (`doc_id` above), so a permission is written once per document, not once per chunk. With `resource_field=None` the chunk id is the resource. A chunk without its resource id is denied. Change the names with `object_type=`, `user_type=`, `relations={"read": "viewer"}`, `group_type=` and `group_relation=`. Pass `authorization_model_id=` to pin a model version. ## Checks `check` sends BatchCheck requests, `batch_size` (default 50) resources at a time, and asks about each distinct resource once even when several chunks share it. A per-item error, or a response that does not decide every resource, raises `AuthorizationError`. ## Pre-filtering with ListObjects `filter_for` calls ListObjects for every document the user may view and returns `In(resource_field, ids)`, so the vector store pre-filters. OpenFGA stops a ListObjects call when it has found `OPENFGA_LIST_OBJECTS_MAX_RESULTS` objects (1000 by default) or when it reaches `OPENFGA_LIST_OBJECTS_DEADLINE` (3 seconds by default), and answers with what it found so far, without marking the answer as partial. A partial list would never let anyone read more than they may, but it would hide documents they may read while the result still looked complete. So when the answer reaches `list_objects_limit` (default 1000), or the call took `list_objects_deadline` seconds or more (default 3), the authorizer returns no filter and the retriever post-filters with BatchCheck instead. Set both to your server's values; `list_objects_deadline=None` suits a server configured without a deadline. With the default `timeout` of 2 seconds, a call slow enough to meet the default deadline fails first and raises. ## Consistency `consistency="HIGHER_CONSISTENCY"` asks OpenFGA to skip its cache for every check and list, so a tuple written a moment ago counts at once, at a cost in latency. `"MINIMIZE_LATENCY"`, OpenFGA's default, may answer from its cache when one is configured (caching is off by default), so a revoked tuple can keep granting access until that cache entry expires. Leaving `consistency` unset sends nothing and lets the server decide. Choose it together with a [`CachingAuthorizer`](../operations.md#permission-caching)'s `ttl`: an answer the cache keeps may itself have come from OpenFGA's cache, so the revocation delay can reach the two added together. ## Groups from your identity provider With `contextual_groups=True` (the default), the subject's groups are sent with every request as contextual tuples (`user: member group:`), so group memberships from your identity provider take effect without being written to OpenFGA first. They are trusted as given, so they must come from authentication, not from the client. ## Failing closed Every failure raises `AuthorizationError`: an unreachable server, a timeout (`timeout=`, 2 seconds by default), an unexpected response, or an action with no configured relation. `headers=` adds headers to every request, for example an API token. Connections are reused and transient failures retried once (`retries=`); `circuit_breaker=` and `ssl_context=` work as described under [resilience](../operations.md#resilience-of-remote-permission-services). For high traffic, wrap it in a [`CachingAuthorizer`](../operations.md#permission-caching).