Overview
Authentication Flow
The platform uses JWT-based authentication with access and refresh tokens. Access tokens are short-lived (typically 15-30 minutes) while refresh tokens have longer validity.
| Token Type | Purpose | Typical Lifetime |
|---|---|---|
| Access Token | Authenticates API requests via Bearer header | 15-30 minutes |
| Refresh Token | Obtains new access tokens without re-login | 7-30 days |
| Pending Token | Temporary token during MFA verification | 5 minutes |
Login States
The login endpoint can return different states based on user configuration.
| State | Response | Next Step |
|---|---|---|
| Success (no MFA) | Returns access_token and refresh_token | Store tokens, redirect to dashboard |
| MFA Required | Returns pending_token only | Show MFA verification form |
| Invalid Credentials | Returns 401 error | Show error message |
Token Storage
Tokens should be stored securely on the client side.
| Storage Option | Security | Recommendation |
|---|---|---|
| localStorage | Vulnerable to XSS | Use for development only |
| httpOnly Cookies | Protected from XSS | Recommended for production |
| Memory | Lost on page refresh | Use with refresh token rotation |
Login
Admin Login
/auth/loginAuthenticates an admin user with email and password. If MFA is enabled, returns a pending_token that must be verified before access is granted.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Response- Login response (varies based on MFA status)
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 1800,
"role": "admin"
}Request Payload
{
"email": "admin@example.com",
"password": "securePassword123"
}Success Response (No MFA)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 1800,
"role": "admin"
}MFA Required Response
{
"pending_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"mfa_required": true
}Example Request
curl -X POST "https://api.example.com/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "securePassword123"
}'Error Responses
| Status | Code | Description |
|---|---|---|
| 401 | invalid_credentials | Email or password is incorrect |
| 403 | account_locked | Account locked due to too many failed attempts |
| 403 | account_disabled | Account has been deactivated |
| 429 | rate_limited | Too many login attempts, try again later |
Token Refresh
Refresh Access Token
/auth/refreshExchanges a valid refresh token for a new access token and refresh token pair. The old refresh token is invalidated (rotation).
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Response- New token pair
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 1800
}Request Payload
{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfMTIzIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MDk4MjQwMDB9..."
}Success Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfMTIzIiwidHlwZSI6ImFjY2VzcyIsImV4cCI6MTcwOTgyNTgwMH0...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfMTIzIiwidHlwZSI6InJlZnJlc2giLCJleHAiOjE3MTI0MTc4MDB9...",
"expires_in": 1800
}Example Request
curl -X POST "https://api.example.com/auth/refresh" \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}'Token Refresh Strategy
Best practices for implementing token refresh in your application.
| Strategy | Description |
|---|---|
| Proactive Refresh | Refresh before expiry (e.g., at 80% of lifetime) |
| Reactive Refresh | Refresh on 401 response, then retry original request |
| Background Refresh | Use a timer to refresh tokens while app is active |
| Rotation | Always store the new refresh token after each refresh |
Error Responses
| Status | Code | Description |
|---|---|---|
| 401 | invalid_token | Refresh token is invalid or malformed |
| 401 | token_expired | Refresh token has expired |
| 401 | token_revoked | Refresh token has been revoked (logout or security event) |
Logout
Admin Logout
/auth/logoutInvalidates the current session and revokes associated tokens. The access token and refresh token will no longer be valid.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer {access_token} |
Content-Type | string | Yes | application/json |
Response- Logout confirmation
{
"message": "Successfully logged out"
}Request Payload
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}Example Request
curl -X POST "https://api.example.com/auth/logout" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}'Client-Side Cleanup
After calling logout, ensure you clean up client-side state.
| Action | Description |
|---|---|
| Clear localStorage | Remove admin_token, admin_refresh_token, pending_token |
| Clear cookies | If using httpOnly cookies, the server handles this |
| Reset app state | Clear any cached user data or session info |
| Redirect | Navigate to login page |
Password Reset
Request Password Reset
/auth/admin/password/reset/requestInitiates a password reset flow by sending a reset link to the user's email. For security, always returns success even if email doesn't exist.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Response- Always returns success for security
{
"message": "If an account exists with this email, a reset link has been sent"
}Request Payload
{
"email": "admin@example.com"
}Example Request
curl -X POST "https://api.example.com/auth/admin/password/reset/request" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com"
}'Confirm Password Reset
/auth/admin/password/reset/confirmCompletes the password reset using the token from the email link. The token is single-use and expires after a set period (typically 1 hour).
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Content-Type | string | Yes | application/json |
Response- Password reset confirmation
{
"message": "Password has been reset successfully"
}Request Payload (Confirm)
{
"token": "rst_550e8400-e29b-41d4-a716-446655440000",
"new_password": "NewSecureP@ssw0rd!"
}Example Request (Confirm)
curl -X POST "https://api.example.com/auth/admin/password/reset/confirm" \
-H "Content-Type: application/json" \
-d '{
"token": "rst_550e8400-e29b-41d4-a716-446655440000",
"new_password": "NewSecureP@ssw0rd!"
}'Password Requirements
| Requirement | Description |
|---|---|
| Minimum Length | At least 8 characters |
| Uppercase | At least one uppercase letter (A-Z) |
| Lowercase | At least one lowercase letter (a-z) |
| Number | At least one digit (0-9) |
| Special Character | At least one special character (!@#$%^&*) |
| No Common Patterns | Cannot be a commonly used password |
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | invalid_token | Reset token is invalid or malformed |
| 400 | token_expired | Reset token has expired (> 1 hour) |
| 400 | token_used | Reset token has already been used |
| 400 | weak_password | Password doesn't meet complexity requirements |
| 429 | rate_limited | Too many reset attempts |
Change Password
Change Admin Password
/auth/admin/password/changeAllows an authenticated admin to change their password. Requires current password verification and optionally MFA code if enabled.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer {access_token} |
Content-Type | string | Yes | application/json |
Response- Password change confirmation
{
"message": "Password changed successfully"
}Request Payload
{
"current_password": "OldP@ssw0rd!",
"new_password": "NewSecureP@ssw0rd!",
"mfa_code": "123456"
}Request Payload (Without MFA)
{
"current_password": "OldP@ssw0rd!",
"new_password": "NewSecureP@ssw0rd!"
}Example Request
curl -X POST "https://api.example.com/auth/admin/password/change" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"current_password": "OldP@ssw0rd!",
"new_password": "NewSecureP@ssw0rd!",
"mfa_code": "123456"
}'Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | invalid_current_password | Current password is incorrect |
| 400 | weak_password | New password doesn't meet requirements |
| 400 | same_password | New password cannot be same as current |
| 400 | mfa_required | MFA code is required but not provided |
| 400 | invalid_mfa_code | MFA code is invalid or expired |
| 401 | unauthorized | Access token is invalid or expired |
Security Best Practices
Token Handling
| Practice | Description |
|---|---|
| Never log tokens | Access and refresh tokens should never be logged |
| Use HTTPS only | All auth requests must be over HTTPS |
| Rotate refresh tokens | Use the new refresh token after each refresh |
| Set short expiry | Access tokens should expire in 15-30 minutes |
| Clear on logout | Remove all tokens from storage on logout |
Error Handling
| Scenario | Recommended Action |
|---|---|
| 401 on any request | Attempt token refresh, retry once |
| Refresh fails | Clear tokens, redirect to login |
| 403 Forbidden | Show access denied message |
| 429 Rate Limited | Show retry message with countdown |
| Network error | Show offline message, queue for retry |
Session Management
| Feature | Implementation |
|---|---|
| Activity timeout | Logout after 30 minutes of inactivity |
| Multi-tab sync | Use BroadcastChannel API to sync logout |
| Session list | Allow users to view and revoke active sessions |
| Force logout | Admin can force logout specific users |