Changelog#
0.4.0#
Changed (breaking)#
Public records, transport shapes, and verifier protocols now use semantic names. The release renames
LocalAccountRecordtoLocalAccountState,SessionRecordtoUserAuthSession,StepUpRecordtoStepUpGrantState,PasskeyRecordtoPasskeyMetadata,APIKeyRecordtoAPIKeyState,WebSocketConnectTokenRecordtoWebSocketConnectAuthorization,AssertionRecordStatustoPasskeyAssertionStatus, andInvalidWebAuthnResponseErrortoWebAuthnVerificationError.The remaining protocol and wire names change from
RequestAuthenticatortoCredentialVerifier,JWKSFetchRequest/JWKSFetchResponsetoJWKSFetchTarget/JWKSFetchOutcome,OAuthHTTPRequesttoOAuthRequestObservation,RouteStatustoOperationMessage,OAuthRouteStatustoOAuthOperationSummary,ConsumeStatus/ConsumeOutcometoVerificationStatus/VerificationOutcome, andJWKSCacheEntrytoJWKSSource. No compatibility aliases are provided; generated clients and stored OpenAPI fixtures must be regenerated.OAuthRouteServiceis replaced by the presentation-neutralOAuthLifecycleprotocol. Custom controllers callbegin(),complete_callback(),establish_login(), unlink, revoke, logout, operations. The concreteOAuthLifecycleService.callback()remains the generated-route adapter.OAuth persistence is one atomic
OAuthAccountStoreaggregate. Provider identities are globally unique by provider, issuer, and subject, one account can link at most one identity for a provider, scope upgrades confirm the callback identity exactly, and unlink is provider-bound. Split token-vault, login-resolution, and revocation-retry store APIs are removed.Authorization guard factories consistently use the
requires_*prefix: for example,require_scopebecomesrequires_scopeandrequire_all_ofbecomesrequires_all_of. The singular spellings are removed without aliases.
Added#
ResolvedUserAuthSessionandUserAuthSessionResolverlet session and hybrid profiles perform one consistent authoritative session/account read. A configured resolver never falls back after an invalid result or outage; applications may omit it to retain the composed three-read path.Remote-key authentication uses a shared 128-entry LRU of prepared verifiers. A selected key change replaces cached material even when
kidand algorithm are unchanged.Google IAP assertions enforce the ten-minute maximum lifetime plus twice the configured skew.
GoogleIAPClaimsnow exposes hosted domain, access levels, device ID, and a validated immutableGoogleIAPExternalIdentityinstead of arbitrary nested claims. IAP authenticates only the WebSocket upgrade; long-lived sockets still require evidence-expiry and authorization refresh supervision.MemoryOAuthTransactionStoreperforms bounded expiry cleanup and defaults to a maximum of 1,024 live transactions, rejecting new state at capacity.OAuth provider confirmation is split into two explicit operations.
REVALIDATEconfirms possession of the exact linked identity and never produces freshness evidence. Capability-gated OIDCREAUTHENTICATEusesmax_age, requires signedauth_time, enforces configured ACR/AMR, account epoch and browser/session bindings, and returns one purpose-boundStepUpCredential. Generated routes expose/revalidateand/reauthenticate/{purpose}under each provider.discover_oidc_provider()anddiscover_google_oidc_provider()compose generic or Google OIDC providers from application-owned shared discovery and JWKS resources. GitHub profile and verified-email requests now run concurrently through the provider’s bounded async HTTP client.
0.3.0#
Changed (breaking)#
Public domain and protocol types now use semantic names. No compatibility aliases are provided:
TOTPEnrollmentbecomesTOTPProvisioningGrant,RecoveryCodesbecomesRecoveryCodeGrant,StepUpGrantbecomesStepUpCredential,AssertionRecordResultbecomesAssertionRecordStatus,PasskeySummarybecomesPasskeyRecord,PasswordPolicyResultbecomesPasswordPolicyDecision,PasswordVerificationResultbecomesPasswordVerificationOutcome,RateLimitRequestbecomesRateLimitAttempt,LocalAccountbecomesLocalAccountRecord,InvalidLifecycleRequestbecomesLifecycleRejected,PasswordChangeResultbecomesPasswordChangeOutcome,RevokeLoginMethodResultbecomesRevokeLoginMethodOutcome,RegistrationResultbecomesRegistrationOutcome,ConsumeResultbecomesConsumeOutcome,PasswordResetResultbecomesPasswordResetOutcome,RotateRefreshResultbecomesRefreshRotationOutcome,PrepareRefreshResultbecomesRefreshPreflightOutcome,RefreshTokenResponsebecomesTokenPair, andUnlinkResultbecomesUnlinkOutcome.Generated-route schemas follow the same rule:
LocalMFARequiredResponsetoLocalMFAChallenge,LocalMFACompletionRequesttoLocalMFACompletion,LocalRegistrationRequesttoLocalRegistration,LocalInvitationRegistrationRequesttoLocalInvitationRegistration,LocalIdentifierRequesttoLocalIdentifier,LocalTokenRequesttoLocalToken,LocalPasswordResetRequesttoLocalPasswordReset,LocalPasswordChangeRequesttoLocalPasswordChange,LocalAccountResponsetoLocalAccount,RouteStatusResponsetoRouteStatus,LocalSessionResponsetoLocalSession, andLocalSessionListResponsetoLocalSessionList.The remaining account schemas are renamed:
TOTPEnrollmentRequesttoTOTPEnrollment,TOTPEnrollmentResponsetoTOTPProvisioning,TOTPVerificationRequesttoTOTPVerification,StepUpAuthorizedRequesttoStepUpAuthorization,RecoveryCodesResponsetoRecoveryCodes,StepUpRequesttoStepUpVerification,StepUpResponsetoStepUpGrant,PasskeyRegistrationOptionsRequesttoPasskeyRegistrationStart,PasskeyAuthenticationOptionsRequesttoPasskeyAuthenticationStart,PasskeyVerifyRequesttoPasskeyVerification,PasskeyOptionsResponsetoPasskeyOptions, andPasskeySummaryResponsetoPasskeySummary.OAuth schemas are renamed:
OAuthRouteResponsetoOAuthRouteStatus,OAuthLinkRequesttoOAuthLink,OAuthScopeRequesttoOAuthScopeUpgrade,OAuthStepUpRequesttoOAuthStepUp,OIDCBackchannelLogoutRequesttoOIDCBackchannelLogout, andOAuthLogoutResulttoOAuthLogout.Custom
JWKSCacheimplementations must return a stableJWKSCacheCoordinatorfromcoordinator(issuer, jwks_uri). It owns refresh, forced-refresh, negative-cache, and lifecycle state shared by every provider using that cache.
Added#
SecurityConfig(exclude=[...])accepts path patterns that remove matching routes from security. Excluded operations are anonymous in runtime policy and OpenAPI. Conflicting route-levelauthis rejected, and unmatched patterns warn once. See the composition guide.ProtectedResourceConfigpublishes an RFC 9728 protected-resource metadata document. Its path derives from the resource URL and optionalroute_prefix; the root-resource default is/.well-known/oauth-protected-resource. The endpoint is anonymous, supports conditional304responses, and configures cache age, authorization servers, scopes, bearer methods, and documentation URL.ProtectedResourceMetadataandbuild_protected_resource_handlerexpose the document contract and handler builder. See the resource-server guide.Applicable RFC 6750 Bearer challenges advertise the canonical metadata URL in
resource_metadata. Setadvertise_resource_metadata=Falseto disable the hint without removing the endpoint.CachedJWKSProvider(cache=...)accepts aJWKSCache.InMemoryJWKSCacheremains the default. Sharing a cache shares immutableJWKSSnapshotvalues and refresh coordination across providers.OIDCDiscoveryClient.discover(issuer, discovery_url=...)supports metadata outside the conventional well-known URL. The override must use the issuer’s exact origin.RouteDocslets applications rename and describe generated-route tag groups and transform operation IDs and route names. Stable group keys remain fixed, and unknown groups are rejected. See the generated-routes guide.SecurityConfig.wire_renameapplies one generated-body naming policy to request decoding, response encoding, and OpenAPI. It acceptslower,upper,camel,pascal,kebab, or a callable; the default stays snake case.wire_forbid_unknown_fieldscontrols unknown request members and defaults to strict rejection.WirePolicy,wire_struct(),RouteError, andProblemDetailexpose generated-route wire and error-schema contracts.RaisedErrorSchemadeclares the schema and media type produced by custom exception rendering. It changes only OpenAPI; exception handling remains application-owned.
Changed#
All ten generated tag groups now include descriptions and pass through the application’s
RouteDocsconfiguration.Generated OAuth routes now let application exception handlers render provider and account failures. Status codes, sanitized messages, and
Retry-Afterbehavior are unchanged.Raised
400,401,429, and503responses are documented asRouteErrorrather thanRouteStatus.RouteStatusremains the body returned by success and conflict responses, and typed second-factor challenges retain their own schemas. Generated clients should be regenerated.With
ProblemDetailsPlugin(enable_for_all_http_exceptions=True), generated routes document raised errors asProblemDetailwithapplication/problem+json. Default plugin configuration leaves the document unchanged.Generated routes warn once when a custom response class could invalidate documented response bodies. A complete
RaisedErrorSchemasupplies the missing OpenAPI contract and suppresses the warning.
Fixed#
Providers sharing a
JWKSCachecoalesce cold, expired, and unknown-key refreshes into one upstream fetch. Closing one provider does not cancel shared work still used by another.Path exclusions compile consistently across runtime authentication and OpenAPI, including routes registered by other Litestar plugins.
Generated error schemas match the bodies and media types applications render without overwriting returned response schemas.
Development#
Reorganized tests by behavior and introduced shared fixtures, adversarial corpora, provider performance checks, and shuffled CI execution.
Replaced the global 100% branch-coverage requirement with a 95% global gate plus a hard 100% gate for security-critical authentication, authorization, token, OAuth transaction, refresh, and WebSocket paths.
Moved architecture and forbidden-import checks into lint-time tooling, removed Hypothesis and low-signal structural tests, and made lint include untracked source files.
Updated CI actions and removed duplicate optional-extra synchronization.
0.2.0#
Changed (breaking)#
intersect_authorizationis renamedresolve_authorization. The old name described the implementation rather than the result, andresolve_matches the naming the rest of the surface uses. No alias ships for the old name.The WebSocket ticket family is renamed for what it authorizes, because the library already issues access and refresh tokens to users and “ticket” gave no clue which kind of value one was:
Before
After
WebSocketTicketRecordWebSocketConnectTokenRecordIssuedWebSocketTicketIssuedWebSocketConnectTokenWebSocketTicketStoreWebSocketConnectTokenStoreWebSocketTicketServiceWebSocketConnectTokenServiceInMemoryWebSocketTicketStoreInMemoryWebSocketConnectTokenStoreWebSocketTicketUnavailableErrorWebSocketConnectTokenUnavailableErrorissue_websocket_ticket()issue_websocket_connect_token()Fields on public WebSocket types move with that rename, so an application constructing them by keyword must be updated:
WebSocketHandshake.ticketbecomes.connect_token,WebSocketConnectTokenRecord.ticket_idbecomes.connect_token_id, andWebSocketSecurityConfig.ticket_store,.ticket_ttl,.maximum_ticket_ttland.ticket_query_parametertake the matchingconnect_tokennames.Three WebSocket values on the wire change with the name. The handshake query parameter defaults to
connect_tokenrather thanticket, so a browser that builds the URL itself sends the new name unless the application setsconnect_token_query_parameterback toticket. The issued credential is prefixedwsct.rather thanwst.. The HMAC domain separator becomeslitestar-security/websocket-connect-token/v1, which invalidates every stored digest; connect tokens are short-lived, so an in-flight credential fails closed and the client requests another. The evidence a connection records also readswebsocket-connect-token.Every generated
/authbody is nowsnake_case. The MFA, passkey, step-up, and OAuth provider routes previously used camel-case members on both requests and responses:stepUpGrant,methodId,enrollmentId,userName,accountId,providerAccountId,credentialId,displayName,createdAt,lastUsedAt,expiresAt,provisioningUri,backupEligible,backupState, andreturnToare now spelled with underscores. Local account, session, token, and password routes were alreadysnake_caseand are unchanged.Every generated
/authbody now rejects a member it does not model with a400. Previously an unrecognized field was silently discarded, so a client on a stale spelling of an optional field received a successful response with the field’s default rather than an error.Six MFA and passkey routes advertised
201 Createdwhile returning200 OK, and the OAuth revoke and logout routes returned201for operations that create nothing. Every route now declares the status it returns. Enrolling a TOTP factor and registering a passkey still return201.The three removal routes moved from
DELETEwith a request body toPOST, since the step-up grant they require travels in that body:POST /auth/mfa/totp/{method_id}/remove,POST /auth/passkeys/{credential_id}/remove, andPOST /auth/oauth/{provider}/links/{provider_account_id}/unlink.The body those three share, and the recovery-code replacement body, was named
RecoveryCodesRequesteven where no recovery code was involved. It is nowStepUpAuthorizedRequest.LocalRouteResponseandMFAStatusResponsewere identical single-field bodies and are replaced by oneRouteStatusResponse.The OAuth lifecycle response carried three unrelated values in
provider_account_id: the provider account, the local account, and a count of revoked sessions rendered as a string. These are nowprovider_account_id,account_id, andrevoked_sessions, and a response omits the members its operation did not resolve.OIDCSessionLogoutStorenow owns atomic back-channeljtireplay consumption.revoke_frontchannel()additionally requires the caller’s browser-binding digest and returnsint | Noneso a rejected binding is distinguishable from a successful zero-session revocation. Custom stores must implement the widened protocol.WebSocket connect-token stores and callers must preserve the account
security_epoch. Issuance requires the epoch, and consumption requires an authoritative asynchronous epoch callback so a burned token cannot establish a connection after account-wide revocation.Argon2, TOTP, and WebAuthn are no longer installed by the core distribution. Applications using local passwords, MFA, or passkeys must install
litestar-security[argon2],litestar-security[mfa], orlitestar-security[passkeys]respectively;litestar-security[all]installs every optional feature.
Added#
WireStruct, the shared base carrying that convention, is exported from the package root so applications can define their own schemas on the same casing and strictness policy.litestar_security.typingexposes dependency-availability flags andrequire_dependency, which raises where a capability is used and names the distribution to install. The flags resolve when they are read, so importing the module never imports what it describes.Typed
SecureControllerandPublicControllerbase classes compile anauthClassVarinto native Litestar route metadata, with explicit precedence and bypass-conflict validation.exclude()is a first-class authentication policy for routes that bypass the security evaluator while retaining ordinary CSRF derivation. It rejects contradictory authentication policy at application startup.LocalKeyRingcan mint and verify bounded, purpose-specific capability JWTs for application-owned flows such as signed download URLs.MFAConfig.require_at_loginadds password-login MFA completion routes:POST /auth/login/mfaandPOST /auth/token/mfa. A verified password can now return the typedMFARequiredoutcome backed by an atomic, digest-only, reveal-onceMFALoginChallengeStore.HttpxJWKSFetcherprovides a default bounded httpx transport with ETag revalidation, redirect and environment-proxy refusal, public-address validation, injectable DNS/transport seams, and owned-client lifecycle.forwarded_client_key()derives rate-limit keys from explicitly trusted proxy CIDRs using a bounded right-to-leftX-Forwarded-Forwalk and safe peer fallback.AESGCMSecretProtectorandAESGCMOAuthTransactionProtectorprovide versioned, non-deterministic authenticated encryption with associated-data binding for MFA secrets and OAuth transaction/vault state.WebSocketConnectTokenIssuerresolves a named WebSocket handler’s compiled security plan and mints a policy-bound connect token through thewebsocket_connect_tokensdependency.InMemoryLocalAccountStoreandInMemorySecurityBackend.accountsprovide deterministic references for local-account, native-session, refresh-family, registration, recovery, purpose-token, and login-method ports. The aggregate backend exposes separate MFA, passkey, OAuth/OIDC, API-key, and WebSocket reference attributes.litestar_security.testingnow exports reference resolvers, revocation sources, and conformance helpers for the shipped atomic security contracts and protectors.StoreConformanceFactoriesandassert_security_backend_conformancerun an application’s selected backend scenarios.StepUpOAuthAuthorizerbridges OAuth link/unlink operations to local step-up grants. The OAuth testing surface also includes an encrypted revocation-retry store and atomic OIDC session-logout references.
Security#
Bearer and session assurance is anchored to the original authentication time, preserves evidence expiry through session establishment, and fails closed when a token omits or corrupts the required freshness claims.
Recovery-code digests are account-bound. Passkey counters remain monotonic from zero, clone-risk state cannot be silently cleared, and the attestation trait is set only after cryptographic chain verification.
Discovered OIDC authorization, token, and end-session endpoints must share the issuer origin or appear in
allowed_oauth_origins. OAuth token and revocation requests pin a freshly resolved public IP while retaining the original HTTP Host and TLS SNI, reject mixed/private DNS answers, compression, redirects, and oversized responses, and keep JWKS trust separate. GitHub profile responses are independently bounded and reject compression.WebSocket connect tokens carry the account security epoch and revalidate it after atomic consumption. Policy fingerprints use a versioned canonical JSON encoding rather than representation-dependent values.
Static security headers are backfilled onto error responses as well as successful responses. Dynamic nonce CSP replaces stale/conflicting response values deterministically,
SecurityHeadersConfig.hardened()supplies an opt-in baseline, and notification destinations reject control characters.Front-channel OIDC logout is browser-binding aware and rate limited. Security evaluators and generated routes classify application-port failures and fail closed without exposing provider or credential details.
Changed#
litestar_security.accountsandlitestar_security.websocketwere reorganized internally. Unrenamed account and configuration imports remain available from their documented package exports: account wire schemas and generated controllers moved intoschemasandcontrollerssub-packages, and the WebSocket module became a package of layered modules. The worker budget, metrics port, and blocking-call bridge now live inlitestar_security.workers;litestar_security.configre-exports them.OAuth now has a formal zero-dependency
oauthfeature marker. Core andaccountsimports remain usable when optional feature packages are absent; accessing an unavailable feature raises an actionable installation error.RateLimiter.acquire()now promises exact atomic admission. The referenceStoreRateLimiterserializes complete multi-bucket accounting across instances in one process and documents that applications need an atomic backend for multi-process deployment.The default local-account rate-limit map is exhaustive and deny-by-default, covering factor removal, verification confirmation, OAuth logout, and step-up operations. Recovery and verification requests equalize durable-store work for absent and present identifiers.
Authorization resolution only narrows credential-derived team roles, verifier caches share configured worker budgets, and local password reauthentication checks current account state and security epoch.
Public routes remain excluded from native CSRF unless they declare
csrf_required=True; authentication bypass routes retain ordinary CSRF derivation instead of inheriting the public-route exception.
0.1.0#
Added#
Typed principal/context/session runtime, authentication policy, authorization guards, native OpenAPI, and strict failure outcomes.
Explicit local session, token, and hybrid account lifecycle with rotating refresh families, MFA, passkeys, step-up, rate limiting, and generated routes.
OAuth/OIDC, Google IAP, GitHub, Keycloak, opaque API-key, external workload JWT, team/tenant, and WebSocket integrations.
Local signing/JWKS, strict discovery, bounded remote rotation, optional CSP, native security headers, conformance helpers, deterministic examples, and complete release automation.
Deferred#
DPoP.
Production mTLS.
Full Keycloak UMA.