Managing Relationships Between Objects
note
Please note that at this point in time, it is not considered production-ready and does not come with any SLAs; availability and uptime are not guaranteed. Limitations of Auth0 FGA during the Developer Community Preview can be found here.
In this guide you will learn how to give a user access to a particular object through a relationship with another object.
Giving user access through a relationship with another object is helpful because it allows scaling as the number of object grows. For example:
- organization that owns many repos
- team that administers many documents
Before you start
In order to understand this guide correctly you must be familiar with some Auth0 FGA Concepts and know how to develop the things that we will list below.
Assume that you have the followingauthorization model
- a
repo
type that can have a admin
relation
repo
type that can have a admin
relation- DSL
- JSON
type repo
relations
define admin as self
{
"type_definitions": [
{
"type": "repo",
"relations": {
"admin": {
"this": {}
}
}
}
]
}
In addition, you will need to know the following:
Modeling Basics
You need to know how to create an authorization model and create a relationship tuple to grant a user access to an object. Learn more →
Auth0 FGA Concepts
- A Type: a class of objects that have similar characteristics
- A User: an entity in the system that can be related to an object
- A Relation: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between objects of this type and other users in the system
- An Object: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
- A Relationship Tuple: a grouping consisting of a user, a relation and an object stored in Auth0 FGA
Step by Step
For the current model, a user can be related as an admin
to an object of type repo
. If we wanted to have Anne be related to two repos, repo:1
and repo:2
, we would have to add two relationship tuples, like so:
- Node.js
- Go
- .NET
- curl
- Pseudocode
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
await fgaClient.write({
writes: {
tuple_keys: [
{ user: 'anne', relation: 'admin', object: 'repo:1'},
{ user: 'anne', relation: 'admin', object: 'repo:2'}
]
}
});
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.WriteRequest{
Writes: &fgaSdk.TupleKeys{
TupleKeys: []fgaSdk.TupleKey {
{
User: fgaSdk.PtrString("anne"),
Relation: fgaSdk.PtrString("admin"),
Object: fgaSdk.PtrString("repo:1"),
},
{
User: fgaSdk.PtrString("anne"),
Relation: fgaSdk.PtrString("admin"),
Object: fgaSdk.PtrString("repo:2"),
},
},
},
}
_, response, err := fgaClient.Auth0FgaApi.Write(context.Background()).Body(body).Execute()
if err != nil {
// .. Handle error
}
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
await fgaClient.Write(new WriteRequest{
Writes = new TupleKeys(new List<TupleKey>() {
new() { User = "anne", Relation = "admin", Object = "repo:1" },
new() { User = "anne", Relation = "admin", Object = "repo:2" }
})
});
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"writes": { "tuple_keys" : [{"user":"anne","relation":"admin","object":"repo:1"},{"user":"anne","relation":"admin","object":"repo:2"}] }}'
write([
{
"user":"anne",
"relation":"admin",
"object":"repo:1"
},
{
"user":"anne",
"relation":"admin",
"object":"repo:2"
}
])
In general, every time we wanted to add a new admin
relationship to a repo
we'd have to add a new tuple. This doesn't scale as the list of repo
s and users grows.
01. Modify authorization model
Another way of modeling this is to have an authorization model as follows:
- DSL
- JSON
type repo
relations
define admin as self or repo_admin from owner
define owner as self
type org
relations
define repo_admin as self
{
"type_definitions": [
{
"type": "repo",
"relations": {
"admin": {
"union": {
"child": [
{
"this": {}
},
{
"tupleToUserset": {
"tupleset": {
"object": "",
"relation": "owner"
},
"computedUserset": {
"object": "",
"relation": "repo_admin"
}
}
}
]
}
},
"owner": {
"this": {}
}
}
},
{
"type": "org",
"relations": {
"repo_admin": {
"this": {}
}
}
}
]
}
In this model, we have:
- added a new type
org
with one relationrepo_admin
. - added a new relation
owner
for typerepo
. - re-defined the relation
admin
forrepo
. A user can be defined as anadmin
directly, as we have seen above, or through therepo_admin from owner
clause. How this works, for example, is that ifuser
is related asrepo_admin
toorg:xyz
, andorg:xyz
is related asowner
torepo:1
, thenuser
is anadmin
ofrepo:1
.
02. Adding relationship tuples where user is another object
With this model, we can add tuples representing that an org
is the owner
of a repo
. By adding following relationship tuples, we are indicating that the xyz organization is the owner of repositories with IDs 1
and 2
:
- Node.js
- Go
- .NET
- curl
- Pseudocode
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
await fgaClient.write({
writes: {
tuple_keys: [
{ user: 'org:xyz', relation: 'owner', object: 'repo:1'},
{ user: 'org:xyz', relation: 'owner', object: 'repo:2'}
]
}
});
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.WriteRequest{
Writes: &fgaSdk.TupleKeys{
TupleKeys: []fgaSdk.TupleKey {
{
User: fgaSdk.PtrString("org:xyz"),
Relation: fgaSdk.PtrString("owner"),
Object: fgaSdk.PtrString("repo:1"),
},
{
User: fgaSdk.PtrString("org:xyz"),
Relation: fgaSdk.PtrString("owner"),
Object: fgaSdk.PtrString("repo:2"),
},
},
},
}
_, response, err := fgaClient.Auth0FgaApi.Write(context.Background()).Body(body).Execute()
if err != nil {
// .. Handle error
}
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
await fgaClient.Write(new WriteRequest{
Writes = new TupleKeys(new List<TupleKey>() {
new() { User = "org:xyz", Relation = "owner", Object = "repo:1" },
new() { User = "org:xyz", Relation = "owner", Object = "repo:2" }
})
});
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"writes": { "tuple_keys" : [{"user":"org:xyz","relation":"owner","object":"repo:1"},{"user":"org:xyz","relation":"owner","object":"repo:2"}] }}'
write([
{
"user":"org:xyz",
"relation":"owner",
"object":"repo:1"
},
{
"user":"org:xyz",
"relation":"owner",
"object":"repo:2"
}
])
03. Adding relationship tuples to the other object
Now, imagine we have a new user Becky. If we wanted to have Becky be the admin
of all repo
s without having to add one tuple per repo
, all we need to do is add one tuple that says that Becky is related as repo_admin
to org:xyz
.
- Node.js
- Go
- .NET
- curl
- Pseudocode
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
await fgaClient.write({
writes: {
tuple_keys: [
{ user: 'becky', relation: 'repo_admin', object: 'org:xyz'}
]
}
});
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.WriteRequest{
Writes: &fgaSdk.TupleKeys{
TupleKeys: []fgaSdk.TupleKey {
{
User: fgaSdk.PtrString("becky"),
Relation: fgaSdk.PtrString("repo_admin"),
Object: fgaSdk.PtrString("org:xyz"),
},
},
},
}
_, response, err := fgaClient.Auth0FgaApi.Write(context.Background()).Body(body).Execute()
if err != nil {
// .. Handle error
}
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
await fgaClient.Write(new WriteRequest{
Writes = new TupleKeys(new List<TupleKey>() {
new() { User = "becky", Relation = "repo_admin", Object = "org:xyz" }
})
});
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"writes": { "tuple_keys" : [{"user":"becky","relation":"repo_admin","object":"org:xyz"}] }}'
write([
{
"user":"becky",
"relation":"repo_admin",
"object":"org:xyz"
}
])
04. Validating user access
We can now verify that Becky an admin
of all the repo
s owned by org:xyz
:
- Node.js
- Go
- .NET
- curl
- Pseudocode
- Playground
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'becky',
relation: 'admin',
object: 'repo:1',
},});
// allowed = true
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.CheckRequest{
TupleKey: &fgaSdk.TupleKey{
User: fgaSdk.PtrString("becky"),
Relation: fgaSdk.PtrString("admin"),
Object: fgaSdk.PtrString("repo:1"),
},
data, response, err := fgaClient.Auth0FgaApi.Check(context.Background()).Body(body).Execute()
// data = { allowed: true }
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
// Run a check
var response = await fgaClient.Check(new CheckRequest(new TupleKey {
User = "becky",
Relation = "admin",
Object = "repo:1"
});
// response.Allowed = true
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
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 '{"tuple_key":{"user":"becky","relation":"admin","object":"repo:1"}}'
# Response: {"allowed":true}
check(
"becky", // check if the user `becky`
"admin", // has an `admin` relation
"repo:1", // with the object `repo:1`
);
Reply: true
is becky related to repo:1 as admin?
# Response: A green path from the user to the object indicating that the response from the API is `{"allowed":true}`
- Node.js
- Go
- .NET
- curl
- Pseudocode
- Playground
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'becky',
relation: 'admin',
object: 'repo:2',
},});
// allowed = true
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.CheckRequest{
TupleKey: &fgaSdk.TupleKey{
User: fgaSdk.PtrString("becky"),
Relation: fgaSdk.PtrString("admin"),
Object: fgaSdk.PtrString("repo:2"),
},
data, response, err := fgaClient.Auth0FgaApi.Check(context.Background()).Body(body).Execute()
// data = { allowed: true }
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
// Run a check
var response = await fgaClient.Check(new CheckRequest(new TupleKey {
User = "becky",
Relation = "admin",
Object = "repo:2"
});
// response.Allowed = true
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
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 '{"tuple_key":{"user":"becky","relation":"admin","object":"repo:2"}}'
# Response: {"allowed":true}
check(
"becky", // check if the user `becky`
"admin", // has an `admin` relation
"repo:2", // with the object `repo:2`
);
Reply: true
is becky related to repo:2 as admin?
# Response: A green path from the user to the object indicating that the response from the API is `{"allowed":true}`
05. Revoking access
Suppose now that we want to prevent users from being an admin
of repo:1
via org:xyz
. We can delete one tuple:
- Node.js
- Go
- .NET
- curl
- Pseudocode
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
await fgaClient.write({
deletes: {
tuple_keys : [
{ user: 'org:xyz', relation: 'owner', object: 'repo:1'}
]
}
});
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.WriteRequest{
Deletes: &fgaSdk.TupleKeys{
TupleKeys: []fgaSdk.TupleKey {
{
User: fgaSdk.PtrString("org:xyz"),
Relation: fgaSdk.PtrString("owner"),
Object: fgaSdk.PtrString("repo:1"),
},
},
},
}
_, response, err := fgaClient.Auth0FgaApi.Write(context.Background()).Body(body).Execute()
if err != nil {
// .. Handle error
}
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
await fgaClient.Write(new WriteRequest{
Deletes = new TupleKeys(new List<TupleKey>() {
new() { User = "org:xyz", Relation = "owner", Object = "repo:1" }
})
});
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/write \
-H "Authorization: Bearer $FGA_BEARER_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"deletes": { "tuple_keys" : [{"user":"org:xyz","relation":"owner","object":"repo:1"}] }}'
delete([
{
"user":"org:xyz",
"relation":"owner",
"object":"repo:1"
}
])
With this change, we may now verify that Becky is no longer an admin
of repo:1
.
- Node.js
- Go
- .NET
- curl
- Pseudocode
- Playground
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
const { Auth0FgaApi } = require('@auth0/fga');
// Initialize the SDK
const fgaClient = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
// Run a check
const { allowed } = await fgaClient.check({
tuple_key: {
user: 'becky',
relation: 'admin',
object: 'repo:1',
},});
// allowed = false
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
import (
fgaSdk "github.com/auth0-lab/fga-go-sdk"
"os"
)
func Main() {
configuration, err := fgaSdk.NewConfiguration(fgaSdk.UserConfiguration{
Environment: os.Getenv("FGA_ENVIRONMENT"),
StoreId: os.Getenv("FGA_STORE_ID"),
ClientId: os.Getenv("FGA_CLIENT_ID"),
ClientSecret: os.Getenv("FGA_CLIENT_SECRET"),
})
if err != nil {
// .. Handle error
}
fgaClient := fgaSdk.NewAPIClient(configuration)
}
body := fgaSdk.CheckRequest{
TupleKey: &fgaSdk.TupleKey{
User: fgaSdk.PtrString("becky"),
Relation: fgaSdk.PtrString("admin"),
Object: fgaSdk.PtrString("repo:1"),
},
data, response, err := fgaClient.Auth0FgaApi.Check(context.Background()).Body(body).Execute()
// data = { allowed: false }
Initialize the SDK
// FGA_ENVIRONMENT can be "us" (default if not set) for Developer Community Preview or "playground" for the Playground API
// import the SDK
using Auth0.Fga.Api;
using Auth0.Fga.Configuration;
using Environment = System.Environment;
namespace ExampleApp;
class MyProgram {
static async Task Main() {
var storeId = Environment.GetEnvironmentVariable("FGA_STORE_ID");
var environment = Environment.GetEnvironmentVariable("FGA_ENVIRONMENT")
var configuration = new Configuration(storeId, environment) {
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
ClientSecret = Environment.GetEnvironmentVariable("FGA_CLIENT_SECRET"),
};
var fgaClient = new Auth0FgaApi(configuration);
}
}
// Run a check
var response = await fgaClient.Check(new CheckRequest(new TupleKey {
User = "becky",
Relation = "admin",
Object = "repo:1"
});
// response.Allowed = false
Get the Bearer Token and set up the FGA_API_URL environment variable
# Not needed when calling the Playground API
curl -X POST \
https://fga.us.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{"client_id":"'$FGA_CLIENT_ID'","client_secret":"'$FGA_CLIENT_SECRET'","audience":"https://api.us1.fga.dev/","grant_type":"client_credentials"}'
# The response will be returned in the form
# {
# "access_token": "eyJ...Ggg",
# "expires_in": 86400,
# "scope": "read:tuples write:tuples check:tuples ... write:authorization-models",
# "token_type": "Bearer"
# }
# Store this `access_token` value in environment variable `FGA_BEARER_TOKEN`
# For non-playground environment
FGA_API_URL='https://api.us1.fga.dev'
# For playground environment
# FGA_API_URL='https://api.playground.fga.dev'
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 '{"tuple_key":{"user":"becky","relation":"admin","object":"repo:1"}}'
# Response: {"allowed":false}
check(
"becky", // check if the user `becky`
"admin", // has an `admin` relation
"repo:1", // with the object `repo:1`
);
Reply: false
is becky related to repo:1 as admin?
# Response: A red object indicating that the response from the API is `{"allowed":false}`