Application Security
Authorization Libraries Compared: Casbin, OpenFGA, and SpiceDB
Check the object, not only the group name. role == admin will still serve another tenant’s id.
Expertise: Application Security · Level: Intermediate · 6 min read
A handler that only checks role == admin still serves tenant 2’s object. The JWT on the gateway already validated, so the request looks authorized. OWASP’s Authorization Cheat Sheet treats that as a different question from authentication: permission has to be checked on the specific object, not only on the role in the token. Changing acct_id from 901 to 523 is the CWE-639 case the sheet names.
That is the wrong assumption. A valid token is not an object check. In-process libraries sit next to the handler. A relationship graph is a query on every request. A policy language lives beside the app. Those shapes differ on where the tuple lives, not on whether TLS already terminated.
The hop that terminates TLS and authentication is on API gateways. This list is the object check in the handler. Read the matrix for which library owns that check, and which one assumes you already have a graph.
How we evaluated
We read first-party docs, source, licenses, and release notes, and we treat a marketing page as a claim, not as proof that two products do the same work.
We cared about whether the check can name an object, whether you run the decision in-process or as a service, whether the policy is a file you can review, and whether the docs show a relationship, not only a role string. Cerbos missed because Oso and Casbin already cover that in-process job.
We read first-party docs, licenses, and release notes. Unless a write-up says otherwise, we did not run a paid tenant.
| Tool | Best for | What to check |
|---|---|---|
| Casbin | Embeddable ACL ABAC RBAC in-process | In-process ACL ABAC RBAC ยท Apache-2.0 ยท model file you read |
| OpenFGA | Open Zanzibar-style checks | Relationship tuples ยท Apache-2.0 ยท you host the store |
| SpiceDB | Zanzibar-shaped store from Authzed | Schema plus relationships ยท Apache-2.0 ยท self-host or Authzed |
| Oso | Polar policies next to the app | Polar next to the app ยท Apache-2.0 library / commercial cloud ยท facts from your objects |
| Cedar | Amazon policy language you can run | Deterministic policy language ยท Apache-2.0 ยท embed it or AWS Verified Permissions |
| Open Policy Agent | Sidecar policy for more than the app | Rego sidecar or library ยท Apache-2.0 ยท same engine as Gatekeeper |
Casbin
Best for embeddable ACL ABAC RBAC in-process

Casbin is a library. Models are CONF files. Policies are rows. Ports exist for Go, Java, Node, and others.
You do not need a new network hop. You still load the policy, because a wrong model is a silent allow. It is not a hosted Zanzibar.
Key features
- Multiple model types
- Adapters for stores
- In-process check
- Apache-2.0
Why we like it
An embeddable check you can call in the handler is the practical no-hop buy.
Limits
You still load the policy. A wrong model is a silent allow. It is not a hosted relation store.
OpenFGA
Best for open Zanzibar-style checks

OpenFGA is a relationship store. Auth0 and Okta opened it. Checks are whether user U can do relation R on object O.
When the org chart is the policy, a tuple store is the practical model. You can run it yourself. Modeling is the hard part, and it will not invent object checks you never wrote.
Key features
- Relationship tuples
- HTTP and SDKs
- You can host it
- Modeled on Zanzibar ideas
Why we like it
A check shaped like a relation matches permission that is already a graph.
Limits
You operate the store. Modeling is the hard part. Latency is a new dependency.
SpiceDB
Best for zanzibar-shaped store from Authzed

SpiceDB is Authzed’s Zanzibar-inspired database. The engine is Apache-2.0. Authzed Cloud is paid.
If you want that paper as running software, this is the open engine with a company behind it. You still write the schema. Cloud is a different bill.
Key features
- Schema and relationships
- Consistency options
- Self-host or Authzed
- gRPC API
Why we like it
Consistency options and a gRPC API are a real store, not a policy file you hope stays in sync.
Limits
You still write the schema. Cloud is commercial. Latency is a new dependency.
Oso
Best for polar policies next to the app

Oso Polar is a policy language that can talk to your objects. The library is open. Oso Cloud is paid.
When the policy must read a field on the invoice, Polar can say that without a second hop if you embed it. Do not confuse the library with the SaaS.
Key features
- Polar language
- Library in several languages
- Facts from your app
- Cloud option
Why we like it
A policy that can see a field on the object matches checks that were never a role table.
Limits
Polar is a language your team must own. Cloud terms are commercial. The library and the SaaS are different products.
Cedar
Best for amazon policy language you can run

Cedar is Amazon’s policy language, opened under Apache-2.0. Verified fragments exist. AWS uses it in Verified Permissions.
A policy language with a verification story is rare. You can run the engine without buying a SIEM. The AWS service is a different bill, and it is not a drop-in for a legacy role table.
Key features
- Deterministic policy language
- Apache-2.0
- AWS Verified Permissions as a host
- Analysis tools in the repo
Why we like it
A language you can analyze is the practical pick when the policy must be deterministic.
Limits
The AWS service is a different bill. Embedding Cedar is on you. It will not migrate a role column by itself.
Open Policy Agent
Best for sidecar policy for more than the app

Open Policy Agent evaluates Rego. It started as a sidecar for clusters and APIs. It can decide an HTTP request if you feed it the input.
If the decision must be the same in CI, on the gateway, and in the app, one engine is the point. The authorization list is the app check. Platform owns admission. Rego is easy to write badly.
Key features
- Rego language
- Sidecar or library
- Same engine as Gatekeeper
- Apache-2.0
Why we like it
One language across the gateway and the app is why teams already run it.
Limits
Rego is easy to write badly. A sidecar is a hop. This is not a user directory.
What we left out
- Cerbos. Teams want policy-as-code they can run next to the app. Oso and Casbin already cover that in-process job.
- Permit.io. People want a hosted control plane on top of an open engine. It is a commercial wrapper, and you still call the library or store.
OpenFGA threads treat the mapping table as the thing that rots. A role column is not an object check.
Questions before you buy
Ask these before the trial clock starts. Vague answers usually mean a neighboring category.
- Can this engine say no when I pass another tenant’s object id?
- Does the check stay in the request process, or are we adding a store we must staff?
- Is the policy a graph of relations or a language next to the code, and which one matches how we already think?
If the handler only checks a role name, you do not have authorization. Pick a model you can test with two tenants.
FAQs
Does a library replace object-level tests?
No. The library is the decision. You still write a test that uses another user’s id. Open the IDOR guide.
Is OPA an IdP?
No. OPA does not log anyone in. Identity owns the session. This page owns the object check.
Should I pick Zanzibar or Polar?
Zanzibar if the graph is the product. Polar or Casbin if the check must stay in the request path without a new store.
Is this a scored bake-off?
No. Order is editorial. We did not load-test the stores.