Keycloak authentication and RBAC for FastAPI — type-safe, configurable, zero project-specific dependencies
Project description
fastapi-keycloak-rbac
Keycloak authentication for FastAPI — role-based access control, type-safe, zero project-specific dependencies.
📢 Hobby Project Notice: This is a research and learning project exploring FastAPI and Keycloak integration best practices. Feel free to use it as a reference, report issues, or suggest improvements! Contributions and feedback are always welcome.
✨ Features
- 🔐 HTTP Authentication — Starlette
AuthenticationMiddlewareintegration - 🛡️ Role-Based Access Control (RBAC) —
require_roles()FastAPI dependency - ⚙️ Configurable — env vars or explicit settings via
KeycloakAuthSettings - ✅ Type-Safe — full
mypy --strictcompliance,py.typedmarker - 🔌 Extensible — bring your own caching, metrics, and circuit breaker
📦 Installation
pip install fastapi-keycloak-rbac
Or in development mode from source:
git clone https://github.com/acikabubo/fastapi-keycloak-rbac
cd fastapi-keycloak-rbac
pip install -e ".[dev]"
🚀 Quick Start
from fastapi import Depends, FastAPI, Request
from starlette.middleware.authentication import AuthenticationMiddleware
from fastapi_keycloak_rbac.backend import AuthBackend
from fastapi_keycloak_rbac.dependencies import require_roles
from fastapi_keycloak_rbac.models import UserModel
app = FastAPI()
# Add Keycloak authentication middleware
# Reads KEYCLOAK_AUTH_* environment variables automatically
app.add_middleware(AuthenticationMiddleware, backend=AuthBackend())
@app.get("/me")
async def me(request: Request):
user: UserModel = request.user
return {"username": user.username, "roles": user.roles}
@app.get("/admin", dependencies=[Depends(require_roles("admin"))])
async def admin_only(request: Request):
user: UserModel = request.user
return {"message": f"Hello, {user.username}!"}
⚙️ Configuration
Environment Variables
All settings use the KEYCLOAK_AUTH_ prefix:
| Variable | Default | Description |
|---|---|---|
KEYCLOAK_AUTH_SERVER_URL |
http://localhost:8080/ |
Keycloak base URL |
KEYCLOAK_AUTH_REALM |
master |
Realm name |
KEYCLOAK_AUTH_CLIENT_ID |
(empty) | Client ID for token validation |
KEYCLOAK_AUTH_ADMIN_USERNAME |
(empty) | Admin username (optional) |
KEYCLOAK_AUTH_ADMIN_PASSWORD |
(empty) | Admin password (optional) |
KEYCLOAK_AUTH_EXCLUDED_PATHS |
^(/docs|/openapi.json|/health|/metrics)$ |
Regex of paths that skip auth |
KEYCLOAK_AUTH_DEBUG |
false |
Enable debug logging |
export KEYCLOAK_AUTH_SERVER_URL=http://keycloak:8080/
export KEYCLOAK_AUTH_REALM=myrealm
export KEYCLOAK_AUTH_CLIENT_ID=myapp
Explicit Settings
from fastapi_keycloak_rbac.backend import AuthBackend
from fastapi_keycloak_rbac.config import KeycloakAuthSettings
settings = KeycloakAuthSettings(
server_url="http://keycloak:8080/",
realm="myrealm",
client_id="myapp",
excluded_paths=r"^(/docs|/openapi.json|/health)$",
)
app.add_middleware(AuthenticationMiddleware, backend=AuthBackend(settings=settings))
📖 API Reference
AuthBackend
Starlette AuthenticationBackend that validates Keycloak Bearer tokens.
- HTTP: reads
Authorization: Bearer <token>header - Paths matching
excluded_pathsare passed through unauthenticated - Raises
AuthenticationErroron expired/invalid tokens
require_roles(*roles)
FastAPI dependency that enforces role-based access control.
from fastapi import Depends
from fastapi_keycloak_rbac.dependencies import require_roles
@router.get("/reports", dependencies=[Depends(require_roles("analyst", "admin"))])
async def get_reports(): ...
Raises HTTP 401 if unauthenticated, HTTP 403 if missing any required role.
UserModel
Pydantic model populated from Keycloak JWT claims, available as request.user.
| Attribute | Type | Description |
|---|---|---|
id |
str |
Keycloak subject UUID (sub claim) |
username |
str |
Preferred username |
roles |
list[str] |
Client roles from resource_access |
expired_in |
int |
Token expiry Unix timestamp |
expired_seconds |
int |
Seconds until expiry (property) |
KeycloakAuthSettings
Pydantic-settings class for all auth configuration. See env vars table above.
RBACManager
Lower-level RBAC for custom permission checking:
from fastapi_keycloak_rbac.rbac import rbac_manager
has_perm, missing = rbac_manager.check_user_has_roles(user, ["admin"])
⚠️ Exceptions
| Exception | Status | Description |
|---|---|---|
AuthenticationError |
401 | Base auth failure |
TokenExpiredError |
401 | JWT past expiry |
InvalidTokenError |
401 | Malformed / invalid signature |
AuthorizationError |
403 | Base authorization failure |
PermissionDeniedError |
403 | Missing required roles |
💡 Examples
See examples/basic_http.py for a full working example.
🛠️ Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Type check
mypy fastapi_keycloak_rbac/
# Lint
ruff check fastapi_keycloak_rbac/
ruff format fastapi_keycloak_rbac/
📊 Status
Phase 1-2 complete — core extraction done, 100% test coverage, mypy strict clean.
| Module | Description |
|---|---|
exceptions.py |
Auth exceptions |
models.py |
UserModel, TokenClaims, NewTypes, AuthResult |
config.py |
KeycloakAuthSettings |
manager.py |
KeycloakManager (Keycloak OpenID client) |
rbac.py |
RBACManager (permission checking) |
backend.py |
AuthBackend (Starlette middleware) |
dependencies.py |
require_roles() FastAPI dependency |
See Issue #1 for the full roadmap.
📄 License
MIT License — see LICENSE for details.
🔗 Related Projects
- fastapi-keycloak-middleware — Alternative Keycloak middleware
- fastapi-http-websocket — Source project
Project details
Release history Release notifications | RSS feed
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_keycloak_rbac-0.2.0.tar.gz.
File metadata
- Download URL: fastapi_keycloak_rbac-0.2.0.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"openSUSE Tumbleweed","version":"20260219","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
149646f3e79be11933a18ddd664e46feb73b88cc87616ab2db79a9b4f924e82f
|
|
| MD5 |
d816f9f00a3c0599fb68fb8bc55818f1
|
|
| BLAKE2b-256 |
7b07708187e2fdf3c32d59dccf7f66bea1a7625ccdcc93a88cbbe71826112de3
|
File details
Details for the file fastapi_keycloak_rbac-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_keycloak_rbac-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"openSUSE Tumbleweed","version":"20260219","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be7b819e814cb7600ab59e3da07d880c5b536e8d45abdd89411bbed6762ca320
|
|
| MD5 |
4798a014e4269147b11005725eab1038
|
|
| BLAKE2b-256 |
731ce63e34425df8ba5de146b76bfb68c65df84c1e02331597ba0e193799dd75
|