Download the PHP package xpiston/openfga without Composer

On this page you can find all versions of the php package xpiston/openfga. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package openfga

# ๐Ÿ” OpenFGA for PHP **Fine-grained authorization, with a DSL parser nobody else has.** Checks, relationship tuples, authorization models written in `.fga` โ€” for Laravel, Symfony, or no framework at all. [![PHP](https://img.shields.io/badge/PHP-8.2%20%E2%86%92%208.5-777BB4?logo=php&logoColor=white)](https://php.net) [![Laravel](https://img.shields.io/badge/Laravel-12%20%7C%2013-FF2D20?logo=laravel&logoColor=white)](https://laravel.com) [![Symfony](https://img.shields.io/badge/Symfony-6.4%20%7C%207-000000?logo=symfony&logoColor=white)](https://symfony.com) [![OpenFGA](https://img.shields.io/badge/OpenFGA-1.x-7D4CDB)](https://openfga.dev) [![Tests](https://img.shields.io/badge/tests-355%20%2B%2030%20live-22C55E)](#-tests) [![PHPStan](https://img.shields.io/badge/PHPStan-max-2563EB)](#-tests) [![License](https://img.shields.io/badge/license-MIT-blue)](#license)

OpenFGA is a CNCF authorization engine built on Google's Zanzibar paper: you describe your permissions as a graph of relationships, write facts into it, and ask it questions. Can Anne view this document? โ€” answered in milliseconds, whether the answer comes from one tuple or from six hops through a folder hierarchy and two group memberships.

This package speaks all of it: the twenty-odd endpoints, the three authentication modes, the streaming queries, and โ€” the part that does not exist anywhere else in PHP โ€” the DSL.

Everything below was checked against a real OpenFGA server

OpenFGA is open source and starts in one command, so unlike a hosted platform there is no excuse for an SDK that has never spoken to it. This one has a live test suite that runs in CI against OpenFGA v1.21.0 on every push, and the DSL parser is additionally held to OpenFGA's own 29 reference fixtures.

Where the documentation and the server disagree, this README says what the server does.

๐Ÿ•ณ๏ธ The gap this fills

There is no official OpenFGA SDK for PHP. Measured rather than asserted:

Official SDKs in the openfga org go, js, python, java, dotnet โ€” all pushed within the last week
Official PHP SDK none
evansims/openfga-php archived on GitHub, marked abandoned on Packagist, last release July 2025, 6 open issues
evansims/openfga-laravel archived, one release ever, ~3 downloads a month

So: no first-party option, and the community one was put down. This package starts from PHP 8.2 rather than 8.3, so it installs where the archived one would not.

โœจ What's inside

๐Ÿš€ Quick start

You also need any PSR-18 client. If you have none:

Start a server โ€” this is the whole setup, there is no account to create:

Then, for the rest of the application's life:

That model: argument is the most important one in this README. See the model id.

๐Ÿ“ The DSL

Nobody writes an authorization model in JSON. They write it in OpenFGA's DSL, in a file next to the code it governs:

The HTTP API accepts only JSON, and every official SDK leaves that gap โ€” the transformer lives once, in TypeScript, in a separate package. This closes it:

What "checked" means here

Three claims, each with a test suite behind it:

DSL โ†’ JSON matches OpenFGA's reference transformer 29/29 of their own fixtures โ€” including the real GitHub, Google Drive, Slack and banking models, and a torture case with eight nested mixed-operator relations
The round trip holds, both ways 29/29 for JSON โ†’ DSL โ†’ JSON, and 29/29 for DSL โ†’ DSL โ†’ JSON
A real server agrees every model above deployed to OpenFGA v1.21.0, read back, and compared as a model
Invalid DSL is refused the 50 syntax-error cases in their validation corpus

Using OpenFGA's fixtures rather than mine is the whole point: a transformer that agrees with examples its own author wrote has proved only that the author is consistent.

Syntax here, semantics there

This parser does syntax. The server does semantics, and that division is deliberate rather than lazy. Three of OpenFGA's own transformer fixtures parse perfectly here and are then refused by the server:

Those are model-validity and CEL-compilation rules. Re-implementing them in PHP would be a second opinion that is sometimes wrong, so a condition's expression is captured verbatim โ€” braces and quotes respected, so OpenFGA's own x=="{}" comes out right โ€” and handed to the server to compile. There is a live test for the harder case the fixtures do not cover: x == "{", where a brace counter without string awareness also breaks.

It tells you where

The grammar has rules that surprise people, and each gets a message rather than a token dump:

โ“ The five questions

Which one you ask is the decision that decides whether your application is fast or slow.

listUsers takes one kind of user per call. The wire field is an array, and the OpenAPI definition declares no constraint on it โ€” but the server answers two elements with "invalid ListUsersRequest.UserFilters: value must contain exactly 1 item(s)". So the signature takes a string: a list would have invited a 400 on the second element. Measured on a live server, which is the only reason this README does not say otherwise.

listObjects is the one that surprises people in production. It is bounded by --listObjects-max-results (1000 by default) and --listObjects-deadline (3s), and it does not tell you it truncated. For a page of objects you already have, batchCheck is almost always right instead. For "show me everything", use streamListObjects, which has no result cap.

expand deserves one note, because reading it wrong is easy. A relation defined as [user] or owner expands into the directly assigned users and a computed reference still to be followed โ€” so users() alone is a half-truth, and usersets() holds the other half:

๐Ÿ”— Tuples

A write is transactional: all of it or none of it. That is a feature โ€” "add Anne, remove Bob" is one change โ€” and it is why a list over the server's --max-tuples-per-write is refused here rather than split. Splitting would turn one atomic change into several that can half-fail, which is worse than an error.

