Skip to content

Plugin and configuration

Public plugin facade and configuration dataclasses exported from litestar_auth.plugin, plus shared configuration helpers from litestar_auth.config.

ApiKeyConfig is the plugin-owned API-key feature switch. When enabled=True, it participates in backend resolution, request-scoped store binding, controller registration, validation, and OpenAPI security scheme generation. apiKeyAuth is registered for the bearer/header API-key transport; apiKeyHmacAuth is registered only when signing support is configured.

litestar_auth.plugin

Litestar plugin/orchestrator facade for wiring the auth library into an app.

LitestarAuth(config)

Bases: InitPlugin, CLIPlugin

Main auth orchestrator that wires middleware, controllers, and DI.

Store the plugin configuration and validate the requested setup.

Parameters:

Name Type Description Default
config LitestarAuthConfig[UP, ID]

Fully specified plugin configuration (session factory, backends, user manager factory, optional OAuth/TOTP settings).

required
Source code in litestar_auth/plugin.py
def __init__(self, config: LitestarAuthConfig[UP, ID]) -> None:
    """Store the plugin configuration and validate the requested setup.

    Args:
        config: Fully specified plugin configuration (session factory, backends,
            user manager factory, optional OAuth/TOTP settings).
    """
    self.config = config
    self._oauth_token_encryption = _build_oauth_token_encryption(self.config)
    validate_config(self.config)
    self._session_maker = _plugin_config.require_session_maker(self.config)
    from litestar_auth._plugin.user_manager_builder import resolve_user_manager_factory  # noqa: PLC0415

    self._user_manager_factory = resolve_user_manager_factory(self.config)
    self._provide_user_manager = _make_user_manager_dependency_provider(
        self._build_user_manager,
        self.config.db_session_dependency_key,
    )
    self._provide_request_backends = _make_backends_dependency_provider(
        self._session_bound_backends,
        self.config.db_session_dependency_key,
    )
    self._provide_oauth_associate_user_manager = _make_user_manager_dependency_provider(
        self._build_user_manager,
        self.config.db_session_dependency_key,
    )

on_app_init(app_config)

Register auth middleware, controllers, and dependencies on the app.

Returns:

Type Description
AppConfig

The updated application config.

Source code in litestar_auth/plugin.py
@override
def on_app_init(self, app_config: AppConfig) -> AppConfig:
    """Register auth middleware, controllers, and dependencies on the app.

    Returns:
        The updated application config.
    """
    require_shared_rate_limit_backends_for_multiworker(self.config)
    require_refreshable_strategy_when_enable_refresh(self.config)
    warn_insecure_plugin_startup_defaults(self.config)
    require_oauth_token_encryption_for_configured_providers(
        config=self.config,
        require_key=partial(require_oauth_token_encryption, self._oauth_token_encryption),
    )
    require_secure_oauth_redirect_in_production(config=self.config, app_config=app_config)
    bootstrap_bundled_token_orm_models(self.config)
    self._register_dependencies(app_config)
    self._register_middleware(app_config)
    security = self._register_openapi_security(app_config)
    self._register_controllers(app_config, security=security)
    self._register_exception_handlers(app_config.route_handlers)
    return app_config

on_cli_init(cli)

Register plugin-owned CLI commands without affecting app startup wiring.

Source code in litestar_auth/plugin.py
@override
def on_cli_init(self, cli: Group) -> None:
    """Register plugin-owned CLI commands without affecting app startup wiring."""
    from litestar_auth._plugin.role_cli import register_roles_cli  # noqa: PLC0415

    register_roles_cli(cli, self.config)

OAuthProviderConfig(name, client) dataclass

One entry in :attr:~litestar_auth._plugin.config.OAuthConfig.oauth_providers.

Plugin-owned OAuth routes use name as the {provider} segment (for example GET {auth_path}/oauth/{name}/callback). client must be an httpx-oauth-compatible OAuth2 client instance (typically :class:httpx_oauth.oauth2.BaseOAuth2).

The client field is typed as :class:object so core modules do not require a hard dependency on httpx-oauth at import time.

Attributes:

Name Type Description
name str

Logical provider name used in URLs and oauth_provider_scopes keys.

client object

OAuth2 client instance passed through to the OAuth service layer.

__post_init__()

Validate provider names before they are used in routes, cookies, and callback URLs.

Source code in litestar_auth/config.py
def __post_init__(self) -> None:
    """Validate provider names before they are used in routes, cookies, and callback URLs."""
    validate_oauth_provider_name(self.name)

coerce(value) classmethod

Return value when it is already an :class:OAuthProviderConfig.

Raises:

Type Description
TypeError

If value is not an :class:OAuthProviderConfig.

Source code in litestar_auth/config.py
@classmethod
def coerce(cls, value: object) -> OAuthProviderConfig:
    """Return ``value`` when it is already an :class:`OAuthProviderConfig`.

    Raises:
        TypeError: If ``value`` is not an :class:`OAuthProviderConfig`.
    """
    if isinstance(value, cls):
        return value
    msg = "OAuth provider entries must be OAuthProviderConfig(name=..., client=...)."
    raise TypeError(msg)

litestar_auth.config

Central configuration helpers for litestar-auth.

This module contains small, shared primitives used across the library to keep security-relevant validation consistent, including secret-length checks and explicit unsafe-testing overrides.

OAuthProviderConfig(name, client) dataclass

One entry in :attr:~litestar_auth._plugin.config.OAuthConfig.oauth_providers.

Plugin-owned OAuth routes use name as the {provider} segment (for example GET {auth_path}/oauth/{name}/callback). client must be an httpx-oauth-compatible OAuth2 client instance (typically :class:httpx_oauth.oauth2.BaseOAuth2).

The client field is typed as :class:object so core modules do not require a hard dependency on httpx-oauth at import time.

Attributes:

Name Type Description
name str

Logical provider name used in URLs and oauth_provider_scopes keys.

client object

OAuth2 client instance passed through to the OAuth service layer.

__post_init__()

Validate provider names before they are used in routes, cookies, and callback URLs.

Source code in litestar_auth/config.py
def __post_init__(self) -> None:
    """Validate provider names before they are used in routes, cookies, and callback URLs."""
    validate_oauth_provider_name(self.name)

coerce(value) classmethod

Return value when it is already an :class:OAuthProviderConfig.

Raises:

Type Description
TypeError

If value is not an :class:OAuthProviderConfig.

Source code in litestar_auth/config.py
@classmethod
def coerce(cls, value: object) -> OAuthProviderConfig:
    """Return ``value`` when it is already an :class:`OAuthProviderConfig`.

    Raises:
        TypeError: If ``value`` is not an :class:`OAuthProviderConfig`.
    """
    if isinstance(value, cls):
        return value
    msg = "OAuth provider entries must be OAuthProviderConfig(name=..., client=...)."
    raise TypeError(msg)

SecretRoleValues(verification_token_secret, reset_password_token_secret, login_identifier_telemetry_secret=None, totp_secret_key=None, totp_pending_secret=None, totp_recovery_code_lookup_secret=None, oauth_flow_cookie_secret=None, api_key_hash_secret=None, api_key_secret_encryption_key=None) dataclass

Configured secret material grouped by the auth role each value protects.

as_role_pairs()

Return role metadata paired with the configured secret material.

