Skip to main content

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 AuthenticationMiddleware integration
  • 🛡️ Role-Based Access Control (RBAC)require_roles() FastAPI dependency
  • ⚙️ Configurable — env vars or explicit settings via KeycloakAuthSettings
  • Type-Safe — full mypy --strict compliance, py.typed marker
  • 🔌 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_paths are passed through unauthenticated
  • Raises AuthenticationError on 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

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

fastapi_keycloak_rbac-0.2.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_keycloak_rbac-0.2.0-py3-none-any.whl (17.9 kB view details)

Uploaded Python 3

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

Hashes for fastapi_keycloak_rbac-0.2.0.tar.gz
Algorithm Hash digest
SHA256 149646f3e79be11933a18ddd664e46feb73b88cc87616ab2db79a9b4f924e82f
MD5 d816f9f00a3c0599fb68fb8bc55818f1
BLAKE2b-256 7b07708187e2fdf3c32d59dccf7f66bea1a7625ccdcc93a88cbbe71826112de3

See more details on using hashes here.

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

Hashes for fastapi_keycloak_rbac-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 be7b819e814cb7600ab59e3da07d880c5b536e8d45abdd89411bbed6762ca320
MD5 4798a014e4269147b11005725eab1038
BLAKE2b-256 731ce63e34425df8ba5de146b76bfb68c65df84c1e02331597ba0e193799dd75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page