OpenFGA CLI
A cross-platform CLI to interact with an OpenFGA server
Table of Contents
- About OpenFGA
- Resources
- Installation
- Building from Source
- Usage
- Contributing
- License
About
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.
Resources
- OpenFGA Documentation
- OpenFGA API Documentation
- OpenFGA Discord Community
- Zanzibar Academy
- Google's Zanzibar Paper (2019)
Installation
Brew
brew install openfga/tap/fga
Linux (deb, rpm and apk) packages
Download the .deb, .rpm or .apk packages from the releases page.
Debian:
sudo apt install ./fga_<version>_linux_<arch>.deb
Fedora:
sudo dnf install ./fga_<version>_linux_<arch>.rpm
Alpine Linux:
sudo apk add --allow-untrusted ./fga_<version>_linux_<arch>.apk
Docker
docker pull openfga/cli; docker run -it openfga/cli
Go
note that the command will be named
cli
go install github.com/openfga/cli@latest
Manually
Download the pre-compiled binaries from the releases page.
Building from Source
Make sure you have Go 1.20 or later installed. See the Go downloads page.
-
Clone the repo to a local directory, and navigate to that directory:
git clone https://github.com/openfga/cli.git && cd cli
-
Then use the build command:
go build -o ./dist/fga main.go
or if you have
make
installed, just run:make build
-
Run the OpenFGA CLI with:
./dist/fga
Usage
Configuration
For any command that interacts with an OpenFGA server, these configuration values can be passed (where applicable)
Name | Flag | CLI | ~/.fga.yaml |
---|---|---|---|
API Url | --api-url |
FGA_API_URL |
api-url |
Shared Secret | --api-token |
FGA_API_TOKEN |
api-token |
Client ID | --client-id |
FGA_CLIENT_ID |
client-id |
Client Secret | --client-secret |
FGA_CLIENT_SECRET |
client-secret |
Token Issuer | --api-token-issuer |
FGA_API_TOKEN_ISSUER |
api-token-issuer |
Token Audience | --api-audience |
FGA_API_AUDIENCE |
api-audience |
Store ID | --store-id |
FGA_STORE_ID |
store-id |
Authorization Model ID | --model-id |
FGA_MODEL_ID |
model-id |
If you are authenticating with a shared secret, you should specify the API Token value. If you are authenticating using OAuth, you should specify the Client ID, Client Secret, API Audience and Token Issuer. For example:
# Note: This example is for Auth0 FGA
api-url: https://api.us1.fga.dev
client-id: 4Zb..UYjaHreLKOJuU8
client-secret: J3...2pBwiauD
api-audience: https://api.us1.fga.dev/
api-token-issuer: fga.us.auth0.com
store-id: 01H0H015178Y2V4CX10C2KGHF4
Commands
Stores
Description | command | parameters | example |
---|---|---|---|
Create a Store | create |
--name |
fga store create --name="FGA Demo Store" |
List Stores | list |
fga store list |
|
Get a Store | get |
--store-id |
fga store get --store-id=01H0H015178Y2V4CX10C2KGHF4 |
Delete a Store | delete |
--store-id |
fga store delete --store-id=01H0H015178Y2V4CX10C2KGHF4 |
Create Store
Command
fga store create
Parameters
--name
: Name of the store to be created. If themodel
parameter is specified, the model file name will be used as the default store name.--model
: File with the authorization model. Can be in JSON or OpenFGA format (optional).--format
: Authorization model input format. Can be "fga" or "json" (optional, defaults to the model file extension if present).
Example
fga store create --name "FGA Demo Store"
Response
{
"id": "01H0H015178Y2V4CX10C2KGHF4",
"name": "FGA Demo Store",
"created_at": "2023-05-19T16:10:07.637585677Z",
"updated_at": "2023-05-19T16:10:07.637585677Z"
}
fga store create --model Model.fga
Response
{
"store": {
"id":"01H6H9CNQRP2TVCFR7899XGNY8",
"name":"Model",
"created_at":"2023-07-29T16:58:28.984402Z",
"updated_at":"2023-07-29T16:58:28.984402Z"
},
"model": {
"authorization_model_id":"01H6H9CNQV36Y9WS1RJGRN8D06"
}
}
To automatically set the created store id as an environment variable that will then be used by the CLI, you can use the following command:
export FGA_STORE_ID=$(fga store create --model model.fga | jq -r .store.id)
List Stores
Command
fga store list
Parameters
--max-pages
: Max number of pages to retrieve (default: 20)
Example
fga store list
Response
{
"stores": [{
"id": "..",
"name": "..",
"created_at": "",
"updated_at": "",
"deleted_at": ""
}, { .. }]
}
Get Store
Command
fga store get
Parameters
--store-id
: Specifies the store id to get
Example
fga store get --store-id=01H0H015178Y2V4CX10C2KGHF4
Response
{
"id": "01H0H015178Y2V4CX10C2KGHF4",
"name": "FGA Demo Store",
"created_at": "2023-05-19T16:10:07.637585677Z",
"updated_at": "2023-05-19T16:10:07.637585677Z"
}
Delete Store
Command
fga store delete
Parameters
--store-id
: Specifies the store id to delete
Example
fga store delete --store-id=01H0H015178Y2V4CX10C2KGHF4
Response
{}
Authorization Models
model
Description | command | parameters | example |
---|---|---|---|
Read Authorization Models | list |
--store-id |
fga model list --store-id=01H0H015178Y2V4CX10C2KGHF4 |
Write Authorization Model | write |
--store-id , --file |
fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file model.fga |
Read a Single Authorization Model | get |
--store-id , --model-id |
fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 |
Validate an Authorization Model | validate |
--file , --format |
fga model validate --file model.fga |
Run Tests on an Authorization Model | test |
--tests , --verbose |
fga model test --tests tests.fga.yaml |
Transform an Authorization Model | transform |
--file , --input-format |
fga model transform --file model.json |
Read Authorization Models
List all authorization models for a store, in descending order by creation date. The first model in the list is the latest one.
Command
fga model list
Parameters
--store-id
: Specifies the store id--max-pages
: Max number of pages to retrieve (default: 20)--field
: Fields to display. Choices are: id, created_at and model. Default are id, created_at.
Example
fga model list --store-id=01H0H015178Y2V4CX10C2KGHF4
Response
{
"authorization_models": [
{
"id":"01H6H9XH1G5Q6DK6PFMGDZNH9S",
"created_at":"2023-07-29T17:07:41Z"
},
{
"id":"01H6H9PPR6C3P45R75X55ZFP46",
"created_at":"2023-07-29T17:03:57Z"
}
]
}
Write Authorization Model
Command
fga model write
Parameters
--store-id
: Specifies the store id--file
: File containing the authorization model.--format
: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional)
Example
fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file model.fga
fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 '{"type_definitions": [ { "type": "user" }, { "type": "document", "relations": { "can_view": { "this": {} } }, "metadata": { "relations": { "can_view": { "directly_related_user_types": [ { "type": "user" } ] }}}} ], "schema_version": "1.1"}' --format json
Response
{
"authorization_model_id":"01GXSA8YR785C4FYS3C0RTG7B1"
}
Read a Single Authorization Model
Command
fga model get
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id--format
: Authorization model output format. Can be "fga" or "json" (default fga).--field
: Fields to display, choices are:id
,created_at
andmodel
. Default ismodel
.
Example
fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1
Response
model
schema 1.1
type user
type document
relations
define can_view: [user]
Read the Latest Authorization Model
If model-id
is not specified when using the get
command, the latest authorization model will be returned.
Command
fga model get
Parameters
--store-id
: Specifies the store id
Example
fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4
Response
model
schema 1.1
type user
type document
relations
define can_view: [user]
Validate an Authorization Model
Command
fga model validate
Parameters
--file
: File containing the authorization model.--format
: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional)
Example
fga model validate --file model.json
JSON Response
- Valid model with an ID
{"id":"01GPGWB8R33HWXS3KK6YG4ETGH","created_at":"2023-01-11T16:59:22Z","is_valid":true}
- Valid model without an ID
{"is_valid":true}
- Invalid model with an ID
{"id":"01GPGTVEH5NYTQ19RYFQKE0Q4Z","created_at":"2023-01-11T16:33:15Z","is_valid":false,"error":"invalid schema version"}
- Invalid model without an ID
{"is_valid":false,"error":"the relation type 'employee' on 'member' in object type 'group' is not valid"}
Run Tests on an Authorization Model
Given a model, and a set of tests (tuples, check and list objects requests, and expected results) report back on any tests that do not return the same results as expected.
Command
fga model test
Parameters
--tests
: Name of the tests file. Must be in yaml format (see below)--verbose
: Outputs the results in JSON
If a model is provided, the test will run in a built-in OpenFGA instance (you do not need a separate server). Otherwise, the test will be run against the configured store of your OpenFGA instance. When running against a remote instance, the tuples will be sent as contextual tuples, and will have to abide by the OpenFGA server limits (20 contextual tuples per request).
The tests file should be in yaml and have the following format:
---
name: Store Name # store name, optional
# model_file: ./model.fga # a global model that would apply to all tests, optional
# model can be used instead of model-file, optional
model: |
model
schema 1.1
type user
type folder
relations
define owner: [user] or owner
define parent: [folder]
define can_view: owner
tuples: # global tuples that would apply to all tests, optional
- user: folder:1
relation: parent
object: folder:2
tests: # required
- name: test-1
description: testing that the model works # optional
tuples:
- user: user:anne
relation: owner
object: folder:1
check: # a set of checks to run
- user: user:anne
object: folder:1
assertions:
# a set of expected results for each relation
can_view: true
can_write: true
can_share: false
list_objects: # a set of list objects to run
- user: user:anne
type: folder
assertions:
# a set of expected results for each relation
can_view:
- folder:1
- folder:2
can_write:
- folder:1
- folder:2
can_share: []
- name: test-2
description: another test
tuples:
- user: user:anne
relation: owner
object: folder:1
check:
- user: user:anne
object: folder:1
assertions:
# a set of expected results for each relation
can_view: true
list_objects:
- user: user:anne
type: folder
assertions:
# a set of expected results for each relation
can_view:
- folder:1
- folder:2
Example
fga model test --tests tests.fga.yaml
Response
- Passing test
(PASSING) test-name: Checks (2/2 passing) | ListObjects (0/0 passing)
- Failing Test
(FAILING) test-name: Checks (1/2 passing) | ListObjects (1/1 passing)
✓ Check(user=user:anne,relation=can_write,object=folder:product-2021)
ⅹ Check(user=user:anne,relation=can_share,object=folder:product-2021): expected=true, got=false, error=<nil>
✓ ListObjects(user=user:anne,relation=can_write,type=folder)
Transform an Authorization Model
Command
fga model transform
Parameters
--file
: File containing the authorization model--input-format
: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional)
Example
fga model transform --file model.json
Response
model
schema 1.1
type user
type document
relations
define can_view: [user]
Relationship Tuples
tuple
Description | command | parameters | example |
---|---|---|---|
Write Relationship Tuples | write |
--store-id , --model-id |
fga tuple write user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4 |
Delete Relationship Tuples | delete |
--store-id , --model-id |
fga tuple delete user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4 |
Read Relationship Tuples | read |
--store-id , --model-id |
fga tuple read --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 |
Read Relationship Tuple Changes (Watch) | changes |
--store-id , --model-id |
fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 |
Import Relationship Tuples | import |
--store-id , --model-id , --file |
fga tuple import --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 --file tuples.json |
Write Relationship Tuples
Command
fga tuple write --store-id=
Parameters
<user>
: User<relation>
: Relation<object>
: Object--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--file
: Specifies the file name,yaml
andjson
files are supported--max-tuples-per-write
: Max tuples to send in a single write (optional, default=1)--max-parallel-requests
: Max requests to send in parallel (optional, default=4)
Example (with arguments)
fga tuple write --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap
Response
{}
Example (with file)
fga tuple write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json
Response
{
"successful": [
{
"object":"document:roadmap",
"relation":"writer",
"user":"user:annie"
}
],
"failed": [
{
"tuple_key": {
"object":"document:roadmap",
"relation":"writer",
"user":"carl"
},
"reason":"Write validation error ..."
}
]
}
Delete Relationship Tuples
Command
fga tuple delete --store-id=
Parameters
<user>
: User<relation>
: Relation<object>
: Object--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--file
: Specifies the file name,yaml
andjson
files are supported--max-tuples-per-write
: Max tuples to send in a single write (optional, default=1)--max-parallel-requests
: Max requests to send in parallel (optional, default=4)
Example (with arguments)
fga tuple delete --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap
Response
{}
Example (with file)
fga tuple delete --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json
Response
{
"successful": [
{
"object":"document:roadmap",
"relation":"writer",
"user":"user:annie"
}
],
"failed": [
{
"tuple_key": {
"object":"document:roadmap",
"relation":"writer",
"user":"carl"
},
"reason":"Write validation error ..."
}
]
}
If you want to delete all the tuples in a store, you can use the following code:
fga tuple read | jq '[.tuples[] | { user: .key.user, relation: .key.relation, object: .key.object }]' > tuples.json
fga tuple delete --file tuples.json
Read Relationship Tuples
Command
fga tuple read [--user=] [--relation=] [--object=] --store-id=
Parameters
--store-id
: Specifies the store id--user
: User--relation
: Relation--object
: Object
Example
fga tuple read --store-id=01H0H015178Y2V4CX10C2KGHF4 --user user:anne --relation can_view --object document:roadmap
Response
{
"tuples": [
{
"key": {
"object": "document:roadmap",
"relation": "can_view",
"user": "user:anne"
},
"timestamp": "2023-07-06T15:12:55.080666875Z"
}
]
}
If you want to transform this output in a way that can be then imported using the fga tuple import
you can run
fga tuple read | jq '[.tuples[] | { user: .key.user, relation: .key.relation, object: .key.object }]' > tuples.json
fga tuple import --file tuples.json
Read Relationship Tuple Changes (Watch)
Command
fga tuple changes --type --store-id=
Parameters
--store-id
: Specifies the store id--type
: restrict to a specific type (optional)--max-pages
: Max number of pages to retrieve (default: 20)
Example
fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type document
Response
{
"changes": [
{
"operation": "TUPLE_OPERATION_WRITE",
"timestamp": "2023-07-06T15:12:40.294950382Z",
"tuple_key": {
"object": "document:roadmap",
"relation": "can_view",
"user": "user:anne"
}
}
]
}
Import Relationship Tuples
Command
fga tuple import --store-id= [--model-id=] --file [--max-tuples-per-write=] [--max-parallel-requests=]
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--file
: Specifies the file name,yaml
andjson
files are supported--max-tuples-per-write
: Max tuples to send in a single write (optional, default=1)--max-parallel-requests
: Max requests to send in parallel (optional, default=4)
File format should be: In YAML:
- user: user:anne
relation: can_view
object: document:roadmap
- user: user:beth
relation: can_view
object: document:roadmap
In JSON:
[{
"user": "user:anne",
"relation": "can_view",
"object": "document:roadmap"
}, {
"user": "user:beth",
"relation": "can_view",
"object": "document:roadmap"
}]
Example
fga tuple import --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json
Response
{
"successful": [
{
"object":"document:roadmap",
"relation":"writer",
"user":"user:annie"
}
],
"failed": [
{
"tuple_key": {
"object":"document:roadmap",
"relation":"writer",
"user":"carl"
},
"reason":"Write validation error ..."
}
]
}
Relationship Queries
query
Description | command | parameters | example |
---|---|---|---|
Check | check |
--store-id , --model-id |
fga query check --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap |
List Objects | list-objects |
--store-id , --model-id |
fga query list-objects --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document |
List Relations | list-relations |
--store-id , --model-id |
fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document |
Expand | expand |
--store-id , --model-id |
fga query expand --store-id=01H0H015178Y2V4CX10C2KGHF4 can_view document:roadmap |
Check
Command
fga query check [--contextual-tuple " "]* --store-id= [--model-id=]
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--contextual-tuple
: Contextual tuples
Example
fga query check --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap --contextual-tuple "user:anne can_view folder:product" --contextual-tuple "folder:product parent document:roadmap"
Response
{
"allowed": true,
}
List Objects
Command
fga query list-objects <object_type> [--contextual-tuple " "]* --store-id= [--model-id=]
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--contextual-tuple
: Contextual tuples (optional) (can be multiple)
Example
fga query list-objects --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document --contextual-tuple "user:anne can_view folder:product" --contextual-tuple "folder:product parent document:roadmap"
Response
{
"objects": [
"document:roadmap",
"document:budget"
],
}
List Relations
Command
fga query list-objects [--relation ]* [--contextual-tuple " "]* --store-id= [--model-id=]
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)--contextual-tuple
: Contextual tuples (optional) (can be multiple)
Example
fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document:roadmap --relation can_view
Response
{
"relations": [
"can_view"
],
}
Expand
Command
fga query expand --store-id= [--model-id=]
Parameters
--store-id
: Specifies the store id--model-id
: Specifies the model id to target (optional)
Example
fga query expand --store-id=01H0H015178Y2V4CX10C2KGHF4 can_view document:roadmap
Response
{
"tree": {
"root": {
"name": "repo:openfga/openfga#reader",
"union": {
"nodes": [{
"leaf": {
"users": {
"users": ["user:anne"]
}
},
"name": "repo:openfga/openfga#reader"
}]
}
}
}
}
Contributing
See CONTRIBUTING.
Author
License
This project is licensed under the Apache-2.0 license. See the LICENSE file for more info.