Three shapes of user, and they are different things rather than variations:

The third makes a relation public. Granting [user:*] grants the unauthenticated, and it looks like a glob. The second is what makes the model worth using: the grant follows the group's membership with no further tuples.

user:* is a perfectly good user and never a valid object โ€” the server refuses document:* outright โ€” which is why this library has separate User and TypedObject types rather than one Identifier that cannot say so.

Reading is not checking

read returns tuples that were written. It does no graph traversal, so a document with no viewer tuples does not mean nobody can view it. There is a live test asserting exactly that asymmetry, because conflating the two is a plausible and expensive mistake.

Mirroring with the change log

Store the token, come back later, apply what happened. OpenFGA hands its final token back unchanged with no new entries, which is an infinite loop for a reader that only stops on a null token โ€” this one stops.

โฑ๏ธ Conditions and contextual tuples

A condition turns a grant into a rule. The tuple carries what was agreed; the check carries what is true now:

Same tuple, two answers. And a check that omits the context the expression needs is an error, not a denial โ€” OpenFGA says so rather than quietly answering no, and this library passes that through.

Contextual tuples answer a hypothetical without writing one:

๐Ÿ“Œ Pin the model id

Models are immutable and append-only: writing one adds a version and returns its id, and nothing is ever edited or deleted. That is what makes pinning safe, and what makes not pinning quietly dangerous.

Without an id, OpenFGA answers every check against whichever model version is newest. So somebody deploying a model change alters the answers a running application gets โ€” mid-request, with no deployment of its own and no way to notice.

Get the id at deploy time, put it in configuration, change it on purpose.

๐Ÿšซ Four things this SDK refuses to do

It never retries. Not a 429, not a 500. A check sits on the request path of whatever it authorises, so a hidden retry turns one slow response into three. More to the point, a write is transactional: a retried write that actually succeeded the first time comes back as a duplicate error, so a retrying client turns a success into a failure.

It never treats a denial as a failure. {"allowed": false} is a successful call with a negative answer. The one thing this client must not do is conflate that with a transport error, because that is how an application fails open โ€” so check() returns a bool, exceptions mean the question was not answered, and a missing allowed field reads as a denial rather than as approval.

It never takes over your authorisation. Laravel's Gate hook and Symfony's voter both abstain unless the object implements FgaObject. Installing this package changes the behaviour of exactly nothing until a model asks it to. (An earlier version checked the object before the ability, which made Gate::allows('viewAny', $unsavedModel) throw in applications that worked before โ€” there is a test named after it.)

It never puts credentials in a URL. Header only.

Each carries OpenFGA's own code and message, because they are specific and worth logging:

Note that Unauthenticated means this SDK was refused, which is a different thing from a user being denied. Conflating them produces an application that opens up when its credentials expire.

๐Ÿงฐ Laravel

The provider is auto-discovered.

Opt a model in, and the Gate answers:

Route middleware:

401 when nobody is authenticated, 403 on a denial โ€” and it refuses to start if the route parameter it was told to read does not exist, because a middleware that allowed a request it could not evaluate would be the worst possible bug in this package.

Or just use the client:

php artisan vendor:publish --tag=openfga-config for the rest โ€” including openfga.gate = false to keep the Gate entirely yours.

๐Ÿงฐ Symfony

The voter is registered automatically, so the attribute is the relation name:

ROLE_* and PUBLIC_ACCESS are left to the voters that own them: only lower-case, relation-shaped attributes are answered, and only for subjects implementing FgaObject.

โœ… What's verified, and how

The DSL parser 29/29 reference fixtures, three directions, plus 50 invalid-DSL cases, plus every model deployed to a live server and read back
Every query โ€” check, batch, list, stream, expand asserted byte-for-byte against a fake transport and run against OpenFGA v1.21.0
Conditions evaluated by the server's real CEL engine: the same tuple allowed at 09:30 and denied at 11:00
Transactional writes proved by making one half-invalid and asserting nothing was written
Nanosecond timestamps the server sends nine fractional digits; the parser was fixed after being fed a value a live server had just produced
Both bridges Laravel in an application booted by Testbench; Symfony compiling a real container, in five configurations
The OAuth2 exchange shape taken from the official SDKs and asserted against a fake issuer. Not run against a real identity provider โ€” the one thing here that is conformant-by-reading rather than measured

๐Ÿงฑ How it's built

Decisions visible in the code

โœ… Tests

The default suite needs nothing but PHP 8.2 and Composer, and runs in under half a second. Four suites:

unit โ€” each piece held still, with every request asserted in full against a fake transport, plus the things a real server cannot be made to do on demand: a 500, a body that is not JSON, a stream that stops halfway.

conformance โ€” this parser against OpenFGA's own, in three directions, plus their 50 syntax-error cases.

laravel โ€” the bridge inside a real Laravel application, booted by Testbench.

symfony โ€” the bundle compiling a real Symfony container in five configurations. It earned its place immediately: it caught a service definition using __construct as a factory, which Symfony does not allow and which no unit test would have noticed.

The live suite is separate on purpose โ€” it needs a server, so a red run there means "OpenFGA is not running" as often as "this is broken", and that is not what a merge gate is for. It runs in CI against a real server on every push. Each test creates its own store and deletes it afterwards.

Some tests are named after specific mistakes rather than specific features: the one proving 'yesterday' is not a timestamp, the one proving an empty body is {}, the one proving each status maps to its own exception class, and the one proving an unhandled Gate ability never touches the object.

โš ๏ธ Not included

Deliberately:

๐Ÿ“š What this was built from

License

MIT. See LICENSE.


All versions of openfga with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-json Version *
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package xpiston/openfga contains the following files

Loading the files please wait ...