This is community-driven Ruby SDK for OpenFGA. It provides a wrapper around the OpenFGA API definition.
- About OpenFGA
- Resources
- Installation
- Getting Started
- Contributing
- License
OpenFGA is an open source Fine-Grained Authorization solution inspired by Google's Zanzibar paper. It was created by the FGA team at Auth0 based on Auth0 Fine-Grained Authorization (FGA), available under a permissive license (Apache-2) and welcomes community contributions.
OpenFGA is designed to make it easy for application builders to model their permission layer, and to add and integrate fine-grained authorization into their applications. OpenFGA’s design is optimized for reliability and low latency at a high scale.
- OpenFGA Documentation
- OpenFGA API Documentation
- X
- OpenFGA Community
- Zanzibar Academy
- Google's Zanzibar Paper (2019)
To install:
gem install openfga
Alternatively, you can add it to your Gemfile:
gem 'openfga'
Then run bundle install to install the gem.
To use in your code, require the gem and create the configuration:
require 'openfga'
sdk_config = {
api_url: 'http://localhost:8080'
}
Learn how to initialize your SDK
We strongly recommend you initialize the SdkClient only once and then re-use it
throughout your app, otherwise you will incur the cost of having to re-initialize
multiple times or at every request, the cost of reduced connection pooling and
re-use, and would be particularly costly in the client credentials flow,
as that flow will be performed on every request.
require 'openfga'
def main
# Initialize the fga_client
fga_client = OpenFga::SdkClient.new(
api_url: ENV['FGA_API_URL'], # required
store_id: ENV['FGA_STORE_ID'], # optional, not needed when calling `create_store` or `list_stores`
authorization_model_id: ENV['FGA_MODEL_ID'] # optional, can be overridden per request
)
api_response = fga_client.read_authorization_models()
return api_response
endrequire 'openfga'
def main
# Initialize the fga_client
fga_client = OpenFga::SdkClient.new(
api_url: ENV['FGA_API_URL'], # required
store_id: ENV['FGA_STORE_ID'], # optional, not needed when calling `create_store` or `list_stores`
authorization_model_id: ENV['FGA_MODEL_ID'], # optional, can be overridden per request
credentials: {
method: :api_token,
api_token: ENV['FGA_API_TOKEN']
}
)
api_response = fga_client.read_authorization_models()
return api_response
endrequire 'openfga'
def main
# Initialize the fga_client
fga_client = OpenFga::SdkClient.new(
api_url: ENV['FGA_API_URL'], # required
store_id: ENV['FGA_STORE_ID'], # optional, not needed when calling `create_store` or `list_stores`
authorization_model_id: ENV['FGA_MODEL_ID'], # optional, can be overridden per request
credentials: {
method: :client_credentials,
api_token_issuer: ENV['FGA_API_TOKEN_ISSUER'],
api_audience: ENV['FGA_API_AUDIENCE'],
client_id: ENV['FGA_CLIENT_ID'],
client_secret: ENV['FGA_CLIENT_SECRET']
}
)
api_response = fga_client.read_authorization_models()
return api_response
endYou can send custom headers on a per-request basis by using the opts parameter. Per-request headers will override any default headers with the same name.
require 'openfga'
def main
# Initialize the fga_client
fga_client = OpenFga::SdkClient.new(
api_url: ENV['FGA_API_URL'],
store_id: ENV['FGA_STORE_ID'],
authorization_model_id: ENV['FGA_MODEL_ID']
)
# Add custom headers to a specific request
result = fga_client.check(
user: "user:anne",
relation: "viewer",
object: "document:roadmap",
opts: {
header_params: {
"X-Request-ID": "123e4567-e89b-12d3-a456-426614174000",
"X-Custom-Header": "custom-value"
}
}
)
return result
endYou need your store id to call the OpenFGA API (unless it is to call the CreateStore or ListStores methods).
If your server is configured with authentication enabled, you also need to have your credentials ready.
Get a paginated list of stores.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
options = { page_size: 25, continuation_token: "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==" }
response = fga_client.list_stores(options)
# response = ListStoresResponse(...)
# response.stores = [Store(id: "01FQH7V8BEG3GPQW93KTRFR8JB", name: "FGA Demo Store", created_at: "2022-01-01T00:00:00.000Z", updated_at: "2022-01-01T00:00:00.000Z")]Create and initialize a store.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
name: "FGA Demo Store"
}
response = fga_client.create_store(body)
# response.id = "01FQH7V8BEG3GPQW93KTRFR8JB"Get information about the current store.
Requires a client initialized with a store_id
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
response = fga_client.get_store()
# response = OpenFga::GetStoreResponse(id: "01FQH7V8BEG3GPQW93KTRFR8JB", name: "FGA Demo Store", created_at: "2022-01-01T00:00:00.000Z", updated_at: "2022-01-01T00:00:00.000Z")Delete a store.
Requires a client initialized with a store_id
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
response = fga_client.delete_store()
# nil Read all authorization models in the store.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
options = { page_size: 25, continuation_token: "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==" }
response = fga_client.read_authorization_models(options)
# response.authorization_models = [OpenFga::AuthorizationModel(id: '01GXSA8YR785C4FYS3C0RTG7B1', schema_version: '1.1', type_definitions: type_definitions[...]), OpenFga::AuthorizationModel(id: '01GXSBM5PVYHCJNRNKXMB4QZTW', schema_version: '1.1', type_definitions: type_definitions[...])]Create a new authorization model.
Note: To learn how to build your authorization model, check the Docs at https://openfga.dev/docs.
Learn more about the OpenFGA configuration language.
You can use the OpenFGA Syntax Transformer to convert between the friendly DSL and the JSON authorization model.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
type_definitions = [
{
type: "user"
},
{
type: "document",
relations: {
writer: { this: {} },
viewer: {
union: {
child: [
{ this: {} },
{
computed_userset: {
object: "",
relation: "writer"
}
}
]
}
}
},
metadata: {
relations: {
writer: {
directly_related_user_types: [
{ type: "user" },
{ type: "user", condition: "ViewCountLessThan200" }
]
},
viewer: {
directly_related_user_types: [
{ type: "user" }
]
}
}
}
}
]
conditions = {
ViewCountLessThan200: {
name: "ViewCountLessThan200",
expression: "ViewCount < 200",
parameters: {
ViewCount: {
type_name: "TYPE_NAME_INT"
},
Type: {
type_name: "TYPE_NAME_STRING"
},
Name: {
type_name: "TYPE_NAME_STRING"
}
}
}
}
body = {
schema_version: "1.1",
type_definitions: type_definitions,
conditions: conditions
}
response = fga_client.write_authorization_model(body)
# response.authorization_model_id = "01GXSA8YR785C4FYS3C0RTG7B1"Read a particular authorization model.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
options = {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
response = fga_client.read_authorization_model(options)
# response.authorization_model = OpenFga::AuthorizationModel(id: '01GXSA8YR785C4FYS3C0RTG7B1', schema_version: '1.1', type_definitions: type_definitions[...])Reads the list of historical relationship tuple writes and deletes.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
options = {
page_size: 25,
continuation_token: "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="
}
response = fga_client.read_changes(options)
# response.continuation_token = ...
# response.changes = [TupleChange(tuple_key: TupleKey(object: "...", relation: "...", user: "..."), operation: TupleOperation("TUPLE_OPERATION_WRITE"), timestamp: ...)]Reads the relationship tuples stored in the database. It does not evaluate nor exclude invalid tuples according to the authorization model.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
# Find if a relationship tuple stating that a certain user is a viewer of certain document
body = {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
response = fga_client.read(body)
# response = ReadResponse(tuples: [Tuple(key: TupleKey(user: "...", relation: "...", object: "..."), timestamp: ...)])# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
# Find all relationship tuples where a certain user has a relationship as any relation to a certain document
body = {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
response = fga_client.read(body)
# response = ReadResponse(tuples: [Tuple(key: TupleKey(user: "...", relation: "...", object: "..."), timestamp: ...)])# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
# Find all relationship tuples where a certain user is a viewer of any document
body = {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "document:"
}
response = fga_client.read(body)
# response = ReadResponse(tuples: [Tuple(key: TupleKey(user: "...", relation: "...", object: "..."), timestamp: ...)])# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
# Find all relationship tuples where any user has a relationship as any relation with a particular document
body = {
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
response = fga_client.read(body)
# response = ReadResponse(tuples: [Tuple(key: TupleKey(user: "...", relation: "...", object: "..."), timestamp: ...)])# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
# Read all stored relationship tuples
body = {}
response = fga_client.read(body)
# response = ReadResponse(tuples: [Tuple(key: TupleKey(user: "...", relation: "...", object: "..."), timestamp: ...)])Create and/or delete relationship tuples to update the system state.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
writes: {
tuple_keys: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "doc:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
},
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "doc:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5"
}
]
},
deletes: {
tuple_keys: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "writer",
object: "doc:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
]
},
opts: {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
}
response = fga_client.write(body)Check if a user has a particular relation with an object.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "writer",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
opts: {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1",
context: {
ViewCount: 100
}
}
}
response = fga_client.check(body)
# response.allowed = truePerforms multiple relationship checks in a single batch request.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
checks: [
{
tuple_key: {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
},
correlation_id: "check-1",
contextual_tuples: {
tuple_keys: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "editor",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
]
},
context: {
ViewCount: 100
}
},
{
tuple_key: {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "admin",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
},
correlation_id: "check-2"
}
],
opts: {
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1",
max_parallel_requests: 10,
max_batch_size: 50
}
}
response = fga_client.batch_check(body)
# response.result contains the results mapped by correlation_idExpands the relationships in userset tree format.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
relation: "viewer",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
opts: {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
}
response = fga_client.expand(body)
# response = ExpandResponse(tree: UsersetTree(root: Node(name: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer", leaf: Leaf(users: Users(users: ["user:81684243-9356-4421-8fbf-a4f8d36aa31b", "user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"])))))List the objects of a particular type a user has access to.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
type: "document",
contextual_tuples: {
tuple_keys: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "writer",
object: "document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5"
}
]
},
context: {
ViewCount: 100
},
opts: {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
}
response = fga_client.list_objects(body)
# response.objects = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]List the users who have a certain relation to a particular type.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
relation: "can_read",
object: "document:2021-budget",
user_filters: [
{ type: "user" },
{ type: "team", relation: "member" }
],
contextual_tuples: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "editor",
object: "folder:product"
},
{
user: "folder:product",
relation: "parent",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"
}
],
context: {},
opts: {
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
}
response = fga_client.list_users(body)
# response.users = [{object: {type: "user", id: "81684243-9356-4421-8fbf-a4f8d36aa31b"}}, {userset: {type: "user"}}, ...]Read assertions for a particular authorization model.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
options = {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
response = fga_client.read_assertions(options)Update the assertions for a particular authorization model.
# Initialize the fga_client
# fga_client = OpenFga::SdkClient.new(configuration)
body = {
assertions: [
{
user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation: "viewer",
object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
expectation: true
}
],
opts: {
# You can rely on the model id set in the configuration or override it for this specific request
authorization_model_id: "01GXSA8YR785C4FYS3C0RTG7B1"
}
}
response = fga_client.write_assertions(body)| Class | Method | HTTP request | Description |
|---|---|---|---|
| OpenFgaApi | batch_check | POST /stores/{store_id}/batch-check | Send a list of `check` operations in a single request |
| OpenFgaApi | check | POST /stores/{store_id}/check | Check whether a user is authorized to access an object |
| OpenFgaApi | create_store | POST /stores | Create a store |
| OpenFgaApi | delete_store | DELETE /stores/{store_id} | Delete a store |
| OpenFgaApi | expand | POST /stores/{store_id}/expand | Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship |
| OpenFgaApi | get_store | GET /stores/{store_id} | Get a store |
| OpenFgaApi | list_objects | POST /stores/{store_id}/list-objects | List all objects of the given type that the user has a relation with |
| OpenFgaApi | list_stores | GET /stores | List all stores |
| OpenFgaApi | list_users | POST /stores/{store_id}/list-users | List the users matching the provided filter who have a certain relation to a particular type. |
| OpenFgaApi | read | POST /stores/{store_id}/read | Get tuples from the store that matches a query, without following userset rewrite rules |
| OpenFgaApi | read_assertions | GET /stores/{store_id}/assertions/{authorization_model_id} | Read assertions for an authorization model ID |
| OpenFgaApi | read_authorization_model | GET /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model |
| OpenFgaApi | read_authorization_models | GET /stores/{store_id}/authorization-models | Return all the authorization models for a particular store |
| OpenFgaApi | read_changes | GET /stores/{store_id}/changes | Return a list of all the tuple changes |
| OpenFgaApi | write | POST /stores/{store_id}/write | Add or delete tuples from the store |
| OpenFgaApi | write_assertions | PUT /stores/{store_id}/assertions/{authorization_model_id} | Upsert assertions for an authorization model ID |
| OpenFgaApi | write_authorization_model | POST /stores/{store_id}/authorization-models | Create a new authorization model |
- AbortedMessageResponse
- Any
- Assertion
- AssertionTupleKey
- AuthErrorCode
- AuthorizationModel
- BatchCheckItem
- BatchCheckRequest
- BatchCheckResponse
- BatchCheckSingleResult
- CheckError
- CheckRequest
- CheckRequestTupleKey
- CheckResponse
- Computed
- Condition
- ConditionMetadata
- ConditionParamTypeRef
- ConsistencyPreference
- ContextualTupleKeys
- CreateStoreRequest
- CreateStoreResponse
- Difference
- ErrorCode
- ExpandRequest
- ExpandRequestTupleKey
- ExpandResponse
- FgaObject
- ForbiddenResponse
- GetStoreResponse
- InternalErrorCode
- InternalErrorMessageResponse
- Leaf
- ListObjectsRequest
- ListObjectsResponse
- ListStoresResponse
- ListUsersRequest
- ListUsersResponse
- Metadata
- Node
- Nodes
- NotFoundErrorCode
- NullValue
- ObjectRelation
- PathUnknownErrorMessageResponse
- ReadAssertionsResponse
- ReadAuthorizationModelResponse
- ReadAuthorizationModelsResponse
- ReadChangesResponse
- ReadRequest
- ReadRequestTupleKey
- ReadResponse
- RelationMetadata
- RelationReference
- RelationshipCondition
- SourceInfo
- Status
- Store
- Tuple
- TupleChange
- TupleKey
- TupleKeyWithoutCondition
- TupleOperation
- TupleToUserset
- TypeDefinition
- TypeName
- TypedWildcard
- UnauthenticatedResponse
- UnprocessableContentErrorCode
- UnprocessableContentMessageResponse
- User
- UserTypeFilter
- Users
- Userset
- UsersetTree
- UsersetTreeDifference
- UsersetTreeTupleToUserset
- UsersetUser
- Usersets
- ValidationErrorMessageResponse
- WriteAssertionsRequest
- WriteAuthorizationModelRequest
- WriteAuthorizationModelResponse
- WriteRequest
- WriteRequestDeletes
- WriteRequestWrites
See CONTRIBUTING for details.
This project is licensed under the Apache-2.0 license. See the LICENSE file for more info.