An open source authorization framework based on Google Zanzibar; used by e.g. Auth0

Terms

  • User: Also called “subject” in our terms
  • Relation: Also called “action” in our terms
  • Object: Also called “resource” in general

Concepts

  • Transports: HTTP (REST, port 8080) and gRPC (port 8081)
  • Stores can be used to differentiate between environments (e.g. prod/test/dev)
  • Types define classes of similar objects, e.g. organization, document
    • An object is an instance of a type. They are expressed like this:
      • organization:12345
      • document:roadmap-2026
  • A type definition defines a type as well as all possible relations that another object (e.g., a user) can have to it (e.g., “the owner of a document is a user”)
    • A relation defines a possible relationship between that object and a user (or another object), e.g.:
      • “A user can be a viewer of a document”
      • “A user can be a member of a team”
      • “A team can administer a folder”
  • An authorization model makes up the permission model by combining one or multiple type definitions
    • A condition is like an if statement (it evaluates to a boolean). We could use it, for example, to express conditions such as:
      • “A user in an organization with a Silver plan can only create 100 users”. We would then have the limit - 100 - defined in a condition in the authorization model, and provide the number of current users to each check.
    • A relationship tuple is a triplet/quadruplet made up of a user, relation, object and an optional condition
    • The combination of the authorization model (schema), a number of relationship tuples and optional conditions
    • A store can contain multiple authorization models, but they function as versions - each time the authorization model gets updated (using the WriteAuthorizationModel function), a new ID is created. When you perform a check you can specify the desired ID, but otherwise, OpenFGA defaults to the latest version.
  • A relationship is an actually existing relation between a user (user type instance) and object (type instance). The relationship may be direct or implied:
    • Direct: “User ABC is an editor of document XYZ”
    • Implied: “User ABC is a member of organisation DEF, which is an owner of document XYZ”
  • A check (or check request) is a request to the check method of OpenFGA (often using an SDK, CLI or curl etc.) that asks whether relation R exists between user X and object Y. It always results in a boolean (i.e. whether the relation exists / access should be granted).
  • The ListObjects method/endpoint responds with a list of objects for a given type that the user has the specified relationship with, e.g. “list documents that Anne can view”.
  • The ListUsers method responds with a list of users for a given type that have the specificed relationship with an object, e.g. “list all users that can view the document ‘roadmap-2026’“.
  • The special syntax user:* denotes public access (relations) to an object, e.g. “document ‘roadmap-2026’ can be viewed by anyone”

Best practices

  • We recommended storing all data that may be related or affect your authorization result in a single store. Separate stores can be created for separate authorization needs or isolated environments, e.g. development/prod.
  • When evaluating OpenFGA, many companies start by replicating their existing permissions structure before moving to more granular controls.
    • A better implementation is to define the application’s resource types in the model (e.g. documents, projects, insurance policies, bank accounts, etc). In this case, the Check() call will be at the resource level. The main advantage of this approach is that your APIs will be checking permissions at the proper level. If you later want to evolve your authorization model to be more fine grained, you won’t need to change your app.
  • When migrating from an existing authorization system to OpenFGA, it’s recommended to first run both systems in parallel, with OpenFGA in “shadow mode”. This means that while the existing system continues to make the actual authorization decisions, you also make calls to OpenFGA asynchronously and compare the results.
    • This approach has several benefits:
      • You can validate that your authorization model and relationship tuples are correctly configured before switching to OpenFGA.
      • You can measure the performance impact of adding OpenFGA calls to your application.
      • You can identify edge cases where the OpenFGA results differ from your existing system.
      • You can gradually build confidence in the OpenFGA implementation.
    • To implement shadow mode:
      1. Configure your application to make authorization checks against both systems
      2. Log any discrepancies between the two systems
      3. Analyze the logs to identify and fix any issues
      4. Once confident in the results, switch to using OpenFGA as the source of truth. The same approach of shallow checks when migrating between models.
    • This pattern is particularly useful for critical systems where authorization errors could have significant impact.
  • The most common mistake when starting with OpenFGA is creating an overly generic model that can represent “anything.” While this seems flexible, it trades clarity for abstraction and often hurts performance.
    • If end-users can define it, store it in tuples. If it’s built into your application, define it in the model.
    • For example: built-in roles like “admin” or “billing_manager” should be relations in your model. User-defined custom roles should be stored as tuples with a role type.
    • Define types and relations that mirror your application’s domain. If your app has organizations, projects, and documents, model exactly that with explicit relationships.
  • For internal support and admin access, use a dedicated system type rather than making organizations recursive. This avoids recursive relations (faster to evaluate).
    • Prefer explicit types when possible; use recursion only when the depth is genuinely unbounded.
  • Getting started with modeling
    • If you have an existing system: forget about how your system works today and start thinking about how you want it to work in the future.
    • [We recommend] thinking about authorization starting from the resources, or objects as OpenFGA calls them.
    • When you are modeling, you need to answer the question: “Why could user U perform an action A on an object O?“. This is an iterative process.

FAQ

List stores

export FGA_API_URL=...
export FGA_API_TOKEN=... # e.g. a preshared key
fga store list