Authentication API¶
This document provides comprehensive information about the authentication endpoints of the FastAPI RBAC API. It is fully aligned with the backend implementation as of July 2025.
Overview¶
- All endpoints are under
/api/v1/auth. - Most endpoints are public, but some require authentication (e.g., logout, change password).
- All responses are wrapped in a
datafield and may include amessage. - Error responses follow the standard error format with appropriate HTTP status codes.
- CSRF protection is required for state-changing operations (see CSRF section).
Endpoints¶
POST /api/v1/auth/login¶
Log in a user with email and password.
Request Body:
Response:
{
"data": {
"access_token": "...",
"token_type": "bearer",
"user": { "...": "..." }
},
"message": "Login successful"
}
Sets an HttpOnly refresh_token cookie (path /api/v1/auth). The refresh token is not returned in the JSON body for the SPA.
Error Responses:
- 422 Unprocessable Entity: Invalid credentials, locked account, unverified email
- 400 Bad Request: Input sanitization failed
POST /api/v1/auth/register¶
Register a new user (self-service, public).
Request Body:
{
"email": "newuser@example.com",
"password": "SecurePassw0rd!47",
"first_name": "Jane",
"last_name": "Smith"
}
The password must satisfy the complexity policy in settings -- the same policy
the password-reset and change-password endpoints apply. SecurePassword123!
would be rejected for the sequential run 123.
Response:
{
"data": {
"id": "uuid",
"email": "newuser@example.com",
"first_name": "Jane",
"last_name": "Smith",
"is_active": true,
"verified": false,
...
},
"message": "Registration successful. Please check your email to verify your account."
}
Error Responses:
- 400 Bad Request: Invalid input, password too long, rate limit exceeded, or a password that fails the complexity policy. A complexity failure answers with an object rather than a string:
{
"detail": {
"message": "Password does not meet complexity requirements.",
"errors": ["Password must be at least 12 characters long"]
}
}
POST /api/v1/auth/verify-email¶
Verify a user's email address using a verification token.
Request Body:
Response:
A second call with the same still-valid JWT, after the Redis key has been consumed, returns 200 with "Email is already verified." when the account is active and already verified (#239). The token remains single-use: the Redis key is deleted on first success.
Error Responses:
- 400 Bad Request:
"This verification link is invalid or has expired. Please request a new verification email and try again."— returned identically for an unknown address, a disabled account (including disabled-and-verified), a still-unverified user whose Redis token is missing or does not match, and a wrong or expired token, so the response never confirms that an account exists (#137). The audit log records which it was. - 401 Unauthorized: the token itself failed JWT validation
POST /api/v1/auth/resend-verification-email¶
Resend the verification email to a user (rate-limited).
Request Body:
Response:
{
"message": "If the email exists and the account is not verified, a verification email has been sent."
}
Error Responses:
- 429 Too Many Requests: Rate limit exceeded
- 400 Bad Request: Invalid email
POST /api/v1/auth/logout¶
End the calling session: revoke that refresh token and every access token that shares its session id, then delete the HttpOnly refresh_token cookie. Other sessions on the account stay usable. Requires CSRF (cookie-authenticated mutation).
Session identity comes from the refresh cookie when present; if the cookie is missing, from the access token's allowlist metadata. If neither yields a session id, the request fails and does not revoke other sessions.
Request Headers:
Response:
Permissions: Authenticated user
Error Responses:
- 400 Bad Request: Unable to identify the current session
- 401 Unauthorized: Not authenticated
POST /api/v1/auth/logout/all¶
Revoke every session for the authenticated user (every allowlist token, of any type) and delete the HttpOnly refresh_token cookie. Same CSRF and authentication rules as logout. Change-password still calls the revocation primitive directly rather than this route. The first-party SPA posts this from Log out everywhere after confirmation.
Request Headers:
Response:
Permissions: Authenticated user
POST /api/v1/auth/change_password¶
Change the current user's password (authenticated).
Request Headers:
Request Body:
Response:
{
"data": {
"access_token": "...",
"token_type": "bearer",
"user": { "...": "..." }
},
"message": "Password changed successfully."
}
Re-issues the HttpOnly refresh_token cookie (refresh token omitted from JSON).
Error Responses:
- 400 Bad Request: Invalid current password, password complexity, password reuse
- 401 Unauthorized: Not authenticated
POST /api/v1/auth/password-reset/request¶
Request a password reset email (public, rate-limited).
Request Body:
Response:
{
"message": "If the email exists and the account is active, a password reset link has been sent."
}
The same 200 and the same message are returned for an unknown address, a disabled account, and an active one (#137). Only MODE=development differs, where the reset token is echoed back for MailHog.
Error Responses:
- 400 Bad Request: Invalid email
- 429 Too Many Requests: Rate limit exceeded
POST /api/v1/auth/password-reset/confirm¶
Reset a user's password using a reset token.
Request Body:
Response:
Error Responses:
- 400 Bad Request:
"This password reset link is invalid or has expired. Please request a new password reset and try again."— returned identically for an unknown address, a disabled account, and a token that is wrong, expired or not allow-listed (#137) - 400 Bad Request: password complexity or password-history failures, which describe the submitted password and stay distinct
POST /api/v1/auth/new_access_token¶
Refresh an access token. Prefer the HttpOnly refresh_token cookie (first-party SPA). Requires CSRF. Optional JSON body refresh_token is a documented fallback for non-browser API clients.
Cookie: refresh_token (HttpOnly; path scoped to /api/v1/auth)
Request Body (optional):
Response:
{
"data": {
"access_token": "...",
"token_type": "bearer"
},
"message": "Access token generated correctly"
}
Error Responses:
- 401 Unauthorized: Missing, invalid, or expired refresh token
- 403 Forbidden: CSRF failure or refresh token not on Redis allowlist
- 403 Forbidden: the session was revoked because this refresh came from a different origin network than the one it was established from (
VALIDATE_TOKEN_IP, ADR 0011 decision 5). The body is identical to the allowlist rejection above, deliberately: the response does not tell a caller that the address is what gave it away
GET /api/v1/auth/csrf-token¶
Get a CSRF token for use in state-changing operations (required for POST/PUT/DELETE).
Response:
Error Responses¶
- 400 Bad Request: Invalid input, password complexity, invalid token
- 401 Unauthorized: Missing or invalid token
- 403 Forbidden: Not allowed
- 404 Not Found: User not found
- 409 Conflict: Email already registered
- 422 Unprocessable Entity: Validation errors, locked account, unverified email
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Unexpected error
Security Notes¶
- All state-changing endpoints require a valid CSRF token (see
/csrf-token). - Passwords must meet complexity requirements and cannot be reused (see backend settings).
- Rate limits apply to registration, password reset, and verification email endpoints.
- Tokens are JWTs and must be included in the
Authorizationheader asBearer <token>. - Always use HTTPS in production.
See Also¶
- Users API for user management endpoints.
- Roles API for role management endpoints.
- Permission API for permission management endpoints.