litestar-keycloak 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. litestar_keycloak-0.1.0/.github/workflows/ci.yml +96 -0
  2. litestar_keycloak-0.1.0/.github/workflows/publish.yml +74 -0
  3. litestar_keycloak-0.1.0/.gitignore +212 -0
  4. litestar_keycloak-0.1.0/.pre-commit-config.yaml +38 -0
  5. litestar_keycloak-0.1.0/Makefile +18 -0
  6. litestar_keycloak-0.1.0/PKG-INFO +180 -0
  7. litestar_keycloak-0.1.0/README.md +151 -0
  8. litestar_keycloak-0.1.0/TESTING.md +371 -0
  9. litestar_keycloak-0.1.0/compose.test.yml +21 -0
  10. litestar_keycloak-0.1.0/docs/api.md +74 -0
  11. litestar_keycloak-0.1.0/docs/configuration.md +96 -0
  12. litestar_keycloak-0.1.0/docs/guides/guards.md +77 -0
  13. litestar_keycloak-0.1.0/docs/guides/oidc-routes.md +62 -0
  14. litestar_keycloak-0.1.0/docs/guides/service-to-service.md +65 -0
  15. litestar_keycloak-0.1.0/docs/guides/testing.md +65 -0
  16. litestar_keycloak-0.1.0/docs/index.md +166 -0
  17. litestar_keycloak-0.1.0/examples/Dockerfile +21 -0
  18. litestar_keycloak-0.1.0/examples/README.md +122 -0
  19. litestar_keycloak-0.1.0/examples/app/main.py +217 -0
  20. litestar_keycloak-0.1.0/examples/docker-compose.yml +43 -0
  21. litestar_keycloak-0.1.0/examples/test.sh +186 -0
  22. litestar_keycloak-0.1.0/mkdocs.yml +27 -0
  23. litestar_keycloak-0.1.0/pyproject.toml +93 -0
  24. litestar_keycloak-0.1.0/src/litestar_keycloak/__init__.py +23 -0
  25. litestar_keycloak-0.1.0/src/litestar_keycloak/auth.py +109 -0
  26. litestar_keycloak-0.1.0/src/litestar_keycloak/config.py +161 -0
  27. litestar_keycloak-0.1.0/src/litestar_keycloak/dependencies.py +58 -0
  28. litestar_keycloak-0.1.0/src/litestar_keycloak/exceptions.py +155 -0
  29. litestar_keycloak-0.1.0/src/litestar_keycloak/guards.py +148 -0
  30. litestar_keycloak-0.1.0/src/litestar_keycloak/models.py +188 -0
  31. litestar_keycloak-0.1.0/src/litestar_keycloak/plugin.py +100 -0
  32. litestar_keycloak-0.1.0/src/litestar_keycloak/py.typed +0 -0
  33. litestar_keycloak-0.1.0/src/litestar_keycloak/routes.py +172 -0
  34. litestar_keycloak-0.1.0/src/litestar_keycloak/token.py +234 -0
  35. litestar_keycloak-0.1.0/tests/__init__.py +1 -0
  36. litestar_keycloak-0.1.0/tests/conftest.py +126 -0
  37. litestar_keycloak-0.1.0/tests/fixtures/realm-export.json +91 -0
  38. litestar_keycloak-0.1.0/tests/integration/conftest.py +115 -0
  39. litestar_keycloak-0.1.0/tests/integration/test_guards_integration.py +42 -0
  40. litestar_keycloak-0.1.0/tests/integration/test_jwks_refresh.py +50 -0
  41. litestar_keycloak-0.1.0/tests/integration/test_oidc_flow.py +41 -0
  42. litestar_keycloak-0.1.0/tests/integration/test_routes_integration.py +64 -0
  43. litestar_keycloak-0.1.0/tests/integration/test_token_validation.py +80 -0
  44. litestar_keycloak-0.1.0/tests/test_plugin.py +139 -0
  45. litestar_keycloak-0.1.0/tests/test_routes.py +215 -0
  46. litestar_keycloak-0.1.0/tests/unit/conftest.py +107 -0
  47. litestar_keycloak-0.1.0/tests/unit/test_auth.py +272 -0
  48. litestar_keycloak-0.1.0/tests/unit/test_config.py +234 -0
  49. litestar_keycloak-0.1.0/tests/unit/test_dependencies.py +90 -0
  50. litestar_keycloak-0.1.0/tests/unit/test_exceptions.py +132 -0
  51. litestar_keycloak-0.1.0/tests/unit/test_guards.py +184 -0
  52. litestar_keycloak-0.1.0/tests/unit/test_jwks_cache.py +170 -0
  53. litestar_keycloak-0.1.0/tests/unit/test_models.py +332 -0
  54. litestar_keycloak-0.1.0/tests/unit/test_token.py +104 -0
  55. litestar_keycloak-0.1.0/uv.lock +2038 -0
@@ -0,0 +1,96 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint-and-test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Set up Python
16
+ uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v4
22
+
23
+ - name: Install dependencies
24
+ run: uv sync
25
+
26
+ - name: Ruff check
27
+ run: uv run ruff check .
28
+
29
+ - name: Ruff format
30
+ run: uv run ruff format --check .
31
+
32
+ - name: Mypy
33
+ run: uv run mypy src/
34
+
35
+ - name: Pytest
36
+ run: uv run pytest
37
+
38
+ integration-tests:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v5
45
+ with:
46
+ python-version: "3.12"
47
+
48
+ - name: Install uv
49
+ uses: astral-sh/setup-uv@v4
50
+
51
+ - name: Install dependencies
52
+ run: uv sync
53
+
54
+ - name: Pytest (integration)
55
+ run: uv run pytest -m integration -v --timeout=120
56
+
57
+ build-docs:
58
+ runs-on: ubuntu-latest
59
+ needs: lint-and-test
60
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
61
+ steps:
62
+ - uses: actions/checkout@v4
63
+
64
+ - name: Set up Python
65
+ uses: actions/setup-python@v5
66
+ with:
67
+ python-version: "3.12"
68
+
69
+ - name: Install uv
70
+ uses: astral-sh/setup-uv@v4
71
+
72
+ - name: Install dependencies
73
+ run: uv sync
74
+
75
+ - name: Build MkDocs
76
+ run: uv run mkdocs build
77
+
78
+ - name: Upload Pages artifact
79
+ uses: actions/upload-pages-artifact@v3
80
+ with:
81
+ path: site
82
+
83
+ deploy-pages:
84
+ runs-on: ubuntu-latest
85
+ needs: build-docs
86
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
87
+ permissions:
88
+ pages: write
89
+ id-token: write
90
+ environment:
91
+ name: github-pages
92
+ url: ${{ steps.deployment.outputs.page_url }}
93
+ steps:
94
+ - name: Deploy to GitHub Pages
95
+ id: deployment
96
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,74 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Set up Python
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+
22
+ - name: Install dependencies
23
+ run: uv sync
24
+
25
+ - name: Ruff check
26
+ run: uv run ruff check .
27
+
28
+ - name: Ruff format
29
+ run: uv run ruff format --check .
30
+
31
+ - name: Mypy
32
+ run: uv run mypy src/
33
+
34
+ - name: Pytest
35
+ run: uv run pytest
36
+
37
+ build:
38
+ runs-on: ubuntu-latest
39
+ needs: test
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v5
45
+ with:
46
+ python-version: "3.12"
47
+
48
+ - name: Install uv
49
+ uses: astral-sh/setup-uv@v4
50
+
51
+ - name: Build package
52
+ run: uv build
53
+
54
+ - name: Upload distributions
55
+ uses: actions/upload-artifact@v4
56
+ with:
57
+ name: dist
58
+ path: dist/
59
+
60
+ publish:
61
+ runs-on: ubuntu-latest
62
+ needs: build
63
+ permissions:
64
+ id-token: write
65
+ environment: pypi
66
+ steps:
67
+ - name: Download distributions
68
+ uses: actions/download-artifact@v4
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+
73
+ - name: Publish to PyPI
74
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,212 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # SageMath parsed files
135
+ *.sage.py
136
+
137
+ # Environments
138
+ .env
139
+ .envrc
140
+ .venv
141
+ env/
142
+ venv/
143
+ ENV/
144
+ env.bak/
145
+ venv.bak/
146
+
147
+ # Spyder project settings
148
+ .spyderproject
149
+ .spyproject
150
+
151
+ # Rope project settings
152
+ .ropeproject
153
+
154
+ # mkdocs documentation
155
+ /site
156
+
157
+ # mypy
158
+ .mypy_cache/
159
+ .dmypy.json
160
+ dmypy.json
161
+
162
+ # Pyre type checker
163
+ .pyre/
164
+
165
+ # pytype static type analyzer
166
+ .pytype/
167
+
168
+ # Cython debug symbols
169
+ cython_debug/
170
+
171
+ # PyCharm
172
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
175
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
+ #.idea/
177
+
178
+ # Abstra
179
+ # Abstra is an AI-powered process automation framework.
180
+ # Ignore directories containing user credentials, local state, and settings.
181
+ # Learn more at https://abstra.io/docs
182
+ .abstra/
183
+
184
+ # Visual Studio Code
185
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
+ # you could uncomment the following to ignore the entire vscode folder
189
+ # .vscode/
190
+
191
+ # Ruff stuff:
192
+ .ruff_cache/
193
+
194
+ # PyPI configuration file
195
+ .pypirc
196
+
197
+ # Cursor
198
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200
+ # refer to https://docs.cursor.com/context/ignore-files
201
+ .cursorignore
202
+ .cursorindexingignore
203
+
204
+ # Marimo
205
+ marimo/_static/
206
+ marimo/_lsp/
207
+ __marimo__/
208
+
209
+ # MacOS
210
+
211
+ .DS_Store
212
+ .python-version
@@ -0,0 +1,38 @@
1
+ # See https://pre-commit.com for more information
2
+ # Ruff and ruff-format run via uv so they use the same version as CI (uv run ruff).
3
+ repos:
4
+ - repo: local
5
+ hooks:
6
+ - id: ruff
7
+ name: ruff
8
+ entry: uv run ruff check
9
+ language: system
10
+ types: [python]
11
+ - id: ruff-format
12
+ name: ruff-format
13
+ entry: uv run ruff format
14
+ language: system
15
+ types: [python]
16
+
17
+ - repo: local
18
+ hooks:
19
+ - id: mypy
20
+ name: mypy
21
+ entry: uv run mypy src/
22
+ language: system
23
+ pass_filenames: false
24
+
25
+ - id: pytest
26
+ name: pytest (unit)
27
+ entry: uv run pytest -m "not integration"
28
+ language: system
29
+ pass_filenames: false
30
+
31
+ - repo: https://github.com/pre-commit/pre-commit-hooks
32
+ rev: v5.0.0
33
+ hooks:
34
+ - id: trailing-whitespace
35
+ - id: end-of-file-fixer
36
+ - id: check-yaml
37
+ - id: check-added-large-files
38
+ args: ["--maxkb=1000"]
@@ -0,0 +1,18 @@
1
+ .PHONY: check test test-unit test-integration test-examples
2
+
3
+ check:
4
+ uv run ruff check .
5
+ uv run ruff format --check .
6
+ uv run mypy src/
7
+ uv run pytest -m "not integration"
8
+
9
+ test-unit:
10
+ uv run pytest
11
+
12
+ test-integration:
13
+ uv run pytest -m integration -v --timeout=120
14
+
15
+ test: test-unit test-integration
16
+
17
+ test-examples:
18
+ ./examples/test.sh
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: litestar-keycloak
3
+ Version: 0.1.0
4
+ Summary: Keycloak OIDC/OAuth2 authentication plugin for Litestar
5
+ Project-URL: Homepage, https://github.com/smirnoffmg/litestar-keycloak
6
+ Project-URL: Documentation, https://smirnoffmg.github.io/litestar-keycloak/
7
+ Project-URL: Repository, https://github.com/smirnoffmg/litestar-keycloak
8
+ Project-URL: Issues, https://github.com/smirnoffmg/litestar-keycloak/issues
9
+ Project-URL: Changelog, https://github.com/smirnoffmg/litestar-keycloak/releases
10
+ Author-email: Maksim Smirnov <smirnoffmg@gmail.com>
11
+ License-Expression: MIT
12
+ Keywords: authentication,jwt,keycloak,litestar,oauth2,oidc
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.12
25
+ Requires-Dist: aiohttp>=3.9
26
+ Requires-Dist: litestar>=2.0
27
+ Requires-Dist: pyjwt[crypto]>=2.0
28
+ Description-Content-Type: text/markdown
29
+
30
+ # litestar-keycloak
31
+
32
+ Keycloak authentication plugin for [Litestar](https://litestar.dev/).
33
+ OIDC/OAuth2 integration using Litestar's native plugin protocol, dependency injection, and guard system.
34
+
35
+ ## Features
36
+
37
+ - OIDC discovery and JWKS caching with automatic key rotation
38
+ - Bearer token validation (header or cookie)
39
+ - Realm and client role guards
40
+ - Scope-based access control
41
+ - `KeycloakUser` injection via Litestar DI
42
+ - Optional login/callback/logout route group
43
+ - Async HTTP via aiohttp for token exchange and JWKS requests
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install litestar-keycloak
49
+ ```
50
+
51
+ ## Quick Start
52
+
53
+ ```python
54
+ from litestar import Litestar, get
55
+ from litestar_keycloak import KeycloakPlugin, KeycloakConfig, KeycloakUser
56
+
57
+ @get("/me")
58
+ async def me(current_user: KeycloakUser) -> dict:
59
+ return {
60
+ "sub": current_user.sub,
61
+ "username": current_user.preferred_username,
62
+ "roles": current_user.realm_roles,
63
+ }
64
+
65
+ app = Litestar(
66
+ route_handlers=[me],
67
+ plugins=[KeycloakPlugin(
68
+ KeycloakConfig(
69
+ server_url="https://keycloak.example.com",
70
+ realm="my-realm",
71
+ client_id="my-app",
72
+ )
73
+ )],
74
+ )
75
+ ```
76
+
77
+ Any route that declares `current_user: KeycloakUser` automatically requires a valid Bearer token.
78
+
79
+ ## Guards
80
+
81
+ Restrict access by roles or scopes:
82
+
83
+ ```python
84
+ from litestar import get
85
+ from litestar_keycloak import require_roles, require_scopes
86
+
87
+ @get("/admin", guards=[require_roles("admin")])
88
+ async def admin_panel() -> dict:
89
+ return {"msg": "welcome, admin"}
90
+
91
+ @get("/reports", guards=[require_scopes("reports:read")])
92
+ async def reports() -> dict:
93
+ return {"msg": "here are your reports"}
94
+ ```
95
+
96
+ ## Configuration
97
+
98
+ ```python
99
+ KeycloakConfig(
100
+ server_url="https://keycloak.example.com",
101
+ realm="my-realm",
102
+ client_id="my-app",
103
+ client_secret="secret", # confidential clients
104
+ token_location=TokenLocation.HEADER, # HEADER (default) or COOKIE
105
+ jwks_cache_ttl=3600, # JWKS cache lifetime in seconds
106
+ algorithms=("RS256",), # JWT signing algorithms
107
+ include_routes=False, # mount /auth/login, /callback, /logout
108
+ optional_audiences=frozenset({"my-service"}), # accept service tokens too
109
+ )
110
+ ```
111
+
112
+ Full option list: [docs/configuration.md](docs/configuration.md).
113
+
114
+ When `include_routes=True`, the plugin mounts:
115
+
116
+ | Endpoint | Description |
117
+ | -------------------- | ---------------------------------- |
118
+ | `GET /auth/login` | Redirect to Keycloak authorize |
119
+ | `GET /auth/callback` | Handle authorization code exchange |
120
+ | `POST /auth/logout` | End session (Keycloak + local) |
121
+ | `POST /auth/refresh` | Refresh access token |
122
+
123
+ ## Testing
124
+
125
+ ### Unit tests (no Keycloak required)
126
+
127
+ 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):
128
+
129
+ ```python
130
+ from tests.conftest import create_test_token, MockKeycloakPlugin
131
+
132
+ # Mint a fake JWT with arbitrary claims
133
+ token = create_test_token(sub="user-1", realm_roles=["admin"])
134
+
135
+ # Use MockKeycloakPlugin to skip real JWKS validation
136
+ app = Litestar(
137
+ route_handlers=[...],
138
+ plugins=[MockKeycloakPlugin()],
139
+ )
140
+ ```
141
+
142
+ ### Integration tests (real Keycloak via testcontainers)
143
+
144
+ ```python
145
+ from testcontainers.keycloak import KeycloakContainer
146
+
147
+ kc = (
148
+ KeycloakContainer("quay.io/keycloak/keycloak:26.0")
149
+ .with_command("start-dev --import-realm")
150
+ .with_volume_mapping(
151
+ "tests/fixtures/realm-export.json",
152
+ "/opt/keycloak/data/import/realm-export.json",
153
+ "ro",
154
+ )
155
+ )
156
+ kc.start()
157
+ ```
158
+
159
+ See `tests/fixtures/realm-export.json` for a pre-configured realm with test users and roles.
160
+
161
+ ## Service-to-service
162
+
163
+ 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](docs/guides/service-to-service.md).
164
+
165
+ ## Documentation
166
+
167
+ - **Guides**: [Configuration](docs/configuration.md), [Guards](docs/guides/guards.md), [OIDC routes](docs/guides/oidc-routes.md), [Testing](docs/guides/testing.md). Build the site with `mkdocs build` (or `mkdocs serve`).
168
+ - **Example app** with docker-compose and smoke tests: [examples/](examples/).
169
+
170
+ ## Dependencies
171
+
172
+ | Package | Purpose |
173
+ | -------------------------- | ------------------------------- |
174
+ | `litestar[standard]` ≥ 2.0 | Web framework (plugin target) |
175
+ | `aiohttp` | Async HTTP for token/JWKS calls |
176
+ | `PyJWT[crypto]` | JWT validation |
177
+
178
+ ## License
179
+
180
+ MIT