Client Configuration

Client API Configuration Guide

Multi-Platform Client Configuration for web, desktop, and mobile applications

Overview

All three client applications (web, desktop, mobile) use similar patterns but with platform-specific implementations:

  • Hardcoded default to localhost:8080 for development
  • Runtime configuration via settings screens for production deployments
  • Persistent configuration storage using platform-appropriate mechanisms
  • No server discovery mechanisms - manual configuration only
  • Separate WebSocket endpoint configuration for real-time features

Configuration by Client Type

1. Web Client (/web-client)

API Service Configuration:

  • Default URLs: http://localhost:8080 (API), ws://localhost:8080 (WebSocket)
  • Configuration Storage: Browser localStorage (incident-web-config)
  • Configuration Interface: Comprehensive settings UI with form validation
  • Proxy Setup: Vite dev server proxies /api, /ws, /sse to localhost:8080

Key Features:

  • Runtime URL validation
  • Connection testing capabilities
  • WebSocket status monitoring
  • Auth token management with automatic retry logic
  • OIDC provider support with callback handling

Configuration Schema:

interface AppConfig {
  apiBaseUrl: string; // Default: 'http://localhost:8080'
  wsBaseUrl: string; // Default: 'ws://localhost:8080'
  notificationsEnabled: boolean;
  theme: "light" | "dark" | "system";
  refreshInterval: number; // Default: 30000ms
  dateFormat: string;
}

Authentication Architecture:

// JWT Token Management
setAuthToken(token: string): void
clearAuthToken(): void
getAuthToken(): string | null
isTokenExpired(): boolean

// OIDC Integration
initiateOIDCLogin(provider: string): void
handleOIDCCallback(code: string, state: string): Promise<void>

2. Desktop Client (/desktop)

Tauri-Based Configuration:

  • Default URLs: Same as web client (http://localhost:8080)
  • Configuration Storage: Tauri’s app-data-dir with JSON config file
  • Configuration Interface: React-based settings screen using desktop components
  • Native Features: System notifications, tray integration, auto-launch

Desktop-Specific Features:

  • Deep linking support for incident URLs
  • System notification handling with click-to-action
  • Auto-launch configuration for startup behavior
  • Native file system access for log exports

Configuration File Location:

# macOS
~/Library/Application Support/com.incidents.app/config.json

# Windows
%APPDATA%/com.incidents.app/config.json

# Linux
~/.config/com.incidents.app/config.json

Configuration Schema:

{
  "apiBaseUrl": "http://localhost:8080",
  "wsBaseUrl": "ws://localhost:8080",
  "notifications": {
    "enabled": true,
    "sound": true,
    "showInTray": true
  },
  "window": {
    "startMinimized": false,
    "minimizeToTray": true
  },
  "autoLaunch": false
}

3. Mobile Client (/mobile)

React Native Configuration:

  • Default URLs: Same localhost pattern for development
  • Configuration Storage: React Native AsyncStorage
  • Configuration Interface: Native mobile settings screens
  • Push Notifications: Expo push notification integration

Mobile-Specific Features:

  • Push notification registration with server sync
  • Offline incident caching for connectivity gaps
  • Biometric authentication for secure access
  • Background refresh for incident updates

Configuration Interface:

// AsyncStorage key patterns
const CONFIG_KEYS = {
  API_BASE_URL: '@incidents:apiBaseUrl',
  WS_BASE_URL: '@incidents:wsBaseUrl',
  NOTIFICATION_TOKEN: '@incidents:pushToken',
  AUTH_TOKEN: '@incidents:authToken',
  BIOMETRIC_ENABLED: '@incidents:biometricAuth'
};

// Configuration methods
export const ConfigService = {
  async setApiUrl(url: string): Promise<void>
  async getApiUrl(): Promise<string>
  async setNotificationToken(token: string): Promise<void>
  async enableBiometrics(enabled: boolean): Promise<void>
};

Common Configuration Patterns

Environment-Based Defaults

All clients support environment-specific defaults:

const getDefaultApiUrl = (): string => {
  const env = process.env.NODE_ENV || "development";

  switch (env) {
    case "development":
      return "http://localhost:8080";
    case "staging":
      return "https://staging-api.incidents.example.com";
    case "production":
      return "https://api.incidents.example.com";
    default:
      return "http://localhost:8080";
  }
};

Connection Testing

All clients implement server connectivity testing:

interface ConnectionTest {
  url: string;
  status: "testing" | "success" | "failed";
  latency?: number;
  error?: string;
}

const testConnection = async (baseUrl: string): Promise<ConnectionTest> => {
  const start = Date.now();

  try {
    const response = await fetch(`${baseUrl}/health`, {
      method: "GET",
      timeout: 5000
    });

    if (response.ok) {
      return {
        url: baseUrl,
        status: "success",
        latency: Date.now() - start
      };
    }

    return {
      url: baseUrl,
      status: "failed",
      error: `HTTP ${response.status}: ${response.statusText}`
    };
  } catch (error) {
    return {
      url: baseUrl,
      status: "failed",
      error: error.message
    };
  }
};

Authentication Token Management

Consistent token handling across all platforms:

interface AuthTokenManager {
  // Token storage
  setToken(token: string): Promise<void>;
  getToken(): Promise<string | null>;
  clearToken(): Promise<void>;

  // Token validation
  isTokenValid(): Promise<boolean>;
  refreshToken(): Promise<string>;

  // Auth state
  onTokenExpired(callback: () => void): void;
  onAuthRequired(callback: () => void): void;
}

Production Deployment Configuration

Server-Side Configuration Discovery

For production deployments, consider implementing server configuration discovery:

// Optional: Server configuration endpoint
GET /api/v1/config/client

Response:
{
  "apiVersion": "v1",
  "endpoints": {
    "api": "https://api.incidents.example.com",
    "websocket": "wss://api.incidents.example.com/ws",
    "sse": "https://api.incidents.example.com/sse"
  },
  "features": {
    "oidcEnabled": true,
    "pushNotificationsEnabled": true,
    "realtimeEnabled": true
  },
  "oidc": {
    "providers": ["okta", "azure", "google"]
  }
}

Environment Variable Override

Support environment variable configuration for containerized deployments:

const config = {
  apiBaseUrl: process.env.REACT_APP_API_URL || "http://localhost:8080",
  wsBaseUrl: process.env.REACT_APP_WS_URL || "ws://localhost:8080",
  oidcClientId: process.env.REACT_APP_OIDC_CLIENT_ID,
  oidcAuthority: process.env.REACT_APP_OIDC_AUTHORITY
};

Docker Deployment Configuration

Configure clients for Docker deployments:

# Web client environment
ENV REACT_APP_API_URL=https://api.incidents.example.com
ENV REACT_APP_WS_URL=wss://api.incidents.example.com/ws
ENV REACT_APP_OIDC_AUTHORITY=https://auth.incidents.example.com

# Build with environment variables
RUN npm run build

Security Considerations

Token Storage Security

Web Client:

  • Store JWT tokens in httpOnly cookies when possible
  • Use secure localStorage with encryption for sensitive data
  • Clear tokens on tab close or browser shutdown

Desktop Client:

  • Use Tauri’s secure storage APIs
  • Encrypt configuration files containing sensitive data
  • Support OS-level keychain/credential manager integration

Mobile Client:

  • Use React Native Keychain for secure token storage
  • Enable biometric authentication for token access
  • Support device encryption requirements

API Endpoint Validation

All clients should validate API endpoints:

const validateApiUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url);

    // Require HTTPS in production
    if (process.env.NODE_ENV === "production" && parsed.protocol !== "https:") {
      return false;
    }

    // Validate hostname format
    if (!parsed.hostname || (parsed.hostname === "localhost" && process.env.NODE_ENV === "production")) {
      return false;
    }

    return true;
  } catch {
    return false;
  }
};

