Role-based access control with contextual authorization for FastAPI
Project description
fastapi-rbac-authz
Role-based access control with contextual authorization for FastAPI.
Installation
pip install fastapi-rbac-authz
Quick Start
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi_rbac import (
RBACAuthz, RBACRouter, Global, Contextual, ContextualAuthz, RBACUser
)
# 1. Define your user model
class User:
def __init__(self, user_id: str, roles: set[str]):
self.user_id = user_id
self.roles = roles
# 2. Define role permissions
PERMISSIONS = {
"admin": {
Global("report:*"), # Admin can do anything with reports
},
"viewer": {
Contextual("report:read"), # Viewer needs context check
},
}
# 3. Create a context check (for contextual permissions)
# All __init__ params are injected by FastAPI's DI system
class ReportAccessContext(ContextualAuthz[User]):
def __init__(
self,
report_id: int, # <-- Injected from path parameter
user: Annotated[User, Depends(RBACUser)],
):
self.user = user
self.report_id = report_id
async def has_permissions(self) -> bool:
# Your logic: check if user can access this specific report
allowed_reports = {1, 2, 3} # e.g., query from database
return self.report_id in allowed_reports
# 4. Create your app and configure RBAC
app = FastAPI()
async def get_current_user() -> User:
# Your authentication logic here
return User(user_id="user-1", roles={"viewer"})
RBACAuthz(
app,
get_roles=lambda u: u.roles,
permissions=PERMISSIONS,
user_dependency=get_current_user,
ui_path="/_rbac", # Optional: mount visualization UI
)
# 5. Create protected routes
router = RBACRouter(permissions={"report:read"}, contexts=[ReportAccessContext])
@router.get("/reports/{report_id}")
async def get_report(report_id: int):
return {"report_id": report_id}
@router.post("/reports", permissions={"report:create"}) # Override permissions
async def create_report():
return {"id": "new-report"}
app.include_router(router, prefix="/api")
Permission Scopes
Permissions can be granted with two scopes:
Global Scope
Global("report:read")
Global permissions bypass context checks entirely. If a user has a global permission, they can access the resource without any additional validation. Use this for admin roles or service accounts that need unrestricted access.
Contextual Scope
Contextual("report:read")
Contextual permissions require context checks to pass. The user must have the permission AND the context check must return True. Use this for regular users who should only access resources they own or are members of.
Example
PERMISSIONS = {
"admin": {
Global("report:*"), # Can access ALL reports, no questions asked
},
"user": {
Contextual("report:read"), # Can only read reports they have access to
Contextual("report:create"),
},
}
Context Checks
Context checks are classes that implement fine-grained authorization logic. They're regular FastAPI dependencies, so you can inject any parameters (path params, query params, request body, database sessions, etc.).
class ReportAccessContext(ContextualAuthz[User]):
def __init__(
self,
report_id: int, # Injected from path parameter
user: Annotated[User, Depends(RBACUser)],
db: Annotated[AsyncSession, Depends(get_db)], # Database session
):
self.user = user
self.report_id = report_id
self.db = db
async def has_permissions(self) -> bool:
# Query database to check if user can access this report
report = await self.db.get(Report, self.report_id)
return report is not None and report.owner_id == self.user.user_id
Authorization Flow
When a request hits an RBAC-protected endpoint:
1. Authentication
└── user_dependency runs → User object available
2. Permission Check
└── Does user have ANY grant (global or contextual) for required permission?
├── No → 403 Forbidden
└── Yes → Continue
3. Scope Evaluation
└── Is the grant Global?
├── Yes → Access granted (skip context checks)
└── No → Continue to context checks
4. Context Checks (only for Contextual grants)
└── Run all context classes via FastAPI DI
└── Do ALL contexts return True?
├── No → 403 Forbidden
└── Yes → Access granted
5. Endpoint Handler Executes
Wildcard Permissions
Use wildcards to grant multiple permissions at once:
| Grant | Implies |
|---|---|
* |
Everything |
report:* |
report:read, report:create, report:delete, etc. |
report:metrics:* |
report:metrics:view, report:metrics:export, etc. |
PERMISSIONS = {
"admin": {Global("*")}, # Full access to everything
"reporter": {Global("report:*")}, # Full access to reports only
}
Running the Example
A complete runnable example is included in the examples/ directory:
# Install dependencies
pip install fastapi-rbac-authz uvicorn
# Run the example
uvicorn examples.basic_app:app --reload
Then open your browser:
- http://localhost:8000/docs - OpenAPI docs to test the API
- http://localhost:8000/_rbac - Authorization visualization UI
Test with different users
The example uses X-Token header for authentication:
# As admin (has Global("*") - full access)
curl -H "X-Token: admin-token" http://localhost:8000/reports
curl -H "X-Token: admin-token" http://localhost:8000/reports/1
# As user (has Contextual permissions - can only access own reports)
curl -H "X-Token: user-token" http://localhost:8000/reports/1 # OK (owns report 1)
curl -H "X-Token: user-token" http://localhost:8000/reports/3 # 403 (doesn't own report 3)
# As viewer (has Contextual read - can only read own reports)
curl -H "X-Token: viewer-token" http://localhost:8000/reports/1 # 403 (doesn't own any)
Visualization UI
Mount the built-in visualization UI to explore your authorization schema:
RBACAuthz(
app,
# ...
ui_path="/_rbac",
)
Visit /_rbac to see an interactive graph showing:
- Roles and their permission grants
- Permissions with scope indicators (global/contextual)
- Endpoints and their required permissions
- Context checks and which endpoints use them
Double-click any node to isolate and explore its relationships.
Screenshots
Full authorization graph
Role isolation view - double-click a role to see what it can access
Endpoint isolation view - double-click an endpoint to see who can access it
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_rbac_authz-0.3.0.tar.gz.
File metadata
- Download URL: fastapi_rbac_authz-0.3.0.tar.gz
- Upload date:
- Size: 64.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.11 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9169a4af1eb8f8b54e07113d3b97e75cba982b3c083fe57ec615d31c5302f941
|
|
| MD5 |
6761eee1f441ab60b98c709b591033e9
|
|
| BLAKE2b-256 |
99c1736397b77c87a1bf73120b429d68e4caf94026aafb68d8b0c26db788604a
|
File details
Details for the file fastapi_rbac_authz-0.3.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_rbac_authz-0.3.0-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.13.11 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cab71cc74a1b688583038a071ced2ddc1c82c55d2926ff2829f4ef8a7a04df8f
|
|
| MD5 |
7ce0f1434335f880b8e634b19d34c472
|
|
| BLAKE2b-256 |
8f865f8e0e9bcc32fc8633ee3b7b85a12781cf9b554c4427fea008c2cc944873
|