Skip to main content

Comparison between Check, Read and Expand API Calls

note
Auth0 Fine Grained Authorization (FGA) is the early-stage product we are building at Auth0 to solve fine-grained authorization at scale. Sign up for the Developer Community Preview to try it out, and join our Discord community if you are interested in learning more about our plans.

Please note that at this point in time, it is not considered production-ready and does not come with any SLAs; availability and uptime are not guaranteed. Limitations of Auth0 FGA during the Developer Community Preview can be found here.

In this guide you will learn the uses and limitation for check, read and expand API.

Before you start

In order to understand this guide correctly you must be familiar with some Auth0 FGA Concepts and know how to develop the things that we will list below.

Assume that you have the following authorization model.
You have a type called document that can have a reader and writer. All writers are readers. bob has a writer relationship with document:planning.

type document
relations
define writer as self
define reader as self or writer
[
// Bob has writer relationship with planning document
{
"user": "bob",
"relation": "writer",
"object": "document:planning",
},
]

In addition, you will need to know the following:

Modeling Basics

You need to know how to create an authorization model and create a relationship tuple to grant a user access to an object. Learn more →

Auth0 FGA Concepts

  • A Type: a class of objects that have similar characteristics
  • A User: an entity in the system that can be related to an object
  • A Relation: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between objects of this type and other users in the system
  • An Object: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
  • A Relationship Tuple: a grouping consisting of a user, a relation and an object stored in Auth0 FGA

Check

What is it for?

The Check API is a call to the Auth0 Fine Grained Authorization (FGA) API endpoint that returns whether the user has a certain relationship with an object. Auth0 FGA will resolve all prerequisite relationships to establish whether a relationship exists.

When to use?

Check can be called if you need to establish whether a particular user has a specific relationship with a particular object.

For example, you can call check to determine whether bob has a reader relationship with document:planning.

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'bob',
relation: 'reader',
object: 'document:planning',
},});

// allowed = true

The Auth0 FGA API will return true because there is an implied relationship as

  • every writer is also a reader
  • bob is a writer for document:planning

Limits

For the developer preview, there is a limit of 25 check requests per second.

Caveats and when not to use it

Check is designed to answer the question "Does user:X have relationship Y with object:Z?". It is not designed to answer the following questions:

  • "Who has relationship Y with object:Z?"
  • "What are the objects that userX has relationship Y with?"
  • "Why does user:X have relationship Y with object:Z?"

Read

What is it for?

The Read API is a call to the Auth0 Fine Grained Authorization (FGA) API endpoint that returns the relationship tuples that are stored in the system that satisfy a query.

When to use?

Read can be called if you need to get all the stored relationship tuples that relate:

  1. a particular user to any objects of a specific type with a particular relation
  2. a particular user to any objects of a specific type with any relation
  3. a particular object to any user with a particular relation

1. A particular user to any objects of a specific type with a particular relation

For example, to query all the stored relationship tuples bob has a writer relationship with, one can ask

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'bob,
relation:'writer',
object:'document:',

},
});

// tuples = [{"key": {"user":"bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]

2. A particular user to any objects of a specific type with any relation

For example, to query all the stored relationship tuples in which bob is related to objects of type document as any relation, one can issue the following call:

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'bob,
object:'document:',

},
});

// tuples = [{"key": {"user":"bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]

3. A particular object to any user with a particular relation

For example, to query all the stored relationship tuples in which any user is related to document:planning as a writer, one can issue the following call:

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
relation:'writer',
object:'document:planning',

},
});

// tuples = [{"key": {"user":"bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]

Limits

For the developer preview, there is a limit of 5 read requests per second.

Caveats and when not to use it

The Read API will only return all the stored relationships that match the query specification. It does not expand or traverse the graph by taking the authorization model into account.

For example, if you specify that writers are viewers in the authorization model, the Read API will ignore that and it will return tuples where a user is a viewer if and only if the (user_id, "viewer", object_type:object_id) relationship tuple exists in the system.

In the following case, although all writers have reader relationships for document objects and bob is a writer for document:planning, if you query for all objects that bob has reader relationships, it will not return document:planning.

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'bob,
relation:'reader',
object:'document:',

},
});

// tuples = []
info

Although bob is a writer to document:planning and every writer is also a reader, the Read API will return an empty list because there are no stored relationship tuples that relate bob to document:planning as reader.

Expand

What is it for?

Expand uses the authorization model to expand the specific relationship on a particular object. The response is in user tree format. To build a full graph, you would iteratively need to call expand on the leaves returned in the previous expand call.

When to use?

Expand is used for debugging and to understand why a user has a particular relationship with a specific object.

For example, to understand why bob can have a reader relationship with document:planning, one could first call

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Run a check
const { tree } = await fgaClient.expand({
tuple_key: {
relation: 'reader',
object: 'document:planning',
},
});

// tree = ...

The result of this call will be like

{
"tree":{
"root":{
"type":"document:planning#reader",
"leaf":{
"computed":{
"userset":"document:planning#writer"
}
}
}
}
}
}

The returned tree will contains writer, for which we will call

Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');

// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});

// Run a check
const { tree } = await fgaClient.expand({
tuple_key: {
relation: 'writer',
object: 'document:planning',
},
});

// tree = ...

The result of this call will be like

{
"tree":{
"root":{
"type":"document:planning#writer",
"leaf":{
"users":{
"users":[
"bob"
]
}
}
}
}
}
}

From there, we will learn that

  • those related to document:planning as reader are all those who are related to that document as writer
  • bob is related to document:planning as writer

Limits

For the developer preview, there is a limit of 30 expand requests per minute.

Caveats and when not to use it

The Expand call is expensive and has high latency. As such, it is designed to be used for debugging and understanding why a user has a particular relationship with a specific object. It is not designed for checking whether a user has a particular relationship with a specific object. In that case the Check API call should be used instead.

Summary

CheckReadExpand
PurposeCheck if user has particular relationship with certain objectReturn all stored relationship tuples that match queryExpand the specific relationship on a particular object
When to useValidate if user X can perform Y on object ZList stored relationships in systemUnderstand why user X can perform Y on object Z
Limits25 check requests per second5 read requests per second30 expand requests per minute
Auth0 FGA API - Check

Details on the check API in the Auth0 FGA reference guide.

Auth0 FGA API - Read

Details on the read API in the Auth0 FGA reference guide.

Auth0 FGA - Expand

Details on the expand API in the Auth0 FGA reference guide.

Have Feedback?

Join us on the Discord community if you have any questions or suggestions.