WorkOS Docs Homepage
FGA
API referenceDashboardSign In
Getting StartedOverviewOverviewQuick StartQuick StartPlaygroundPlaygroundKey ConceptsSchemaSchemaWarrantsWarrantsResourcesResourcesPoliciesPoliciesQuery LanguageQuery LanguageWarrant TokensWarrant TokensOperations & UsageOperations & UsageManagementSchema ManagementSchema ManagementLocal DevelopmentLocal DevelopmentIdentity Provider SessionsIdentity Provider SessionsModelingOrg Roles & PermissionsOrg Roles & PermissionsCustom RolesCustom RolesGoogle DocsGoogle DocsEntitlementsEntitlementsUser GroupsUser GroupsManaged Service ProviderManaged Service ProviderAttribute-Based Access ControlAttribute-Based Access ControlConditional RolesConditional RolesPolicy ContextPolicy ContextPublic AccessPublic AccessSuperusersSuperusersBlocklistsBlocklists
API Reference
API Reference
Events
Events
Integrations
Integrations
Migrate to WorkOS
Migrate to WorkOS
SDKs
SDKs

Schema

Define authorization logic independently from application code using a domain-specific language (DSL).

On this page

  • Overview
  • JSON vs Schema Language
    • JSON Representation
    • Schema Language Representation
  • Schema Syntax
    • Version
    • Comments
    • Resource Types
    • Relations
    • Inheritance Rules
    • Logical Operators
    • Policies
    • Group Warrants
  • Converting Schema Language to JSON
  • Schema Changelog
    • v0.3
    • v0.2
    • v0.1

Overview

A schema is the core structure of an authorization model in FGA. It defines the types of resources, the relations between them, and the policies that govern access.

A schema can be represented in two formats:

  • JSON – Accepted by Schema API endpoints when using Content-Type: application/json.
  • FGA Schema Language – A more developer-friendly domain-specific language (DSL) that is applied via the apply command with the CLI or on the FGA Dashboard.

Schemas allow you to manage authorization logic independently from application logic. They can be versioned, stored in Git, and applied via the CLI:

workos fga schema apply ./schema.txt

Once applied, changes take effect immediately, meaning any updates to authorization logic will instantly reflect in subsequent permission checks and queries.

FGA Schema Language transpiles into JSON format so that you can write your authorization model in a more readable and maintainable way, but still use JSON for API calls if you prefer.

JSON vs Schema Language

The JSON representation of a schema is the raw format that FGA uses to define resource types, relations, and inheritance rules. However, it can be verbose and difficult to read – especially for complex authorization models.

Consider the following examples:

JSON Representation

{
"resource_types": [
{
"type": "user",
"relations": {
"manager": {
"allowed_types": ["user"]
}
}
},
{
"type": "store",
"relations": {
"owner": {
"allowed_types": ["user"]
},
"editor": {
"allowed_types": ["user"],
"inherit_if": "owner"
},
"viewer": {
"allowed_types": ["user"],
"inherit_if": "editor"
}
}
},
{
"type": "item",
"relations": {
"owner": {
"allowed_types": ["user"]
"inherit_if": "owner",
"of_type": "store",
"with_relation": "parent"
},
"editor": {
"allowed_types": ["user"],
"inherit_if": "any_of",
"rules": [
{
"inherit_if": "owner"
},
{
"inherit_if": "editor",
"of_type": "store",
"with_relation": "parent"
},
{
"inherit_if": "manager",
"of_type": "user",
"with_relation": "owner"
}
]
},
"viewer": {
"allowed_types": ["user"],
"inherit_if": "editor"
},
"parent": {
"allowed_types": ["store"]
}
}
}
]
}

Schema Language Representation

