This API uses secure opaque tokens for authentication. These tokens are cryptographically secure random strings that are validated against database sessions.
Authorization
header as a Bearer token for all
protected endpoints.Example authorization header:
Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
The authentication system uses opaque tokens with dual expiration times for enhanced security and user experience:
Token Type | Purpose | Lifetime | Storage |
---|---|---|---|
Access Token | API request authentication | 30 minutes | Client memory/secure storage |
Refresh Token | Obtain new access tokens | 6 months (sliding window) | Secure client storage only |
CSRF Token | Request validation | Same as access token | Client memory |
For persistent authentication, clients should implement automatic token refresh:
/api/auth/refresh
Token Refresh Example:
// Client detects 401 response if (response.status === 401) { const refreshResponse = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Authorization': `Bearer ${currentAccessToken}`, // Required for consistency 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: storedRefreshToken // Takes priority over header token }) }); if (refreshResponse.ok) { const newTokens = await refreshResponse.json(); // Update stored tokens and retry original request updateStoredTokens(newTokens); return retryOriginalRequest(); } else { // Refresh failed, redirect to login redirectToLogin(); } }
For a seamless user experience, implement automatic token refresh in your client application:
Token Manager Class:
class TokenManager { setTokens(authResponse) { this.accessToken = authResponse.token; // API returns 'token' this.refreshToken = authResponse.refreshToken; this.csrfToken = authResponse.csrfToken; this.expiresAt = new Date(Date.now() + authResponse.expiresIn * 1000); // Store in secure storage localStorage.setItem('tokens', JSON.stringify({ accessToken: this.accessToken, refreshToken: this.refreshToken, csrfToken: this.csrfToken, expiresAt: this.expiresAt.toISOString() })); } isAccessTokenExpired() { return !this.expiresAt || new Date() >= this.expiresAt; } }
API Client with Auto-Refresh:
class ApiClient { async makeRequest(url, options = {}) { // Check if token needs refresh if (tokenManager.isAccessTokenExpired()) { await authService.refreshTokens(); } // Add auth headers const headers = { ...options.headers, 'Authorization': `Bearer ${tokenManager.getAccessToken()}`, 'x-csrf-token': tokenManager.getCsrfToken() }; const response = await fetch(url, { ...options, headers }); // Handle 401 with retry if (response.status === 401) { const newToken = await authService.refreshTokens(); if (newToken) { headers.Authorization = `Bearer ${newToken}`; return fetch(url, { ...options, headers }); } } return response; } }
Key Implementation Features:
This API implements CSRF (Cross-Site Request Forgery) protection for all mutation operations (POST, PUT, PATCH, DELETE).
x-csrf-token
header for all subsequent mutation
requests.Example authentication response with CSRF token:
{ "token": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...", "refreshToken": "XYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn...", "csrfToken": "a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3", "expiresIn": 1800, "user": { "id": 1, "username": "John Doe", "email": "john@example.com", "typeCode": "REGU", "typeName": "Regular", "firstName": "John", "lastName": "Doe", "isActive": true, "createdAt": "2023-01-15T08:30:00Z", "updatedAt": "2023-01-15T08:30:00Z", "subscription": null } }
Example request with CSRF protection:
// Request headers Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr... x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3 Content-Type: application/json
/api/auth/login
Log in with email and password
Request Body:
{ "email": "user@example.com", "password": "yourpassword" }
Response (200 OK):
{ "token": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...", // Valid for 30 minutes "refreshToken": "XYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn...", // Valid for 6 months "csrfToken": "a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3", "expiresIn": 1800, // 30 minutes in seconds "user": { "id": 1, "username": "John Doe", "email": "john@example.com", "typeCode": "REGU", "typeName": "Regular", "firstName": "John", "lastName": "Doe", "isActive": true, "createdAt": "2023-01-15T08:30:00Z", "updatedAt": "2023-01-15T08:30:00Z", "subscription": null } }
Error Responses:
{ "message": "Invalid credentials", "code": "AUTH_INVALID_CREDENTIALS" } { "message": "Your email address has not been verified. A verification code has been sent to your email address.", "code": "AUTH_EMAIL_NOT_VERIFIED" }
/api/auth/logout
Log out the current user (invalidate token)
Headers (Optional):
Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
Note: Authorization header is optional. If provided, the specific session will be invalidated. If not provided, logout will still succeed.
Response (200 OK):
{ "success": true, "message": "Logged out successfully" }
/api/auth/refresh-token
/api/auth/refresh
Refresh authentication token when the current one is about to expire or has expired. Both endpoints are supported for compatibility.
Headers (Optional):
Authorization: Bearer [current_access_token_or_refresh_token]
Request Body (Optional - takes priority if provided):
{ "refreshToken": "XYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn..." }
Notes:
refreshToken
is provided in the request body, it will be used instead of the token from the Authorization header/api/auth/refresh-token
is the legacy endpoint, /api/auth/refresh
is preferredResponse (200 OK):
{ "token": "DEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu...", // New token valid for 30 minutes "refreshToken": "XYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn...", // New refresh token valid for 6 months "csrfToken": "f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3", // New CSRF token "expiresIn": 1800, // 30 minutes in seconds "user": { "id": 1, "username": "John Doe", "email": "john@example.com", "typeCode": "REGU", "typeName": "Regular", "firstName": "John", "lastName": "Doe", "isActive": true, "createdAt": "2023-01-15T08:30:00Z", "updatedAt": "2023-01-15T08:30:00Z", "subscription": null } }
Error Responses:
{ "message": "No token provided", "code": "AUTH_NO_TOKEN" } { "message": "Invalid token format", "code": "AUTH_INVALID_TOKEN_FORMAT" } { "message": "No active session found. Please log in again.", "code": "AUTH_SESSION_NOT_FOUND", "requiresLogout": true } { "message": "Your session has been revoked. Please log in again.", "code": "AUTH_SESSION_REVOKED", "requiresLogout": true } { "message": "Your session has expired. Please log in again.", "code": "AUTH_REFRESH_EXPIRED", "requiresLogout": true } { "message": "User account issue. Please log in again.", "code": "AUTH_USER_NOT_FOUND", "requiresLogout": true } { "message": "Authentication error. Please log in again.", "code": "AUTH_ERROR", "requiresLogout": true }
/api/auth/register
Register a new user account
Request Body:
{ "username": "johnsmith", "email": "john@example.com", "password": "securePassword123", "firstName": "John", "lastName": "Smith" }
Field Descriptions:
username
(required) - User's display name, will be trimmed of whitespaceemail
(required) - Valid email address, must be uniquepassword
(required) - Must be at least 8 characters longfirstName
(optional) - User's first name, max 50 characterslastName
(optional) - User's last name, max 50 charactersResponse (201 Created):
{ "token": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...", // Valid for 30 minutes "refreshToken": "XYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn...", // Valid for 6 months "csrfToken": "f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3", "expiresIn": 1800, // 30 minutes in seconds "user": { "id": 2, "username": "johnsmith", "email": "john@example.com", "typeCode": "REGU", "typeName": "Regular", "firstName": "John", "lastName": "Smith", "isActive": true, "createdAt": "2023-01-15T08:30:00Z", "updatedAt": null, "subscription": null } }
Error Responses:
{ "message": "Email address is already registered", "code": "AUTH_EMAIL_EXISTS" } { "message": "Validation error", "code": "VALIDATION_ERROR", "errors": [ { "type": "field", "value": "", "msg": "Username is required", "path": "username", "location": "body" } ] }
Notes:
/api/auth/reset-password
Request a password reset code
Request Body:
{ "email": "user@example.com" }
Response (200 OK):
{ "message": "Password reset code has been sent to your email address" }
Error Responses:
{ "message": "Email address not found" }
Notes:
/api/auth/verify-registration
Verify a registration with the verification code
Request Body:
{ "email": "user@example.com", "verificationCode": "123456" }
Response (200 OK):
{ "message": "Email verified successfully" }
Error Responses:
{ "message": "Invalid email address" } { "message": "No verification request found" } { "message": "Maximum attempts exceeded" } { "message": "Verification code has expired" } { "message": "Invalid verification code" }
Notes:
/api/auth/verify-reset-password
Reset password using verification code
POST/api/auth/verify-reset-password
Reset password using verification code
Request Body:
{ "email": "user@example.com", "verificationCode": "123456", "newPassword": "newSecurePassword" }
Response (200 OK):
{ "message": "Password has been successfully reset" }
Error Responses:
{ "message": "Password must be at least 8 characters long" } { "message": "Invalid email address" } { "message": "No password reset request found" } { "message": "Maximum attempts exceeded. Please request a new verification code" } { "message": "Verification code has expired. Please request a new one" } { "message": "Invalid verification code" }
Notes: