Now.js Framework Documentation
AuthErrorHandler - Error Management for Authentication
AuthErrorHandler - Error Management for Authentication
Documentation for AuthErrorHandler, the error management system for authentication in the Now.js Framework
📋 Table of Contents
- Overview
- Installation and Import
- Getting Started
- Error Types
- Error Actions
- Custom Error Handling
- Error Events
- Retry Logic
- Error Recovery
- Usage Examples
- API Reference
- Best Practices
- Common Pitfalls
Overview
AuthErrorHandler manages errors and exceptions that occur during the authentication process, providing appropriate responses and recovery mechanisms.
Key Features
- ✅ Error Classification: Clear categorization of errors
- ✅ Configurable Actions: Define actions for each error type
- ✅ Custom Handlers: Support for custom error handlers
- ✅ Retry Logic: Automatic retry for specific error types
- ✅ Error Recovery: Recovery mechanisms from errors
- ✅ Event System: Events for error tracking
- ✅ User Notifications: Notify users when errors occur
- ✅ Error Logging: Record errors for debugging
- ✅ Graceful Degradation: Gracefully degrade features
- ✅ Error Boundaries: Limit the scope of errors
When to Use AuthErrorHandler
✅ Use AuthErrorHandler when:
- Need systematic authentication error management
- Want custom error handling logic
- Need to track and log errors
- Want retry logic for errors
- Need to notify users when errors occur
❌ Don't use when:
- Need to handle all errors manually
- Not using authentication system
Installation and Import
AuthErrorHandler is loaded with the Now.js Framework and is ready to use immediately via the window object:
// No import needed - ready to use immediately
console.log(window.AuthErrorHandler); // AuthErrorHandler objectGetting Started
Basic Setup
// AuthErrorHandler works automatically with AuthManager
// No separate initialization needed
await AuthManager.init({
enabled: true,
endpoints: {
login: '/api/auth/login',
verify: '/api/auth/verify'
},
// Error handling configuration
errorHandling: {
// Max retry attempts
maxRetries: 3,
// Retry delay (ms)
retryDelay: 1000,
// Show user notifications
showNotifications: true,
// Log errors to console
logErrors: true
}
});
console.log('AuthErrorHandler initialized with AuthManager');Error Handling Flow
Operation fails
↓
┌────────────────────┐
│ Classify Error │
│ - Network? │
│ - Authentication? │
│ - Authorization? │
│ - Validation? │
└──────┬─────────────┘
↓
┌────────────────────┐
│ Determine Action │
│ - Retry? │
│ - Redirect? │
│ - Notify? │
│ - Log? │
└──────┬─────────────┘
↓
┌────────────────────┐
│ Execute Action │
│ - Retry operation │
│ - Navigate away │
│ - Show message │
│ - Log error │
└──────┬─────────────┘
↓
┌────────────────────┐
│ Emit Error Event │
│ - Custom handlers │
│ - Tracking │
│ - Logging │
└────────────────────┘Error Types
AuthErrorHandler supports the following main error types:
1. NETWORK_ERROR
Connection or network failure
// Triggered when:
// - No internet connection
// - Server unreachable
// - Request timeout
// - CORS errors
// Default action: Retry with exponential backoff
{
type: 'NETWORK_ERROR',
message: 'Network connection failed',
retryable: true,
action: 'retry'
}2. UNAUTHORIZED
Not authenticated or token expired
// Triggered when:
// - No token provided
// - Token expired
// - Invalid token
// - Token revoked
// Default action: Redirect to login
{
type: 'UNAUTHORIZED',
message: 'Authentication required',
retryable: false,
action: 'redirect',
target: '/login'
}3. FORBIDDEN
Authenticated but no permission
// Triggered when:
// - Missing required role
// - Missing required permission
// - Access denied by policy
// Default action: Show error page
{
type: 'FORBIDDEN',
message: 'Access denied',
retryable: false,
action: 'render',
target: '/403'
}4. VALIDATION_ERROR
Data does not meet requirements
// Triggered when:
// - Invalid credentials
// - Missing required fields
// - Invalid format
// Default action: Show validation errors
{
type: 'VALIDATION_ERROR',
message: 'Invalid input',
errors: {
email: 'Invalid email format',
password: 'Password too short'
},
retryable: true,
action: 'notify'
}5. TOKEN_EXPIRED
Token has expired
// Triggered when:
// - Access token expired
// - Refresh token expired
// Default action: Try refresh, then redirect
{
type: 'TOKEN_EXPIRED',
message: 'Token expired',
retryable: true,
action: 'refresh',
fallback: 'redirect',
target: '/login'
}6. TOKEN_REFRESH_FAILED
Token refresh failed
// Triggered when:
// - Refresh token invalid
// - Refresh endpoint failed
// - No refresh token
// Default action: Redirect to login
{
type: 'TOKEN_REFRESH_FAILED',
message: 'Failed to refresh token',
retryable: false,
action: 'redirect',
target: '/login'
}7. SESSION_EXPIRED
Session has expired
// Triggered when:
// - Session timeout
// - Session invalidated
// - Logged out from another device
// Default action: Redirect to login with message
{
type: 'SESSION_EXPIRED',
message: 'Your session has expired',
retryable: false,
action: 'redirect',
target: '/login',
notify: true
}8. RATE_LIMIT_EXCEEDED
Rate limit exceeded
// Triggered when:
// - Too many requests
// - Rate limit exceeded
// Default action: Retry after delay
{
type: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests',
retryable: true,
action: 'retry',
retryAfter: 60000, // 1 minute
notify: true
}9. SERVER_ERROR
Internal server error
// Triggered when:
// - 500 errors
// - Server exceptions
// - Database errors
// Default action: Retry with limit
{
type: 'SERVER_ERROR',
message: 'Server error occurred',
retryable: true,
action: 'retry',
maxRetries: 3
}10. CSRF_ERROR
CSRF token invalid
// Triggered when:
// - CSRF token missing
// - CSRF token invalid
// - CSRF token expired
// Default action: Refresh CSRF and retry
{
type: 'CSRF_ERROR',
message: 'CSRF validation failed',
retryable: true,
action: 'refresh_csrf',
fallback: 'reload'
}11. CUSTOM_ERROR
Custom application-defined error
// Triggered by application logic
{
type: 'CUSTOM_ERROR',
code: 'SUBSCRIPTION_REQUIRED',
message: 'Active subscription required',
retryable: false,
action: 'custom',
handler: 'handleSubscriptionError'
}Error Actions
1. Retry Action
// Automatically retry failed operation
{
action: 'retry',
maxRetries: 3,
retryDelay: 1000,
backoff: 'exponential' // linear, exponential, fixed
}
// Retry with exponential backoff
// Attempt 1: 1s delay
// Attempt 2: 2s delay
// Attempt 3: 4s delay2. Redirect Action
// Redirect to another route
{
action: 'redirect',
target: '/login',
reason: 'Authentication required',
storeIntendedRoute: true // Store current route
}
// After login, redirect back to intended route3. Render Action
// Render error page
{
action: 'render',
target: '/403',
context: {
error: 'Access denied',
requiredRole: 'admin'
}
}4. Notify Action
// Show notification to user
{
action: 'notify',
notification: {
type: 'error',
title: 'Login Failed',
message: 'Invalid credentials',
duration: 5000
}
}5. Block Action
// Block navigation, stay on current page
{
action: 'block',
reason: 'Unsaved changes',
confirm: true // Show confirmation dialog
}6. Refresh Action
// Refresh tokens
{
action: 'refresh',
type: 'token',
fallback: {
action: 'redirect',
target: '/login'
}
}7. Logout Action
// Force logout
{
action: 'logout',
reason: 'Session invalidated',
redirect: '/login',
notify: true
}8. Custom Action
// Execute custom handler
{
action: 'custom',
handler: async (error, context) => {
console.log('Custom error handler:', error);
// Custom logic
if (error.code === 'PAYMENT_REQUIRED') {
await showPaymentModal();
}
return { handled: true };
}
}Custom Error Handling
1. Global Error Handler
// Set global error handler
AuthErrorHandler.setGlobalHandler(async (error, context) => {
console.log('Global error handler:', error.type);
// Log to external service
await logErrorToService(error);
// Custom notification
if (error.type === 'UNAUTHORIZED') {
showCustomLoginModal();
return { handled: true };
}
// Let default handler process
return { handled: false };
});2. Error Type-Specific Handlers
// Register handler for specific error type
AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
console.log('Validation errors:', error.errors);
// Show validation errors in form
Object.keys(error.errors).forEach(field => {
showFieldError(field, error.errors[field]);
});
return { handled: true };
});
// Handler for rate limiting
AuthErrorHandler.registerHandler('RATE_LIMIT_EXCEEDED', async (error) => {
const retryAfter = error.retryAfter || 60000;
const minutes = Math.ceil(retryAfter / 60000);
showNotification(
`Too many attempts. Please try again in ${minutes} minute(s).`,
'warning'
);
return { handled: true };
});3. Context-Aware Error Handling
// Handle errors based on context
AuthErrorHandler.setContextHandler(async (error, context) => {
console.log('Context:', context);
// Different handling for different operations
if (context.operation === 'login') {
if (error.type === 'VALIDATION_ERROR') {
highlightInvalidFields(error.errors);
return { handled: true };
}
}
if (context.operation === 'refresh_token') {
if (error.type === 'TOKEN_REFRESH_FAILED') {
// Force logout on refresh failure
await AuthManager.logout();
return { handled: true };
}
}
return { handled: false };
});4. Chained Error Handlers
// Multiple handlers in sequence
AuthErrorHandler.chainHandlers([
// 1. Log all errors
async (error) => {
await logError(error);
return { handled: false }; // Continue chain
},
// 2. Track in analytics
async (error) => {
trackErrorInAnalytics(error);
return { handled: false }; // Continue chain
},
// 3. Custom handling
async (error) => {
if (error.type === 'NETWORK_ERROR') {
showOfflineMode();
return { handled: true }; // Stop chain
}
return { handled: false };
},
// 4. Default handler (only if not handled)
async (error) => {
showGenericErrorMessage(error);
return { handled: true };
}
]);Error Events
Available Events
// Listen to error events
// 1. Any error
document.addEventListener('auth:error', (e) => {
const { error, context } = e.detail;
console.log('Auth error:', error.type);
});
// 2. Specific error type
document.addEventListener('auth:error:unauthorized', (e) => {
console.log('User unauthorized');
});
document.addEventListener('auth:error:forbidden', (e) => {
console.log('Access forbidden');
});
// 3. Network errors
document.addEventListener('auth:error:network', (e) => {
console.log('Network error');
showOfflineIndicator();
});
// 4. Validation errors
document.addEventListener('auth:error:validation', (e) => {
const { errors } = e.detail;
console.log('Validation errors:', errors);
});
// 5. Token errors
document.addEventListener('auth:error:token', (e) => {
console.log('Token error');
});
// 6. Session errors
document.addEventListener('auth:error:session', (e) => {
console.log('Session error');
});
// 7. Rate limit errors
document.addEventListener('auth:error:ratelimit', (e) => {
const { retryAfter } = e.detail;
console.log(`Rate limited. Retry after ${retryAfter}ms`);
});
// 8. Server errors
document.addEventListener('auth:error:server', (e) => {
console.log('Server error');
});
// 9. Error recovery
document.addEventListener('auth:error:recovered', (e) => {
console.log('Error recovered');
});
// 10. Error retry
document.addEventListener('auth:error:retry', (e) => {
const { attempt, maxRetries } = e.detail;
console.log(`Retry attempt ${attempt}/${maxRetries}`);
});Retry Logic
1. Automatic Retry
// Configure retry behavior
await AuthManager.init({
errorHandling: {
maxRetries: 3,
retryDelay: 1000,
retryBackoff: 'exponential', // linear, exponential, fixed
// Which errors to retry
retryableErrors: [
'NETWORK_ERROR',
'SERVER_ERROR',
'TOKEN_EXPIRED',
'RATE_LIMIT_EXCEEDED'
]
}
});2. Retry Strategies
Exponential Backoff
// Retry delays increase exponentially
// Attempt 1: 1s
// Attempt 2: 2s
// Attempt 3: 4s
// Attempt 4: 8s
{
retryBackoff: 'exponential',
retryDelay: 1000,
maxRetries: 4
}Linear Backoff
// Retry delays increase linearly
// Attempt 1: 1s
// Attempt 2: 2s
// Attempt 3: 3s
// Attempt 4: 4s
{
retryBackoff: 'linear',
retryDelay: 1000,
maxRetries: 4
}Fixed Delay
// Same delay for all retries
// Attempt 1: 1s
// Attempt 2: 1s
// Attempt 3: 1s
{
retryBackoff: 'fixed',
retryDelay: 1000,
maxRetries: 3
}3. Custom Retry Logic
// Custom retry decision
AuthErrorHandler.setRetryDecider(async (error, attempt, context) => {
console.log(`Retry decision for ${error.type}, attempt ${attempt}`);
// Custom logic
if (error.type === 'NETWORK_ERROR') {
// Check if online
if (!navigator.onLine) {
return { retry: false, reason: 'Offline' };
}
// Retry up to 5 times for network errors
if (attempt < 5) {
return {
retry: true,
delay: attempt * 2000 // Custom delay
};
}
}
if (error.type === 'RATE_LIMIT_EXCEEDED') {
// Use server's retry-after header
return {
retry: true,
delay: error.retryAfter || 60000
};
}
// Default behavior
return { retry: false };
});4. Retry with Circuit Breaker
// Prevent retry storms
const circuitBreaker = {
failures: 0,
threshold: 5,
timeout: 60000,
state: 'CLOSED' // CLOSED, OPEN, HALF_OPEN
};
AuthErrorHandler.setRetryDecider(async (error, attempt) => {
// Check circuit breaker state
if (circuitBreaker.state === 'OPEN') {
const now = Date.now();
const timeSinceOpen = now - circuitBreaker.openedAt;
if (timeSinceOpen >= circuitBreaker.timeout) {
// Try half-open
circuitBreaker.state = 'HALF_OPEN';
} else {
// Still open, don't retry
return {
retry: false,
reason: 'Circuit breaker open'
};
}
}
// Retry logic
if (error.type === 'SERVER_ERROR') {
circuitBreaker.failures++;
if (circuitBreaker.failures >= circuitBreaker.threshold) {
// Open circuit
circuitBreaker.state = 'OPEN';
circuitBreaker.openedAt = Date.now();
return { retry: false, reason: 'Circuit breaker opened' };
}
return { retry: true, delay: 1000 };
}
return { retry: false };
});
// Reset on success
document.addEventListener('auth:success', () => {
if (circuitBreaker.state === 'HALF_OPEN') {
circuitBreaker.state = 'CLOSED';
circuitBreaker.failures = 0;
}
});Error Recovery
1. Automatic Recovery
// AuthErrorHandler attempts recovery automatically
// For TOKEN_EXPIRED:
// 1. Try refresh token
// 2. If refresh fails, redirect to login
// For NETWORK_ERROR:
// 1. Retry with backoff
// 2. Show offline indicator
// 3. Queue operations for when online
// For SESSION_EXPIRED:
// 1. Clear session data
// 2. Store intended route
// 3. Redirect to login2. Custom Recovery Strategies
// Register recovery strategy
AuthErrorHandler.registerRecovery('TOKEN_EXPIRED', async (error, context) => {
console.log('Recovering from token expiration');
try {
// Try to refresh token
await AuthManager.refreshToken();
// Retry original operation
if (context.operation) {
await context.operation.retry();
}
return { recovered: true };
} catch (refreshError) {
// Refresh failed, redirect to login
await Router.navigate('/login');
return { recovered: false };
}
});3. Graceful Degradation
// Degrade functionality instead of complete failure
AuthErrorHandler.registerRecovery('NETWORK_ERROR', async (error) => {
console.log('Network error - enabling offline mode');
// Enable offline mode
app.offlineMode = true;
// Show offline indicator
showOfflineBanner();
// Use cached data
const cachedData = await loadFromCache();
if (cachedData) {
renderWithCachedData(cachedData);
return { recovered: true };
}
return { recovered: false };
});
// Recover when online
window.addEventListener('online', async () => {
if (app.offlineMode) {
app.offlineMode = false;
hideOfflineBanner();
// Sync offline changes
await syncOfflineChanges();
// Reload current page
await Router.reload();
}
});Usage Examples
1. Complete Error Handling Setup
// Initialize with comprehensive error handling
await AuthManager.init({
enabled: true,
endpoints: {
login: '/api/auth/login',
logout: '/api/auth/logout',
refresh: '/api/auth/refresh',
verify: '/api/auth/verify'
},
errorHandling: {
// Retry configuration
maxRetries: 3,
retryDelay: 1000,
retryBackoff: 'exponential',
// Notification configuration
showNotifications: true,
notificationDuration: 5000,
// Logging
logErrors: true,
logLevel: 'error',
// Error types to retry
retryableErrors: [
'NETWORK_ERROR',
'SERVER_ERROR',
'TOKEN_EXPIRED',
'RATE_LIMIT_EXCEEDED'
]
}
});
// Listen to all errors
document.addEventListener('auth:error', (e) => {
const { error, context } = e.detail;
// Log to analytics
logErrorToAnalytics({
type: error.type,
message: error.message,
context: context.operation,
timestamp: new Date().toISOString()
});
});
// Handle network errors
document.addEventListener('auth:error:network', () => {
showOfflineIndicator();
});
// Handle unauthorized errors
document.addEventListener('auth:error:unauthorized', () => {
// Clear any cached auth state
localStorage.removeItem('authState');
});
console.log('Error handling configured');2. Custom Validation Error Display
// Register handler for validation errors
AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
console.log('Validation errors:', error.errors);
// Clear previous errors
document.querySelectorAll('.error-message').forEach(el => {
el.remove();
});
document.querySelectorAll('.is-invalid').forEach(el => {
el.classList.remove('is-invalid');
});
// Display new errors
Object.keys(error.errors).forEach(field => {
const input = document.querySelector(`[name="${field}"]`);
if (input) {
// Mark input as invalid
input.classList.add('is-invalid');
// Create error message
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message text-danger';
errorDiv.textContent = error.errors[field];
// Insert after input
input.parentNode.insertBefore(errorDiv, input.nextSibling);
}
});
// Focus first invalid field
const firstInvalid = document.querySelector('.is-invalid');
if (firstInvalid) {
firstInvalid.focus();
}
return { handled: true };
});
// Example login with validation
async function handleLogin(e) {
e.preventDefault();
try {
await AuthManager.login({
email: emailInput.value,
password: passwordInput.value
});
// Success - redirect
await Router.navigate('/dashboard');
} catch (error) {
// AuthErrorHandler automatically handles validation errors
console.log('Login failed:', error.type);
}
}3. Rate Limiting with User Feedback
// Handle rate limit errors
AuthErrorHandler.registerHandler('RATE_LIMIT_EXCEEDED', async (error) => {
const retryAfter = error.retryAfter || 60000;
const minutes = Math.ceil(retryAfter / 60000);
// Show countdown modal
const modal = createModal({
title: 'Too Many Attempts',
message: `You've made too many login attempts. Please wait ${minutes} minute(s).`,
countdown: retryAfter,
cancelable: false
});
// Start countdown
let remaining = retryAfter;
const interval = setInterval(() => {
remaining -= 1000;
if (remaining <= 0) {
clearInterval(interval);
modal.close();
return;
}
const secs = Math.ceil(remaining / 1000);
modal.updateMessage(`Please wait ${secs} seconds...`);
}, 1000);
return { handled: true };
});
// Prevent form submission during rate limit
document.addEventListener('auth:error:ratelimit', (e) => {
const { retryAfter } = e.detail;
// Disable login form
const loginForm = document.getElementById('loginForm');
const submitBtn = loginForm.querySelector('[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = 'Please wait...';
// Re-enable after delay
setTimeout(() => {
submitBtn.disabled = false;
submitBtn.textContent = 'Login';
}, retryAfter);
});4. Offline Mode with Queue
// Operation queue for offline mode
const operationQueue = [];
// Handle network errors
AuthErrorHandler.registerHandler('NETWORK_ERROR', async (error, context) => {
console.log('Network error - queueing operation');
// Queue operation
if (context.operation) {
operationQueue.push({
operation: context.operation,
timestamp: Date.now(),
error: error
});
}
// Show offline mode
showOfflineMode();
return { handled: true };
});
// Process queue when online
window.addEventListener('online', async () => {
console.log('Back online - processing queue');
hideOfflineMode();
// Process queued operations
while (operationQueue.length > 0) {
const item = operationQueue.shift();
try {
await item.operation.retry();
console.log('Operation completed:', item.operation.type);
} catch (error) {
console.error('Operation failed:', error);
// Re-queue if still network error
if (error.type === 'NETWORK_ERROR') {
operationQueue.push(item);
break; // Stop processing, still offline
}
}
}
// Show notification
if (operationQueue.length === 0) {
showNotification('All operations completed', 'success');
} else {
showNotification(`${operationQueue.length} operations pending`, 'warning');
}
});
// Clear old queued operations (older than 1 hour)
setInterval(() => {
const now = Date.now();
const maxAge = 60 * 60 * 1000; // 1 hour
const filtered = operationQueue.filter(item => {
return (now - item.timestamp) < maxAge;
});
const removed = operationQueue.length - filtered.length;
if (removed > 0) {
console.log(`Removed ${removed} old operations from queue`);
operationQueue.length = 0;
operationQueue.push(...filtered);
}
}, 5 * 60 * 1000); // Check every 5 minutes5. Token Refresh with Fallback
// Handle token expiration
AuthErrorHandler.registerHandler('TOKEN_EXPIRED', async (error, context) => {
console.log('Token expired - attempting refresh');
try {
// Try to refresh token
await AuthManager.refreshToken();
console.log('Token refreshed successfully');
// Retry original operation
if (context.operation) {
return await context.operation.retry();
}
return { handled: true };
} catch (refreshError) {
console.error('Token refresh failed:', refreshError);
// Store intended route
const currentRoute = Router.getCurrentRoute();
if (currentRoute && !currentRoute.metadata?.public) {
AuthGuard.storeIntendedRoute(currentRoute, Router);
}
// Redirect to login
await Router.navigate('/login');
// Show notification
showNotification(
'Your session has expired. Please login again.',
'warning'
);
return { handled: true };
}
});
// Handle refresh failures
document.addEventListener('auth:error:token_refresh_failed', async () => {
// Force logout
await AuthManager.logout();
// Clear all auth data
localStorage.clear();
sessionStorage.clear();
// Redirect to login
await Router.navigate('/login');
});6. Error Tracking and Analytics
// Track all errors in analytics
document.addEventListener('auth:error', async (e) => {
const { error, context } = e.detail;
// Prepare error data
const errorData = {
type: error.type,
message: error.message,
context: {
operation: context.operation?.type,
route: Router.getCurrentRoute()?.path,
user: AuthManager.getUser()?.id
},
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href
};
// Send to analytics
try {
await fetch('/api/analytics/error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(errorData)
});
} catch (err) {
console.error('Failed to log error:', err);
}
// Log to console in development
if (isDevelopment) {
console.group('Auth Error');
console.error('Type:', error.type);
console.error('Message:', error.message);
console.error('Context:', context);
console.groupEnd();
}
});
// Track error recovery
document.addEventListener('auth:error:recovered', (e) => {
const { error, recoveryMethod } = e.detail;
trackEvent('auth_error_recovered', {
error_type: error.type,
recovery_method: recoveryMethod
});
});
// Track retry attempts
document.addEventListener('auth:error:retry', (e) => {
const { error, attempt, maxRetries } = e.detail;
trackEvent('auth_error_retry', {
error_type: error.type,
attempt: attempt,
max_retries: maxRetries
});
});API Reference
Methods
handleError(error, context)
Handle an error with configured actions
Parameters:
error(Object/Error) - Error objectcontext(Object) - Error context
Returns: Promise<Object> - Handling result
Example:
try {
await AuthManager.login(credentials);
} catch (error) {
await AuthErrorHandler.handleError(error, {
operation: 'login',
route: Router.getCurrentRoute()
});
}registerHandler(errorType, handler)
Register handler for specific error type
Parameters:
errorType(string) - Error typehandler(Function) - Handler function
Returns: void
Example:
AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
displayValidationErrors(error.errors);
return { handled: true };
});setGlobalHandler(handler)
Set global error handler (called for all errors)
Parameters:
handler(Function) - Global handler function
Returns: void
Example:
AuthErrorHandler.setGlobalHandler(async (error, context) => {
await logErrorToService(error);
return { handled: false }; // Continue to specific handlers
});setRetryDecider(decider)
Set custom retry decision logic
Parameters:
decider(Function) - Retry decision function
Returns: void
Example:
AuthErrorHandler.setRetryDecider(async (error, attempt) => {
if (error.type === 'NETWORK_ERROR' && attempt < 5) {
return { retry: true, delay: attempt * 1000 };
}
return { retry: false };
});registerRecovery(errorType, strategy)
Register recovery strategy for error type
Parameters:
errorType(string) - Error typestrategy(Function) - Recovery strategy function
Returns: void
Example:
AuthErrorHandler.registerRecovery('TOKEN_EXPIRED', async (error, context) => {
await AuthManager.refreshToken();
return { recovered: true };
});clearHandlers()
Clear all registered error handlers
Returns: void
Example:
AuthErrorHandler.clearHandlers();Best Practices
1. Handle Errors Gracefully
// ✅ Good - graceful error handling
try {
await AuthManager.login(credentials);
} catch (error) {
// Error automatically handled by AuthErrorHandler
console.log('Login failed, error handled');
}
// ❌ Bad - no error handling
await AuthManager.login(credentials); // Uncaught errors!2. Provide User Feedback
// ✅ Good - clear user feedback
document.addEventListener('auth:error:validation', (e) => {
showValidationErrors(e.detail.errors);
showNotification('Please fix the errors and try again', 'error');
});
// ❌ Bad - silent failures
// User doesn't know what went wrong3. Log Errors Appropriately
// ✅ Good - structured error logging
document.addEventListener('auth:error', (e) => {
const { error, context } = e.detail;
logger.error({
type: error.type,
message: error.message,
context: context,
timestamp: new Date().toISOString()
});
});
// ❌ Bad - no logging
// Cannot debug production issues4. Implement Retry with Limits
// ✅ Good - retry with exponential backoff and limit
{
maxRetries: 3,
retryBackoff: 'exponential',
retryDelay: 1000
}
// ❌ Bad - unlimited retries
{
maxRetries: Infinity // Never stops trying!
}Common Pitfalls
1. Not Handling All Error Types
// ❌ Bad - only handles unauthorized
document.addEventListener('auth:error:unauthorized', () => {
redirectToLogin();
});
// What about network errors? Validation errors?
// ✅ Good - handle all error types
document.addEventListener('auth:error', (e) => {
const { error } = e.detail;
switch (error.type) {
case 'UNAUTHORIZED':
redirectToLogin();
break;
case 'NETWORK_ERROR':
showOfflineMode();
break;
case 'VALIDATION_ERROR':
showValidationErrors(error.errors);
break;
default:
showGenericError(error.message);
}
});2. Swallowing Errors
// ❌ Bad - error disappears
try {
await AuthManager.login(credentials);
} catch (error) {
// Do nothing - error is lost
}
// ✅ Good - at least log the error
try {
await AuthManager.login(credentials);
} catch (error) {
console.error('Login failed:', error);
// Let AuthErrorHandler handle it
throw error;
}3. Retry Without Backoff
// ❌ Bad - retry immediately forever
{
retryBackoff: 'fixed',
retryDelay: 0,
maxRetries: 999
}
// This hammers the server!
// ✅ Good - exponential backoff with limit
{
retryBackoff: 'exponential',
retryDelay: 1000,
maxRetries: 3
}Related Documentation
- Authentication.md - Authentication system overview
- AuthManager.md - Core authentication manager
- AuthGuard.md - Route protection
- TokenService.md - JWT token management
- AuthLoadingManager.md - Loading states