Fine-grained authorization (FGA) models resource access as relationships. Use it when roles alone cannot express rules such as “Alice can edit document 42,” “engineering group can view folder 7,” or “document viewers inherit access from parent folder.”
Public API
Authdog exposes an environment-scoped, Zanzibar-style FGA engine as a public REST API at https://api.authdog.com/fga/v1, authenticated with an environment API secret (adenv_…), the same credential the AuthZEN API uses. The environment is resolved from the token, so no tenant or environment id appears in the path.
| Operation | Endpoint |
|---|---|
| Check | POST /fga/v1/check |
| BatchCheck | POST /fga/v1/batch-check |
| ListObjects | POST /fga/v1/list-objects |
| Expand | POST /fga/v1/expand |
| Read | POST /fga/v1/read |
| Write / Delete | POST /fga/v1/write |
| List model versions | GET /fga/v1/models |
| Read a model | GET /fga/v1/models/{id} (latest for the active model) |
| Write a model | POST /fga/v1/models |
| Read assertions | GET /fga/v1/assertions/{modelId} |
| Write assertions | PUT /fga/v1/assertions/{modelId} |
| List indexed objects (early access) | GET /fga/v1/indexes/{indexId}/objects?user=… |
Objects and subjects use the compact string form namespace:id (a subject may carry a relation, namespace:id#relation). Example check:
curl https://api.authdog.com/fga/v1/check \
-H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "object": "document:readme", "relation": "viewer", "subject": "user:alice" }'
# → { "allowed": true }Add "trace": true to a check to also receive the proof path that granted access:
curl https://api.authdog.com/fga/v1/check \
-H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "object": "group:001", "relation": "member", "subject": "user:angela", "trace": true }'
# → { "allowed": true, "trace": { "kind": "relation", "label": "group:001#member", "children": [ … ] } }Write tuples with { "writes": [...], "deletes": [...] }, each tuple { "object", "relation", "subject" }. contextual_tuples may be supplied on Check/BatchCheck/ListObjects to evaluate against tuples not yet persisted.
For coarse role-based access, RBAC permissions remain the simpler choice.
Core model
A relationship tuple has this conceptual form:
document:roadmap#editor@user:aliceParts:
document: object namespaceroadmap: application-defined object IDeditor: relationuser:alice: subject
The subject can also be a userset:
document:roadmap#viewer@group:engineering#memberThis grants the document viewer relation to members of the engineering group.
Authdog stores tuples per environment with uniqueness across object, relation, and subject. Deleting an environment removes its FGA tuples.
Authorization model
An environment is one store. Each save of the model creates an immutable version with a model ID (a ULID). The latest version is the one enforced; earlier versions stay readable by ID. The model is written in an OpenFGA-style language (schema 1.1):
model
schema 1.1
type user
type group
relations
define member: [user]
type folder
relations
define owner: [user]
define viewer: [user, user:*, group#member] or owner
type doc
relations
define parent: [folder]
define owner: [user]
define blocked: [user]
define viewer: [user, user:*, group#member] or viewer from parent
define can_read: viewer but not blocked[user, user:*, group#member]lists the subject types a direct tuple may use: a user, everyone of a type (user:*), or the members of a group (group#member). Writes with any other subject type are rejected.or,andandbut notcombine rules. Use parentheses to mixorandand.viewer from parentfollows theparentrelation and evaluatesvieweron the related object (inheritance).- Conditions are not supported yet.
The same model is stored as JSON (namespaces, relations, rewrite rules this, computedUserset, tupleToUserset, union, intersection, exclusion, plus directTypes). Models saved before schema 1.1 keep working; opening one in the console shows it as DSL, and saving it turns on the subject type checks.
A built-in fallback model demonstrates groups, environments, folders, and documents until you save your own. The fallback is an implementation default, not a substitute for explicit production design.
Assertions
Assertions are saved checks that belong to one model version. Each states that a user should, or should not, hold a relation on an object, optionally with contextual tuples. Run them from Developer Mode while you iterate, or write them through PUT /fga/v1/assertions/{modelId}, which replaces the whole list.
Permissions Index (early access)
The index precomputes the objects a user can reach through chosen type#relation pairs, so a list page needs one lookup instead of one check per object. Writes mark an index stale and the next lookup rebuilds it. Indexes are bounded: users come from existing tuples, wildcard grants are not expanded for unknown users, and very large builds are refused. Access is enabled per organization on request.
Clients and scopes
Applications call the API with an environment API secret. Create one under Store Settings, Authorized Clients with these permissions:
| Scope | Allows |
|---|---|
fga:check |
Check, BatchCheck, ListObjects, Expand |
fga:read |
Read tuples, models, assertions and indexed objects |
fga:write |
Write and delete tuples, write assertions |
fga:model |
Write models |
A secret without any fga: scope predates scopes and keeps full access. As soon as it carries one fga: scope it is limited to exactly those. A missing scope returns 403.
Logs
Every API and console call is logged and searchable for seven days under Logs: timestamp, response status, operation, method, actor IP and client name. Search with operation:Write status:400 client:"Backend service". Free text matches client, IP and operation, not request bodies.
Design a model
- List protected object types:
document,folder,workspace. - Define relations in product language:
owner,editor,viewer. - Identify direct grants, group grants, and inheritance.
- Map every sensitive operation to a required object relation.
- Add tuples when membership or ownership changes.
- Remove tuples on revocation and resource deletion.
- Test allowed and denied cases, including inherited access.
Prefer a few stable relations. Avoid encoding transient request context into persistent tuples; use contextual relationships only when a supported integration explicitly defines them.
Runtime decisions
The engine supports these conceptual operations:
- Check: whether a subject holds a relation on an object.
- BatchCheck: multiple checks in one request.
- ListObjects: objects in a namespace available to a subject.
- Write/Delete: mutate tuples.
- Read: inspect tuples.
- Expand: flatten a relation to concrete subjects.
An unknown namespace or relation is rejected. Check returns a boolean and fails closed when the relation cannot be established. The per-environment model is cached briefly for decision performance; model changes may not be visible instantly across every isolate.
Enforce in your application
Authorization checks must run on the backend for every protected operation. Client-side visibility is only UX. Use stable user and object identifiers, include the correct Authdog environment, and default to deny on timeout or error.
For list pages, avoid fetching every object and checking one by one. Use a supported list operation or maintain an application index consistent with the relationship tuples. Always re-check before a mutation because list results can become stale.
Security and operations
- Keep tuple writes behind a trusted backend.
- Validate object ownership before writing a delegation tuple.
- Treat group membership updates as security-sensitive.
- Remove relationships when users, groups, or resources are deleted.
- Log model and tuple changes through Audit logs.
- Test cycles and deep inheritance before rollout.
- Separate development and production models by Authdog environment.
- Maintain a break-glass process outside the normal relationship graph.
FGA answers the relationship question; it does not authenticate the user. Validate the Authdog session first, then evaluate authorization.