Source code in litestar_auth/_secret_roles.py
def as_role_pairs(self) -> tuple[tuple[_SecretRole, str | None], ...]:
    """Return role metadata paired with the configured secret material."""
    return (
        (_VERIFICATION_TOKEN_SECRET_ROLE, self.verification_token_secret),
        (_RESET_PASSWORD_TOKEN_SECRET_ROLE, self.reset_password_token_secret),
        (_LOGIN_IDENTIFIER_TELEMETRY_SECRET_ROLE, self.login_identifier_telemetry_secret),
        (_TOTP_SECRET_KEY_ROLE, self.totp_secret_key),
        (_TOTP_PENDING_SECRET_ROLE, self.totp_pending_secret),
        (_TOTP_RECOVERY_CODE_LOOKUP_SECRET_ROLE, self.totp_recovery_code_lookup_secret),
        (_OAUTH_FLOW_COOKIE_SECRET_ROLE, self.oauth_flow_cookie_secret),
        (_API_KEY_HASH_SECRET_ROLE, self.api_key_hash_secret),
        (_API_KEY_SECRET_ENCRYPTION_KEY_ROLE, self.api_key_secret_encryption_key),
    )

require_password_length(password, minimum_length=DEFAULT_MINIMUM_PASSWORD_LENGTH, *, maximum_length=MAX_PASSWORD_LENGTH)

Raise when a password falls outside the configured length bounds.

The default minimum_length matches the password-length metadata exposed for app-owned user schemas via litestar_auth.schemas.UserPasswordField.

Raises:

Type Description
ValueError

If password exceeds maximum_length or is shorter than minimum_length.

Source code in litestar_auth/config.py
def require_password_length(
    password: str,
    minimum_length: int = DEFAULT_MINIMUM_PASSWORD_LENGTH,
    *,
    maximum_length: int = MAX_PASSWORD_LENGTH,
) -> None:
    """Raise when a password falls outside the configured length bounds.

    The default ``minimum_length`` matches the password-length metadata exposed
    for app-owned user schemas via ``litestar_auth.schemas.UserPasswordField``.

    Raises:
        ValueError: If ``password`` exceeds ``maximum_length`` or is shorter
            than ``minimum_length``.
    """
    if len(password) > maximum_length:
        msg = f"Password must be at most {maximum_length} characters long."
        raise ValueError(msg)

    if len(password) < minimum_length:
        msg = f"Password must be at least {minimum_length} characters long."
        raise ValueError(msg)

resolve_trusted_proxy_setting(*, trusted_proxy)

Validate and normalize trusted-proxy configuration flags.

Parameters:

Name Type Description Default
trusted_proxy object

Candidate trusted-proxy value from configuration.

required

Returns:

Type Description
bool

Normalized trusted-proxy boolean.

Raises:

Type Description
ConfigurationError

If trusted_proxy is not a boolean value.

Source code in litestar_auth/config.py
def resolve_trusted_proxy_setting(*, trusted_proxy: object) -> bool:
    """Validate and normalize trusted-proxy configuration flags.

    Args:
        trusted_proxy: Candidate trusted-proxy value from configuration.

    Returns:
        Normalized trusted-proxy boolean.

    Raises:
        ConfigurationError: If ``trusted_proxy`` is not a boolean value.
    """
    if isinstance(trusted_proxy, bool):
        return trusted_proxy

    msg = "trusted_proxy must be a boolean."
    raise ConfigurationError(msg)

validate_oauth_provider_name(name, *, label='OAuth provider name')

Validate and return a route/cookie/callback-safe OAuth provider name.

Returns:

Type Description
str

The validated provider name.

Raises:

Type Description
ConfigurationError

If name is empty or contains path, cookie, or callback-URL unsafe characters.

Source code in litestar_auth/config.py
def validate_oauth_provider_name(name: str, *, label: str = "OAuth provider name") -> str:
    """Validate and return a route/cookie/callback-safe OAuth provider name.

    Returns:
        The validated provider name.

    Raises:
        ConfigurationError: If ``name`` is empty or contains path, cookie, or
            callback-URL unsafe characters.
    """
    if fullmatch(OAUTH_PROVIDER_NAME_PATTERN, name):
        return name

    msg = (
        f"{label} must match {OAUTH_PROVIDER_NAME_PATTERN!r}: use 1-64 ASCII letters, "
        "digits, underscores, or hyphens, and start/end with an alphanumeric character."
    )
    raise ConfigurationError(msg)