version 0.3
type user
relation manager [user]
type store
relation owner [user]
relation viewer [user]
inherit viewer if
relation editor // editors are also viewers
relation editor [user]
inherit editor if
relation owner
type item
// An item can have a parent store
relation parent [store]
relation owner [user]
inherit owner if
relation owner on parent [store]
relation editor [user]
inherit editor if
any_of
relation owner
relation editor on parent [store]
relation manager on owner [user]
relation viewer [user]
inherit viewer if
relation editor

The FGA schema language representation is more concise, easier to read, and supports comments. These features make it simpler to define and manage complex authorization models in a more developer-friendly format.

Schema Syntax

Version

Each schema must start with a version declaration. This version declaration dictates the version of the schema language the transpiler will use to convert the schema into its JSON representation. As we add support for new features and functionality to the schema language, we will release new versions of it. Versioning the language in this way allows us to ensure backwards compatibility as we roll out these enhancements. See a full changelog of schema versions here.

version 0.3

Comments

Comments are prefixed with //. Comments are ignored by the transpiler.

// a comment

Resource Types

Resource types are the basic building blocks of an authorization model in FGA. Each resource type defines a set of relationships that can exist on a specific type of resource (e.g. store, item, etc). These relationships can be assigned to other resources (e.g. user) known as subjects.

Resource types are an incredibly flexible way to define authorization models, allowing you to express complex hierarchical and inherited relationships. They can be created directly in the FGA dashboard, via the Resource Types API or by applying the schema with the CLI.

Let’s explore the various attributes of resource types by creating a schema-based authorization model for a simple e-commerce application that has three resource types: users, stores, and items.

First, define a resource type using the type keyword. Each resource type must have a unique string as its type. Let’s start defining the resource types for our e-commerce application:

version 0.3
type user
type store
type item

Relations

With the basic definitions above, we’ve started building an authorization model for our application that will allow us to create fine grained access control rules for stores, items, and users, helping us answer questions like:

Does [user:1] have the ability to [edit] [item:x]?
is [user:1] the [owner] of [store:3]?

In order to create access rules using our resource types, we first need to define the relationships available on a resource of that type. For example, if we want to specify that [user:A] is an [owner] of [store:S], we must add an owner relation to the store resource type.

By default, a subject can only have a relation on a resource explicitly. This means the relation must be explicitly granted via a warrant.

Let’s add some relations to our resource types.

In our application, a store can have owners, editors, and viewers. owners and editors have more privileged access (like being able to modify details about a store) than viewers (who have read-only access).

An item can have the same three relations as a store plus a fourth relation called parent. This is because a store can be the parent of an item, meaning the item belongs to that store. We’ll use this relation later to implement inherited relations on items.

Lastly, our user resource type is relatively simple and has one relation: manager. This is because a user can be the manager of another user. We’ll use this relation later to enable inherited relations based on user hierarchies.

Let’s add these relations to our resource types:

version 0.3
type user
relation manager [user]
type store
relation owner [user]
relation editor [user]
relation viewer [user]
type item
relation owner [user]
relation editor [user]
relation viewer [user]
relation parent [store]

With these resource types, we can now create authorization rules that specify exactly which users are owners, editors, and viewers of each store or item. We can also assign stores as parents of items, and users as managers of other users.

Use brackets [] in the schema language after defining a relation to enforce which type(s) of subjects can be assigned the relation.

Use empty type restrictions to define computed relationships with no direct subjects. This is useful for defining a relation that cannot be assigned directly to a subject but is used to make an authorization check from your application.

Version 0.1 of the schema language does not support type safety on relations.

Inheritance Rules

