FastAPI-Topaz
FastAPI-Topaz asks a Topaz authorizer whether each request may proceed, and returns HTTP 403 Forbidden when the answer is no.
Full documentation: jmanteau.github.io/fastapi-topaz
Upgrading from 1.x? See Upgrading to 2.0.
How it works
Your FastAPI app sends the user's identity and the route to Topaz. Topaz evaluates a policy and returns allowed or denied.
flowchart LR
C[Client] --> F[FastAPI route]
F --> D["Dependency<br/>or TopazMiddleware"]
D --> TC[TopazConfig]
TC -->|"gRPC"| T[Topaz authorizer]
T --> P["Policies (Rego)"]
T --> R["Directory (relationships)"]
TC -.->|"denied"| X[HTTP 403]
| Component | Role | Source |
|---|---|---|
TopazConfig |
Holds the Topaz connection, how to find the user, and the policy root. Create it once at startup. | config.py L153–L243 |
| Dependencies | Protect one route at a time with Depends(...). |
dependencies.py |
TopazMiddleware |
Protects every route in the app, except the paths you exclude. | middleware.py L131–L186 |
Requirements
- Python 3.10 or later
- FastAPI 0.100 or later (except 0.137.0 and 0.137.1)
- A running Topaz instance
Install
pip install fastapi-topaz
Quick start
This example protects GET /documents. Topaz evaluates the policy myapp.GET.documents for the user in request.state.user_id.
from fastapi import Depends, FastAPI, Request
from fastapi_topaz import (
AuthorizerOptions,
Identity,
IdentityType,
TopazConfig,
require_policy_allowed,
)
config = TopazConfig(
authorizer_options=AuthorizerOptions(url="localhost:8282"),
policy_path_root="myapp",
identity_provider=lambda req: Identity(
type=IdentityType.IDENTITY_TYPE_SUB,
value=req.state.user_id,
),
policy_instance_name="myapp",
)
app = FastAPI()
@app.get("/documents")
async def list_documents(
request: Request,
_: None = Depends(require_policy_allowed(config, "myapp.GET.documents")),
):
return {"documents": [...]}
Choose a check
Pick the check that matches your question. Not sure which one? Read Choosing an authorization approach.
| Question to answer | Use | Source |
|---|---|---|
| May this user call this route? (you name the policy) | require_policy_allowed() |
dependencies.py L137–L169 |
| May this user call this route? (policy name built from the route) | require_policy_auto() |
dependencies.py L172–L232 |
Does this user have a relation to this object, such as can_write on a document? |
require_rebac_allowed() |
dependencies.py L235–L305 |
| Does this user have access at every level of a nested path, such as org → project → document? | require_rebac_hierarchy() |
dependencies.py L497 |
| Fetch one object, and return it only if the user may see it | get_authorized_resource() |
dependencies.py L308 |
| Keep only the objects in a list that the user may see | filter_authorized_resources() |
dependencies.py L413 |
| Protect every route without editing each one | TopazMiddleware |
middleware.py L131 |
require_policy_auto() builds the policy name from the HTTP method and the route template:
| Route | Policy name |
|---|---|
GET /documents |
myapp.GET.documents |
GET /documents/{id} |
myapp.GET.documents.__id |
PUT /users/{user_id}/docs/{doc_id} |
myapp.PUT.users.__user_id.docs.__doc_id |
Example of a relationship check: the user must have the can_write relation on the document whose ID is in the {id} path parameter.
@app.put("/documents/{id}")
async def update_document(
id: int,
_: None = Depends(require_rebac_allowed(config, "document", "can_write")),
):
...
Production features
Turn each feature on by passing an object to TopazConfig. All are optional.
| Feature | What it does | TopazConfig argument |
Guide |
|---|---|---|---|
| Decision cache | Reuses recent decisions for a set Time To Live (TTL), so Topaz gets fewer calls. | decision_cache |
Reference |
| Circuit breaker | Stops calling Topaz after repeated failures, then falls back to cached decisions. | circuit_breaker |
Circuit breaker |
| Audit logging | Writes each decision as a structured JSON log line. | audit_logger |
Audit logging |
| Metrics | Exports Prometheus metrics. | metrics |
Observability |
| Tracing | Exports OpenTelemetry (OTel) traces. | tracing |
Observability |
| Test doubles | Replaces Topaz with fixed rules in unit tests. | — (fastapi_topaz.testing) |
Testing |
Command-line tool
The package installs a fastapi-topaz command. Each subcommand reads your app from module:attribute.
| Command | What it does |
|---|---|
generate-policies |
Writes a Rego policy skeleton for each route. |
policy-diff |
Lists routes that have no policy, and policies that match no route. |
generate-rights-matrix |
Writes a Markdown table of routes and their policies. |
policy-map |
Prints which policy each route uses. |
check |
Shows which policy a given request URL resolves to. Add --live to ask Topaz for the decision. |
fastapi-topaz policy-diff --app myapp.main:app --policies policies/
Full options: CLI reference. Source: cli.py L329.
Terms
| Term | Meaning |
|---|---|
| Policy | A rule, written in Rego, that Topaz evaluates to return allowed or denied. |
| Rego | The policy language of Open Policy Agent (OPA), which Topaz uses. |
| ReBAC | Relationship-Based Access Control. Access depends on how the user relates to an object, for example "owner of document 42". |
| gRPC | The remote procedure call protocol this library uses to talk to Topaz (default port 8282). |
| Identity provider | Your function that reads the current user from the request. |
Documentation
The documentation follows the Diátaxis framework.
| Section | Read it when you want to… |
|---|---|
| Tutorials | learn step by step, from zero |
| How-to guides | solve one specific problem |
| Reference | look up an exact function, argument, or CLI option |
| Explanation | understand the design and the concepts |
Related: Topaz documentation · FastAPI documentation
License
Apache 2.0
Metadata
Release files for fastapi-topaz 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_topaz-2.0.0.tar.gz | 182.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_topaz-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 244.2 kB
Release files / fastapi_topaz-2.0.0.tar.gz
| Download URL | fastapi_topaz-2.0.0.tar.gz |
|---|---|
| Size | 182.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2446cd45c5c146df75f05482112179ef5597df74999a8fa35733a57ceb916040
|
|
BLAKE2b-256 checksum How to use checksums |
feafc62d9ed66dd655d1e3c8c42f7ed0c74398ead5b2cd0e5ebdd4c58033926d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.12
|
Release files / fastapi_topaz-2.0.0-py3-none-any.whl
| Download URL | fastapi_topaz-2.0.0-py3-none-any.whl |
|---|---|
| Size | 62.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c4e6f5a4306e0f61b738762a613a3117410761ef00c19a82bc968a14978bb19c
|
|
BLAKE2b-256 checksum How to use checksums |
75011c12d79cfe9ccb0fd53a81acc6fb418b4c290579f7fe2b2908e4fc204067
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.12
|