validate_production_secret(secret, *, label, unsafe_testing=False, minimum_length=MINIMUM_SECRET_LENGTH, minimum_entropy_bits=MINIMUM_SECRET_ENTROPY_BITS)

Validate production secret material, preserving explicit test shortcuts.

Production token, HMAC, and encryption secrets need both a length floor and a basic entropy floor. Repeated-character strings such as "a" * 32 pass length-only validation but are not safe signing or encryption material. unsafe_testing=True keeps fixture-only shortcuts possible while making the weaker posture explicit at the call site.

Source code in litestar_auth/config.py
def validate_production_secret(
    secret: str,
    *,
    label: str,
    unsafe_testing: bool = False,
    minimum_length: int = MINIMUM_SECRET_LENGTH,
    minimum_entropy_bits: float = MINIMUM_SECRET_ENTROPY_BITS,
) -> None:
    """Validate production secret material, preserving explicit test shortcuts.

    Production token, HMAC, and encryption secrets need both a length floor and
    a basic entropy floor. Repeated-character strings such as ``"a" * 32`` pass
    length-only validation but are not safe signing or encryption material.
    ``unsafe_testing=True`` keeps fixture-only shortcuts possible while making
    the weaker posture explicit at the call site.
    """
    if unsafe_testing:
        return
    validate_secret_strength(
        secret,
        label=label,
        minimum_length=minimum_length,
        minimum_entropy_bits=minimum_entropy_bits,
    )

validate_secret_length(secret, *, label, minimum_length=MINIMUM_SECRET_LENGTH)

Validate the configured secret length.

Parameters:

Name Type Description Default
secret str

Secret value to validate.

required
label str

Human-readable label used in error messages.

required
minimum_length int

Minimum length in characters.

MINIMUM_SECRET_LENGTH

Raises:

Type Description
ConfigurationError

When secret is shorter than minimum_length.

Source code in litestar_auth/config.py
def validate_secret_length(secret: str, *, label: str, minimum_length: int = MINIMUM_SECRET_LENGTH) -> None:
    """Validate the configured secret length.

    Args:
        secret: Secret value to validate.
        label: Human-readable label used in error messages.
        minimum_length: Minimum length in characters.

    Raises:
        ConfigurationError: When ``secret`` is shorter than ``minimum_length``.
    """
    if len(secret) >= minimum_length:
        return

    msg = f"{label} must be at least {minimum_length} characters."
    raise ConfigurationError(msg)

validate_secret_roles_are_distinct(role_values)

Raise when one configured secret value is reused across distinct auth roles.

Distinct JWT audiences already keep verification, reset-password, and TOTP tokens scoped to their own flows. Production deployments must still keep those secrets separate so one compromise does not widen the blast radius across multiple roles.

Raises:

Type Description
ConfigurationError

If one configured secret value is reused across multiple roles.

Source code in litestar_auth/_secret_roles.py
def validate_secret_roles_are_distinct(role_values: SecretRoleValues) -> None:
    """Raise when one configured secret value is reused across distinct auth roles.

    Distinct JWT audiences already keep verification, reset-password, and TOTP
    tokens scoped to their own flows. Production deployments must still keep
    those secrets separate so one compromise does not widen the blast radius
    across multiple roles.

    Raises:
        ConfigurationError: If one configured secret value is reused across
            multiple roles.
    """
    roles_by_secret: dict[str, list[_SecretRole]] = {}
    for role, secret in role_values.as_role_pairs():
        if not secret:
            continue
        roles_by_secret.setdefault(secret, []).append(role)

    reused_roles = [
        tuple(sorted(roles, key=lambda current_role: current_role.setting_name))
        for roles in roles_by_secret.values()
        if len(roles) > 1
    ]
    if not reused_roles:
        return

    reused_roles.sort(key=lambda roles: tuple(role.setting_name for role in roles))
    role_descriptions = "; ".join(", ".join(role.render_usage() for role in roles) for roles in reused_roles)
    msg = (
        "Distinct secrets/keys are the supported production posture for "
        "verification, reset-password, login telemetry, TOTP, OAuth flow-cookie, and API-key roles. "
        "Distinct JWT audiences "
        "and encrypted-cookie envelopes still prevent token cross-use, but reusing one configured value across "
        "roles increases blast radius if that secret leaks. "
        f"Detected shared secret material across: {role_descriptions}. "
        "Configure one distinct high-entropy value for each secret role, or use "
        "unsafe_testing=True only for test-owned single-process setups."
    )
    raise ConfigurationError(msg)

