FastAPI cookbook · Intermediate
Implement role-based access control in FastAPI
Restrict endpoints by permission and make sure users only access their own resources.
Approach
Map roles to permissions in one place, expose a require(permission) dependency factory for function-level checks, and enforce object-level access by scoping queries to the current user or tenant. Test both: wrong role → 403, other user's resource → 404.
Solution
from enum import StrEnum
from fastapi import Depends, HTTPException
class Perm(StrEnum):
ORDERS_READ_ALL = "orders:read_all"
PRODUCTS_WRITE = "products:write"
USERS_ADMIN = "users:admin"
ROLE_PERMISSIONS: dict[str, set[Perm]] = {
"customer": set(),
"support": {Perm.ORDERS_READ_ALL},
"admin": set(Perm),
}
def require(*perms: Perm):
async def checker(user: CurrentUser) -> User:
granted = set().union(*(ROLE_PERMISSIONS.get(r, set()) for r in user.roles))
if not set(perms) <= granted:
raise HTTPException(403, "Insufficient permissions")
return user
return Depends(checker)
@router.put("/products/{product_id}", dependencies=[require(Perm.PRODUCTS_WRITE)])
async def update_product(product_id: int, body: ProductIn, db: DB): ...
@router.get("/orders/{order_id}")
async def get_order(order_id: int, db: DB, user: CurrentUser) -> OrderOut:
stmt = select(Order).where(Order.id == order_id)
if Perm.ORDERS_READ_ALL not in set().union(*(ROLE_PERMISSIONS[r] for r in user.roles)):
stmt = stmt.where(Order.user_id == user.id) # object-level scoping
order = await db.scalar(stmt)
if order is None:
raise HTTPException(404, "Order not found")
return order
Pitfalls
- Checking roles but forgetting ownership (IDOR).
- Role checks scattered as string comparisons.
- Permissions baked into long-lived tokens.
Variations
- Policy engines (OPA, Cerbos, Oso) or ReBAC (OpenFGA) for complex sharing rules.
Further reading: OWASP Authorization Cheat Sheet
Reference: https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
More FastAPI recipes
- Add liveness and readiness endpoints to FastAPI
- Add OpenTelemetry tracing to FastAPI
- Add request IDs and structured logs to FastAPI
- Build a WebSocket chat endpoint in FastAPI
- Cache FastAPI responses in Redis
- Call external APIs with retries and timeouts from FastAPI
- Isolate tenants in a multi-tenant FastAPI app
- Paginate with cursors in FastAPI