Skip to main content

AuthzX + FastAPI

Add authorization to your FastAPI app in 5 minutes.

Install

pip install authzx

Set up the client

import os
from authzx import AuthzX

authzx = AuthzX(api_key=os.environ["AUTHZX_API_KEY"])

Built-in dependency

The SDK includes a FastAPI dependency out of the box:

from fastapi import FastAPI, Depends

app = FastAPI()

@app.get("/documents/{id}")
async def get_document(id: str, _=Depends(authzx.require("document", "read"))):
return {"id": id, "content": "..."}

@app.put("/documents/{id}")
async def update_document(id: str, _=Depends(authzx.require("document", "write"))):
return {"updated": True}

@app.delete("/documents/{id}")
async def delete_document(id: str, _=Depends(authzx.require("document", "delete"))):
return {"deleted": True}

The require() dependency extracts subject ID from the X-User-ID header by default. Returns 401 if missing, 403 if denied.

Custom subject header

If your user ID comes from a different header:

@app.get("/documents/{id}")
async def get_document(
id: str,
_=Depends(authzx.require("document", "read", subject_header="authorization-user-id"))
):
return {"id": id}

Manual check

For more control, call async_check() directly:

from fastapi import FastAPI, Request, HTTPException
from authzx import Subject, Resource

@app.get("/documents/{id}")
async def get_document(id: str, request: Request):
user_id = request.headers.get("x-user-id")
if not user_id:
raise HTTPException(status_code=401, detail="unauthorized")

allowed = await authzx.async_check(
subject=Subject(id=user_id, type="user"),
action="read",
resource=Resource(id=id, type="document"),
)

if not allowed:
raise HTTPException(status_code=403, detail="forbidden")

return {"id": id, "content": "..."}

Full response with reason

from authzx import AuthorizeRequest, Action

@app.get("/documents/{id}")
async def get_document(id: str, request: Request):
user_id = request.headers.get("x-user-id")

resp = await authzx.async_authorize(AuthorizeRequest(
subject=Subject(id=user_id, type="user"),
resource=Resource(id=id, type="document"),
action=Action(name="read"),
))

if not resp.decision:
raise HTTPException(status_code=403, detail=resp.context.reason)

return {"id": id, "policy": resp.context.policy_id, "path": resp.context.access_path}

Using the local agent

authzx = AuthzX(base_url="http://localhost:8181")

No API key needed. Same API, sub-millisecond responses.

Client lifecycle

The SDK reuses HTTP connections. Close on shutdown:

@app.on_event("shutdown")
async def shutdown():
await authzx.async_close()

Error handling

from authzx import AuthzXError

@app.exception_handler(AuthzXError)
async def authzx_error_handler(request, exc):
if exc.is_auth_error:
return JSONResponse(status_code=500, content={"error": "AuthzX configuration error"})
return JSONResponse(status_code=502, content={"error": "Authorization service error"})