While only using explicitly assigned relations to build your authorization model can be powerful, creating warrants for each and every relationship in an application can become tedious or infeasible for larger, more complex use cases. That’s why relations can define rules under which they can be inherited (e.g. a user is an editor of a store if they're an owner of that store).

There are two ways in which relations can be inherited:

  • Relation Inheritance
  • Resource Inheritance

Relation Inheritance

In practice, it’s common for relations to have overlap (e.g. an owner has the same privileges as an editor + additional privileges). For example, in many applications a user with write privileges inherits read privileges too.

In our example application, an owner will inherit both the editor and the viewer relations, and an editor will inherit the viewer relation. Instead of having to explicitly assign each of the owner, editor, and viewer relations to a user who is an owner, resource types allow you to specify an inheritance hierarchy (e.g. the editor relation is inherited if the user is an owner) using the inherit_if property.

Let’s add inherit <relation> if rules to our store and item resource types specifying that:

  • owners are also editors
  • editors are also viewers
version 0.3
type user
relation manager [user]
type store
relation owner [user]
relation editor [user]
relation viewer [user]
inherit viewer if
relation editor
inherit editor if
relation owner
type item
relation owner [user]
relation editor [user]
relation viewer [user]
relation parent [store]
inherit viewer if
relation editor
inherit editor if
relation owner

With our inherit <relation> if rules in place, we can simply grant a user the editor relation and they will implicitly inherit the viewer relation. inherit rules also work recursively on other inherited relations, so assigning a user the owner relation will implicitly grant that user both the editor and viewer relations. This is because owner will inherit editor and editor will in turn inherit viewer.

This will simplify our access checks and cut down on the number of warrants we need to create for each user.

Resource Inheritance

In many applications, resources themselves have a hierarchy (e.g. a document belongs to a folder, a user belongs to a team, a team belongs to an organization, etc.) and the access rules for these resources follow that hierarchy (e.g. the owner of a folder is the owner of any document in that folder).

Using the following two rules:

inherit <relation> if
relation <resource_type.relation> on <relation> [<resource_type>]

We can specify that a relation can be inherited when a user has a particular relation (<resource_type.relation>) on another resource (<resource_type>) that has a particular relation (<relation>) on the resource we are checking access to.

For example, a user is an editor of a document if they are an editor of a folder that is the document’s parent. In our example app, let’s define the following three resource inheritance rules:

  1. A user is an owner of an item if that user is an owner of a store that is the item’s parent.
  2. A user is an editor of an item if that user is an editor of a store that is the item’s parent.
  3. A user is an editor of an item if that user is the manager of the user that is the item’s owner.

NOTE: Some of the relations below will be composing multiple inheritance rules together using logical operators. We’ll cover this in detail later.

version 0.3
type user
relation manager [user]
type store
relation owner [user]
relation editor [user]
relation viewer [user]
inherit viewer if
relation editor
inherit editor if
relation owner
type item
relation owner [user]
relation editor [user]
relation viewer [user]
relation parent [store]
inherit owner if
relation owner on parent [store]
inherit editor if
any_of
relation owner
relation editor on parent [store]
relation manager on owner [user]
inherit viewer if
relation editor

These rules make it easy to define inheritance rules for complex relationships between resources so we don’t have to create a large number of explicit warrants. Without them, we’d need to create a warrant for every item ↔ store ↔ user relationship in our application. This could easily be thousands, if not hundreds of thousands of rules.

Logical Operators

With both the two types of relation inheritance rules in our toolkit, we can create authorization models for a majority of use cases, but there are still some scenarios that require a combination of these inheritance rules (e.g. a user is an editor of an item if they are an owner of that item OR they are the manager of another user who is an editor of that item).

To design authorization models that cover such scenarios, relations can compose multiple inheritance rules using logical operators to form more complex conditions.

The three supported logical operations are any_of, all_of, and none_of.

any_of

The any_of operation allows you to specify that a relation be inherited if at least one of the rules in the set is satisfied. In other words, it works like the logical OR operation.

The following resource type specifies an editor-or-viewer relation that is inherited if the user is an editor OR if the user is a viewer:

version 0.3
type item
relation editor [user]
relation viewer [user]
relation editor-or-viewer []
inherit editor-or-viewer if
any_of
relation editor
relation viewer

all_of

The all_of rule type allows you to specify that a relation be inherited if all of the rules in the set are satisfied. In other words, it works like the logical AND operation.

The following resource type specifies an editor-and-viewer relation that is implicitly granted if the user is an editor AND the user is a viewer:

version 0.3
type user
type item
relation editor [user]
relation viewer [user]
relation editor-and-viewer []
inherit editor-and-viewer if
all_of
relation editor
relation viewer

none_of

The none_of rule type allows you to specify that a relation be inherited if none of the rules in the set are satisfied. In other words, it works like the logical NOR operation.

The following resource type specifies a not-editor-and-not-viewer relation that is implicitly granted if the user is not an editor AND the user is not a viewer:

version 0.3
type user
type item
relation editor [user]
relation viewer [user]
relation not-editor-and-not-viewer []
inherit not-editor-and-not-viewer if
none_of
relation editor
relation viewer

Policies

Policies are a way to define custom logic that can be used in your schema. They allow you to create complex rules that go beyond simple relation inheritance. Policies can be defined using the policy keyword and can include parameters, expressions, and logical conditions.

version 0.3
type user
type group
relation member [user]
type asset
relation service_manager [group]
relation access_diagnostics []
inherit access_diagnostics if
all_of
relation member on service_manager [group]
policy is_in_geo_fence
policy is_in_geo_fence(user_location map, geofence map) {
user_location.lat >= geofence.min_lat &&
user_location.lat <= geofence.max_lat &&
user_location.lon >= geofence.min_lon &&
user_location.lon <= geofence.max
}

Read more about policies in the Policies documentation.

Group Warrants

Define type restrictions on group warrants by joining the type and expected relation with a #. For example, relation editor [group#member] means that the editor relation can be assigned to warrants where group is the subject type and member is the subject relation.

Group warrants are a special type of warrant that allow you to define exceptions to schema relationships at runtime. See the Group Warrant documentation for more details.

version 0.3
type user
type group
relation member [user]
type document
relation editor [group#member]

If your relation type defines a resource type and no group warrant types, it will default to allow all group warrants.

For example:

// Allows subject_type == "group" and subject_relation == null | <any_value>
relation editor [group]
// Allows subject_type == "group" and subject_relation == "member"
relation editor [group#member]
// Allows subject_type == "group" and subject_relation == "member" | "owner"
relation editor [group#member, group#ownwer]
// Allows subject_type == "group" and subject_relation == null | "member"
relation editor [group, group#member]

Converting Schema Language to JSON

You can convert the FGA schema language to JSON using the workos fga schema convert command. This command transpiles the schema language into its JSON representation, which can then be used with the FGA API.

workos fga schema convert schema.txt --to json --output raw > schema.json

Schema Changelog

v0.3

  • Add support for policy in the schema
version 0.3
type user
type group
relation member [user]
type asset
relation access_diagnostics []
relation service_manager [group]
inherit access_diagnostics if
all_of
relation member on service_manager [group]
policy is_in_geo_fence
policy is_in_geo_fence(user_location map, geofence map) {
user_location.lat >= geofence.min_lat &&
user_location.lat <= geofence.max_lat &&
user_location.lon >= geofence.min_lon &&
user_location.lon <= geofence.max_lon
}

v0.2

  • Add support for resource-type relation type safety
  • Add support for group warrant types
version 0.2
type report
relation parent [organization, organization#member]
relation owner [user]
relation editor [user]

v0.1

  • Initial implementation of the schema language
  • Supported features:
    • Transpiler version
    • Resource types
    • Relations
    • Inheritance rules
    • Resource inheritance
    • Logical operators
WarrantsWarrants specify relationships between resources in your application
Up next
© WorkOS, Inc.
FeaturesUser ManagementSingle Sign-OnDirectory SyncAdmin PortalFine-Grained Authorization
DevelopersDocumentationChangelogAPI Status
ResourcesBlogPodcastPricingSecuritySupport
CompanyAboutCustomersCareersLegalPrivacy
© WorkOS, Inc.