Troubleshooting

Common Configuration Issues

Connection Failures:

Error: Network request failed
  • Verify API server is running and accessible
  • Check firewall/proxy settings
  • Validate API URL format and protocol

Authentication Issues:

Error: 401 Unauthorized
  • Check JWT token expiration
  • Verify OIDC provider configuration
  • Confirm user permissions

WebSocket Connection Problems:

Error: WebSocket connection failed
  • Verify WebSocket URL format (ws:// or wss://)
  • Check proxy configuration for WebSocket support
  • Confirm server WebSocket endpoint is enabled

Debug Configuration

Enable debug logging for configuration issues:

// Debug configuration loading
console.log("Loading configuration:", {
  apiBaseUrl: config.apiBaseUrl,
  wsBaseUrl: config.wsBaseUrl,
  authEnabled: !!config.authToken,
  storageAvailable: typeof Storage !== "undefined"
});

// Test connectivity
testConnection(config.apiBaseUrl)
  .then((result) => console.log("Connection test:", result))
  .catch((error) => console.error("Connection test failed:", error));

Configuration Migration

Version Compatibility

Handle configuration schema changes gracefully:

const migrateConfig = (storedConfig: any): AppConfig => {
  // Handle legacy configuration formats
  if (storedConfig.version === 1) {
    return {
      ...storedConfig,
      apiBaseUrl: storedConfig.serverUrl, // Renamed field
      version: 2
    };
  }

  return storedConfig;
};

Configuration Reset

Provide configuration reset functionality:

const resetConfiguration = async (): Promise<void> => {
  // Clear stored configuration
  await AsyncStorage.multiRemove([CONFIG_KEYS.API_BASE_URL, CONFIG_KEYS.WS_BASE_URL, CONFIG_KEYS.AUTH_TOKEN]);

  // Reload default configuration
  window.location.reload(); // Web
  // or app restart for desktop/mobile
};

Related Documentation: