Direct Access
This article describes how to grant a user access to an object in OpenFGA.
Granting access with relationship tuples is a core part of OpenFGA. Without relationship tuples, any checks_ will fail. You should use:
- authorization model to represent what relations are possible between the users and objects in the system
- relationship tuples to represent the facts about the relationships between users and objects in your system.
Before you start
Familiarize yourself with OpenFGA Concepts to understand how to develop a relationship tuple and authorization model.
Assume that you have the following authorization model.
You have a type called document
that can have a viewer
and/or an editor
.
You have a type called
document
that can have a viewer
and/or an editor
.- DSL
- JSON
model
schema 1.1
type user
type document
relations
define viewer: [user]
define editor: [user]
{
"schema_version": "1.1",
"type_definitions": [
{
"type": "user"
},
{
"type": "document",
"relations": {
"viewer": {
"this": {}
},
"editor": {
"this": {}
}
},
"metadata": {
"relations": {
"viewer": {
"directly_related_user_types": [
{
"type": "user"
}
]
},
"editor": {
"directly_related_user_types": [
{
"type": "user"
}
]
}
}
}
}
]
}
In addition, you will need to know the following:
OpenFGA Concepts
- A Type: a class of objects that have similar characteristics
- A User: an entity in the system that can be related to an object
- A Relation: a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- An Object: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
- A Relationship Tuple: a grouping consisting of a user, a relation and an object stored in OpenFGA
Step By Step
For an application to understand that user x has access to document y, it must provide OpenFGA that information with relationship tuples. Each relationship tuple has three basic parameters: a user, a relation and an object.
01. Create A Relationship Tuple
Below, you'll add a relationship tuple to indicate that bob
is an editor
of document:meeting_notes.doc
by adding the following:
- 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: [
{"user":"user:bob","relation":"editor","object":"document:meeting_notes.doc"}
],
}, {
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{
{
User: "user:bob",
Relation: "editor",
Object: "document:meeting_notes.doc",
},
},
}
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>() {
new() {
User = "user:bob",
Relation = "editor",
Object = "document:meeting_notes.doc"
}
},
};
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(
user="user:bob",
relation="editor",
object="document:meeting_notes.doc",
),
],
)
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(
new ClientTupleKey()
.user("user:bob")
.relation("editor")
._object("document:meeting_notes.doc")
));
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" : [{"user":"user:bob","relation":"editor","object":"document:meeting_notes.doc"}] }, "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:bob editor document:meeting_notes.doc
write([
{
"user":"user:bob",
"relation":"editor",
"object":"document:meeting_notes.doc"
}
], authorization_model_id="01HVMMBCMGZNT3SED4Z17ECXCA")
02. Check That The Relationship Exists
Once you add that relationship tuple to OpenFGA, you can check if the relationship is valid by asking if bob is an editor of document:meeting_notes.doc:
- 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:bob',
relation: 'editor',
object: 'document:meeting_notes.doc',
}, {
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:bob",
Relation: "editor",
Object: "document:meeting_notes.doc",
}
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:bob",
Relation = "editor",
Object = "document:meeting_notes.doc",
};
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:bob",
relation="editor",
object="document:meeting_notes.doc",
)
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:bob")
.relation("editor")
._object("document:meeting_notes.doc");
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:bob editor document:meeting_notes.doc
# 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:bob","relation":"editor","object":"document:meeting_notes.doc"}}'
# Response: {"allowed":true}
check(
user = "user:bob", // check if the user `user:bob`
relation = "editor", // has an `editor` relation
object = "document:meeting_notes.doc", // with the object `document:meeting_notes.doc`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: true
Checking whether bob
is an viewer
of document:meeting_notes.doc
returns false because that relationship tuple does not exist in OpenFGA yet.
- 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:bob',
relation: 'viewer',
object: 'document:meeting_notes.doc',
}, {
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:bob",
Relation: "viewer",
Object: "document:meeting_notes.doc",
}
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:bob",
Relation = "viewer",
Object = "document:meeting_notes.doc",
};
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:bob",
relation="viewer",
object="document:meeting_notes.doc",
)
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:bob")
.relation("viewer")
._object("document:meeting_notes.doc");
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:bob viewer document:meeting_notes.doc
# 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:bob","relation":"viewer","object":"document:meeting_notes.doc"}}'
# Response: {"allowed":false}
check(
user = "user:bob", // check if the user `user:bob`
relation = "viewer", // has an `viewer` relation
object = "document:meeting_notes.doc", // with the object `document:meeting_notes.doc`
authorization_id = "01HVMMBCMGZNT3SED4Z17ECXCA"
);
Reply: false
When creating relationship tuples for OpenFGA, use unique ids for each object and user within your application domain. We're using first names and simple ids to as an easy-to-follow example.