Modeling Entitlements for a System with OpenFGA
This tutorial explains how to model entitlements for a platform like GitHub using OpenFGA.
- How to model an entitlement use case in OpenFGA
- How to start with a given set of requirements and scenarios and iterate on the OpenFGA model until those requirements are met
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.
OpenFGA concepts
It would be helpful to have an understanding of some concepts of OpenFGA before you start.
Modeling object-to-object relationships
You need to know how to create relationships between objects and how that might affect a user's relationships to those objects. Learn more →
Used here to indicate that members of an org are subscriber members of the plan the org is subscriber to, and subscriber members of a plan get access to all the plan's features.
Direct relationships
You need to know how to disallow granting direct relation to an object and requiring the user to have a relation with another object that would imply a relation with the first one. Learn more →
Used here to indicate that "access" to a feature cannot be directly granted to a user, but is implied through the users organization subscribing to a plan that offers that feature.
Concepts & configuration language
What you will be modeling
In many product offerings, the features are behind multiple tiers. In this tutorial, you will build an authorization model for a subset of GitHub's entitlements (detailed below) using OpenFGA. You will use some scenarios to validate the model.
At their core, entitlements is just asking: does a user X have access to feature Y? In GitHub's case for example, they have a concept called "Draft Pull Requests". Once the user loads the Pull Request page, the frontend needs to know whether it can show the "Draft Pull Request" option, as in it needs to know: "Does the current user have access to feature Draft Pull Request?".
Note: For brevity, this tutorial will not model all of GitHub entitlements. Instead, it will focus on modeling for the scenarios outlined below
Requirements
You will model an entitlement system similar to GitHub's, focusing on a few scenarios.
GitHub has 3 plans: "Free", "Team" and "Enterprise", with each of them offering several features. The higher-priced plans include all the features of the lower priced plans. You will be focusing on a subset of the features offered.
A summary of GitHub's entitlement system:
- Free
- Issues
- Team
- Everything from the free plan
- Draft Pull Requests
- Enterprise
- Everything from the team plan
- SAML Single Sign-On
Defined scenarios
Use the following scenarios to be able to validate whether the model of the requirements is correct.
-
Take these three organizations
- Alpha Beta Gamma (
alpha
), a subscriber on the free plan - Bayer Water Supplies (
bayer
), a subscriber on the team plan - Cups and Dishes (
cups
), a subscriber on the enterprise plan
- Alpha Beta Gamma (
-
Take these three users
- Anne, member of Alpha Beta Gamma
- Beth, member of Bayer Water Supplies
- Charles, member of Cups and Dishes
By the end of this tutorial, you should be able to query OpenFGA with queries like:
- Anne has access to Issues (expecting
yes
) - Anne has access to Draft Pull Requests (expecting
no
) - Anne has access to Single Sign-on (expecting
no
) - Beth has access to Issues (expecting
yes
) - Beth has access to Draft Pull Requests (expecting
yes
) - Beth has access to Single Sign-on (expecting
no
) - Charles has access to Issues (expecting
yes
) - Charles has access to Draft Pull Requests (expecting
yes
) - Charles has access to Single Sign-on (expecting
yes
)
Modeling entitlements for GitHub
01. Building The Initial Authorization Model And Relationship Tuples
In this tutorial you are going to take a different approach to previous tutorials. You will start with a simple authorization model, add relationship tuples to represent some sample scenarios, and iterate until those scenarios return the results you expect.
In the scenarios outlined above, you have organizations
, plans
and features
.
Similar to the example above, start with a basic listing of the types and their relations:
- A
feature
has aplan
associated to it, we'll call the relation between themassociated_plan
- A
plan
has an organization as asubscriber
to it - An
organization
has users asmembers
- DSL
- JSON
model
schema 1.1
type user
type feature
relations
define associated_plan: [plan]
type plan
relations
define subscriber: [organization]
type organization
relations
define member: [user]
{
"schema_version": "1.1",
"type_definitions": [
{
"type": "user"
},
{
"type": "feature",
"relations": {
"associated_plan": {
"this": {}
}
},
"metadata": {
"relations": {
"associated_plan": {
"directly_related_user_types": [
{
"type": "plan"
}
]
}
}
}
},
{
"type": "plan",
"relations": {
"subscriber": {
"this": {}
}
},
"metadata": {
"relations": {
"subscriber": {
"directly_related_user_types": [
{
"type": "organization"
}
]
}
}
}
},
{
"type": "organization",
"relations": {
"member": {
"this": {}
}
},
"metadata": {
"relations": {
"member": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
}
]
}
02. Populating the relationship tuples
Now you can add the relationship tuples to represent these relationships mentioned in the requirements and scenarios sections:
The relations between the features and plans are as follows:
- Node.js
- Go
- .NET
- Python
- Java
- curl
- CLI
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
await fgaClient.write({
writes: [
// the free plan is the associated plan of the issues feature
{"_description":"the free plan is the associated plan of the issues feature","user":"plan:free","relation":"associated_plan","object":"feature:issues"},
// the team plan is the associated plan of the issues feature
{"_description":"the team plan is the associated plan of the issues feature","user":"plan:team","relation":"associated_plan","object":"feature:issues"},
// the team plan is the associated plan of the draft pull requests feature
{"_description":"the team plan is the associated plan of the draft pull requests feature","user":"plan:team","relation":"associated_plan","object":"feature:draft_prs"},
// the enterprise plan is the associated plan of the issues feature
{"_description":"the enterprise plan is the associated plan of the issues feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:issues"},
// the enterprise plan is the associated plan of the draft pull requests feature
{"_description":"the enterprise plan is the associated plan of the draft pull requests feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:draft_prs"},
// the enterprise plan is the associated plan of the SAML Single Sign-on feature
{"_description":"the enterprise plan is the associated plan of the SAML Single Sign-on feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:sso"}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
{
// the free plan is the associated plan of the issues feature
User: "plan:free",
Relation: "associated_plan",
Object: "feature:issues",
}, {
// the team plan is the associated plan of the issues feature
User: "plan:team",
Relation: "associated_plan",
Object: "feature:issues",
}, {
// the team plan is the associated plan of the draft pull requests feature
User: "plan:team",
Relation: "associated_plan",
Object: "feature:draft_prs",
}, {
// the enterprise plan is the associated plan of the issues feature
User: "plan:enterprise",
Relation: "associated_plan",
Object: "feature:issues",
}, {
// the enterprise plan is the associated plan of the draft pull requests feature
User: "plan:enterprise",
Relation: "associated_plan",
Object: "feature:draft_prs",
}, {
// the enterprise plan is the associated plan of the SAML Single Sign-on feature
User: "plan:enterprise",
Relation: "associated_plan",
Object: "feature:sso",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Writes = new List<ClientTupleKey>() {
// the free plan is the associated plan of the issues feature
new() {
User = "plan:free",
Relation = "associated_plan",
Object = "feature:issues"
},
// the team plan is the associated plan of the issues feature
new() {
User = "plan:team",
Relation = "associated_plan",
Object = "feature:issues"
},
// the team plan is the associated plan of the draft pull requests feature
new() {
User = "plan:team",
Relation = "associated_plan",
Object = "feature:draft_prs"
},
// the enterprise plan is the associated plan of the issues feature
new() {
User = "plan:enterprise",
Relation = "associated_plan",
Object = "feature:issues"
},
// the enterprise plan is the associated plan of the draft pull requests feature
new() {
User = "plan:enterprise",
Relation = "associated_plan",
Object = "feature:draft_prs"
},
// the enterprise plan is the associated plan of the SAML Single Sign-on feature
new() {
User = "plan:enterprise",
Relation = "associated_plan",
Object = "feature:sso"
}
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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(
# the free plan is the associated plan of the issues feature
user="plan:free",
relation="associated_plan",
object="feature:issues",
),
ClientTuple(
# the team plan is the associated plan of the issues feature
user="plan:team",
relation="associated_plan",
object="feature:issues",
),
ClientTuple(
# the team plan is the associated plan of the draft pull requests feature
user="plan:team",
relation="associated_plan",
object="feature:draft_prs",
),
ClientTuple(
# the enterprise plan is the associated plan of the issues feature
user="plan:enterprise",
relation="associated_plan",
object="feature:issues",
),
ClientTuple(
# the enterprise plan is the associated plan of the draft pull requests feature
user="plan:enterprise",
relation="associated_plan",
object="feature:draft_prs",
),
ClientTuple(
# the enterprise plan is the associated plan of the SAML Single Sign-on feature
user="plan:enterprise",
relation="associated_plan",
object="feature:sso",
),
],
)
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;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.writes(List.of(
// the free plan is the associated plan of the issues feature
new ClientTupleKey()
.user("plan:free")
.relation("associated_plan")
._object("feature:issues"),
// the team plan is the associated plan of the issues feature
new ClientTupleKey()
.user("plan:team")
.relation("associated_plan")
._object("feature:issues"),
// the team plan is the associated plan of the draft pull requests feature
new ClientTupleKey()
.user("plan:team")
.relation("associated_plan")
._object("feature:draft_prs"),
// the enterprise plan is the associated plan of the issues feature
new ClientTupleKey()
.user("plan:enterprise")
.relation("associated_plan")
._object("feature:issues"),
// the enterprise plan is the associated plan of the draft pull requests feature
new ClientTupleKey()
.user("plan:enterprise")
.relation("associated_plan")
._object("feature:draft_prs"),
// the enterprise plan is the associated plan of the SAML Single Sign-on feature
new ClientTupleKey()
.user("plan:enterprise")
.relation("associated_plan")
._object("feature:sso")
));
var response = fgaClient.write(body, options).get();
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
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" : [{"_description":"the free plan is the associated plan of the issues feature","user":"plan:free","relation":"associated_plan","object":"feature:issues"},{"_description":"the team plan is the associated plan of the issues feature","user":"plan:team","relation":"associated_plan","object":"feature:issues"},{"_description":"the team plan is the associated plan of the draft pull requests feature","user":"plan:team","relation":"associated_plan","object":"feature:draft_prs"},{"_description":"the enterprise plan is the associated plan of the issues feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:issues"},{"_description":"the enterprise plan is the associated plan of the draft pull requests feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:draft_prs"},{"_description":"the enterprise plan is the associated plan of the SAML Single Sign-on feature","user":"plan:enterprise","relation":"associated_plan","object":"feature:sso"}] }, "authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"}'
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:free associated_plan feature:issues
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:team associated_plan feature:issues
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:team associated_plan feature:draft_prs
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:enterprise associated_plan feature:issues
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:enterprise associated_plan feature:draft_prs
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:enterprise associated_plan feature:sso
write([
// the free plan is the associated plan of the issues feature
{
"user":"plan:free",
"relation":"associated_plan",
"object":"feature:issues"
},
// the team plan is the associated plan of the issues feature
{
"user":"plan:team",
"relation":"associated_plan",
"object":"feature:issues"
},
// the team plan is the associated plan of the draft pull requests feature
{
"user":"plan:team",
"relation":"associated_plan",
"object":"feature:draft_prs"
},
// the enterprise plan is the associated plan of the issues feature
{
"user":"plan:enterprise",
"relation":"associated_plan",
"object":"feature:issues"
},
// the enterprise plan is the associated plan of the draft pull requests feature
{
"user":"plan:enterprise",
"relation":"associated_plan",
"object":"feature:draft_prs"
},
// the enterprise plan is the associated plan of the SAML Single Sign-on feature
{
"user":"plan:enterprise",
"relation":"associated_plan",
"object":"feature:sso"
}
], authorization_model_id="01HVMMBCMGZNT3SED4Z17ECXCA")
The relations between the plans and the organizations are as follows:
- Node.js
- Go
- .NET
- Python
- Java
- curl
- CLI
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
await fgaClient.write({
writes: [
// the Alpha Beta Gamma organization is a subscriber of the free plan
{"_description":"the Alpha Beta Gamma organization is a subscriber of the free plan","user":"organization:alpha","relation":"subscriber","object":"plan:free"},
// the Bayer Water Supplies organization is a subscriber of the team plan
{"_description":"the Bayer Water Supplies organization is a subscriber of the team plan","user":"organization:bayer","relation":"subscriber","object":"plan:team"},
// the Cups and Dishes organization is a subscriber of the enterprise plan
{"_description":"the Cups and Dishes organization is a subscriber of the enterprise plan","user":"organization:cups","relation":"subscriber","object":"plan:enterprise"}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
{
// the Alpha Beta Gamma organization is a subscriber of the free plan
User: "organization:alpha",
Relation: "subscriber",
Object: "plan:free",
}, {
// the Bayer Water Supplies organization is a subscriber of the team plan
User: "organization:bayer",
Relation: "subscriber",
Object: "plan:team",
}, {
// the Cups and Dishes organization is a subscriber of the enterprise plan
User: "organization:cups",
Relation: "subscriber",
Object: "plan:enterprise",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Writes = new List<ClientTupleKey>() {
// the Alpha Beta Gamma organization is a subscriber of the free plan
new() {
User = "organization:alpha",
Relation = "subscriber",
Object = "plan:free"
},
// the Bayer Water Supplies organization is a subscriber of the team plan
new() {
User = "organization:bayer",
Relation = "subscriber",
Object = "plan:team"
},
// the Cups and Dishes organization is a subscriber of the enterprise plan
new() {
User = "organization:cups",
Relation = "subscriber",
Object = "plan:enterprise"
}
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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(
# the Alpha Beta Gamma organization is a subscriber of the free plan
user="organization:alpha",
relation="subscriber",
object="plan:free",
),
ClientTuple(
# the Bayer Water Supplies organization is a subscriber of the team plan
user="organization:bayer",
relation="subscriber",
object="plan:team",
),
ClientTuple(
# the Cups and Dishes organization is a subscriber of the enterprise plan
user="organization:cups",
relation="subscriber",
object="plan:enterprise",
),
],
)
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;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.writes(List.of(
// the Alpha Beta Gamma organization is a subscriber of the free plan
new ClientTupleKey()
.user("organization:alpha")
.relation("subscriber")
._object("plan:free"),
// the Bayer Water Supplies organization is a subscriber of the team plan
new ClientTupleKey()
.user("organization:bayer")
.relation("subscriber")
._object("plan:team"),
// the Cups and Dishes organization is a subscriber of the enterprise plan
new ClientTupleKey()
.user("organization:cups")
.relation("subscriber")
._object("plan:enterprise")
));
var response = fgaClient.write(body, options).get();
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
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" : [{"_description":"the Alpha Beta Gamma organization is a subscriber of the free plan","user":"organization:alpha","relation":"subscriber","object":"plan:free"},{"_description":"the Bayer Water Supplies organization is a subscriber of the team plan","user":"organization:bayer","relation":"subscriber","object":"plan:team"},{"_description":"the Cups and Dishes organization is a subscriber of the enterprise plan","user":"organization:cups","relation":"subscriber","object":"plan:enterprise"}] }, "authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"}'
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA organization:alpha subscriber plan:free
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA organization:bayer subscriber plan:team
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA organization:cups subscriber plan:enterprise
write([
// the Alpha Beta Gamma organization is a subscriber of the free plan
{
"user":"organization:alpha",
"relation":"subscriber",
"object":"plan:free"
},
// the Bayer Water Supplies organization is a subscriber of the team plan
{
"user":"organization:bayer",
"relation":"subscriber",
"object":"plan:team"
},
// the Cups and Dishes organization is a subscriber of the enterprise plan
{
"user":"organization:cups",
"relation":"subscriber",
"object":"plan:enterprise"
}
], authorization_model_id="01HVMMBCMGZNT3SED4Z17ECXCA")
The relations between the organizations and the users are as follows:
- Node.js
- Go
- .NET
- Python
- Java
- curl
- CLI
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
await fgaClient.write({
writes: [
// anne is a member of the Alpha Beta Gamma organization
{"_description":"anne is a member of the Alpha Beta Gamma organization","user":"user:anne","relation":"member","object":"organization:alpha"},
// beth is a member of the Bayer Water Supplies
{"_description":"beth is a member of the Bayer Water Supplies","user":"user:beth","relation":"member","object":"organization:bayer"},
// charles is a member of the Cups and Dishes organization
{"_description":"charles is a member of the Cups and Dishes organization","user":"user:charles","relation":"member","object":"organization:cups"}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientWriteOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
{
// anne is a member of the Alpha Beta Gamma organization
User: "user:anne",
Relation: "member",
Object: "organization:alpha",
}, {
// beth is a member of the Bayer Water Supplies
User: "user:beth",
Relation: "member",
Object: "organization:bayer",
}, {
// charles is a member of the Cups and Dishes organization
User: "user:charles",
Relation: "member",
Object: "organization:cups",
},
},
}
data, err := fgaClient.Write(context.Background()).
Body(body).
Options(options).
Execute()
if err != nil {
// .. Handle error
}
_ = data // use the response
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientWriteOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientWriteRequest() {
Writes = new List<ClientTupleKey>() {
// anne is a member of the Alpha Beta Gamma organization
new() {
User = "user:anne",
Relation = "member",
Object = "organization:alpha"
},
// beth is a member of the Bayer Water Supplies
new() {
User = "user:beth",
Relation = "member",
Object = "organization:bayer"
},
// charles is a member of the Cups and Dishes organization
new() {
User = "user:charles",
Relation = "member",
Object = "organization:cups"
}
},
};
var response = await fgaClient.Write(body, options);
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 is a member of the Alpha Beta Gamma organization
user="user:anne",
relation="member",
object="organization:alpha",
),
ClientTuple(
# beth is a member of the Bayer Water Supplies
user="user:beth",
relation="member",
object="organization:bayer",
),
ClientTuple(
# charles is a member of the Cups and Dishes organization
user="user:charles",
relation="member",
object="organization:cups",
),
],
)
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;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientWriteOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientWriteRequest()
.writes(List.of(
// anne is a member of the Alpha Beta Gamma organization
new ClientTupleKey()
.user("user:anne")
.relation("member")
._object("organization:alpha"),
// beth is a member of the Bayer Water Supplies
new ClientTupleKey()
.user("user:beth")
.relation("member")
._object("organization:bayer"),
// charles is a member of the Cups and Dishes organization
new ClientTupleKey()
.user("user:charles")
.relation("member")
._object("organization:cups")
));
var response = fgaClient.write(body, options).get();
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
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" : [{"_description":"anne is a member of the Alpha Beta Gamma organization","user":"user:anne","relation":"member","object":"organization:alpha"},{"_description":"beth is a member of the Bayer Water Supplies","user":"user:beth","relation":"member","object":"organization:bayer"},{"_description":"charles is a member of the Cups and Dishes organization","user":"user:charles","relation":"member","object":"organization:cups"}] }, "authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA"}'
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:anne member organization:alpha
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:beth member organization:bayer
fga tuple write --store-id=${FGA_STORE_ID} --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:charles member organization:cups
write([
// anne is a member of the Alpha Beta Gamma organization
{
"user":"user:anne",
"relation":"member",
"object":"organization:alpha"
},
// beth is a member of the Bayer Water Supplies
{
"user":"user:beth",
"relation":"member",
"object":"organization:bayer"
},
// charles is a member of the Cups and Dishes organization
{
"user":"user:charles",
"relation":"member",
"object":"organization:cups"
}
], authorization_model_id="01HVMMBCMGZNT3SED4Z17ECXCA")
So far you have given OpenFGA a representation of the current state of your system's relationships. You will keep iterating and updating the authorization model until the results of the queries match what you expect.
In production, it is highly recommended to use unique, immutable identifiers. Names are used in this article to make it easier to read and follow. For example, the relationship tuple indicating that anne is a member of organization:alpha could be written as:
- user: user:2b4840f2-7c9c-42c8-9329-911002051524
- relation: member
- object: project:52e529c6-c571-4d5c-b78a-bc574cf98b54
Verification
Now that you have some data, you can start using it to ask is ${USER} related to ${OBJECT} as ${RELATION}?
First, you will check if anne
is a member of organization:alpha
. This is one of the relationship tuples you previously added, you will make sure OpenFGA can detect a relation in this case.
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'user:anne',
relation: 'member',
object: 'organization:alpha',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "user:anne",
Relation: "member",
Object: "organization:alpha",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: true }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "user:anne",
Relation = "member",
Object = "organization:alpha",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = true
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="user:anne",
relation="member",
object="organization:alpha",
)
response = await fga_client.check(body, options)
# response.allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("user:anne")
.relation("member")
._object("organization:alpha");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:anne member organization:alpha
# Response: {"allowed":true}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"user:anne","relation":"member","object":"organization:alpha"}}'
# Response: {"allowed":true}
check(
user = "user:anne", // check if the user `user:anne`
relation = "member", // has an `member` relation
object = "organization:alpha", // with the object `organization:alpha`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: true
Querying for relationship tuples that you fed into OpenFGA earlier should work, try a few before proceeding to make sure everything is working well.
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'user:anne',
relation: 'member',
object: 'organization:bayer',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = false
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "user:anne",
Relation: "member",
Object: "organization:bayer",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: false }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "user:anne",
Relation = "member",
Object = "organization:bayer",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = false
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="user:anne",
relation="member",
object="organization:bayer",
)
response = await fga_client.check(body, options)
# response.allowed = false
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("user:anne")
.relation("member")
._object("organization:bayer");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:anne member organization:bayer
# Response: {"allowed":false}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"user:anne","relation":"member","object":"organization:bayer"}}'
# Response: {"allowed":false}
check(
user = "user:anne", // check if the user `user:anne`
relation = "member", // has an `member` relation
object = "organization:bayer", // with the object `organization:bayer`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: false
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'organization:bayer',
relation: 'subscriber',
object: 'plan:team',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "organization:bayer",
Relation: "subscriber",
Object: "plan:team",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: true }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "organization:bayer",
Relation = "subscriber",
Object = "plan:team",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = true
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="organization:bayer",
relation="subscriber",
object="plan:team",
)
response = await fga_client.check(body, options)
# response.allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("organization:bayer")
.relation("subscriber")
._object("plan:team");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA organization:bayer subscriber plan:team
# Response: {"allowed":true}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"organization:bayer","relation":"subscriber","object":"plan:team"}}'
# Response: {"allowed":true}
check(
user = "organization:bayer", // check if the user `organization:bayer`
relation = "subscriber", // has an `subscriber` relation
object = "plan:team", // with the object `plan:team`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: true
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'plan:free',
relation: 'associated_plan',
object: 'feature:issues',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "plan:free",
Relation: "associated_plan",
Object: "feature:issues",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: true }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "plan:free",
Relation = "associated_plan",
Object = "feature:issues",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = true
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="plan:free",
relation="associated_plan",
object="feature:issues",
)
response = await fga_client.check(body, options)
# response.allowed = true
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("plan:free")
.relation("associated_plan")
._object("feature:issues");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA plan:free associated_plan feature:issues
# Response: {"allowed":true}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"plan:free","relation":"associated_plan","object":"feature:issues"}}'
# Response: {"allowed":true}
check(
user = "plan:free", // check if the user `plan:free`
relation = "associated_plan", // has an `associated_plan` relation
object = "feature:issues", // with the object `feature:issues`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: true
03. Updating the authorization model
You are working towards OpenFGA returning the correct answer when you query whether anne
has access
to feature:issues
. It won't work yet, but you will keep updating your configuration to reach that goal.
To start, try to run that query on is anne related to feature:issues as access?
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'user:anne',
relation: 'access',
object: 'feature:issues',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = undefined
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "user:anne",
Relation: "access",
Object: "feature:issues",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: undefined }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "user:anne",
Relation = "access",
Object = "feature:issues",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = undefined
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="user:anne",
relation="access",
object="feature:issues",
)
response = await fga_client.check(body, options)
# response.allowed = undefined
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("user:anne")
.relation("access")
._object("feature:issues");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:anne access feature:issues
# Response: {"allowed":undefined}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"user:anne","relation":"access","object":"feature:issues"}}'
# Response: {"allowed":undefined}
check(
user = "user:anne", // check if the user `user:anne`
relation = "access", // has an `access` relation
object = "feature:issues", // with the object `feature:issues`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: undefined
The OpenFGA service is returning that the query tuple is invalid. That is because you are asking for relation as access
, but that relation is not in the configuration of the feature
type!
Add it now. Like so:
- DSL
- JSON
type feature
relations
define associated_plan: [plan]
define access: [user]
{
"type": "feature",
"relations": {
"associated_plan": {
"this": {}
},
"access": {
"this": {}
}
},
"metadata": {
"relations": {
"associated_plan": {
"directly_related_user_types": [
{
"type": "plan"
}
]
},
"access": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
}
In this tutorial, you will find the phrases direct relationship and implied relationship.
A direct relationship R between user X and object Y means the relationship tuple (user=X, relation=R, object=Y) exists, and the OpenFGA authorization model for that relation allows this direct relationship (by use of direct relationship type restrictions).
An implied relationship R exists between user X and object Y if user X is related to an object Z that is in direct or implied relationship with object Y, and the OpenFGA authorization model allows it.
The resulting updated configuration would be:
- DSL
- JSON
model
schema 1.1
type user
type feature
relations
define associated_plan: [plan]
define access: [user]
type plan
relations
define subscriber: [organization]
type organization
relations
define member: []
{
"schema_version": "1.1",
"type_definitions": [
{
"type": "user"
},
{
"type": "feature",
"relations": {
"associated_plan": {
"this": {}
},
"access": {
"this": {}
}
},
"metadata": {
"relations": {
"associated_plan": {
"directly_related_user_types": [
{
"type": "plan"
}
]
},
"access": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
},
{
"type": "plan",
"relations": {
"subscriber": {
"this": {}
}
},
"metadata": {
"relations": {
"subscriber": {
"directly_related_user_types": [
{
"type": "organization"
}
]
}
}
}
},
{
"type": "organization",
"relations": {
"member": {
"this": {}
}
},
"metadata": {
"relations": {
"subscriber": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
}
]
}
Adding modeling pattern of parent-child objects
Now we can ask the following query: is anne related to feature:issues as access?
again.
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'user:anne',
relation: 'access',
object: 'feature:issues',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = false
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional, can be overridden per request
})
if err != nil {
// .. Handle error
}
}
options := ClientCheckOptions{
AuthorizationModelId: PtrString("01HVMMBCMGZNT3SED4Z17ECXCA"),
}
body := ClientCheckRequest{
User: "user:anne",
Relation: "access",
Object: "feature:issues",
}
data, err := fgaClient.Check(context.Background()).
Body(body).
Options(options).
Execute()
// data = { allowed: false }
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
namespace Example;
class Example {
public static async Task Main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
var configuration = new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"), ?? "http://localhost:8080", // required, e.g. https://api.fga.example
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"), // optional, not needed for `CreateStore` and `ListStores`, required before calling for all other methods
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional, can be overridden per request
};
var fgaClient = new OpenFgaClient(configuration);
}
}
var options = new ClientCheckOptions {
AuthorizationModelId = "01HVMMBCMGZNT3SED4Z17ECXCA",
};
var body = new ClientCheckRequest {
User = "user:anne",
Relation = "access",
Object = "feature:issues",
};
var response = await fgaClient.Check(body, options);
// response.Allowed = false
Initialize the SDK
# ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import asyncio
import os
import json
from openfga_sdk.client import ClientConfiguration, OpenFgaClient
async def main():
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 = ClientCheckRequest(
user="user:anne",
relation="access",
object="feature:issues",
)
response = await fga_client.check(body, options)
# response.allowed = false
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
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_AUTHORIZATION_MODEL_ID")); // Optional, can be overridden per request
var fgaClient = new OpenFgaClient(config);
}
}
var options = new ClientCheckOptions()
.authorizationModelId("01HVMMBCMGZNT3SED4Z17ECXCA");
var body = new ClientCheckRequest()
.user("user:anne")
.relation("access")
._object("feature:issues");
var response = fgaClient.check(body, options).get();
// response.getAllowed() = true
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
fga query check --store-id=$FGA_STORE_ID --model-id=01HVMMBCMGZNT3SED4Z17ECXCA user:anne access feature:issues
# Response: {"allowed":false}
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
Set FGA_API_URL according to the service you are using (e.g. https://api.fga.example)
curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \
-H "Authorization: Bearer $FGA_API_TOKEN" \ # Not needed if service does not require authorization
-H "content-type: application/json" \
-d '{"authorization_model_id": "01HVMMBCMGZNT3SED4Z17ECXCA", "tuple_key":{"user":"user:anne","relation":"access","object":"feature:issues"}}'
# Response: {"allowed":false}
check(
user = "user:anne", // check if the user `user:anne`
relation = "access", // has an `access` relation
object = "feature:issues", // with the object `feature:issues`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: false
So far so good. OpenFGA understood your query, but said that no relation exists. That is because according to the configuration provided so far, there is no access
relation between anne
and feature:issues
.
We can also try to query is organization:alpha related to feature:issues as access?
and we see that there is no relationship.
- Node.js
- Go
- .NET
- Python
- Java
- CLI
- curl
- Pseudocode
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});
// Run a check
const { allowed } = await fgaClient.check({
user: 'organization:alpha',
relation: 'access',
object: 'feature:issues',
}, {
authorization_model_id: '01HVMMBCMGZNT3SED4Z17ECXCA',
});
// allowed = false
Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
import (
"os"
. "github.com/openfga/go-sdk"
. "github.com/openfga/go-sdk/client"
)
func main() {
// Initialize the SDK with no auth - see "How to setup SDK client" for more options
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"), // required, e.g. https://api.fga.example
StoreId: os.Getenv(