Skip to content

Commit 1b77f11

Browse files
feat(extensions): versioned public extension SDK
Promote the internal extension kernel to a stable public authoring SDK: - public `litestar_auth.extensions` facade as the single author import path - versioned boundary: EXTENSION_API_VERSION + optional requires_api, fail-closed - enabled toggle and opt-in entry-point discovery (group "litestar_auth.extensions") - AuthCliExtension and AuthEventSubscriberExtension contribution surfaces - self-declaring internal extension descriptors; resolve_extensions memoized
1 parent 5613d42 commit 1b77f11

81 files changed

Lines changed: 9056 additions & 418 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/1_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ jobs:
176176
177177
# Codecov flags: ^[\w.\-]{1,45}$ (alphanumeric, _, -, .); see https://docs.codecov.com/docs/flags
178178
- name: Upload coverage to Codecov
179-
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
179+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
180180
with:
181181
fail_ci_if_error: true
182182
files: coverage.xml

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ repos:
8787
exclude: |
8888
(?x)^(
8989
tests/_helpers\.py|
90-
tests/integration/_di_probes\.py
90+
tests/integration/_di_probes\.py|
91+
tests/support/extensions\.py
9192
)$
9293
- id: no-commit-to-branch
9394
args: [--branch, main]

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1+
## Unreleased
2+
3+
### Added
4+
5+
- **Public extension SDK (`litestar_auth.extensions`).** The internal extension kernel is now a stable,
6+
publicly documented authoring surface. `litestar_auth.extensions` is the only import path an external
7+
extension needs — it re-exports the `AuthExtension` contract together with its
8+
`AuthExtensionValidationContext` / `AuthExtensionRegistrationContext` typed contexts (all three are now
9+
also exported from the root `litestar_auth` package), `EXTENSION_API_VERSION`,
10+
`EXTENSION_ENTRY_POINT_GROUP`, and the public controller factories authors need (reaching into
11+
`litestar_auth._plugin.*` is unsupported and guarded by an import-isolation regression test). Register
12+
extensions explicitly via `LitestarAuthConfig.extensions=(...)`; an extension contributes controllers,
13+
dependencies, middleware, OpenAPI security schemes, startup/shutdown hooks, and exception handlers
14+
through the typed contexts. An optional `enabled` flag toggles an extension off without removing it from
15+
configuration (omitting it is treated as enabled).
16+
17+
- **Versioned extension boundary.** `EXTENSION_API_VERSION` (currently `(1, 0)`) versions the authoring
18+
contract independently of the rest of the library. An extension may declare a `requires_api` minimum;
19+
an incompatible requirement fails closed with `ConfigurationError` before any application wiring runs.
20+
21+
- **Opt-in entry-point discovery.** New `LitestarAuthConfig.auto_discover_extensions` (default `False`)
22+
loads external extensions published under the `litestar_auth.extensions` entry-point group, following
23+
the `litestar_auth_ext_*` distribution-naming convention. Discovery is fail-closed: an entry point that
24+
cannot load, cannot instantiate, or does not produce a valid `AuthExtension` raises `ConfigurationError`.
25+
26+
- **CLI and manager-event contribution surfaces.** New optional `AuthCliExtension` lets an extension
27+
contribute CLI commands; `on_cli_init` now routes through it (the built-in organization administration
28+
CLI is its first internal consumer). New optional `AuthEventSubscriberExtension` lets an extension
29+
observe manager lifecycle events through **redacted** payloads — token-bearing values and credential
30+
fields (passwords, hashed passwords, invitation tokens, and similar) are stripped before delivery.
31+
32+
- **`RoleAdminExtension` first-party extension.** The contrib role-administration controller is now also
33+
available as a public `RoleAdminExtension` (import from `litestar_auth.contrib.role_admin`) for explicit
34+
`extensions=(...)` registration — the canonical example of the user-facing authoring API. A runnable
35+
reference is bundled at `examples/demo_external_extension/`.
36+
37+
### Changed
38+
39+
- **Built-in optional features now wire through the extension pipeline.** OAuth, TOTP, user-owned API
40+
keys, and organization administration/invitations are auto-materialized into the same extension pipeline
41+
from their existing configuration flags. Behavior for existing configurations is unchanged. As part of
42+
this, `LitestarAuthConfig.resolve_extensions()` is memoized, so extension-relevant configuration must be
43+
set before the plugin first resolves extensions during application init.
44+
45+
- **Extension OpenAPI security scheme names must be globally unique and fail closed on collision.**
46+
Extension-contributed OpenAPI security scheme names may not collide with each other or with core auth
47+
scheme names (bearer/cookie backend names, `apiKeyAuth`, and `apiKeyHmacAuth` for signed API keys). A
48+
collision raises `ConfigurationError` during application initialization, before the app is usable.
49+
150
## 4.2.0 (2026-06-05)
251

352
### Added

