litestar-keycloak
Keycloak authentication plugin for Litestar. OIDC/OAuth2 integration using Litestar's native plugin protocol, dependency injection, and guard system.
Features
- OIDC discovery and JWKS caching with automatic key rotation
- Bearer token validation (header or cookie)
- Realm and client role guards
- Scope-based access control
KeycloakUserinjection via Litestar DI- Optional login/callback/logout route group
- Async HTTP via aiohttp for token exchange and JWKS requests
Installation
pip install litestar-keycloak
Quick Start
from litestar import Litestar, get
from litestar_keycloak import KeycloakPlugin, KeycloakConfig, CurrentUser
@get("/me")
async def me(current_user: CurrentUser) -> dict:
return {
"sub": current_user.sub,
"username": current_user.preferred_username,
"roles": current_user.realm_roles,
}
app = Litestar(
route_handlers=[me],
plugins=[KeycloakPlugin(
KeycloakConfig(
server_url="https://keycloak.example.com",
realm="my-realm",
client_id="my-app",
)
)],
)
Any route that declares current_user: CurrentUser automatically requires a valid Bearer token.
Guards
Restrict access by roles or scopes:
from litestar import get
from litestar_keycloak import require_roles, require_scopes
@get("/admin", guards=[require_roles("admin")])
async def admin_panel() -> dict:
return {"msg": "welcome, admin"}
@get("/reports", guards=[require_scopes("reports:read")])
async def reports() -> dict:
return {"msg": "here are your reports"}
Configuration
KeycloakConfig(
server_url="https://keycloak.example.com",
realm="my-realm",
client_id="my-app",
client_secret="secret", # confidential clients
token_location=TokenLocation.HEADER, # HEADER (default) or COOKIE
jwks_cache_ttl=3600, # JWKS cache lifetime in seconds
algorithms=("RS256",), # JWT signing algorithms
include_routes=False, # mount /auth/login, /callback, /logout
optional_audiences=frozenset({"my-service"}), # accept service tokens too
)
Full option list: docs/configuration.md.
When include_routes=True, the plugin mounts:
| Endpoint | Description |
|---|---|
GET /auth/login |
Redirect to Keycloak authorize |
GET /auth/callback |
Handle authorization code exchange |
POST /auth/logout |
End session (Keycloak + local) |
POST /auth/refresh |
Refresh access token |
Testing
Unit tests (no Keycloak required)
Test utilities live in tests/conftest.py: create_test_token() and MockKeycloakPlugin(). Use them in your tests (e.g. when running pytest from this repo, or import from conftest):
from tests.conftest import create_test_token, MockKeycloakPlugin
# Mint a fake JWT with arbitrary claims
token = create_test_token(sub="user-1", realm_roles=["admin"])
# Use MockKeycloakPlugin to skip real JWKS validation
app = Litestar(
route_handlers=[...],
plugins=[MockKeycloakPlugin()],
)
Integration tests (real Keycloak via testcontainers)
from testcontainers.keycloak import KeycloakContainer
kc = (
KeycloakContainer("quay.io/keycloak/keycloak:26.0")
.with_command("start-dev --import-realm")
.with_volume_mapping(
"tests/fixtures/realm-export.json",
"/opt/keycloak/data/import/realm-export.json",
"ro",
)
)
kc.start()
See tests/fixtures/realm-export.json for a pre-configured realm with test users and roles.
Service-to-service
To accept tokens from a service client (e.g. client_credentials), add its client ID to optional_audiences. Use the raw_token dependency to forward the caller's token to downstream APIs. See Service-to-service.
Documentation
- Guides: Configuration, Guards, OIDC routes, Testing. Build the site with
mkdocs build(ormkdocs serve). - Example app with docker-compose and smoke tests: examples/.
Dependencies
| Package | Purpose |
|---|---|
litestar[standard] ≥ 2.0 |
Web framework (plugin target) |
aiohttp |
Async HTTP for token/JWKS calls |
PyJWT[crypto] |
JWT validation |
License
MIT
Metadata
Release files for litestar-keycloak 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| litestar_keycloak-0.2.0.tar.gz | 174.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| litestar_keycloak-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 196.7 kB
Release files / litestar_keycloak-0.2.0.tar.gz
| Download URL | litestar_keycloak-0.2.0.tar.gz |
|---|---|
| Size | 174.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c7e7d6ca79cf37b3af0ee0725a34e5515054ad86e36e5308e38b816bebeea82
|
|
BLAKE2b-256 checksum How to use checksums |
17908c81f0548988046ea8ebe382f6de8ba5ca7b3433dd5579b656605fd1ba6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency logRelease files / litestar_keycloak-0.2.0-py3-none-any.whl
| Download URL | litestar_keycloak-0.2.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
812a732146f7131049964ed67ed83cfa10bad4e20b96ab2f623b47e8f4b137ea
|
|
BLAKE2b-256 checksum How to use checksums |
6450916ce112eb78cdeb20c8110cc895028c38ab93cdb380bf00ec4fbb7099eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency log