REST API Authentication
REST API Authentication
Section titled “REST API Authentication”Complete guide to authenticating REST API requests using JWT tokens and development mode.
Overview
Section titled “Overview”The REST API supports two authentication methods:
- Production Mode: JWT tokens via Authorization header (default)
- Development Mode: User ID and permissions via headers (local only)
Production Authentication (JWT)
Section titled “Production Authentication (JWT)”Authorization Header
Section titled “Authorization Header”All authenticated requests require a JWT token:
GET /api/users/user-123Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Obtaining a Token
Section titled “Obtaining a Token”Endpoint: POST /api/auth/authenticate
POST /api/auth/authenticateContent-Type: application/json
{ "email": "user@example.com", "password": "SecurePassword123!"}Response:
{ "success": true, "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600, "user": { "id": "user-123", "email": "user@example.com", "permissions": ["users:read", "orders:read"] }}Using the Token
Section titled “Using the Token”Include in all subsequent requests:
GET /api/users/user-123Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Token Structure
Section titled “Token Structure”JWT tokens contain:
{ "sub": "user-123", "email": "user@example.com", "name": "John Doe", "permissions": [ "users:read", "users:create", "orders:read" ], "iat": 1700056800, "exp": 1700060400}Claims:
sub- User ID (subject)email- User’s emailname- Display namepermissions- Array of permission stringsiat- Issued at timestampexp- Expiration timestamp
Token Refresh
Section titled “Token Refresh”When to Refresh
Section titled “When to Refresh”- Token expires after 1 hour (3600 seconds)
- Refresh before expiration for uninterrupted access
- Use refresh token (valid for 7 days)
Refresh Endpoint
Section titled “Refresh Endpoint”Endpoint: POST /api/auth/refresh-token
POST /api/auth/refresh-tokenContent-Type: application/json
{ "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}Response:
{ "success": true, "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600}Client-Side Token Management
Section titled “Client-Side Token Management”class ApiClient { private accessToken: string | null = null; private refreshToken: string | null = null;
async request(url: string, options: RequestInit = {}) { // Add authorization header const headers = { ...options.headers, 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' };
let response = await fetch(url, { ...options, headers });
// Handle token expiration if (response.status === 401) { await this.refreshAccessToken();
// Retry with new token headers.Authorization = `Bearer ${this.accessToken}`; response = await fetch(url, { ...options, headers }); }
return response; }
private async refreshAccessToken() { const response = await fetch('/api/auth/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: this.refreshToken }) });
if (!response.ok) { // Refresh token expired, need to re-authenticate this.redirectToLogin(); return; }
const data = await response.json(); this.accessToken = data.accessToken; }
async login(email: string, password: string) { const response = await fetch('/api/auth/authenticate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) });
const data = await response.json();
if (data.success) { this.accessToken = data.accessToken; this.refreshToken = data.refreshToken; }
return data; }
async logout() { if (this.accessToken) { await fetch('/api/auth/revoke-token', { method: 'POST', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ token: this.accessToken }) }); }
this.accessToken = null; this.refreshToken = null; }}Development Mode
Section titled “Development Mode”CRITICAL SECURITY WARNING
Development mode (
DEVELOPMENT_AUTH_ENABLED=true) BYPASSES ALL AUTHENTICATION.
- ❌ NO JWT validation
- ❌ NO token verification
- ❌ NO permission enforcement
- ⚠️ Anyone can impersonate ANY user with ANY permissions
NEVER enable in production. NEVER commit .env with this enabled.
Enabling Development Mode
Section titled “Enabling Development Mode”Environment variable:
DEVELOPMENT_AUTH_ENABLED=trueDevelopment Headers
Section titled “Development Headers”GET /api/users/user-123X-Dev-User-Id: dev-user-123X-Dev-Permissions: users:read,users:create,orders:readHeaders:
X-Dev-User-Id- User ID to impersonateX-Dev-Permissions- Comma-separated list of permissions
Example Request
Section titled “Example Request”POST /api/usersX-Dev-User-Id: dev-adminX-Dev-Permissions: users:create,users:read,admin:allContent-Type: application/json
{ "email": "newuser@example.com", "password": "password123", "profile": { "firstName": "Test", "lastName": "User" }}JavaScript Example
Section titled “JavaScript Example”const response = await fetch('http://localhost:3003/api/users', { method: 'POST', headers: { 'X-Dev-User-Id': 'dev-admin', 'X-Dev-Permissions': 'users:create,users:read', 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'newuser@example.com', password: 'password123', profile: { firstName: 'Test', lastName: 'User' } })});
const data = await response.json();console.log(data);HTTP Headers Reference
Section titled “HTTP Headers Reference”Required Headers
Section titled “Required Headers”Production
Section titled “Production”Authorization: Bearer <jwt-token>Content-Type: application/jsonDevelopment
Section titled “Development”X-Dev-User-Id: <user-id>X-Dev-Permissions: <comma-separated-permissions>Content-Type: application/jsonOptional Headers
Section titled “Optional Headers”X-Correlation-Id: <custom-trace-id>Accept: application/jsonAccept-Language: en-USResponse Headers
Section titled “Response Headers”The API Gateway includes these headers in responses:
X-Correlation-Id: abc-123-def-456X-Request-Id: req-789-ghi-012Content-Type: application/jsonAccess-Control-Allow-Origin: *CORS Configuration
Section titled “CORS Configuration”The API Gateway automatically handles CORS:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONSAccess-Control-Allow-Headers: Authorization, Content-Type, X-Dev-User-Id, X-Dev-PermissionsAccess-Control-Max-Age: 86400Preflight Requests
Section titled “Preflight Requests”OPTIONS /api/usersAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: Authorization, Content-TypeResponse:
HTTP/1.1 204 No ContentAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONSAccess-Control-Allow-Headers: Authorization, Content-Type, X-Dev-User-Id, X-Dev-PermissionsAccess-Control-Max-Age: 86400Error Responses
Section titled “Error Responses”Invalid Token
Section titled “Invalid Token”HTTP/1.1 401 UnauthorizedContent-Type: application/json
{ "error": "Unauthorized", "message": "Invalid or expired token", "code": "INVALID_TOKEN"}Token Expired
Section titled “Token Expired”HTTP/1.1 401 UnauthorizedContent-Type: application/json
{ "error": "Unauthorized", "message": "Token has expired", "code": "TOKEN_EXPIRED"}Missing Permission
Section titled “Missing Permission”HTTP/1.1 403 ForbiddenContent-Type: application/json
{ "error": "Forbidden", "message": "Missing required permission: users:create", "code": "INSUFFICIENT_PERMISSIONS", "requiredPermission": "users:create"}Missing Header
Section titled “Missing Header”HTTP/1.1 401 UnauthorizedContent-Type: application/json
{ "error": "Unauthorized", "message": "No authorization header provided", "code": "NO_AUTH_HEADER"}Security Best Practices
Section titled “Security Best Practices”Token Storage
Section titled “Token Storage”✅ DO:
- Store tokens in httpOnly cookies (server-side)
- Use secure session storage
- Clear tokens on logout
❌ DON’T:
- Store tokens in localStorage (XSS risk)
- Store tokens in sessionStorage (XSS risk)
- Log tokens in console or logs
Token Transmission
Section titled “Token Transmission”✅ DO:
- Use HTTPS in production
- Use WSS for WebSocket connections
- Rotate JWT secrets periodically
❌ DON’T:
- Use HTTP in production
- Share tokens across domains
- Transmit tokens in URLs
Development Mode
Section titled “Development Mode”✅ DO:
- Use for local development only
- Document required permissions
- Disable before deployment
❌ DON’T:
- Enable in production
- Commit DEVELOPMENT_AUTH_ENABLED=true
- Use in CI/CD pipelines
- Skip security testing
Next Steps
Section titled “Next Steps”- REST API Endpoints - Complete endpoint reference
- REST API Errors - HTTP status codes and error handling
- Authentication Reference - Complete auth guide
- GraphQL API - Alternative API option