docs/api/package.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,16 @@ Treat the startup templates as plugin-assembly inventory only: they preserve bac
8585
Relational role storage changes persistence only. Public HTTP payloads, managers, and guard
8686
factories still use one normalized flat `roles` collection. The core plugin-owned auth/users route
8787
table does not auto-mount role catalog or user-assignment endpoints; use the opt-in
88-
`litestar_auth.contrib.role_admin` controller or the `litestar roles` CLI for admin operations.
88+
`litestar_auth.contrib.role_admin.RoleAdminExtension`, the manual contrib controller
89+
factory, or the `litestar roles` CLI for admin operations.
8990
The library does not ship permission matrices.
9091

92+
Organization administration is also available through the extension kernel. Use
93+
`litestar_auth.contrib.organization_admin.OrganizationAdminExtension` for plugin-managed
94+
organization-admin HTTP routes, or keep using the public manual factories for custom route tables.
95+
The extension requires `OrganizationConfig(enabled=True, store_factory=...)` and an `id_parser`, and
96+
its invitee-facing accept/decline routes are mounted only with `include_invitations=True`.
97+
9198
## Public surface (high level)
9299

93100
| Area | Types / functions |
@@ -101,7 +108,8 @@ The library does not ship permission matrices.
101108
| Errors | `ErrorCode`, `LitestarAuthError`; typed subclasses from `litestar_auth.exceptions` |
102109
| Protocols | `UserProtocol`, `GuardedUserProtocol`, `RoleCapableUserProtocol`, `TotpUserProtocol`[Types](types.md) |
103110
| Controllers (advanced) | `create_*_controller` factories from `litestar_auth.controllers`[Controllers API](controllers.md) |
104-
| Contrib role admin | `create_role_admin_controller` from `litestar_auth.contrib.role_admin`[HTTP role administration](../guides/role_admin_http.md) |
111+
| Contrib role admin | `RoleAdminExtension`, `RoleAdminControllerConfig`, `create_role_admin_controller` from `litestar_auth.contrib.role_admin`[HTTP role administration](../guides/role_admin_http.md) |
112+
| Contrib organization admin | `OrganizationAdminExtension`, `OrganizationAdminControllerConfig`, `OrganizationInvitationControllerConfig`, `create_organization_admin_controller`, `create_organization_invitation_controller` from `litestar_auth.contrib.organization_admin`[Organizations](../configuration/organizations.md#administration) |
105113
| OAuth helpers | Plugin-managed route table via `OAuthConfig`; manual login helper and lazy client loader from `litestar_auth.oauth` |
106114
| TOTP | `generate_totp_secret`, `generate_totp_uri`, `verify_totp`, stores from `litestar_auth.totp` |
107115
| Rate limit | `AuthRateLimitConfig`, `EndpointRateLimit`, `InMemoryRateLimiter`, `RedisRateLimiter` from `litestar_auth.ratelimit` |

docs/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ from litestar_auth.models import User # or your own model
2424
| `TotpConfig` fields, TOTP route behavior, and TOTP step-up policy | [TOTP](configuration/totp.md) |
2525
| `OAuthConfig`, provider inventory, redirect policy, and token encryption | [OAuth](configuration/oauth.md) |
2626
| `OrganizationConfig`, organization model imports, and organization store wiring | [Organizations](configuration/organizations.md) |
27+
| `extensions` config field, Step-1 `AuthExtension` contract, and extension context interfaces | [Extensions](configuration/extensions.md) |
2728
| CSRF, legacy-token policy, dependency keys, and shared helpers | [Security and DI](configuration/security.md) |
2829
| Operator-side proxy, cookie, and secrets-at-rest preconditions | [Deployment security contract](deployment.md#deployment-security-contract) |
2930

docs/configuration/api_keys.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
API-key authentication is opt-in. Set `LitestarAuthConfig.api_keys` with `ApiKeyConfig(enabled=True, ...)` to add the
44
API-key backend, self-service routes under `/api-keys`, admin routes under `/users/{user_id}/api-keys`, and the
5-
`apiKeyAuth` OpenAPI security scheme.
5+
`apiKeyAuth` OpenAPI security scheme. The plugin mounts the management routes through an internal extension derived from
6+
`api_keys.enabled`; the API-key authentication backend remains configured by the normal backend registry.
67

78
```python
89
from datetime import timedelta
@@ -69,6 +70,10 @@ Admin routes are guarded by `is_superuser` and use the path user id as authority
6970
`DELETE /users/{user_id}/api-keys/{key_id}`. Request bodies never choose the target user, and admin
7071
create requests do not require the target user's `current_password`.
7172

73+
For advanced manual routing, `create_api_keys_controllers(...)` and `ApiKeysControllerConfig(...)` remain public. That
74+
factory builds the same self-service and admin controller classes used by the plugin-managed extension; leave
75+
`api_keys.enabled=False` when mounting those controllers yourself to avoid duplicate routes.
76+
7277
When TOTP is enrolled, API-key create/update/revoke routes use the `api_keys.*` entries in
7378
`LitestarAuthConfig.totp_stepup_policy` and default to `required_when_enrolled`. `POST` and `PATCH`
7479
bodies accept `totp_code` as an inline proof; otherwise callers need a recent TOTP marker from the

0 commit comments

Comments
 (0)