Transactional Writes
Using Auth0 FGA, you can update multiple relationship tuples in a single transaction.
Updating multiple relationship tuples can keep your system state consistent.
Before you start
Familiarize yourself with basic Auth0 FGA Concepts before completing this guide.
In the following authorization model, there is type called tweet that can have a reader. There is another type called user that can have a follower and followed_by relationship.
tweet that can have a reader. There is another type called user that can have a follower and followed_by relationship.model
schema 1.1
type tweet
relations
define viewer: [user, user:*, user#follower]
type user
relations
define follower: [user]
define followed_by: [user]
In addition:
Direct access
Creating an authorization model and a relationship tuple grants a user access to an object. To learn more, read about Direct Access.
Modeling public access
The following example uses public access. To learn more, read about Public Access.
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 an object of the same type as the type definition and a user in the system
- A Relation: 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
- A Relationship Tuple: a group stored in Auth0 FGA that consists of a user, a relation, and an object
Step by step
01. Add and remove relationship tuples in the same transaction
A call to the Write API can add or delete tuples in your store. For example, the following tuple makes tweet:1 public by making everyone a viewer:
- Node.js
- Go
- .NET
- Python
- Java
- curl
- CLI
- Pseudocode
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});
const options = {
authorizationModelId: "01HVMMBCMGZNT3SED4Z17ECXCA",
};
await fgaClient.write({
writes: [
{"user":"user:*","relation":"viewer","object":"tweet:1"}
],
}, options);
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
import (
"os"
openfga "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
"github.com/openfga/go-sdk/credentials"
)
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
Credentials: &credentials.Credentials{ // Credentials are not needed if connecting to the Playground API
Method: credentials.CredentialsMethodClientCredentials,
Config: &credentials.Config{
ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"),
ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"),
ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
},
},
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: openfga.PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
{
User: "user:*",
Relation: "viewer",
Object: "tweet:1",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
class MyProgram {
static async Task Main() {
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"),
Credentials = new Credentials() { // Credentials are not needed if connecting to the Playground API
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
}
}
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Writes = new List<ClientTupleKey>() {
new() {
User = "user:*",
Relation = "viewer",
Object = "tweet:1"
}
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# Checkout the "How to Setup the SDK Client" page for more details.
import os
import openfga_sdk
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.credentials import Credentials, CredentialConfiguration
# FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
# FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
# FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
# FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
# FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
async def main():
credentials = Credentials(
method='client_credentials',
configuration=CredentialConfiguration(
api_issuer= os.environ.get('FGA_API_TOKEN_ISSUER'),
api_audience= os.environ.get('FGA_API_AUDIENCE'),
client_id= os.environ.get('FGA_CLIENT_ID'),
client_secret= os.environ.get('FGA_CLIENT_SECRET'),
)
)
configuration = ClientConfiguration(
api_url = os.environ.get('FGA_API_URL'), # required, e.g. https://api.fga.example
store_id = os.environ.get('FGA_STORE_ID'), # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
authorization_model_id = os.environ.get('FGA_MODEL_ID'), # Optional, can be overridden per request
)
# Enter a context with an instance of the OpenFgaClient
async with OpenFgaClient(configuration) as fga_client:
api_response = await fga_client.read_authorization_models()
await fga_client.close()
asyncio.run(main())
options = {
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}
body = ClientWriteRequest(
writes=[
ClientTuple(
user="user:*",
relation="viewer",
object="tweet:1",
),
],
)
response = await fga_client.write(body, options)
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
// FGA_API_URL = 'https://api.us1.fga.dev' for Dev Preview and Early Access / 'https://api.playground.fga.dev' for the FGA Playground
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "https://localhost:8080"
.storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
.authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
.credentials(new Credentials(
new ClientCredentials()
.apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
.apiAudience(System.getenv("FGA_API_AUDIENCE"))
.clientId(System.getenv("FGA_CLIENT_ID"))
.clientSecret(System.getenv("FGA_CLIENT_SECRET"))
));
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.writes(List.of(
new ClientTupleKey()
.user("user:*")
.relation("viewer")
._object("tweet:1")
));
var response = fgaClient.write(body, options).get();
Set the environment variables according to the "How to get your API keys"
Set the required environment variables
# For all environments
export FGA_STORE_ID = 'YOUR_STORE_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_MODEL_ID = 'YOUR_MODEL_ID' # optional, can be overridden per request, helps reduce latency
export FGA_API_URL = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
export FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_CLIENT_ID = 'YOUR_CLIENT_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{
"user": "user:*",
"relation": "viewer",
"object": "tweet:1"
}
]
},
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}'
Set the environment variables according to the "How to get your API keys"
Set the required environment variables
# For all environments
export FGA_STORE_ID = 'YOUR_STORE_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_MODEL_ID = 'YOUR_MODEL_ID' # optional, can be overridden per request, helps reduce latency
export FGA_API_URL = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
export FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_CLIENT_ID = 'YOUR_CLIENT_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:* viewer tweet:1
write([
{
"user":"user:*",
"relation":"viewer",
"object":"tweet:1"
}
])
Deleting the previous tuple converts this tweet to private:
- Node.js
- Go
- .NET
- Python
- Java
- curl
- CLI
- Pseudocode
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});
const options = {
authorizationModelId: "01HVMMBCMGZNT3SED4Z17ECXCA",
};
await fgaClient.write({
deletes: [
{ user: 'user:*', relation: 'viewer', object: 'tweet:1'}
],
}, options);
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
import (
"os"
openfga "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
"github.com/openfga/go-sdk/credentials"
)
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
Credentials: &credentials.Credentials{ // Credentials are not needed if connecting to the Playground API
Method: credentials.CredentialsMethodClientCredentials,
Config: &credentials.Config{
ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"),
ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"),
ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
},
},
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: openfga.PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Deletes: []ClientTupleKeyWithoutCondition{
{
User: "user:*",
Relation: "viewer",
Object: "tweet:1",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
class MyProgram {
static async Task Main() {
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"),
Credentials = new Credentials() { // Credentials are not needed if connecting to the Playground API
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
}
}
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Deletes = new List<ClientTupleKeyWithoutCondition>() {
new() { User = "user:*", Relation = "viewer", Object = "tweet:1" }
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# Checkout the "How to Setup the SDK Client" page for more details.
import os
import openfga_sdk
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.credentials import Credentials, CredentialConfiguration
# FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
# FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
# FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
# FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
# FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
async def main():
credentials = Credentials(
method='client_credentials',
configuration=CredentialConfiguration(
api_issuer= os.environ.get('FGA_API_TOKEN_ISSUER'),
api_audience= os.environ.get('FGA_API_AUDIENCE'),
client_id= os.environ.get('FGA_CLIENT_ID'),
client_secret= os.environ.get('FGA_CLIENT_SECRET'),
)
)
configuration = ClientConfiguration(
api_url = os.environ.get('FGA_API_URL'), # required, e.g. https://api.fga.example
store_id = os.environ.get('FGA_STORE_ID'), # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
authorization_model_id = os.environ.get('FGA_MODEL_ID'), # Optional, can be overridden per request
)
# Enter a context with an instance of the OpenFgaClient
async with OpenFgaClient(configuration) as fga_client:
api_response = await fga_client.read_authorization_models()
await fga_client.close()
asyncio.run(main())
options = {
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}
body = ClientWriteRequest(
deletes=[
ClientTuple(
user="user:*",
relation="viewer",
object="tweet:1",
),
],
)
response = await fga_client.write(body, options)
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
// FGA_API_URL = 'https://api.us1.fga.dev' for Dev Preview and Early Access / 'https://api.playground.fga.dev' for the FGA Playground
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "https://localhost:8080"
.storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
.authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
.credentials(new Credentials(
new ClientCredentials()
.apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
.apiAudience(System.getenv("FGA_API_AUDIENCE"))
.clientId(System.getenv("FGA_CLIENT_ID"))
.clientSecret(System.getenv("FGA_CLIENT_SECRET"))
));
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.deletes(List.of(
new ClientTupleKey()
.user("user:*")
.relation("viewer")
._object("tweet:1")
));
var response = fgaClient.write(body, options).get();
Set the environment variables according to the "How to get your API keys"
Set the required environment variables
# For all environments
export FGA_STORE_ID = 'YOUR_STORE_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_MODEL_ID = 'YOUR_MODEL_ID' # optional, can be overridden per request, helps reduce latency
export FGA_API_URL = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
export FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_CLIENT_ID = 'YOUR_CLIENT_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{
"deletes": {
"tuple_keys": [
{
"user": "user:*",
"relation": "viewer",
"object": "tweet:1"
}
]
},
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}'
Set the environment variables according to the "How to get your API keys"
Set the required environment variables
# For all environments
export FGA_STORE_ID = 'YOUR_STORE_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_MODEL_ID = 'YOUR_MODEL_ID' # optional, can be overridden per request, helps reduce latency
export FGA_API_URL = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
export FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_CLIENT_ID = 'YOUR_CLIENT_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
fga tuple delete --store-id=${FGA_STORE_ID} user:* viewer tweet:1
delete([
{
"user":"user:*",
"relation":"viewer",
"object":"tweet:1"
}
])
By removing the tuple, we made the tweet visible to no-one, which may not be what we want.
Limitations on duplicate tuples in a single request
When using the Write API, you cannot include the same tuple (same user, relation, and object) in both the writes and deletes arrays within a single request. The API will return an error with code cannot_allow_duplicate_tuples_in_one_request if duplicate tuples are detected.
For example, this request would fail:
curl -X POST "${FGA_API_URL}/stores/${FGA_STORE_ID}/write" \
-H 'content-type: application/json' \
--data '{
"writes": {
"tuple_keys": [{
"user": "user:anne",
"relation": "member",
"object": "group:2"
}]
},
"deletes": {
"tuple_keys": [{
"user": "user:anne",
"relation": "member",
"object": "group:2"
}]
}
}'
The Write API allows you to send up to 40 unique tuples in the request. (This limit applies to the sum of both writes and deletes in that request). This means we can submit one API call that converts the tweet from public to visible to only the user's followers.
- Node.js
- Go
- .NET
- Python
- Java
- curl
- Pseudocode
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});
const options = {
authorizationModelId: "01HVMMBCMGZNT3SED4Z17ECXCA",
};
await fgaClient.write({
writes: [
// Anne's followers can view tweet:1
{"_description":"Anne's followers can view tweet:1","user":"user:anne#follower","relation":"viewer","object":"tweet:1"}
],
deletes: [
// tweet:1 is no longer viewable by everyone (*)
{ user: 'user:*', relation: 'viewer', object: 'tweet:1'}
],
}, options);
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
import (
"os"
openfga "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
"github.com/openfga/go-sdk/credentials"
)
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
Credentials: &credentials.Credentials{ // Credentials are not needed if connecting to the Playground API
Method: credentials.CredentialsMethodClientCredentials,
Config: &credentials.Config{
ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"),
ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"),
ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
},
},
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: openfga.PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
{
// Anne's followers can view tweet:1
User: "user:anne#follower",
Relation: "viewer",
Object: "tweet:1",
},
},
Deletes: []ClientTupleKeyWithoutCondition{
{
// tweet:1 is no longer viewable by everyone (*)
User: "user:*",
Relation: "viewer",
Object: "tweet:1",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
class MyProgram {
static async Task Main() {
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"),
Credentials = new Credentials() { // Credentials are not needed if connecting to the Playground API
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
}
}
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Writes = new List<ClientTupleKey>() {
// Anne's followers can view tweet:1
new() {
User = "user:anne#follower",
Relation = "viewer",
Object = "tweet:1"
}
},
Deletes = new List<ClientTupleKeyWithoutCondition>() {
// tweet:1 is no longer viewable by everyone (*)
new() { User = "user:*", Relation = "viewer", Object = "tweet:1" }
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# Checkout the "How to Setup the SDK Client" page for more details.
import os
import openfga_sdk
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.credentials import Credentials, CredentialConfiguration
# FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
# FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
# FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
# FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
# FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
# FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
async def main():
credentials = Credentials(
method='client_credentials',
configuration=CredentialConfiguration(
api_issuer= os.environ.get('FGA_API_TOKEN_ISSUER'),
api_audience= os.environ.get('FGA_API_AUDIENCE'),
client_id= os.environ.get('FGA_CLIENT_ID'),
client_secret= os.environ.get('FGA_CLIENT_SECRET'),
)
)
configuration = ClientConfiguration(
api_url = os.environ.get('FGA_API_URL'), # required, e.g. https://api.fga.example
store_id = os.environ.get('FGA_STORE_ID'), # optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
authorization_model_id = os.environ.get('FGA_MODEL_ID'), # Optional, can be overridden per request
)
# Enter a context with an instance of the OpenFgaClient
async with OpenFgaClient(configuration) as fga_client:
api_response = await fga_client.read_authorization_models()
await fga_client.close()
asyncio.run(main())
options = {
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}
body = ClientWriteRequest(
writes=[
ClientTuple(
# Anne's followers can view tweet:1
user="user:anne#follower",
relation="viewer",
object="tweet:1",
),
], deletes=[
ClientTuple(
# tweet:1 is no longer viewable by everyone (*)
user="user:*",
relation="viewer",
object="tweet:1",
),
],
)
response = await fga_client.write(body, options)
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
// FGA_API_URL = 'https://api.us1.fga.dev' for Dev Preview and Early Access / 'https://api.playground.fga.dev' for the FGA Playground
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'auth.fga.dev' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' for Dev Preview and Early Access / not needed for the FGA Playground
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page / not needed for the FGA Playground
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "https://localhost:8080"
.storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
.authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
.credentials(new Credentials(
new ClientCredentials()
.apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
.apiAudience(System.getenv("FGA_API_AUDIENCE"))
.clientId(System.getenv("FGA_CLIENT_ID"))
.clientSecret(System.getenv("FGA_CLIENT_SECRET"))
));
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.writes(List.of(
// Anne's followers can view tweet:1
new ClientTupleKey()
.user("user:anne#follower")
.relation("viewer")
._object("tweet:1")
))
.deletes(List.of(
// tweet:1 is no longer viewable by everyone (*)
new ClientTupleKey()
.user("user:*")
.relation("viewer")
._object("tweet:1")
));
var response = fgaClient.write(body, options).get();
Set the environment variables according to the "How to get your API keys"
Set the required environment variables
# For all environments
export FGA_STORE_ID = 'YOUR_STORE_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_MODEL_ID = 'YOUR_MODEL_ID' # optional, can be overridden per request, helps reduce latency
export FGA_API_URL = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_API_TOKEN_ISSUER = 'auth.fga.dev'
export FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
export FGA_CLIENT_ID = 'YOUR_CLIENT_ID' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
export FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' # Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{
"user": "user:anne#follower",
"relation": "viewer",
"object": "tweet:1"
}
]
},
"deletes": {
"tuple_keys": [
{
"user": "user:*",
"relation": "viewer",
"object": "tweet:1"
}
]
},
"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"
}'
write([
// Anne's followers can view tweet:1
{
"user":"user:anne#follower",
"relation":"viewer",
"object":"tweet:1"
}
]),
delete([
// tweet:1 is no longer viewable by everyone (*)
{
"user":"user:*",
"relation":"viewer",
"object":"tweet:1"
}
])
02. Add multiple related relationship tuples in the same transaction
Sending multiple tuples per request can also help maintain consistency. For example, if anne follows becky, you can save the following two tuples or neither of them:
[// Anne is a follower of Becky
{
"_description": "Anne is a follower of Becky",
"user": "user:anne",
"relation": "follower",
"object": "user:becky"
}// Becky is followed by Anne
{
"_description": "Becky is followed by Anne",
"user": "user:becky",
"relation": "followed_by",
"object": "user:anne"
}]
In this case, the type user exists because users can be related to each other, so users now are a type in the system.
The Auth0 Fine-Grained Authorization (FGA) service attempts to perform all the changes sent in a single Write API call in one transaction. If it cannot complete all the changes, it rejects all of them.