Complete Documentation for User Authentication Endpoints
This router handles all user authentication operations including login, registration, password management, OTP verification, and user availability checks.
- Overview
- Endpoints
- Token Management
- Workflows
- Error Handling
- Best Practices
- Environment Variables
- Architecture
The Authentication router provides comprehensive user authentication functionality including:
- Password-based Authentication: Traditional email/phone + password login
- OTP-based Authentication: One-time password via email, SMS, or WhatsApp
- User Registration: Account creation with OTP verification
- Password Management: Set, change, and reset passwords
- User Verification: Email and phone number verification
- Multi-Token System: Access, refresh, and session tokens for secure authentication
- Session Management: Stateless session management with token blacklisting
Base Path: /{MODE}/auth or /{MODE}/auth/logout
Authentication: Most endpoints do not require authentication (except password change, logout, and token-info)
The authentication system uses a multi-token approach for optimal security and performance:
-
Session Token (Recommended for Frontend - Fastest & Most Secure)
- Lifespan: 7 days (configurable via
SESSION_TOKEN_EXPIRY_MINUTES) - Purpose: Preferred token for API authentication - contains full user profile
- Payload: Complete user profile and permissions (no database lookup needed)
- Storage: Store securely (httpOnly cookie or secure storage)
- Usage: Include in
X-Session-Tokenheader orAuthorization: Bearerheader - Benefits:
- Fastest validation (no database queries)
- Contains full user data
- Longer lifespan (7 days vs 1 hour)
- Optimized for client-side validation
- Lifespan: 7 days (configurable via
-
Access Token (Alternative for API Calls)
- Lifespan: 1 hour (60 minutes, configurable via
ACCESS_TOKEN_EXPIRY_MINUTES) - Purpose: Lightweight token for API requests
- Payload: Minimal - contains user_id, username, email, is_active, is_verified, jti (JWT ID)
- Storage: Store in memory or secure storage
- Usage: Include in
Authorization: Bearer <access_token>header - Note: Requires database lookup if full user data needed
- Features: Includes JTI (JWT ID) for efficient blacklisting
- Lifespan: 1 hour (60 minutes, configurable via
-
Refresh Token
- Lifespan: 30 days (configurable via
REFRESH_TOKEN_EXPIRY_MINUTES) - Purpose: Use to obtain new tokens when they expire
- Payload: Minimal - contains only user_id and session_id
- Storage: Store securely (httpOnly cookie or secure storage)
- Usage: Send to
/auth/refresh-tokenendpoint when tokens expire - Note: Cannot be used for API authentication
- Lifespan: 30 days (configurable via
-
Session ID
- Purpose: Unique identifier for the session, used for logout operations
- Storage: Store with tokens for logout functionality
- Usage: Include in logout requests to revoke specific sessions
Frontend Token Usage (Recommended - Session Token):
// Store tokens after login
const { access_token, refresh_token, session_token, session_id } = loginResponse.data;
// RECOMMENDED: Use session_token for API calls (fastest and most secure)
// Option 1: X-Session-Token header (preferred method)
fetch('/api/protected-endpoint', {
headers: {
'X-Session-Token': session_token
}
});
// Option 2: Authorization Bearer header (session_token works here too!)
fetch('/api/protected-endpoint', {
headers: {
'Authorization': `Bearer ${session_token}` // session_token in Bearer header
}
});
// Alternative: Use access_token with Bearer header (still supported)
fetch('/api/protected-endpoint', {
headers: {
'Authorization': `Bearer ${access_token}` // access_token in Bearer header
}
});
// When tokens expire, use refresh_token
fetch('/api/auth/refresh-token', {
method: 'POST',
body: JSON.stringify({ refresh_token })
});Token Validation Priority: The server accepts tokens in this order (first match wins):
X-Session-Tokenheader (preferred - fastest validation)Authorization: Bearer <token>header (accepts both session_token and access_token)- OAuth2 scheme (for Swagger UI)
access_tokenquery parameter (backward compatibility)
Important Notes:
- β
Bearer header accepts both: You can use
session_tokenORaccess_tokeninAuthorization: Bearerheader - β
X-Session-Token is preferred: Fastest validation path when using
X-Session-Tokenheader - β Flexible usage: Use whichever method fits your frontend architecture
Why Session Token is Recommended:
- β Fastest: No database lookup needed (full user data in token)
- β Secure: Same security as access token with blacklist checking
- β Longer lifespan: 7 days vs 1 hour (fewer refresh operations)
- β Complete data: Full user profile embedded in token
- β Optimized: Server-side validation optimized for session tokens
- β
Flexible: Works with both
X-Session-Tokenheader andAuthorization: Bearerheader
Endpoint: POST /{MODE}/auth/login-with-password
Description: Authenticate user with email/phone and password. Returns all tokens (access, refresh, session) upon successful authentication.
Note: This is the single unified login endpoint for password-based authentication.
Authentication: Not required
Request Body:
{
"username": "user@example.com",
"password": "your-password"
}Response:
{
"success": true,
"id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd",
"message": "Login successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_id": "f533589d-48d3-4b67-9430-c0b4793ac13e",
"token_type": "bearer"
},
"meta": {
"type": "dict"
},
"timestamp": "2025-01-28T15:51:55.980680Z"
}Token Details:
access_token: Use this for API authentication (60 minutes expiry, configurable)refresh_token: Use to refresh all tokens (30 days expiry, configurable)session_token: Contains full user profile (7 days expiry, configurable) - RECOMMENDEDsession_id: Unique session identifier for logout and session management
Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User submits login form
βββΊ User enters email/phone and password
βββΊ Client validates form (email format, password length)
Step 2: Client sends login request
POST /{MODE}/auth/login-with-password
Content-Type: application/x-www-form-urlencoded
Body: username=user@example.com&password=secret123
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates request
βββΊ Check username exists β β 400 if missing
βββΊ Check password exists β β 400 if missing
Step 4: Server authenticates user
βββΊ Query database: Get user by email/phone
β βββΊ β 401 if user not found
βββΊ Verify password (bcrypt/PBKDF2)
β βββΊ β 401 if password incorrect
βββΊ Check user status
β βββΊ is_active = true β β 401 if false
β βββΊ is_verified = true β β 401 if false
βββΊ Update last_sign_in_at timestamp
Step 5: Server clears user blacklist (if exists)
βββΊ Clear user-level session blacklist
βββΊ Clear user refresh token blacklist
(Allows re-login after previous logout)
Step 6: Server generates tokens
βββΊ Generate session_id (UUID)
βββΊ Generate access_token (60 min, includes JTI)
βββΊ Generate session_token (7 days, includes full user profile)
βββΊ Generate refresh_token (30 days, includes session_id)
Step 7: Server returns response
HTTP 200 OK
{
"success": true,
"data": {
"access_token": "...",
"refresh_token": "...",
"session_token": "...",
"session_id": "...",
"token_type": "bearer"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 8: Client receives response
βββΊ Check response.success === true
βββΊ Extract tokens from response.data
Step 9: Client stores tokens securely
βββΊ Store session_token (RECOMMENDED for API calls)
β βββΊ localStorage.setItem('session_token', token)
β OR httpOnly cookie (more secure)
βββΊ Store refresh_token (for token renewal)
β βββΊ localStorage.setItem('refresh_token', token)
β OR httpOnly cookie (preferred)
βββΊ Store session_id (for logout)
β βββΊ localStorage.setItem('session_id', id)
βββΊ Store access_token (optional, if not using session_token)
βββΊ localStorage.setItem('access_token', token)
Step 10: Client updates UI
βββΊ Redirect to dashboard/home
βββΊ Update user state/context
βββΊ Show success message
Step 11: Client uses tokens for API calls
βββΊ Add to request headers:
β βββΊ X-Session-Token: <session_token> (RECOMMENDED)
β OR
β βββΊ Authorization: Bearer <session_token>
βββΊ All subsequent API requests include token
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ Show: "Username and password are required"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid username or password" β Show error, clear password field
β βββΊ "User account is not active" β Show: "Account is disabled"
β βββΊ "User account is not verified" β Show: "Please verify your email/phone"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Login failed. Please try again."
Use Cases:
- User login
- Session establishment
- API access token generation
Endpoint: POST /{MODE}/auth/send-one-time-password
Description: Send one-time password via email, SMS, or WhatsApp. OTP is valid for 10 minutes.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"channel": "email"
}Channel Options:
email: Send OTP via emailsms: Send OTP via SMSwhatsapp: Send OTP via WhatsApp
Response:
{
"success": true,
"message": "OTP sent successfully",
"data": {
"message": "OTP sent successfully"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User requests OTP
βββΊ User enters email/phone on login/signup form
βββΊ User clicks "Send OTP" button
Step 2: Client sends OTP request
POST /{MODE}/auth/send-one-time-password
Content-Type: application/json
{
"user_id": "user@example.com",
"channel": "email"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check channel exists β β 400 if missing
βββΊ Validate channel value β β 400 if not email/sms/whatsapp
Step 4: Server generates OTP
βββΊ Generate 6-digit random code (e.g., "123456")
βββΊ Store in cache (Redis or in-memory)
βββΊ Key: "otp:{channel}:{user_id}"
βββΊ Value: OTP code
βββΊ TTL: 600 seconds (10 minutes)
Step 5: Server sends OTP via channel
βββΊ channel = "email"
β βββΊ Send email with OTP code
β βββΊ Subject: "Your OTP Code"
β
βββΊ channel = "sms"
β βββΊ Send SMS via Twilio
β βββΊ Message: "Your OTP is: 123456"
β
βββΊ channel = "whatsapp"
βββΊ Send WhatsApp message via Twilio
βββΊ Message: "Your OTP is: 123456"
Step 6: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "OTP sent successfully",
"data": {
"message": "OTP sent successfully"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 7: Client receives response
βββΊ Check response.success === true
βββΊ Show success message to user
Step 8: Client shows OTP input form
βββΊ Display: "OTP sent to your email/phone"
βββΊ Show OTP input field
βββΊ Start countdown timer (10 minutes)
βββΊ Enable "Resend OTP" button (after 60 seconds)
Step 9: User enters OTP
βββΊ User types 6-digit code from email/SMS
Step 10: Client validates OTP format
βββΊ Check: OTP is 6 digits
βββΊ β Show error if invalid format
Step 11: Client sends OTP for verification
βββΊ Proceed to verify-otp or login-with-otp endpoint
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "user_id is required" β Show: "Please enter email/phone"
β βββΊ "channel is required" β Show: "Please select channel"
β
βββΊ 500 Internal Server Error
β βββΊ "Failed to send OTP" β Show: "Unable to send OTP. Please try again."
β
βββΊ Network Error
βββΊ Show: "Connection error. Check your internet."
Use Cases:
- Password reset
- Email/phone verification
- Two-factor authentication
- Account recovery
Endpoint: POST /{MODE}/auth/verify-one-time-password
Description: Verify one-time password without logging in. Used for verification purposes.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}Response:
{
"success": true,
"message": "Verify Successfully",
"data": {
"user_id": "user@example.com"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User enters OTP
βββΊ User receives OTP via email/SMS/WhatsApp
βββΊ User types OTP into input field
Step 2: Client validates OTP format
βββΊ Check: OTP is 6 digits
βββΊ β Show error if invalid format
Step 3: Client sends verification request
POST /{MODE}/auth/verify-one-time-password
Content-Type: application/json
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 4: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check channel exists β β 400 if missing
βββΊ Check otp exists β β 400 if missing
Step 5: Server retrieves stored OTP
βββΊ Build cache key: "otp:{channel}:{user_id}"
βββΊ Get OTP from cache (Redis or in-memory)
βββΊ β 401 if OTP not found (expired or never sent)
Step 6: Server compares OTPs
βββΊ Compare stored OTP with provided OTP
βββΊ β 401 if OTPs don't match
Step 7: Server checks expiration
βββΊ Check if OTP is still valid (within 10 minutes)
βββΊ β 401 if expired
Step 8: Server returns verification result
βββΊ OTP is NOT deleted (can be reused)
βββΊ HTTP 200 OK
{
"success": true,
"message": "Verify Successfully",
"data": {
"user_id": "user@example.com"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 9: Client receives response
βββΊ Check response.success === true
βββΊ Proceed to next step (e.g., signup, password reset)
Step 10: Client updates UI
βββΊ Show success message: "OTP verified successfully"
βββΊ Enable next step button (e.g., "Create Account")
βββΊ Hide OTP input field
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ Show: "Please enter all required fields"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid OTP" β Show: "Incorrect OTP. Please try again."
β βββΊ "OTP expired" β Show: "OTP has expired. Please request a new one."
β βββΊ "OTP not found" β Show: "OTP not found. Please request a new OTP."
β
βββΊ 500 Internal Server Error
βββΊ Show: "Verification failed. Please try again."
Use Cases:
- Email verification
- Phone verification
- Pre-login verification
Endpoint: POST /{MODE}/auth/login-with-otp
Description: Verify OTP and login user. Returns access token upon successful verification. OTP is deleted after successful login.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}Response:
{
"success": true,
"id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd",
"message": "Login successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_id": "f533589d-48d3-4b67-9430-c0b4793ac13e",
"token_type": "bearer",
"user": {
"user_id": "uuid",
"email": "user@example.com",
"first_name": "John",
"last_name": "Doe"
}
},
"meta": {
"type": "dict"
},
"timestamp": "2025-01-28T15:51:55.980680Z"
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User has received OTP
βββΊ User received OTP via email/SMS/WhatsApp (from send-otp)
βββΊ User enters OTP in login form
Step 2: Client sends login request
POST /{MODE}/auth/login-with-otp
Content-Type: application/json
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check channel exists β β 400 if missing
βββΊ Check otp exists β β 400 if missing
βββΊ Validate email/phone format β β 400 if invalid
Step 4: Server gets user from database
βββΊ Query: getUserByEmailOrPhone(user_id)
βββΊ β 404 if user not found
Step 5: Server checks user status
βββΊ Check is_active = true β β 401 if false
βββΊ Check is_verified = true β β 401 if false
Step 6: Server verifies OTP
βββΊ Get stored OTP from cache: "otp:{channel}:{user_id}"
βββΊ Compare stored OTP with provided OTP
βββΊ Check expiration (10 minutes)
βββΊ β 401 if invalid/expired
Step 7: Server deletes OTP (consume)
βββΊ Delete OTP from cache (one-time use)
Step 8: Server updates last sign-in
βββΊ Update last_sign_in_at = current timestamp
Step 9: Server clears user blacklist
βββΊ Clear user-level session blacklist
βββΊ Clear user refresh token blacklist
Step 10: Server generates all tokens
βββΊ Generate session_id (UUID)
βββΊ Generate access_token (60 min)
βββΊ Generate session_token (7 days, with full profile)
βββΊ Generate refresh_token (30 days)
Step 11: Server returns tokens and user data
HTTP 200 OK
{
"success": true,
"data": {
"access_token": "...",
"refresh_token": "...",
"session_token": "...",
"session_id": "...",
"token_type": "bearer",
"user": { ... }
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 12: Client stores tokens
βββΊ Store session_token (RECOMMENDED)
βββΊ Store refresh_token
βββΊ Store session_id
βββΊ Store user data in app state
Step 13: Client updates UI
βββΊ Redirect to dashboard
βββΊ Show welcome message
βββΊ Update user context/state
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ Show: "Please enter all required fields"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid OTP" β Show: "Incorrect OTP"
β βββΊ "OTP expired" β Show: "OTP expired. Request new one."
β βββΊ "User account is not active" β Show: "Account disabled"
β βββΊ "User account is not verified" β Show: "Please verify account"
β
βββΊ 404 Not Found
β βββΊ "User not found" β Show: "User does not exist"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Login failed. Please try again."
Use Cases:
- Passwordless login
- Quick authentication
- Mobile app login
Endpoint: POST /{MODE}/auth/verify
Description: Verify OTP and create new user account. Supports master OTP for admin account creation.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}Response:
{
"success": true,
"id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd",
"message": "Signup successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_id": "f533589d-48d3-4b67-9430-c0b4793ac13e",
"token_type": "bearer",
"user": { ... }
},
"meta": {
"type": "dict"
},
"timestamp": "2025-01-28T15:51:55.980680Z"
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User completes signup form
βββΊ User enters email/phone
βββΊ User requests OTP (via send-otp endpoint)
βββΊ User receives and enters OTP
Step 2: Client sends signup request
POST /{MODE}/auth/verify
Content-Type: application/json
{
"user_id": "newuser@example.com",
"channel": "email",
"otp": "123456"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check channel exists β β 400 if missing
βββΊ Check otp exists β β 400 if missing
Step 4: Server checks master OTP (optional)
βββΊ Compare OTP with MASTER_OTP env variable
βββΊ If matches β Skip OTP verification, assign admin group
Step 5: Server verifies OTP (if not master OTP)
βββΊ Get stored OTP from cache
βββΊ Compare with provided OTP
βββΊ Check expiration
βββΊ β 401 if invalid/expired
(Note: OTP not deleted yet - will be deleted after signup)
Step 6: Server validates email/phone format
βββΊ If channel = "email" β validateEmail()
βββΊ If channel = "sms/whatsapp" β validatePhone()
βββΊ β 400 if invalid format
Step 7: Server checks if user already exists
βββΊ Query: getUserByEmailOrPhone(user_id)
βββΊ β 409 if user already exists
Step 8: Server creates new user account
βββΊ Generate user_id (UUID)
βββΊ Set default values:
β βββΊ is_active: true
β βββΊ is_verified: true
β βββΊ profile_accessibility: "public"
β βββΊ theme: "light"
β βββΊ user_type: "customer"
β βββΊ language: "en"
β βββΊ status: "ACTIVE"
βββΊ Set auth_type based on channel
βββΊ Set verification status:
β βββΊ is_email_verified: true (if channel=email)
β βββΊ is_phone_verified: true (if channel=sms/whatsapp)
βββΊ Insert user into database
Step 9: Server assigns groups (if master OTP)
βββΊ Assign admin group to user
Step 10: Server clears user blacklist
βββΊ Clear user-level session blacklist
βββΊ Clear user refresh token blacklist
Step 11: Server generates all tokens
βββΊ Generate session_id
βββΊ Generate access_token (60 min)
βββΊ Generate session_token (7 days)
βββΊ Generate refresh_token (30 days)
Step 12: Server deletes OTP (if not master OTP)
βββΊ Delete OTP from cache (consume=true)
Step 13: Server returns tokens and user data
HTTP 200 OK
{
"success": true,
"data": {
"access_token": "...",
"refresh_token": "...",
"session_token": "...",
"session_id": "...",
"token_type": "bearer",
"user": { ... }
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 14: Client stores tokens
βββΊ Store session_token (RECOMMENDED)
βββΊ Store refresh_token
βββΊ Store session_id
βββΊ Store user data
Step 15: Client updates UI
βββΊ Redirect to onboarding/dashboard
βββΊ Show welcome message
βββΊ Update user context/state
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Invalid email format" β Show: "Please enter valid email"
β βββΊ "Invalid phone format" β Show: "Please enter valid phone"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid OTP" β Show: "Incorrect OTP. Please try again."
β
βββΊ 409 Conflict
β βββΊ "User already exists" β Show: "Account already exists. Please login."
β
βββΊ 500 Internal Server Error
βββΊ Show: "Signup failed. Please try again."
Special Features:
- Master OTP: If
MASTER_OTPenvironment variable matches, user is assigned admin group - Auto-verification: Email/phone is automatically verified during signup
- Default Settings: New users get sensible defaults
Use Cases:
- New user registration
- Account creation
- Onboarding flow
Endpoint: POST /{MODE}/auth/set-password
Description: Set password for authenticated user (for users who signed up with OTP).
Authentication: Required
Permission: edit_profile
Request Body:
{
"password": "new-password",
"confirm_password": "new-password"
}Response:
{
"success": true,
"message": "Password set successfully",
"data": {
"message": "Password set successfully"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User wants to set password
βββΊ User is logged in (has valid token)
βββΊ User navigates to "Set Password" page
Step 2: User enters password
βββΊ User enters new password
βββΊ User confirms password
βββΊ Client validates: passwords match
Step 3: Client sends set password request
POST /{MODE}/auth/set-password
Authorization: Bearer <session_token>
Content-Type: application/json
{
"password": "newSecurePassword123",
"confirm_password": "newSecurePassword123"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 4: Server validates authentication
βββΊ Extract token from Authorization header
βββΊ Validate token (decode, check expiration, blacklist)
βββΊ β 401 if invalid/expired
Step 5: Server validates request
βββΊ Check password exists β β 400 if missing
βββΊ Check confirm_password exists β β 400 if missing
βββΊ Check password === confirm_password β β 400 if mismatch
Step 6: Server hashes password
βββΊ Use bcrypt with 10 salt rounds
βββΊ Generate secure hash
Step 7: Server updates user password
βββΊ Get user_id from token
βββΊ Update password in database
βββΊ Update last_updated timestamp
Step 8: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "Password set successfully",
"data": {
"message": "Password set successfully"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 9: Client receives response
βββΊ Check response.success === true
βββΊ Show success message
Step 10: Client updates UI
βββΊ Show: "Password set successfully"
βββΊ Redirect to profile/settings
βββΊ Clear password fields
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Password is required" β Show: "Please enter password"
β βββΊ "Passwords do not match" β Show: "Passwords don't match"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid token" β Redirect to login
β
βββΊ 500 Internal Server Error
βββΊ Show: "Failed to set password. Please try again."
Use Cases:
- Initial password setup
- Passwordless signup completion
Endpoint: POST /{MODE}/auth/change-password
Description: Change user's existing password. Requires old password verification.
Authentication: Required
Permission: edit_profile
Request Body:
{
"user_id": "user@example.com",
"channel": "email",
"old_password": "current-password",
"password": "new-password",
"confirm_password": "new-password"
}Response:
{
"success": true,
"message": "Password updated successfully",
"data": {
"message": "Password updated successfully"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User wants to change password
βββΊ User is logged in (has valid token)
βββΊ User navigates to "Change Password" page
Step 2: User enters password information
βββΊ User enters current password
βββΊ User enters new password
βββΊ User confirms new password
Step 3: Client validates passwords
βββΊ Check: new password !== old password
βββΊ Check: new password === confirm password
βββΊ β Show error if validation fails
Step 4: Client sends change password request
POST /{MODE}/auth/change-password
Authorization: Bearer <session_token>
Content-Type: application/json
{
"user_id": "user@example.com",
"channel": "email",
"old_password": "currentPassword123",
"password": "newSecurePassword123",
"confirm_password": "newSecurePassword123"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 5: Server validates authentication
βββΊ Extract token from Authorization header
βββΊ Validate token (decode, check expiration, blacklist)
βββΊ β 401 if invalid/expired
Step 6: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check old_password exists β β 400 if missing
βββΊ Check password exists β β 400 if missing
βββΊ Check confirm_password exists β β 400 if missing
Step 7: Server validates passwords match
βββΊ Check password === confirm_password
βββΊ β 400 if mismatch
Step 8: Server verifies old password
βββΊ Get user from database (by user_id from token)
βββΊ Authenticate user with old_password
β βββΊ Get user by email/phone
β βββΊ Verify password (bcrypt/PBKDF2)
β βββΊ Check user status (is_active, is_verified)
βββΊ β 401 if old password incorrect
Step 9: Server hashes new password
βββΊ Use bcrypt with 10 salt rounds
βββΊ Generate secure hash
Step 10: Server updates user password
βββΊ Get user_id from authenticated token
βββΊ Update password in database
βββΊ Update last_updated timestamp
Step 11: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "Password updated successfully",
"data": {
"message": "Password updated successfully"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 12: Client receives response
βββΊ Check response.success === true
βββΊ Show success message
Step 13: Client updates UI
βββΊ Show: "Password changed successfully"
βββΊ Clear password fields
βββΊ Optionally: Force re-login for security
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Passwords do not match" β Show: "New passwords don't match"
β βββΊ "Missing required fields" β Show: "Please fill all fields"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid token" β Redirect to login
β βββΊ "Invalid old password" β Show: "Current password is incorrect"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Failed to change password. Please try again."
Use Cases:
- Password change
- Security updates
- Account security
Endpoint: POST /{MODE}/auth/forget-password
Description: Reset password after verifying OTP. Used for password recovery.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"otp": "123456",
"password": "new-password",
"confirm_password": "new-password"
}Response:
{
"success": true,
"message": "Password updated successfully",
"data": {
"message": "Password updated successfully"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User forgot password
βββΊ User clicks "Forgot Password" link
βββΊ User enters email/phone
Step 2: Client requests OTP
POST /{MODE}/auth/send-one-time-password
{
"user_id": "user@example.com",
"channel": "email"
}
βββΊ User receives OTP via email/SMS
Step 3: User enters OTP and new password
βββΊ User enters OTP from email/SMS
βββΊ User enters new password
βββΊ User confirms new password
Step 4: Client validates passwords match
βββΊ β Show error if passwords don't match
Step 5: Client sends password reset request
POST /{MODE}/auth/forget-password
Content-Type: application/json
{
"user_id": "user@example.com",
"otp": "123456",
"password": "newSecurePassword123",
"confirm_password": "newSecurePassword123"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 6: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check otp exists β β 400 if missing
βββΊ Check password exists β β 400 if missing
βββΊ Check confirm_password exists β β 400 if missing
Step 7: Server verifies OTP
βββΊ Get stored OTP from cache
βββΊ Compare with provided OTP
βββΊ Check expiration (10 minutes)
βββΊ β 401 if invalid/expired
Step 8: Server validates email/phone format
βββΊ Validate email format (if contains @)
βββΊ Validate phone format (if doesn't contain @)
βββΊ β 400 if invalid format
Step 9: Server gets user from database
βββΊ Query: getUserByEmailOrPhone(user_id)
βββΊ β 404 if user not found
Step 10: Server validates passwords match
βββΊ Check password === confirm_password
βββΊ β 400 if mismatch
Step 11: Server hashes new password
βββΊ Use bcrypt with 10 salt rounds
βββΊ Generate secure hash
Step 12: Server updates user password
βββΊ Update password in database
βββΊ Update last_updated timestamp
Step 13: Server deletes OTP (consume)
βββΊ Delete OTP from cache (one-time use)
Step 14: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "Password updated successfully",
"data": {
"message": "Password updated successfully"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 15: Client receives response
βββΊ Check response.success === true
βββΊ Show success message
Step 16: Client updates UI
βββΊ Show: "Password reset successfully"
βββΊ Redirect to login page
βββΊ Clear form fields
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Passwords do not match" β Show: "Passwords don't match"
β βββΊ "Invalid email/phone format" β Show: "Invalid format"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid OTP" β Show: "Incorrect or expired OTP"
β
βββΊ 404 Not Found
β βββΊ "User not found" β Show: "User does not exist"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Password reset failed. Please try again."
Use Cases:
- Password recovery
- Account reset
- Security recovery
Endpoint: POST /{MODE}/auth/refresh-token
Description: Refresh access token using refresh token. Returns new tokens with a new session.
Authentication: Not required (uses refresh token)
Request Body:
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response:
{
"success": true,
"id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd",
"message": "Token refreshed successfully",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_id": "f533589d-48d3-4b67-9430-c0b4793ac13e",
"token_type": "bearer"
},
"meta": {
"type": "dict"
},
"timestamp": "2025-01-28T15:51:55.980680Z"
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: Token expires or about to expire
βββΊ Client makes API request with expired token
βββΊ Server returns 401 Unauthorized
Step 2: Client detects token expiration
βββΊ Intercept 401 response
βββΊ Check if refresh_token exists
Step 3: Client sends refresh token request
POST /{MODE}/auth/refresh-token
Content-Type: application/json
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 4: Server validates refresh token
βββΊ Check refresh_token exists β β 400 if missing
βββΊ Decode JWT token
βββΊ Try with audience="authenticated"
βββΊ Fallback: decode without audience
βββΊ β 401 if invalid/expired
Step 5: Server validates token type
βββΊ Check token.type === "refresh"
βββΊ β 401 if not refresh token
Step 6: Server checks blacklist status
βββΊ Check if token is blacklisted β β 401 if blacklisted
βββΊ Check if user refresh tokens revoked β β 401 if revoked
βββΊ Check if session is blacklisted β β 401 if blacklisted
Step 7: Server extracts user info
βββΊ Get user_id from token.sub
βββΊ Get session_id from token (if exists)
Step 8: Server gets user from database
βββΊ Query: getUserById(user_id)
βββΊ β 404 if user not found
Step 9: Server blacklists old tokens
βββΊ Blacklist old refresh token
βββΊ Blacklist old session (if session_id exists)
Step 10: Server generates new tokens
βββΊ Generate new session_id (UUID)
βββΊ Generate new access_token (60 min, new JTI)
βββΊ Generate new session_token (7 days, full profile)
βββΊ Generate new refresh_token (30 days, rotated)
Step 11: Server returns new tokens
HTTP 200 OK
{
"success": true,
"data": {
"access_token": "...",
"refresh_token": "...",
"session_token": "...",
"session_id": "...",
"token_type": "bearer"
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 12: Client receives new tokens
βββΊ Check response.success === true
βββΊ Extract new tokens from response.data
Step 13: Client updates stored tokens
βββΊ Update session_token (RECOMMENDED)
βββΊ Update refresh_token (rotated)
βββΊ Update session_id (new)
βββΊ Update access_token (optional)
Step 14: Client retries original request
βββΊ Use new session_token in request header
βββΊ Original API call succeeds
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "refresh_token is required" β Show: "Please login again"
β
βββΊ 401 Unauthorized
β βββΊ "Refresh token expired" β Redirect to login
β βββΊ "Refresh token revoked" β Redirect to login
β βββΊ "Invalid refresh token" β Redirect to login
β
βββΊ 404 Not Found
β βββΊ "User not found" β Redirect to login
β
βββΊ 500 Internal Server Error
βββΊ Show: "Token refresh failed. Please login again."
Client-Side Implementation Example:
// Axios interceptor for automatic token refresh
axios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// If 401 and not already retrying
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Get refresh token
const refreshToken = localStorage.getItem('refresh_token');
// Request new tokens
const response = await axios.post('/api/auth/refresh-token', {
refresh_token: refreshToken
});
// Update stored tokens
localStorage.setItem('session_token', response.data.data.session_token);
localStorage.setItem('refresh_token', response.data.data.refresh_token);
localStorage.setItem('session_id', response.data.data.session_id);
// Retry original request with new token
originalRequest.headers['X-Session-Token'] = response.data.data.session_token;
return axios(originalRequest);
} catch (refreshError) {
// Refresh failed, redirect to login
localStorage.clear();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);Token Rotation:
- All tokens are rotated (new tokens generated)
- Old refresh token is blacklisted
- Old session is blacklisted (if session_id exists)
- New session_id is created for all new tokens
- Complete token rotation for security
Use Cases:
- Access token expiration
- Token rotation for security
- Session renewal
Error Responses:
400: Refresh token not provided401: Invalid or expired refresh token401: Refresh token has been revoked (user-level blacklist)401: Session has been revoked401: Invalid token type (not a refresh token)404: User not found500: JWT configuration error
Endpoint: POST /{MODE}/auth/logout
Description: Logout user and revoke all tokens and sessions from all devices. Returns detailed revocation status.
Authentication: Required (permission: view_profile)
Request Body: None
Headers:
Authorization: Bearer <access_token>
or
X-Session-Token: <session_token>
Response:
{
"success": true,
"id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd",
"message": "Logged out successfully. All tokens and sessions have been revoked.",
"data": {
"message": "Logged out successfully",
"access_token_revoked": true,
"refresh_tokens_revoked": true,
"sessions_revoked": true,
"tokens_revoked": true
},
"meta": {
"type": "dict"
},
"timestamp": "2025-01-28T15:51:55.980680Z"
}Response Fields:
access_token_revoked: Whether the current access token was blacklisted (by JTI)refresh_tokens_revoked: Whether all refresh tokens for the user were revokedsessions_revoked: Whether all sessions for the user were blacklisted (complete logout from all devices)tokens_revoked: Overall status - true if all operations succeeded
Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User clicks logout
βββΊ User clicks "Logout" button
βββΊ Client shows confirmation (optional)
Step 2: Client sends logout request
POST /{MODE}/auth/logout
Authorization: Bearer <session_token>
OR
X-Session-Token: <session_token>
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates authentication
βββΊ Extract token from Authorization header or X-Session-Token
βββΊ Validate token (decode, check signature)
βββΊ β 401 if invalid (but continues with logout if expired)
Step 4: Server decodes token (even if expired)
βββΊ Decode with verify_exp: false (allows expired tokens)
βββΊ Extract JTI (JWT ID) from token
βββΊ Extract user_id from token.sub
βββΊ Extract session_id from token (if available)
Step 5: Server blacklists access token
βββΊ Blacklist by JTI: "blacklist:access:jti:{jti}"
βββΊ TTL: 45 days (3888000 seconds)
Step 6: Server revokes all refresh tokens
βββΊ Set user-level blacklist: "blacklist:refresh:user:{user_id}"
βββΊ TTL: 30 days (all refresh tokens for user invalidated)
Step 7: Server blacklists all user sessions
βββΊ Set user-level blacklist: "blacklist:user:{user_id}"
βββΊ TTL: 30 days (all sessions for user invalidated)
βββΊ Complete logout from all devices
Step 8: Server tracks revocation status
βββΊ access_token_revoked: true/false
βββΊ refresh_tokens_revoked: true/false
βββΊ sessions_revoked: true/false
βββΊ tokens_revoked: overall status
Step 9: Server returns logout status
HTTP 200 OK
{
"success": true,
"message": "Logged out successfully. All tokens and sessions have been revoked.",
"data": {
"message": "Logged out successfully",
"access_token_revoked": true,
"refresh_tokens_revoked": true,
"sessions_revoked": true,
"tokens_revoked": true
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 10: Client receives logout response
βββΊ Check response.success === true
βββΊ Check tokens_revoked status
Step 11: Client clears local storage
βββΊ Remove session_token
βββΊ Remove refresh_token
βββΊ Remove access_token
βββΊ Remove session_id
βββΊ Clear user data from app state
Step 12: Client updates UI
βββΊ Redirect to login page
βββΊ Clear user context/state
βββΊ Show logout success message (optional)
Error Handling:
Client receives error response:
βββΊ 401 Unauthorized
β βββΊ Token invalid/expired β Still proceed with logout
β βββΊ Clear local storage and redirect to login
β
βββΊ 500 Internal Server Error
βββΊ Logout may have partially succeeded
βββΊ Still clear local storage and redirect to login
Important Notes:
- Logout works even with expired tokens (server decodes without expiration check)
- All tokens are invalidated (access, refresh, session)
- All sessions are revoked (complete logout from all devices)
- Client should always clear local storage regardless of response
Token Blacklisting:
- Access tokens are blacklisted by JTI (JWT ID) for efficiency
- All refresh tokens for the user are revoked (user-level blacklist)
- All sessions for the user are blacklisted (complete logout from all devices)
- Blacklist entries expire automatically based on token expiration times
- Works even with expired tokens (decodes without expiration check)
Security Features:
- Complete logout from all devices (all sessions revoked)
- All refresh tokens invalidated (prevents token refresh)
- Access token blacklisted (prevents reuse)
- Works with expired tokens (for cleanup)
Use Cases:
- User logout
- Complete session termination (all devices)
- Security logout
- Account security
- Force logout from all devices
Endpoint: POST /{MODE}/auth/check-user-availability
Description: Check if email or phone number is available for registration.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com"
}Alternative:
{
"email": "user@example.com"
}or
{
"phone": "+1234567890"
}Response (Available):
{
"success": true,
"message": "User is not available",
"data": {
"available": false,
"first_name": null,
"last_name": null
}
}Response (Not Available):
{
"success": true,
"message": "User is available",
"data": {
"available": true,
"first_name": "John",
"last_name": "Doe"
}
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User enters email/phone in registration form
βββΊ User types email or phone number
βββΊ Client may validate format client-side (optional)
Step 2: Client sends availability check (on blur or debounced)
POST /{MODE}/auth/check-user-availability
Content-Type: application/json
{
"user_id": "user@example.com"
}
OR
{
"email": "user@example.com"
}
OR
{
"phone": "+1234567890"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 3: Server validates request
βββΊ Check: user_id OR email OR phone exists
βββΊ β 400 if none provided
Step 4: Server determines identifier
βββΊ Use user_id if provided
βββΊ Use email if provided
βββΊ Use phone if provided
Step 5: Server validates format
βββΊ If email format β validateEmail()
βββΊ If phone format β validatePhone()
βββΊ β 400 if invalid format
Step 6: Server queries database
βββΊ Query: getUserByEmailOrPhone(identifier)
βββΊ Returns user if exists, None if not exists
Step 7: Server determines availability
βββΊ If user exists:
β βββΊ available: false
β βββΊ first_name: user.first_name (if exists)
β βββΊ last_name: user.last_name (if exists)
βββΊ If user not exists:
βββΊ available: true
βββΊ first_name: null
βββΊ last_name: null
Step 8: Server returns availability status
HTTP 200 OK
{
"success": true,
"message": "User is not available", // or "User is available"
"data": {
"available": false, // or true
"first_name": "John", // or null
"last_name": "Doe" // or null
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 9: Client receives response
βββΊ Check response.data.available
βββΊ Update UI based on availability
Step 10: Client updates UI
βββΊ If available === false:
β βββΊ Show: "Email/phone already registered"
β βββΊ Show user name if provided: "This email belongs to John Doe"
β βββΊ Disable submit button or show error
βββΊ If available === true:
βββΊ Show: "Email/phone is available" (optional)
βββΊ Enable submit button
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "user_id, email, or phone is required" β Show: "Please enter email/phone"
β βββΊ "Invalid email/phone format" β Show: "Invalid format"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Unable to check availability. Please try again."
Client-Side Implementation Example:
// Debounced availability check
let checkTimeout;
const emailInput = document.getElementById('email');
emailInput.addEventListener('input', (e) => {
clearTimeout(checkTimeout);
const email = e.target.value;
// Wait 500ms after user stops typing
checkTimeout = setTimeout(async () => {
if (email && validateEmail(email)) {
try {
const response = await fetch('/api/auth/check-user-availability', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const data = await response.json();
if (!data.data.available) {
// Email already registered
showError('Email already registered');
if (data.data.first_name) {
showInfo(`This email belongs to ${data.data.first_name} ${data.data.last_name}`);
}
} else {
// Email available
clearError();
}
} catch (error) {
console.error('Availability check failed:', error);
}
}
}, 500);
});Use Cases:
- Registration form validation
- Username/email availability check
- Phone number availability check
Endpoint: POST /{MODE}/auth/verify-email-and-phone
Description: Verify email or phone number with OTP.
Authentication: Not required
Request Body:
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}Response:
{
"success": true,
"message": "Email/Phone verified successfully",
"data": { ... }
}Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User wants to verify email/phone
βββΊ User is on verification page
βββΊ User has received OTP (from send-otp endpoint)
Step 2: User enters OTP
βββΊ User enters OTP from email/SMS
βββΊ User clicks "Verify" button
Step 3: Client sends verification request
POST /{MODE}/auth/verify-email-and-phone
Content-Type: application/json
{
"user_id": "user@example.com",
"channel": "email",
"otp": "123456"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 4: Server validates request
βββΊ Check user_id exists β β 400 if missing
βββΊ Check channel exists β β 400 if missing
βββΊ Check otp exists β β 400 if missing
Step 5: Server validates channel
βββΊ Check channel is "email" or "sms"
βββΊ β 400 if invalid channel
Step 6: Server validates format
βββΊ If channel = "email" β validateEmail(user_id)
βββΊ If channel = "sms" β validatePhone(user_id)
βββΊ β 400 if invalid format
Step 7: Server verifies OTP
βββΊ Get stored OTP from cache: "otp:{channel}:{user_id}"
βββΊ Compare stored OTP with provided OTP
βββΊ Check expiration (10 minutes)
βββΊ β 401 if invalid/expired
βββΊ OTP is NOT deleted (consume=false, can be reused)
Step 8: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "Email/Phone verified successfully",
"data": {
"user_id": "user@example.com",
"channel": "email",
"verified": true
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 9: Client receives response
βββΊ Check response.success === true
βββΊ Check response.data.verified === true
Step 10: Client updates UI
βββΊ Show: "Email/Phone verified successfully"
βββΊ Mark verification status as complete
βββΊ Enable next step (e.g., complete profile)
βββΊ Hide OTP input field
Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Invalid channel" β Show: "Channel must be email or sms"
β βββΊ "Invalid email format" β Show: "Please enter valid email"
β βββΊ "Invalid phone format" β Show: "Please enter valid phone"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid OTP" β Show: "Incorrect OTP. Please try again."
β βββΊ "OTP expired" β Show: "OTP expired. Please request a new one."
β
βββΊ 500 Internal Server Error
βββΊ Show: "Verification failed. Please try again."
Note: This endpoint does NOT delete the OTP after verification (consume=false), allowing the OTP to be reused for other verification steps if needed.
Use Cases:
- Email verification
- Phone verification
- Account verification
Description: Secure two-step verification process for changing email or phone number. This workflow ensures both the current and new contact information are verified before making changes.
Recommended Workflow Steps:
-
Step 1: Verify Primary Email/Phone (Optional but Recommended)
- Verify user owns the current email/phone before allowing changes
- Provides additional security layer
-
Step 2: Request OTP for New Email/Phone
- Request OTP to be sent to the new email/phone address
- Ensures user has access to the new contact information
-
Step 3: Change Email/Phone
- Call the change email/phone API with the OTP
- Server verifies OTP and updates the contact information
Complete Client-Server Communication Flow:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User initiates email/phone change
βββΊ User is logged in (has valid token)
βββΊ User navigates to "Change Email" or "Change Phone" page
βββΊ User enters new email/phone address
Step 2: (OPTIONAL) Verify Primary Email/Phone
βββΊ Client requests OTP for current email/phone
β POST /{MODE}/auth/send-one-time-password
β {
β "user_id": "current@example.com", // Current email/phone
β "channel": "email"
β }
β
βββΊ User receives OTP on current email/phone
βββΊ User enters OTP to verify ownership
Step 3: Client verifies primary email/phone (OPTIONAL)
βββΊ POST /{MODE}/auth/verify-one-time-password
β {
β "user_id": "current@example.com",
β "channel": "email",
β "otp": "123456"
β }
β
βββΊ Server verifies OTP (does NOT delete it)
βββΊ Response: { "success": true, "message": "Verify Successfully" }
Step 4: Client requests OTP for NEW email/phone
βββΊ POST /{MODE}/auth/send-one-time-password
β {
β "user_id": "newemail@example.com", // NEW email/phone
β "channel": "email"
β }
β
βββΊ User receives OTP on NEW email/phone
βββΊ This proves user has access to new contact info
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Server β Generates OTP, stores in cache (10 min TTL)
Server β Sends OTP via email/SMS/WhatsApp to NEW address
Server β Response: { "success": true, "message": "OTP sent successfully" }
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 5: User receives OTP on new email/phone
βββΊ User checks new email/SMS, gets 6-digit code
Step 6: Client calls change email/phone API
βββΊ POST /{MODE}/settings/change-email
β Authorization: Bearer <session_token>
β {
β "new_email": "newemail@example.com",
β "otp": "123456" // OTP received on new email
β }
β
OR
β
βββΊ POST /{MODE}/settings/change-phone
β Authorization: Bearer <session_token>
β {
β "new_phone": "+1234567890",
β "otp": "123456" // OTP received on new phone
β }
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 7: Server validates authentication
βββΊ Extract token from Authorization header
βββΊ Validate token (decode, check expiration, blacklist)
βββΊ β 401 if invalid/expired
Step 8: Server verifies OTP for NEW email/phone
βββΊ Get stored OTP from cache: "otp:{channel}:{new_email/phone}"
βββΊ Compare stored OTP with provided OTP
βββΊ Check expiration (10 minutes)
βββΊ β 400 if invalid/expired
Step 9: Server checks email/phone availability
βββΊ Check if new email/phone already exists for another user
βββΊ β 400 if already exists
Step 10: Server updates email/phone
βββΊ Update user.email or user.phone_number
βββΊ Set is_email_verified = TRUE or is_phone_verified = TRUE
βββΊ Set email_verified_at or phone_number_verified_at = NOW()
βββΊ Update last_updated = NOW()
Step 11: Server deletes OTP (consume)
βββΊ Delete OTP from cache (one-time use)
Step 12: Server returns success response
HTTP 200 OK
{
"success": true,
"message": "Email/Phone updated and verified successfully",
"data": {
"user": {
"id": "uuid",
"email": "newemail@example.com",
"is_email_verified": true,
"email_verified_at": "2025-01-28T15:51:55Z"
}
}
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 13: Client receives response
βββΊ Check response.success === true
βββΊ Extract updated user data
Step 14: Client updates UI
βββΊ Show: "Email/Phone updated successfully"
βββΊ Update user profile display
βββΊ Clear form fields
βββΊ Redirect to profile/settings page
Why This Workflow is Recommended:
- Security: Verifies ownership of both current and new contact information
- Prevents Unauthorized Changes: Requires access to both email/phone addresses
- Two-Step Verification: Adds an extra layer of security
- User Experience: Clear step-by-step process
- Error Prevention: Catches issues before making changes
Alternative Simplified Workflow (Less Secure):
If you skip Step 2-3 (primary verification), you can directly:
- Request OTP for new email/phone
- Call change email/phone API
However, the recommended workflow provides better security.
Client-Side Implementation Example:
// Complete email change workflow
async function changeEmailWithVerification(currentEmail, newEmail) {
try {
// Step 1: (Optional) Verify current email
// Request OTP for current email
await fetch('/api/v1/auth/send-one-time-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: currentEmail,
channel: 'email'
})
});
// User enters OTP for current email
const currentOtp = prompt('Enter OTP sent to your current email:');
// Verify current email
const verifyResponse = await fetch('/api/v1/auth/verify-one-time-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: currentEmail,
channel: 'email',
otp: currentOtp
})
});
const verifyData = await verifyResponse.json();
if (!verifyData.success) {
throw new Error('Current email verification failed');
}
// Step 2: Request OTP for new email
await fetch('/api/v1/auth/send-one-time-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: newEmail,
channel: 'email'
})
});
// User enters OTP for new email
const newOtp = prompt('Enter OTP sent to your new email:');
// Step 3: Change email
const token = localStorage.getItem('session_token');
const changeResponse = await fetch('/api/v1/settings/change-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
new_email: newEmail,
otp: newOtp
})
});
const changeData = await changeResponse.json();
if (changeData.success) {
console.log('Email changed successfully:', changeData.data.user.email);
return changeData.data;
} else {
throw new Error(changeData.error?.message || 'Failed to change email');
}
} catch (error) {
console.error('Email change error:', error);
throw error;
}
}
// Simplified workflow (without primary verification)
async function changeEmailSimple(newEmail) {
try {
// Step 1: Request OTP for new email
await fetch('/api/v1/auth/send-one-time-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: newEmail,
channel: 'email'
})
});
// User enters OTP
const otp = prompt('Enter OTP sent to your new email:');
// Step 2: Change email
const token = localStorage.getItem('session_token');
const response = await fetch('/api/v1/settings/change-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
new_email: newEmail,
otp: otp
})
});
const data = await response.json();
return data;
} catch (error) {
console.error('Email change error:', error);
throw error;
}
}Error Handling:
Client receives error response:
βββΊ 400 Bad Request
β βββΊ "Invalid OTP" β Show: "Incorrect OTP. Please try again."
β βββΊ "Email already exists" β Show: "This email is already registered"
β βββΊ "Invalid email format" β Show: "Please enter valid email"
β
βββΊ 401 Unauthorized
β βββΊ "Invalid token" β Redirect to login
β βββΊ "OTP expired" β Show: "OTP expired. Please request a new one."
β
βββΊ 403 Forbidden
β βββΊ "Permission denied" β Show: "You don't have permission to change email"
β
βββΊ 500 Internal Server Error
βββΊ Show: "Failed to change email. Please try again."
Use Cases:
- Secure email change with two-step verification
- Secure phone change with two-step verification
- Account security updates
- Contact information updates
Related Endpoints:
POST /{MODE}/auth/send-one-time-password- Request OTPPOST /{MODE}/auth/verify-one-time-password- Verify OTP (doesn't delete)POST /{MODE}/settings/change-email- Change email (requires OTP to new email)POST /{MODE}/settings/change-phone- Change phone (requires OTP to new phone)
Endpoint: GET /{MODE}/auth/token-info or POST /{MODE}/auth/token-info
Description: Get information about the current authentication token. Useful for debugging and understanding token configuration.
Authentication: Required
Request Body (POST only, optional):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Headers:
Authorization: Bearer <access_token>
or
X-Session-Token: <session_token>
Response:
{
"success": true,
"message": "Token information retrieved successfully",
"data": {
"current_token": {
"type": "session",
"user_id": "f533589d-48d3-4b67-9430-c0b4793ac13e",
"expires_at": "2025-02-04T15:51:55Z",
"expires_in": "7 days",
"issued_at": "2025-01-28T15:51:55Z",
"session_id": "a2cfa5fc-5963-4a53-a0a8-6d2d250af8fd"
},
"token_config": {
"access_token": {
"expiry_minutes": 60,
"expires_in": "1 hour"
},
"session_token": {
"expiry_minutes": 10080,
"expires_in": "7 days"
},
"refresh_token": {
"expiry_minutes": 43200,
"expires_in": "30 days"
}
},
"extension_info": {
"access_token_extension": "1 hour",
"session_token_extension": "7 days",
"refresh_token_extension": "30 days"
}
}
}Response Fields:
current_token: Information about the token used for authenticationtype: Token type (access, session, or refresh)user_id: User ID from tokenexpires_at: Token expiration timestampexpires_in: Human-readable expiration timeissued_at: Token issuance timestampsession_id: Session identifier (if available)
token_config: Configuration for all token typesextension_info: How long tokens are extended when refreshed
Workflow:
1. Authenticated Request
β
βββΊ Extract Token from Headers
β βββΊ Authorization Bearer header
β βββΊ X-Session-Token header (fallback)
β
βββΊ Decode Token
β βββΊ Extract token type
β βββΊ Extract user_id
β βββΊ Extract expiration time
β βββΊ Extract session_id
β
βββΊ Get Token Configuration
β βββΊ Read from environment variables
β
βββΊ Return Token Information
Use Cases:
- Debug authentication issues
- Check token expiration
- Understand token configuration
- Verify token type and payload
- Client-side token validation
Note: This endpoint is excluded from API schema (include_in_schema=False) but is available for use.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Authentication Flow β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Registration? β
ββββββββββ¬βββββββββ
β
ββββββββββββββ΄βββββββββββββ
β β
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β Signup β β Login β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β Send OTP β β Password/OTP β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β Verify OTP β β Authenticate β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
ββββββββββββββ¬βββββββββββββ
β
βΌ
βββββββββββββββββββ
β Generate Token β
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Return Token β
βββββββββββββββββββ
Flow Explanation:
- User Decision: User chooses between Registration (Signup) or Login
- Signup Path: Send OTP β Verify OTP β Generate Token
- Login Path: Password/OTP β Authenticate β Generate Token
- Token Generation: Server generates all tokens (access, refresh, session)
- Token Return: Client receives tokens and stores them securely
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β User Action: Login or Signup?
βΌ
βββββββββββββββββββββββ
β Registration? β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββ΄ββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β SIGNUP β β LOGIN β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
β β
βΌ β
βββββββββββββββββ β
β Send OTP β β
β (Client) β β
βββββββββ¬ββββββββ β
β β
β POST /auth/send-otp β
βΌ β
βββββββββββββββββ β
β SERVER β β
β - Generate β β
β - Store OTP β β
β - Send Email β β
βββββββββ¬ββββββββ β
β β
β Response: OTP Sent β
βΌ β
βββββββββββββββββ β
β User Receivesβ β
β OTP via Emailβ β
βββββββββ¬ββββββββ β
β β
β POST /auth/verify β
β (Signup) β
βΌ β
βββββββββββββββββ β
β SERVER β β
β - Verify OTP β β
β - Create Userβ β
β - Generate β β
β Tokens β β
βββββββββ¬ββββββββ β
β β
β Response: Tokens + User β
βΌ β
βββββββββββββββββ β
β CLIENT β β
β - Store β β
β Tokens β β
β - Redirect β β
βββββββββββββββββ β
β
β
βΌ
βββββββββββββββββ
β Password/ β
β OTP Login β
βββββββββ¬ββββββββ
β
βββββββββββββββββββββββββ΄ββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β Password β β OTP Login β
β Login β β β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
β POST /auth/login-with-password β POST /auth/send-otp
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β SERVER β β SERVER β
β - Validate β β - Generate β
β - Authenticateβ β - Send OTP β
β - Generate β βββββββββ¬ββββββββ
β Tokens β β
βββββββββ¬ββββββββ β
β β User Receives OTP
β βΌ
β βββββββββββββββββ
β β POST /auth/ β
β β login-with-otpβ
β βββββββββ¬ββββββββ
β β
β βΌ
β βββββββββββββββββ
β β SERVER β
β β - Verify OTP β
β β - Generate β
β β Tokens β
β βββββββββ¬ββββββββ
β β
βββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
β
β Response: Tokens + User
βΌ
βββββββββββββββββ
β CLIENT β
β - Store β
β Tokens β
β - Use for APIβ
β Requests β
βββββββββββββββββ
Key Points:
- Blue boxes: Client-side actions
- Green boxes: Server-side processing
- Arrows: Request/response flow direction
- Multiple paths: Shows different authentication methods
- Token storage: Final step shows token usage for API requests
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User Action: Wants to login or signup
β
βββΊ Decision: New User (Signup) or Existing User (Login)?
β
βββΊ User enters email/phone
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SCENARIO 1: NEW USER (SIGNUP)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: Check Availability (Optional)
Client β POST /auth/check-user-availability
Body: { "email": "user@example.com" }
Server β Response: { "available": true }
Client: If available, proceed to signup
Step 2: Request OTP
Client β POST /auth/send-one-time-password
Body: { "user_id": "user@example.com", "channel": "email" }
Server β Generates OTP, stores in cache, sends email
Server β Response: { "success": true, "message": "OTP sent" }
Client: Shows "OTP sent to your email"
Step 3: User receives OTP
User: Checks email, gets 6-digit code
Step 4: Verify OTP and Signup
Client β POST /auth/verify
Body: { "user_id": "user@example.com", "channel": "email", "otp": "123456" }
Server β Verifies OTP, creates user account, generates tokens
Server β Response: { "success": true, "data": { tokens, user } }
Client: Stores tokens, redirects to dashboard
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
SCENARIO 2: EXISTING USER (LOGIN)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
OPTION A: Password Login
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User enters credentials
Client: User enters email/phone and password
Step 2: Login Request
Client β POST /auth/login-with-password
Body: username=user@example.com&password=secret123
Server β Validates credentials, checks user status, generates tokens
Server β Response: { "success": true, "data": { tokens, user } }
Client: Stores tokens, redirects to dashboard
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
OPTION B: OTP Login
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: Request OTP
Client β POST /auth/send-one-time-password
Body: { "user_id": "user@example.com", "channel": "email" }
Server β Generates OTP, sends email
Server β Response: { "success": true }
Client: Shows "OTP sent to your email"
Step 2: User receives OTP
User: Checks email, gets 6-digit code
Step 3: Login with OTP
Client β POST /auth/login-with-otp
Body: { "user_id": "user@example.com", "channel": "email", "otp": "123456" }
Server β Verifies OTP, checks user status, generates tokens
Server β Response: { "success": true, "data": { tokens, user } }
Client: Stores tokens, redirects to dashboard
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ONGOING: TOKEN USAGE
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: Client makes API requests
Client β GET /api/protected-endpoint
Headers: X-Session-Token: <session_token>
Server β Validates token, processes request
Server β Response: { "success": true, "data": {...} }
Step 2: Token expires
Client β GET /api/protected-endpoint
Headers: X-Session-Token: <expired_token>
Server β Response: 401 Unauthorized
Client: Detects 401, triggers token refresh
Step 3: Refresh tokens
Client β POST /auth/refresh-token
Body: { "refresh_token": "..." }
Server β Validates refresh token, generates new tokens
Server β Response: { "success": true, "data": { new_tokens } }
Client: Updates stored tokens, retries original request
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
LOGOUT FLOW
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User clicks logout
Client β POST /auth/logout
Headers: Authorization: Bearer <session_token>
Server β Blacklists tokens, revokes all sessions
Server β Response: { "success": true, "data": { revocation_status } }
Client: Clears local storage, redirects to login
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 1: User clicks "Forgot Password"
βββΊ User navigates to password reset page
Step 2: User enters email/phone
βββΊ Client validates format
Step 3: Request OTP
Client β POST /auth/send-one-time-password
Body: { "user_id": "user@example.com", "channel": "email" }
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Server β Generates OTP, stores in cache (10 min TTL)
Server β Sends OTP via email/SMS
Server β Response: { "success": true, "message": "OTP sent" }
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 4: User receives OTP
βββΊ User checks email/SMS, gets 6-digit code
Step 5: User enters OTP and new password
βββΊ User enters OTP
βββΊ User enters new password
βββΊ User confirms new password
Step 6: Reset password
Client β POST /auth/forget-password
Body: {
"user_id": "user@example.com",
"otp": "123456",
"password": "newPassword123",
"confirm_password": "newPassword123"
}
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SERVER SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Server β Verifies OTP
Server β Validates passwords match
Server β Hashes new password (bcrypt)
Server β Updates user password in database
Server β Deletes OTP (consume=true)
Server β Response: { "success": true, "message": "Password updated" }
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 7: Password reset complete
βββΊ Show: "Password reset successfully"
βββΊ Redirect to login page
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT SIDE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
LOGIN β Token Generation
βββΊ POST /auth/login-with-password
βββΊ Receive: access_token, refresh_token, session_token, session_id
βββΊ Store tokens securely
API REQUESTS β Token Usage
βββΊ Include token in request header
β βββΊ X-Session-Token: <session_token> (RECOMMENDED)
β OR
β βββΊ Authorization: Bearer <session_token>
βββΊ Server validates token, processes request
TOKEN EXPIRATION β Automatic Refresh
βββΊ API request returns 401
βββΊ Client intercepts 401 error
βββΊ POST /auth/refresh-token
β βββΊ Body: { "refresh_token": "..." }
βββΊ Receive new tokens
βββΊ Update stored tokens
βββΊ Retry original request with new token
LOGOUT β Token Revocation
βββΊ POST /auth/logout
βββΊ Server blacklists all tokens
βββΊ Server revokes all sessions
βββΊ Client clears local storage
400 Bad Request - Invalid Payload:
{
"success": false,
"message": "Invalid request payload",
"error": "Validation error details",
"statusCode": 400
}401 Unauthorized - Invalid Credentials:
{
"success": false,
"message": "Invalid credentials",
"error": "Email/phone or password is incorrect",
"statusCode": 401
}401 Unauthorized - Invalid OTP:
{
"success": false,
"message": "Invalid OTP",
"error": "OTP is incorrect or expired",
"statusCode": 401
}404 Not Found - User Not Found:
{
"success": false,
"message": "User not found",
"error": "User with provided email/phone does not exist",
"statusCode": 404
}409 Conflict - User Already Exists:
{
"success": false,
"message": "User already exists",
"error": "User with this email/phone already registered",
"statusCode": 409
}Recommended Approach (Session Token - Fastest & Most Secure):
-
Session Token: Use for all API requests (RECOMMENDED)
// Store after login localStorage.setItem('session_token', response.data.session_token); localStorage.setItem('refresh_token', response.data.refresh_token); localStorage.setItem('session_id', response.data.session_id); // Use session_token for API calls (fastest validation) // Option 1: X-Session-Token header (preferred) const headers = { 'X-Session-Token': localStorage.getItem('session_token') }; // Option 2: Authorization Bearer header (session_token works here too!) const headers = { 'Authorization': `Bearer ${localStorage.getItem('session_token')}` }; // Decode session token for client-side user info (no API call needed) import jwtDecode from 'jwt-decode'; const userInfo = jwtDecode(localStorage.getItem('session_token')); console.log(userInfo.user_profile); // Full user profile available
-
Alternative: Access Token (Still supported)
// Store after login localStorage.setItem('access_token', response.data.access_token); // Use in API calls const headers = { 'Authorization': `Bearer ${localStorage.getItem('access_token')}` };
-
Refresh Token: Store securely and use when tokens expire
// Store securely (prefer httpOnly cookie if possible) localStorage.setItem('refresh_token', response.data.refresh_token); // Refresh when session/access token expires async function refreshTokens() { const refreshToken = localStorage.getItem('refresh_token'); const response = await fetch('/api/auth/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: refreshToken }) }); const data = await response.json(); // Update all tokens localStorage.setItem('session_token', data.data.session_token); localStorage.setItem('access_token', data.data.access_token); localStorage.setItem('refresh_token', data.data.refresh_token); localStorage.setItem('session_id', data.data.session_id); return data.data; }
-
Session ID: Store for logout operations
localStorage.setItem('session_id', response.data.session_id);
Using Session Token (Recommended):
// Intercept API responses to handle token expiration
axios.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Session token expired, try to refresh
try {
const newTokens = await refreshTokens();
// Retry original request with new session token
// You can use either method:
error.config.headers['X-Session-Token'] = newTokens.session_token;
// OR
// error.config.headers['Authorization'] = `Bearer ${newTokens.session_token}`;
return axios.request(error.config);
} catch (refreshError) {
// Refresh failed, redirect to login
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);
// Request interceptor to add session token
axios.interceptors.request.use(
(config) => {
const sessionToken = localStorage.getItem('session_token');
if (sessionToken) {
// Option 1: X-Session-Token header (preferred)
config.headers['X-Session-Token'] = sessionToken;
// OR
// Option 2: Authorization Bearer header (session_token works here too!)
// config.headers['Authorization'] = `Bearer ${sessionToken}`;
}
return config;
},
(error) => Promise.reject(error)
);Using Access Token (Alternative):
// Intercept API responses to handle token expiration
axios.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Access token expired, try to refresh
try {
const newTokens = await refreshTokens();
// Retry original request with new access token
error.config.headers.Authorization = `Bearer ${newTokens.access_token}`;
return axios.request(error.config);
} catch (refreshError) {
// Refresh failed, redirect to login
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);-
Token Storage:
- Session Token: httpOnly cookie (preferred) or secure storage - RECOMMENDED for API calls
- Access Token: Memory or secure storage (avoid localStorage for sensitive apps)
- Refresh Token: httpOnly cookie (preferred) or secure storage
- Session ID: Store with tokens
-
Token Usage Priority:
- Primary: Use
session_tokenwithX-Session-Tokenheader (fastest, most secure) - Alternative 1: Use
session_tokenwithAuthorization: Bearerheader (also works!) - Alternative 2: Use
access_tokenwithAuthorization: Bearerheader (still supported) - Never: Use
refresh_tokenfor API authentication (only for token refresh)
Note: The
Authorization: Bearerheader accepts bothsession_tokenandaccess_token. The server automatically detects the token type and validates accordingly. - Primary: Use
-
Token Rotation: Refresh tokens are rotated on each refresh for security
-
Token Blacklisting: Tokens are blacklisted on logout and cannot be reused
-
Session Management: Each login creates a new session with unique session_id
-
Client-Side Validation: Session tokens can be decoded client-side for user info display without API calls
- Use Strong Passwords: Enforce password complexity requirements
- OTP Expiration: OTPs expire after 10 minutes for security
- Rate Limiting: Implement rate limiting on authentication endpoints
- Token Storage:
- Access tokens: Store in memory when possible
- Refresh tokens: Use httpOnly cookies for web apps
- Never store tokens in localStorage for sensitive applications
- Password Hashing: Always use bcrypt with appropriate salt rounds (10 rounds)
- Email/Phone Validation: Validate format before processing
- Error Messages: Don't reveal if email/phone exists in system
- Token Refresh: Implement automatic token refresh before expiration
- Session Management: Track active sessions and allow users to revoke them
- Token Blacklisting: Tokens are automatically blacklisted on logout
Configure token expiration times:
# Token Expiration Times (in minutes)
ACCESS_TOKEN_EXPIRY_MINUTES=60 # Access token lifetime (default: 60 minutes = 1 hour)
SESSION_TOKEN_EXPIRY_MINUTES=10080 # Session token lifetime (default: 10080 minutes = 7 days)
REFRESH_TOKEN_EXPIRY_MINUTES=43200 # Refresh token lifetime (default: 43200 minutes = 30 days)
# JWT Configuration
JWT_SECRET_KEY=your-secret-key-here # Required: Secret key for JWT signing
JWT_ALGORITHM=HS256 # Optional: JWT algorithm (default: HS256)
# Password Hashing
BCRYPT_SALT_ROUNDS=10 # Optional: Bcrypt salt rounds (default: 10)- No database storage for sessions
- All session info embedded in JWT tokens
- Token blacklisting via cache (Redis or in-memory)
- Fast and scalable
- Tokens blacklisted in cache with TTL matching token expiration
- Automatic cleanup when tokens expire
- Supports Redis for distributed systems
- Falls back to in-memory cache if Redis unavailable
- Lightweight access tokens (minimal payload - only essential fields)
- Session tokens with full user profile (no database lookup needed)
- Non-blocking sign-in updates
- Optimized blacklist check order
- Fast token generation and validation
- JTI-based access token blacklisting (efficient)
- User-level blacklist for refresh tokens and sessions
- Token validation priority: X-Session-Token > Authorization Bearer > OAuth2 > query param
- Access Tokens: Blacklisted by JTI (JWT ID) for efficiency
- Refresh Tokens: User-level blacklist (revokes all refresh tokens for user)
- Sessions: User-level blacklist (revokes all sessions for user - complete logout)
- Automatic Expiration: Blacklist entries expire with token expiration times
- Cache Storage: Uses Redis (if available) or in-memory cache
- Logout Behavior: Complete logout from all devices (all sessions revoked)
Last Updated: January 2025
- β Access token expiration updated to 60 minutes (1 hour)
- β Session token contains full user profile (no database lookup needed)
- β JTI-based access token blacklisting for efficiency
- β Complete logout from all devices (all sessions revoked)
- β User-level blacklist for refresh tokens and sessions
- β Token rotation on refresh (all tokens regenerated)
- β
GET/POST /auth/token-info- Get token information and configuration
- β Complete logout from all devices
- β All refresh tokens revoked on logout
- β Access token blacklisted by JTI
- β Token rotation on refresh for security
- β User-level blacklist clearing on login (allows re-login after logout)