HTTP API reference¶
Unless noted, paths are relative to your app root. Defaults: auth_path="/auth", users_path="/users".
Placeholders:
{auth}— value ofauth_pathwithout trailing slash ambiguity (routes are registered underauth_path).{users}—users_path.{provider}— OAuth provider name fromOAuthConfig.oauth_providersor a manual controller factory.
Generated OpenAPI publishes the built-in request/response payload names from litestar_auth.payloads. login_identifier only changes how LoginCredentials.identifier is resolved during login; it does not rename the email/token fields used by the built-in registration, verification, reset-password, refresh, or TOTP routes.
For plugin-mounted protected routes, LitestarAuth also publishes per-operation OpenAPI security requirements derived from the configured auth transports so Swagger / other OpenAPI UIs can authorize requests with the standard mechanism. Disable that metadata with include_openapi_security=False. For app-owned protected routes, reuse config.resolve_openapi_security_requirements(); if you mount protected controllers manually, pass security= to the relevant controller factory.
Auth core¶
| Method | Path | Request body | Enabled when | Description |
|---|---|---|---|---|
| POST | {auth}/login |
LoginCredentials (identifier, password) |
Always (auth controller) | Credentials → tokens / session. |
| POST | {auth}/logout |
None | Always | Authenticated; clears session per strategy. |
| POST | {auth}/refresh |
RefreshTokenRequest (refresh_token) |
enable_refresh=True |
New access token from refresh token / cookie. |
| GET | {auth}/sessions |
None | include_session_devices=True |
Authenticated; list the current user's active DB-backed refresh sessions. CookieTransport clients can be marked current from the refresh cookie. |
| POST | {auth}/sessions |
RefreshTokenRequest (refresh_token) |
include_session_devices=True |
Authenticated; list active refresh sessions while identifying the current bearer refresh session. |
| DELETE | {auth}/sessions/{session_id} |
None | include_session_devices=True |
Authenticated; revoke one of the current user's refresh sessions by public session id. |
| POST | {auth}/sessions/revoke-others |
Optional RefreshTokenRequest (refresh_token) for bearer clients |
include_session_devices=True |
Authenticated; revoke the current user's other refresh sessions. |
Session/device response contracts¶
Session/device management routes use response DTOs from litestar_auth.payloads. Bearer clients use
POST {auth}/sessions with the existing RefreshTokenRequest body when they want the server to
identify the current refresh session in a list response, and may include the same body on
POST {auth}/sessions/revoke-others. Cookie clients do not submit that body; the
controller reads the configured CookieTransport refresh cookie instead.
RefreshSessionRead serializes one active DB-backed refresh session:
session_id— stable public session identifier. It is not the raw refresh token, access token, token digest, or keyed token digest.created_at— original refresh-session creation timestamp.last_used_at— timestamp of the last successful refresh rotation, ornullwhen the session has not been used after login.is_current—true/falsewhen the current refresh credential can be matched to the row, ornullwhen the route cannot compute that marker.client_metadata— optional bounded safe client hints. The built-in DB strategy stores only normalizeduser_agentmetadata capped at 255 characters; exact IP addresses and token material are not part of this contract.
List responses use RefreshSessionListResponse:
{
"sessions": [
{
"session_id": "a4ff5e6a-60f8-4a8e-9684-7239150fd91b",
"created_at": "2026-05-09T01:20:00Z",
"last_used_at": "2026-05-09T01:25:00Z",
"is_current": true,
"client_metadata": {
"user_agent": "Example Browser/1.0"
}
}
]
}
Plugin-owned session/device routes are opt-in with include_session_devices=True and are mounted
under auth_path. They are authenticated routes and always scope strategy calls to request.user.
The first controller slice supports strategies implementing the refresh-session management protocol,
including the built-in DB token strategy. JWT and Redis token strategies do not provide a session
dashboard in this slice.
| Status | Error code | Applies to | Meaning |
|---|---|---|---|
| 400 | SESSION_MANAGEMENT_UNSUPPORTED |
All session/device routes | The configured strategy does not implement refresh-session management. |
| 401 | None | All session/device routes | Authentication credentials are absent or invalid. |
| 404 | REFRESH_SESSION_NOT_FOUND |
DELETE {auth}/sessions/{session_id} |
The public session id is missing or does not belong to the authenticated user. |
Current-session detection is available when the configured strategy can identify a public session id
from the current raw refresh credential. The built-in DB token strategy supports that lookup by
hashing the supplied refresh token and comparing it to stored digests; it does not store or expose raw
refresh tokens. When the credential is available and matches one of the current user's active refresh
sessions, cookie clients can use GET {auth}/sessions and bearer clients can use
POST {auth}/sessions to mark exactly that item with is_current: true and the other active items
with false; POST {auth}/sessions/revoke-others preserves that session.
When no current refresh credential is present, the credential is invalid, the credential belongs to a
different user, the refresh session is expired, or the configured strategy does not support
identification, the current session is unresolved. In that fallback, list responses keep
is_current: null, and revoke-others fails closed by passing an unknown current-session marker to the
strategy. For the built-in DB strategy this revokes all active refresh sessions for the current user.
Registration and email¶
| Method | Path | Request body | Enabled when | Description |
|---|---|---|---|---|
| POST | {auth}/register |
UserCreate (email, password) |
include_register=True |
Create user; triggers hooks (e.g. send verification email). |
| POST | {auth}/verify |
VerifyToken (token) |
include_verify=True |
Confirm email with token. |
| POST | {auth}/request-verify-token |
RequestVerifyToken (email) |
include_verify=True |
Re-issue verification token. |
When you replace the built-in UserCreate request body with user_create_schema, reuse
litestar_auth.schemas.UserEmailField and litestar_auth.schemas.UserPasswordField for email / password when
you want the documented built-in validation metadata. Existing UserPasswordField imports remain valid; add
UserEmailField when you also want the built-in email regex and max length. The default runtime validator still
enforces password length through require_password_length. See
Configuration for the full schema-helper,
password-validator, and shared-helper contract.
Built-in user-returning responses from POST {auth}/register, POST {auth}/verify, and
POST {auth}/reset-password use UserRead, which now serializes id, email, is_active,
is_verified, and normalized roles.
That response contract is intentionally role-centric after the superuser migration. The HTTP API
exposes one flat roles array, not a legacy superuser boolean, raw role / user_role rows,
permission matrices, or role-catalog or user-assignment endpoints on the core plugin-owned auth routes. For operational
catalog and user-role administration, use the plugin-owned
litestar roles CLI surface or mount the opt-in contrib controller from
HTTP role administration.
Password reset¶
| Method | Path | Request body | Enabled when | Description |
|---|---|---|---|---|
| POST | {auth}/forgot-password |
ForgotPassword (email) |
include_reset_password=True |
Always returns 202; enumeration-safe. |
| POST | {auth}/reset-password |
ResetPassword (token, password) |
include_reset_password=True |
Apply new password with reset token. |
TOTP (2FA)¶
Mounted under {auth}/2fa/... when totp_config is set.
| Method | Path | Request body | Notes |
|---|---|---|---|
| POST | {auth}/2fa/enable |
TotpEnableRequest (password) by default; no body when totp_enable_requires_password=False |
Authenticated; starts enrollment. |
| POST | {auth}/2fa/enable/confirm |
TotpConfirmEnableRequest (enrollment_token, code) |
Authenticated; confirms enrollment and returns one-time recovery codes. |
| POST | {auth}/2fa/verify |
TotpVerifyRequest (pending_token, code) |
Completes login when TOTP is enabled; code accepts either a current TOTP code or an unused recovery code. Successful app-code verification records the server-side recent-TOTP marker used by downstream step-up checks; recovery-code verification only does so when totp_stepup_allow_recovery=True. |
| POST | {auth}/2fa/disable |
TotpDisableRequest (code) |
Authenticated; disables TOTP. code accepts either a current TOTP code or an unused recovery code. A recovery code remains accepted for lockout recovery. |
| POST | {auth}/2fa/recovery-codes/regenerate |
TotpRegenerateRecoveryCodesRequest (optional current_password, optional totp_code) |
Authenticated; replaces the stored recovery-code set and returns the new plaintext codes once. current_password is required when totp_enable_requires_password=True; totp_code can satisfy the TOTP step-up gate. |
TotpConfirmEnableResponse and TotpRecoveryCodesResponse carry recovery_codes; those plaintext
values are returned once and only their hashes are stored. Generated recovery codes are 28 lowercase
hex characters (112 bits). TotpEnableResponse necessarily carries the plaintext TOTP secret and
otpauth URI for QR-code rendering, so production deployments must serve {auth}/2fa/enable only
over HTTPS. Pending-login JWTs are client-bound by default with cip / uaf fingerprints, so a
/2fa/verify request from a different client receives the same TOTP_PENDING_BAD_TOKEN response as
an invalid pending token.
The built-in TOTP flow remains email-oriented internally: the otpauth URI and default password step-up for POST {auth}/2fa/enable use request.user.email, not login_identifier.
Sensitive operations use LitestarAuthConfig.totp_stepup_policy and default to required_when_enrolled
for users.update_self, api_keys.create, api_keys.update, api_keys.revoke, oauth.associate,
totp.disable, and totp.regenerate_recovery_codes. Enrolled users must present either a recent
server-side TOTP marker from /2fa/verify or a valid inline totp_code field where the request body
supports it. Missing proof returns HTTP 403 with TOTP_STEPUP_REQUIRED; users without TOTP are not
blocked unless the endpoint policy is always_required. The full configuration and per-endpoint
default table lives in
TOTP step-up for sensitive operations.
OAuth2 login¶
When oauth_config.oauth_providers is configured with oauth_redirect_base_url, the plugin auto-mounts login routes under {auth}/oauth/{provider}.
| Method | Path pattern | Description |
|---|---|---|
| GET | {auth}/oauth/{provider}/authorize |
Redirect to provider. |
| GET | {auth}/oauth/{provider}/callback |
Provider redirect; completes the plugin-owned OAuth login flow. |
If you mount create_provider_oauth_controller() or create_oauth_controller() directly for a custom route table, the prefix may differ from {auth}/oauth/{provider}.
OAuth account linking (associate)¶
When oauth_config.include_oauth_associate=True, the plugin auto-mounts associate routes under {auth}/associate/{provider} for the same oauth_providers inventory.
| Method | Path pattern | Description |
|---|---|---|
| POST | {auth}/associate/{provider}/authorize |
Authenticated user starts linking. CSRF-protected: cookie-transport deployments must mirror the plugin-managed CSRF cookie into the configured csrf_header_name (defaults to X-CSRF-Token); bearer-only deployments rely on the cross-origin attachment of Authorization to be impossible. The route is POST (not GET) so Litestar's CSRF middleware can enforce that token check before the body runs and forced-association attacks fail closed. |
| GET | {auth}/associate/{provider}/callback |
Completes linking for request.user. Stays GET because OAuth providers redirect there with GET. |
Users CRUD¶
When include_users=True, routes are under {users}.
| Method | Path | Guard |
|---|---|---|
| GET | {users}/me |
Authenticated |
| PATCH | {users}/me |
Authenticated |
| POST | {users}/me/change-password |
Authenticated |
| GET | {users}/{id} |
Superuser |
| PATCH | {users}/{id} |
Superuser |
| DELETE | {users}/{id} |
Superuser |
| GET | {users} |
Superuser (list) |
The built-in users surface also serializes UserRead, so all /users reads include normalized
roles. PATCH {users}/me strips roles and the other privileged fields from self-service
payloads even when a custom user_update_schema includes them. Email changes use UserUpdate
(email, optional current_password, optional totp_code) and require current_password; non-email self-service
updates from custom schemas do not. PATCH {users}/me does not rotate passwords.
Authenticated password rotation uses POST {users}/me/change-password with ChangePasswordRequest
(current_password, new_password); the controller re-verifies the current password before
delegating the replacement password through the manager update lifecycle. Wrong or missing
current-password submissions on email changes and password rotation return 400 with
LOGIN_BAD_CREDENTIALS, invalid replacement passwords return 400 with
UPDATE_USER_INVALID_PASSWORD, malformed request payloads use REQUEST_BODY_INVALID,
unauthenticated requests return 401, and configured rate limits return 429 with Retry-After.
Superuser PATCH {users}/{id} uses AdminUserUpdate, can persist validated roles, and remains
the admin-initiated password rotation path.
The storage redesign does not add separate CRUD endpoints for the relational role tables. Built-in
users routes continue to manage only the normalized flat roles contract on the user boundary.
Operator-driven catalog and assignment administration lives on the
litestar roles CLI surface, while applications that need an HTTP admin
surface can opt into HTTP role administration.
Contrib role administration (opt-in)¶
If you mount litestar_auth.contrib.role_admin.create_role_admin_controller(...), the library
adds an admin-only HTTP role-management surface under its configured prefix (default /roles).
The factory defaults to guards=[is_superuser]; see
HTTP role administration for mounting and override guidance.
Payload contracts live in litestar_auth.contrib.role_admin._schemas:
RoleCreate, RoleUpdate, RoleRead, and UserBrief. Paginated list routes return
{"items": [...], "total": int, "limit": int, "offset": int}. The controller also reserves
these machine-readable ErrorCode values for role-catalog and assignment failures:
ROLE_ALREADY_EXISTS, ROLE_NOT_FOUND, ROLE_STILL_ASSIGNED,
ROLE_ASSIGNMENT_USER_NOT_FOUND, and ROLE_NAME_INVALID.
| Method | Path | Request body | Success | Other documented statuses | Error code(s) |
|---|---|---|---|---|---|
GET |
/roles |
None | 200 paginated RoleRead page |
403, 422 |
None |
POST |
/roles |
RoleCreate |
201 RoleRead |
403, 409, 422 |
ROLE_ALREADY_EXISTS, ROLE_NAME_INVALID |
GET |
/roles/{role_name} |
None | 200 RoleRead |
403, 404, 422 |
ROLE_NOT_FOUND, ROLE_NAME_INVALID |
PATCH |
/roles/{role_name} |
RoleUpdate |
200 RoleRead |
403, 404, 422 |
ROLE_NOT_FOUND, ROLE_NAME_INVALID |
DELETE |
/roles/{role_name} |
None | 204 empty body |
403, 404, 409, 422 |
ROLE_NOT_FOUND, ROLE_STILL_ASSIGNED, ROLE_NAME_INVALID |
POST |
/roles/{role_name}/users/{user_id} |
None | 200 RoleRead |
403, 404, 422 |
ROLE_NOT_FOUND, ROLE_ASSIGNMENT_USER_NOT_FOUND, ROLE_NAME_INVALID |
DELETE |
/roles/{role_name}/users/{user_id} |
None | 204 empty body |
403, 404, 422 |
ROLE_ASSIGNMENT_USER_NOT_FOUND, ROLE_NAME_INVALID |
GET |
/roles/{role_name}/users |
None | 200 paginated UserBrief page |
403, 404, 422 |
ROLE_NOT_FOUND, ROLE_NAME_INVALID |
Assignment writes are idempotent and run through the manager lifecycle instead of mutating
association rows behind BaseUserManager. The user_id path parameter is parsed UUID-first and
then falls back to the configured model's primary-key shape so the same controller works with the
bundled UUID user model and integer-key custom models.
Multiple backends¶
If more than one backend is configured, the first uses {auth} as above. Additional backends use {auth}/{backend-name}/... for their auth routes (see Backends).
Rate limiting¶
When rate_limit_config is set, selected endpoints may return 429 with Retry-After. See the Rate limiting guide and the Python API.