Relationship Queries: Check, Read, Expand, and ListObjects
In this guide you will learn the uses of and limitations for the Check, Read, Expand, and ListObjects API endpoints.
Before You Start
In order to understand this guide correctly you must be familiar with some OpenFGA 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
.
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
.- DSL
- JSON
model
schema 1.1
type user
type document
relations
define writer: [user]
define reader: [user] or writer
{
"schema_version": "1.1",
"type_definitions": [
{
"type": "user"
},
{
"type": "document",
"relations": {
"writer": {
"this": {}
},
"reader": {
"union": {
"child": [
{
"this": {}
},
{
"computedUserset": {
"object": "",
"relation": "writer"
}
}
]
}
}
},
"metadata": {
"relations": {
"writer": {
"directly_related_user_types": [
{
"type": "user"
}
]
},
"reader": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
}
]
}
[
// Bob has writer relationship with planning document
{
"user": "user:bob",
"relation": "writer",
"object": "document:planning",
},
]
In addition, you will need to know the following:
Direct Access
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 →
OpenFGA 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 an object of the same type as the type definition and a user 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 OpenFGA
Check
What Is It For?
The Check API is an API endpoint that returns whether the user has a certain relationship with an object. OpenFGA 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
.
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
// Run a check
const { allowed } = await fgaClient.check({
authorization_model_id: '1uHxCSuTP0VKPYSnkq1pbb1jeZw',
tuple_key: {
user: 'user:bob',
relation: 'reader',
object: 'document:planning',
},
});
// allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.CheckRequest{
AuthorizationModelId: fgaSdk.PtrString("1uHxCSuTP0VKPYSnkq1pbb1jeZw"),
TupleKey: fgaSdk.TupleKey{
User: "user:bob",
Relation: "reader",
Object: "document:planning",
},
}
data, response, err := fgaClient.OpenFgaApi.Check(context.Background()).Body(body).Execute()
// data = { allowed: true }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
// Run a check
var response = await fgaClient.Check{TupleKey = new CheckRequest(new TupleKey() {
User = "user:bob",
Relation = "reader",
Object = "document:planning"
}, AuthorizationModelId = "1uHxCSuTP0VKPYSnkq1pbb1jeZw"};
// response.Allowed = true
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.check_request import CheckRequest
# from openfga_sdk.models.tuple_key import TupleKey
# from openfga_sdk.models.contextual_tuple_keys import ContextualTupleKeys
# Run a check
async def check():
body = CheckRequest(
tuple_key=TupleKey(
user="user:bob",
relation="reader",
object="document:planning",
),
authorization_model_id="1uHxCSuTP0VKPYSnkq1pbb1jeZw",
)
response = await fga_client_instance.check(body)
# response.allowed = true
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "1uHxCSuTP0VKPYSnkq1pbb1jeZw", "tuple_key":{"user":"user:bob","relation":"reader","object":"document:planning"}}'
# Response: {"allowed":true}
check(
user = "user:bob", // check if the user `user:bob`
relation = "reader", // has an `reader` relation
object = "document:planning", // with the object `document:planning`
authorization_id = "1uHxCSuTP0VKPYSnkq1pbb1jeZw"
);
Reply: true
The OpenFGA API will return true
because there is an implied relationship as
- every
writer
is also areader
bob
is awriter
fordocument:planning
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 an 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:
- a particular user to any objects of a specific type with a particular relation
- a particular user to any objects of a specific type with any relation
- 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
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'user:bob',
relation:'writer',
object:'document:',
},
});
// tuples = [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ReadRequest{
TupleKey: fgaSdk.TupleKey{
User: fgaSdk.PtrString("user:bob"),
Relation: fgaSdk.PtrString("writer"),
Object: fgaSdk.PtrString("document:"),
},
}
data, response, err := fgaClient.OpenFgaApi.Read(context.Background()).Body(body).Execute()
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = fgaClient.Read(new ReadRequest(new TupleKey() {
User = "user:bob",
Relation = "writer",
Object = "document:",
}));
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.read_request import ReadRequest
# from openfga_sdk.models.read_response import ReadResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def read():
body = ReadRequest(
tuple_key=TupleKey(
user="user:bob",
relation="writer",
object="document:",
),
)
response = await fga_client_instance.read(body)
# response = ReadResponse({"tuples":[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"user":"user:bob","relation":"writer","object":"document:"}}'
# Response: "tuples": {[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]}
read(
// read all stored tuples
"user:bob", // where user `user:bob` has $(opts.relation ? '': 'any ' )relation
"writer", // `writer`
"document:", // with the type `document:`
);
Reply: tuples:[{"key": {"user":"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:
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'user:bob',
object:'document:',
},
});
// tuples = [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ReadRequest{
TupleKey: fgaSdk.TupleKey{
User: fgaSdk.PtrString("user:bob"),
Object: fgaSdk.PtrString("document:"),
},
}
data, response, err := fgaClient.OpenFgaApi.Read(context.Background()).Body(body).Execute()
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = fgaClient.Read(new ReadRequest(new TupleKey() {
User = "user:bob",
Object = "document:",
}));
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.read_request import ReadRequest
# from openfga_sdk.models.read_response import ReadResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def read():
body = ReadRequest(
tuple_key=TupleKey(
user="user:bob",
object="document:",
),
)
response = await fga_client_instance.read(body)
# response = ReadResponse({"tuples":[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"user":"user:bob","object":"document:"}}'
# Response: "tuples": {[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]}
read(
// read all stored tuples
"user:bob", // where user `user:bob` has $(opts.relation ? '': 'any ' )relation
"document:", // with the type `document:`
);
Reply: tuples:[{"key": {"user":"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:
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
relation:'writer',
object:'document:planning',
},
});
// tuples = [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ReadRequest{
TupleKey: fgaSdk.TupleKey{
Relation: fgaSdk.PtrString("writer"),
Object: fgaSdk.PtrString("document:planning"),
},
}
data, response, err := fgaClient.OpenFgaApi.Read(context.Background()).Body(body).Execute()
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = fgaClient.Read(new ReadRequest(new TupleKey() {
Relation = "writer",
Object = "document:planning",
}));
// data = { "tuples": [{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}] }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.read_request import ReadRequest
# from openfga_sdk.models.read_response import ReadResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def read():
body = ReadRequest(
tuple_key=TupleKey(
relation="writer",
object="document:planning",
),
)
response = await fga_client_instance.read(body)
# response = ReadResponse({"tuples":[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"relation":"writer","object":"document:planning"}}'
# Response: "tuples": {[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]}
read(
// read all stored tuples
// for users who have relation
"writer", // `writer`
"document:planning", // with the object `document:planning`
);
Reply: tuples:[{"key": {"user":"user:bob","relation":"writer","object":"document:planning"}, "timestamp": "2021-10-06T15:32:11.128Z"}]
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
.
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
// Execute a read
const { tuples } = await fgaClient.read({
tuple_key: {
user:'user:bob',
relation:'reader',
object:'document:',
},
});
// tuples = []
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ReadRequest{
TupleKey: fgaSdk.TupleKey{
User: fgaSdk.PtrString("user:bob"),
Relation: fgaSdk.PtrString("reader"),
Object: fgaSdk.PtrString("document:"),
},
}
data, response, err := fgaClient.OpenFgaApi.Read(context.Background()).Body(body).Execute()
// data = { "tuples": [] }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = fgaClient.Read(new ReadRequest(new TupleKey() {
User = "user:bob",
Relation = "reader",
Object = "document:",
}));
// data = { "tuples": [] }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.read_request import ReadRequest
# from openfga_sdk.models.read_response import ReadResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def read():
body = ReadRequest(
tuple_key=TupleKey(
user="user:bob",
relation="reader",
object="document:",
),
)
response = await fga_client_instance.read(body)
# response = ReadResponse({"tuples":[]})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"user":"user:bob","relation":"reader","object":"document:"}}'
# Response: "tuples": {[]}
read(
// read all stored tuples
"user:bob", // where user `user:bob` has $(opts.relation ? '': 'any ' )relation
"reader", // `reader`
"document:", // with the type `document:`
);
Reply: tuples:[]
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?
The Expand API returns all users (including users and usersets) that have a specific relationship with an object. The response is represented as a tree of users or usersets. To build the full graph of access, you would need to recursively call expand on the leaves returned from 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
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
const { tree } = await fgaClient.expand({
tuple_key: {
relation: 'reader', // expand all who has 'reader' relation
object: 'document:planning', // with the object 'document:planning'
},
authorization_model_id: '1uHxCSuTP0VKPYSnkq1pbb1jeZw'
});
// tree = ...
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ExpandRequest{
TupleKey: fgaSdk.TupleKey{
Relation: fgaSdk.PtrString("reader"), // expand all who has "reader" relation
Object: fgaSdk.PtrString("document:planning"), // with the object "document:planning"
},
AuthorizationModelId: fgaSdk.PtrString("1uHxCSuTP0VKPYSnkq1pbb1jeZw"),
}
data, response, err := fgaClient.OpenFgaApi.Expand(context.Background()).Body(body).Execute()
// data = { tree: ...}
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = await fgaClient.Expand{TupleKey = new ExpandRequest(new TupleKey() {
Relation = "reader", // expand all who has "reader" relation
Object = "document:planning" // with the object "document:planning"
}, AuthorizationModelId = "1uHxCSuTP0VKPYSnkq1pbb1jeZw"});
// response = { tree: ... }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.expand_request import ExpandRequest
# from openfga_sdk.models.expand_response import ExpandResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def expand():
body = ExpandRequest(
tuple_key=TupleKey(
relation: "reader",
object: "document:planning",
),
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw",
)
response = await fga_client_instance.expand(body)
# response = ExpandResponse({"tree":...})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/expand \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"relation":"reader","object":"document:planning"}, "authorization_model_id": "1uHxCSuTP0VKPYSnkq1pbb1jeZw"}'
# Response: {"tree": ...}
expand(
"reader", // expand all who has `reader` relation
"document:planning", // with the object `document:planning`
authorization_model_id="1uHxCSuTP0VKPYSnkq1pbb1jeZw"
);
Reply: {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 contain writer
, for which we will call
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
const { tree } = await fgaClient.expand({
tuple_key: {
relation: 'writer', // expand all who has 'writer' relation
object: 'document:planning', // with the object 'document:planning'
},
authorization_model_id: '1uHxCSuTP0VKPYSnkq1pbb1jeZw'
});
// tree = ...
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ExpandRequest{
TupleKey: fgaSdk.TupleKey{
Relation: fgaSdk.PtrString("writer"), // expand all who has "writer" relation
Object: fgaSdk.PtrString("document:planning"), // with the object "document:planning"
},
AuthorizationModelId: fgaSdk.PtrString("1uHxCSuTP0VKPYSnkq1pbb1jeZw"),
}
data, response, err := fgaClient.OpenFgaApi.Expand(context.Background()).Body(body).Execute()
// data = { tree: ...}
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var response = await fgaClient.Expand{TupleKey = new ExpandRequest(new TupleKey() {
Relation = "writer", // expand all who has "writer" relation
Object = "document:planning" // with the object "document:planning"
}, AuthorizationModelId = "1uHxCSuTP0VKPYSnkq1pbb1jeZw"});
// response = { tree: ... }
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.expand_request import ExpandRequest
# from openfga_sdk.models.expand_response import ExpandResponse
# from openfga_sdk.models.tuple_key import TupleKey
async def expand():
body = ExpandRequest(
tuple_key=TupleKey(
relation: "writer",
object: "document:planning",
),
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw",
)
response = await fga_client_instance.expand(body)
# response = ExpandResponse({"tree":...})
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/expand \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"tuple_key":{"relation":"writer","object":"document:planning"}, "authorization_model_id": "1uHxCSuTP0VKPYSnkq1pbb1jeZw"}'
# Response: {"tree": ...}
expand(
"writer", // expand all who has `writer` relation
"document:planning", // with the object `document:planning`
authorization_model_id="1uHxCSuTP0VKPYSnkq1pbb1jeZw"
);
Reply: {tree:...}
The result of this call will be like
{
"tree":{
"root":{
"type":"document:planning#writer",
"leaf":{
"users":{
"users":[
"user:bob"
]
}
}
}
}
}
}
From there, we will learn that
- those related to
document:planning
asreader
are all those who are related to that document aswriter
bob
is related todocument:planning
aswriter
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.
ListObjects
What Is It For?
The ListObjects API is an API endpoint that returns the list of all the object IDs of a particular type that a specific user has a specific relationship with.
It provides a solution to the Search with Permissions (Option 3) use case for access-aware filtering on small object collections.
The current implementation of List Objects API is not optimized and can return incomplete results. See below for more details.
When To Use?
Use the ListObjects API to get what objects a user can see based on the relationships they have. See Search with Permissions for more guidance.
- Node.js
- Go
- .NET
- Python
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaApi } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaApi({
apiScheme: process.env.FGA_API_SCHEME, // Either "http" or "https", defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
storeId: process.env.FGA_STORE_ID, // Either "http" or "https", defaults to "https"
});
const response = await fgaClient.listObjects({
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw",
user: "user:bob",
relation: "reader",
type: "document",
contextual_tuples: {
tuple_keys: [{
user: "user:bob",
relation: "reader",
object: "document:otherdoc"
}]
},
});
// response.objects = ["document:otherdoc", "document:planning"]
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
fgaSdk "github.com/openfga/go-sdk"
"os"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
ApiScheme: os.Getenv("FGA_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost: os.Getenv("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.ListObjectsRequest{
AuthorizationModelId: PtrString("1uHxCSuTP0VKPYSnkq1pbb1jeZw"),
User: "user:bob",
Relation: "reader",
Type: "document",
ContextualTuples: &ContextualTupleKeys{
TupleKeys: []TupleKey{{
User: PtrString("user:bob"),
Relation: PtrString("reader"),
Object: PtrString("document:otherdoc"),
},
},
}
data, response, err := apiClient.OpenFgaApi.ListObjects(context.Background()).Body(body).Execute()
// data = { "objects": ["document:otherdoc", "document:planning"] }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Api;
using OpenFga.Sdk.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new Configuration() {
ApiScheme = Environment.GetEnvironmentVariable("FGA_API_SCHEME"), // Either "http" or "https", defaults to "https"
ApiHost = Environment.GetEnvironmentVariable("FGA_API_HOST"), // required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
};
var fgaClient = new OpenFgaApi(configuration);
}
}
var body = new ListObjectsRequest{
AuthorizationModelId = "1uHxCSuTP0VKPYSnkq1pbb1jeZw",
User = "user:bob",
Relation = "reader",
Type = "document",
ContextualTuples = new ContextualTupleKeys() {
TupleKeys = new List<TupleKey> {
new("document:otherdoc", "reader", "user:bob")
}
}
};
var response = await openFgaApi.ListObjects(body);
// response.Objects = ["document:otherdoc", "document:planning"]
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import os
import json
import openfga_sdk
from openfga_sdk.api import open_fga_api
configuration = openfga_sdk.Configuration(
api_scheme = os.environ.get('FGA_API_SCHEME'), # Either "http" or "https", defaults to "https"
api_host = os.environ.get('FGA_API_HOST'), # required, define without the scheme (e.g. api.openfga.example instead of https://api.openfga.example)
store_id = os.environ.get('FGA_STORE_ID') # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
)
# Create an instance of the API class
fga_client_instance = open_fga_api.OpenFgaApi(openfga_sdk.ApiClient(configuration))
# from openfga_sdk.models.list_objects_request import ListObjectsRequest
# from openfga_sdk.models.tuple_key import TupleKey
# from openfga_sdk.models.contextual_tuple_keys import ContextualTupleKeys
async def list_objects():
body = ListObjectsRequest(
authorization_model_id="1uHxCSuTP0VKPYSnkq1pbb1jeZw",
user="user:bob",
relation="reader",
type="document",
contextual_tuples=ContextualTupleKeys(
tuple_keys=[
TupleKey(
user="user:bob",
relation="reader",
object="document:otherdoc")
]
)
)
response = await fga_client_instance.list_objects(body)
Get the Bearer Token and set up the FGA_API_URL environment variable
Set FGA_API_URL according to the service you are using
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/list-objects \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{ "authorization_model_id": "1uHxCSuTP0VKPYSnkq1pbb1jeZw",
"type": "document",
"relation": "reader",
"user":"user:bob",
"contextual_tuples": {
"tuple_keys": [
{"object": "document:otherdoc", "relation": "reader", "user": "user:bob"}
]
}
}'
# Response: {"objects": ["document:otherdoc", "document:planning"]}
listObjects(
"user:bob", // list the objects that the user `user:bob`
"reader", // has an `reader` relation
"document", // and that are of type `document`
authorization_model_id = "1uHxCSuTP0VKPYSnkq1pbb1jeZw", // for this particular authorization model id
contextual_tuples = [ // Assuming the following is true
{user = "user:bob", relation = "reader", object = "document:otherdoc"}
]
);
Reply: ["document:otherdoc", "document:planning"]
There's two variations of the List Objects API.
- The standard version, which waits until all results are ready and sends them in one response.
- The streaming version, which should be used if you want the individual results as soon as they become available.
Caveats And When Not To Use It
The number of results returned by this API (both the standard and streaming version) may be incomplete depending on the listObjectsDeadline
and listObjectsMaxResults
configuration values (see Configuring the Server) provided to the OpenFGA server at startup. If you configure both values, the first one to be satisfied will terminate the request.
- If you set
listObjectsDeadline
to1s
, the server will spend at most 1 second finding results. - If you set
listObjectsMaxResults
to10
, the server will return, at most, 10 object IDs.
Internally, the API runs concurrent checks against all the objects of the specified type. If the number of objects of that type is high, you should set a high value for listObjectsDeadline
. If the number of objects of that type the user could have access to is high, you should set a high value for listObjectsMaxResults
.
This API is provided to gather your feedback. Due to performance and high-latency considerations, we do not recommend using it in production yet.
Summary
Check | Read | Expand | ListObjects | |
---|---|---|---|---|
Purpose | Check if user has particular relationship with certain object | Return all stored relationship tuples that match query | Expand the specific relationship on a particular object | List all objects of a particular type that a user has a specific relationship with |
When to use | Validate if user X can perform Y on object Z | List stored relationships in system | Understand why user X can perform Y on object Z | Filter the objects a user has access to |