validate_secret_strength(secret, *, label, minimum_length=MINIMUM_SECRET_LENGTH, minimum_entropy_bits=MINIMUM_SECRET_ENTROPY_BITS)

Validate that a configured secret clears length and Shannon-entropy floors.

The base library checks length only at constructor seams to keep test fixtures interchangeable with production config. validate_secret_strength is the recommended operator-side gate for production deployments: wire it into the application's startup hook (or a custom LitestarAuthConfig bootstrap path) to fail closed on degenerate inputs like "a" * 32, keyboard-mashed strings, or other low-entropy material that the chars-count check alone cannot reject.

Parameters:

Name Type Description Default
secret str

Secret value to validate.

required
label str

Human-readable label used in error messages.

required
minimum_length int

Minimum length in characters (defaults to :data:MINIMUM_SECRET_LENGTH).

MINIMUM_SECRET_LENGTH
minimum_entropy_bits float

Minimum approximate Shannon entropy in bits (defaults to :data:MINIMUM_SECRET_ENTROPY_BITS). Pass 0 to skip the entropy check while keeping length validation.

MINIMUM_SECRET_ENTROPY_BITS

Raises:

Type Description
ConfigurationError

When secret fails either the length floor or the entropy floor. The same exception type is used as :func:validate_secret_length so existing operator handlers stay compatible.

Source code in litestar_auth/config.py
def validate_secret_strength(
    secret: str,
    *,
    label: str,
    minimum_length: int = MINIMUM_SECRET_LENGTH,
    minimum_entropy_bits: float = MINIMUM_SECRET_ENTROPY_BITS,
) -> None:
    """Validate that a configured secret clears length and Shannon-entropy floors.

    The base library checks length only at constructor seams to keep test
    fixtures interchangeable with production config. ``validate_secret_strength``
    is the recommended operator-side gate for production deployments: wire it
    into the application's startup hook (or a custom `LitestarAuthConfig`
    bootstrap path) to fail closed on degenerate inputs like ``"a" * 32``,
    keyboard-mashed strings, or other low-entropy material that the chars-count
    check alone cannot reject.

    Args:
        secret: Secret value to validate.
        label: Human-readable label used in error messages.
        minimum_length: Minimum length in characters (defaults to
            :data:`MINIMUM_SECRET_LENGTH`).
        minimum_entropy_bits: Minimum approximate Shannon entropy in bits
            (defaults to :data:`MINIMUM_SECRET_ENTROPY_BITS`). Pass ``0`` to
            skip the entropy check while keeping length validation.

    Raises:
        ConfigurationError: When ``secret`` fails either the length floor or
            the entropy floor. The same exception type is used as
            :func:`validate_secret_length` so existing operator handlers stay
            compatible.
    """
    validate_secret_length(secret, label=label, minimum_length=minimum_length)
    if minimum_entropy_bits <= 0:
        return
    bits = _shannon_entropy_bits(secret)
    if bits < minimum_entropy_bits:
        msg = (
            f"{label} has insufficient entropy (~{bits:.0f} bits; required "
            f"{minimum_entropy_bits:.0f}). Generate via "
            'python -c "import secrets; print(secrets.token_hex(32))".'
        )
        raise ConfigurationError(msg)