Customization and application ownership#
Core integration ports are deliberately small and atomic. Implement the
protocols needed by the selected feature, then run the matching helpers from
litestar_security.testing. InMemorySecurityBackend and deterministic
provider transports are references for tests and examples, not production
persistence.
Verify your backend#
Pass a fresh, isolated instance of each application-owned atomic port to its matching conformance helper. The in-memory backend is a deterministic reference for the store contracts; it is not a production persistence implementation.
Protocol |
Conformance helper |
Reference |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OAuth account-store conformance helpers |
|
|
|
|
|
|
For example, supply only the capabilities that the application implements. A factory must return a new store each time so the isolation checks can detect shared state:
from litestar_security.testing import (
InMemoryMFAStore,
InMemorySecurityBackend,
StoreConformanceFactories,
assert_security_backend_conformance,
)
async def verify_backend() -> None:
await assert_security_backend_conformance(
StoreConformanceFactories(
api_key_store=lambda: InMemorySecurityBackend().api_keys,
mfa_store=InMemoryMFAStore,
)
)
Rate limiters use the standalone
assert_rate_limiter_conformance() helper,
because their factory is not a security-store factory.
The OIDC logout helper requires each fresh factory to seed two matching local session mappings, one unrelated mapping, and the exact front-channel browser binding described by the helper docstring. This fixed scenario makes replay, ownership, binding, and contention results portable across backends.
OAuth token retention and revocation-retry staging are part of the aggregate
OAuthAccountStore contract. Applications should test their implementation
with assert_oauth_account_store_conformance rather than exposing separate
token-vault or retry-store collaborators.
Testing-only resolver and WebSocket lifetime references keep examples small:
from litestar_security import Principal
from litestar_security.context import AuthorizationSnapshot
from litestar_security.testing import (
InMemoryWebSocketRevocationSource,
StaticAuthorizationResolver,
StaticAuthorizationSnapshotRefresher,
StaticIdentityResolver,
)
identity = StaticIdentityResolver(Principal(id="test-user"))
authorization = StaticAuthorizationResolver(
AuthorizationSnapshot(scopes={"reports:read"})
)
revocations = InMemoryWebSocketRevocationSource()
refresher = StaticAuthorizationSnapshotRefresher(
AuthorizationSnapshot(scopes={"reports:read"})
)
These deterministic objects are for tests and examples, not production identity resolution, authorization, or cross-worker revocation delivery.
Async implementations stay on the event loop. Wrap a complete synchronous port
in BlockingIntegration so the runtime can use the configured bounded worker
budget. Do not wrap individual calls or perform hidden blocking I/O in async
methods.
Litestar Security does not install a general administrator API. The
custom-admin example owns a controller and applies application guards while
orchestrating disable, forced reset, and credential/factor/session/key
revocation services:
LITESTAR_SECURITY_EXAMPLE=custom-admin uv run litestar --app examples.app:create_app run
Provider HTTP transports, delivery commands, audit sinks, metrics, rate-limit stores, user resolution, role/team/tenant snapshots, and key-management clients remain application choices.