{"version":3,"file":"authService.mjs","sources":["../../../src/lib/auth/authService.ts"],"sourcesContent":["import { apiClient } from '@/lib/api';\nimport type { SecureStorage } from '@/lib/security/secure-storage';\nimport { createSecureLocalStorage } from '@/lib/security/secure-storage';\nimport type {\n  User,\n  AuthTokens,\n  LoginCredentials,\n  RegisterCredentials\n} from './types';\n\n/**\n * Storage keys for auth tokens within SecureStorage.\n * These are internal keys used by the encrypted storage layer.\n */\nconst SECURE_TOKEN_KEYS = {\n  ACCESS_TOKEN: 'auth_access_token',\n  REFRESH_TOKEN: 'auth_refresh_token',\n  TOKEN_EXPIRY: 'auth_token_expiry',\n} as const;\n\n/**\n * Rate limiting configuration for authentication attempts.\n *\n * SECURITY: Implements exponential backoff to prevent brute force attacks.\n * - Initial delay starts at BASE_DELAY_MS after first failure\n * - Each subsequent failure doubles the delay (exponential backoff)\n * - Maximum delay is capped at MAX_DELAY_MS\n * - Counter resets after successful authentication\n */\nconst RATE_LIMIT_CONFIG = {\n  /** Maximum number of failed attempts before lockout */\n  MAX_ATTEMPTS: 5,\n  /** Base delay in milliseconds (1 second) */\n  BASE_DELAY_MS: 1000,\n  /** Maximum delay in milliseconds (5 minutes) */\n  MAX_DELAY_MS: 300000,\n  /** Window in milliseconds to track failed attempts (15 minutes) */\n  ATTEMPT_WINDOW_MS: 900000,\n} as const;\n\n/**\n * Session encryption key management.\n *\n * SECURITY: The encryption key is now generated using cryptographically\n * secure random bytes via the Web Crypto API. This key is:\n * - Generated fresh per session/initialization\n * - Stored only in memory (never persisted to storage)\n * - Used for encrypting tokens at rest\n *\n * This provides defense-in-depth against:\n * - XSS attacks reading raw tokens from localStorage\n * - Browser extension access to localStorage\n * - Debugging tools inspecting storage\n * - Predictable key derivation attacks\n */\nlet sessionEncryptionKey: string | null = null;\n\n/**\n * Generates a cryptographically secure encryption key using Web Crypto API.\n *\n * SECURITY: Uses crypto.getRandomValues() for true cryptographic randomness.\n * The key is 256 bits (32 bytes) encoded as base64.\n *\n * @returns A base64-encoded 256-bit random key\n */\nfunction generateSessionKey(): string {\n  const keyBytes = new Uint8Array(32);\n  crypto.getRandomValues(keyBytes);\n  return btoa(String.fromCharCode(...keyBytes));\n}\n\n/**\n * Initializes the session security by generating a fresh encryption key.\n *\n * SECURITY: This function should be called during application initialization\n * or when a new authentication session begins. The generated key is stored\n * only in memory and is not predictable.\n *\n * @returns The generated encryption key (for use during session)\n */\nexport function initializeSessionSecurity(): string {\n  sessionEncryptionKey = generateSessionKey();\n  return sessionEncryptionKey;\n}\n\n/**\n * Gets the current session encryption key, initializing if necessary.\n *\n * SECURITY: Ensures a key exists before any encryption operations.\n * If no key has been initialized, generates one automatically.\n *\n * @returns The current session encryption key\n */\nfunction getSessionEncryptionKey(): string {\n  sessionEncryptionKey ??= initializeSessionSecurity();\n  return sessionEncryptionKey;\n}\n\n/**\n * Rate limiting state for tracking failed authentication attempts.\n *\n * SECURITY: Tracks failed attempts to implement exponential backoff.\n */\ninterface RateLimitState {\n  /** Number of consecutive failed attempts */\n  failedAttempts: number;\n  /** Timestamp of first failed attempt in current window */\n  firstAttemptTime: number;\n  /** Timestamp when lockout expires (if locked) */\n  lockedUntil: number | null;\n}\n\n/**\n * Low-level token handling with secure encrypted storage.\n *\n * SECURITY IMPROVEMENTS:\n * - Tokens are now encrypted at rest using AES-GCM via SecureStorage\n * - PBKDF2 key derivation with configurable iterations\n * - Integrity verification via checksums\n * - All token operations are now async for proper crypto handling\n * - Rate limiting with exponential backoff for failed auth attempts\n * - Cryptographically secure session key generation\n *\n * @see {@link SecureStorage} for encryption implementation details\n */\nclass AuthService {\n  private refreshPromise: Promise<AuthTokens> | null = null;\n  private secureStorage: SecureStorage | null = null;\n\n  /**\n   * In-memory cache for tokens to support synchronous access patterns.\n   * This cache is populated on initialization and updated on token changes.\n   *\n   * SECURITY NOTE: In-memory cache is cleared on page reload and is not\n   * accessible to other scripts due to JavaScript's memory isolation.\n   */\n  private tokenCache: {\n    accessToken: string | null;\n    refreshToken: string | null;\n    expiresAt: number | null;\n  } = {\n    accessToken: null,\n    refreshToken: null,\n    expiresAt: null,\n  };\n\n  /**\n   * Rate limiting state for authentication attempts.\n   *\n   * SECURITY: Stored in memory only, resets on page reload.\n   * This prevents brute force attacks during a single session.\n   */\n  private rateLimitState: RateLimitState = {\n    failedAttempts: 0,\n    firstAttemptTime: 0,\n    lockedUntil: null,\n  };\n\n  constructor() {\n    // Secure storage is lazily initialized to use session-generated key\n    this.secureStorage = null;\n  }\n\n  /**\n   * Checks if the user is currently rate limited.\n   *\n   * SECURITY: Returns true if too many failed attempts have occurred.\n   *\n   * @returns Object containing rate limit status and remaining time\n   */\n  checkRateLimit(): { isLimited: boolean; retryAfterMs: number; attemptsRemaining: number } {\n    const now = Date.now();\n\n    // Clean up old attempts outside the window\n    if (this.rateLimitState.firstAttemptTime > 0 &&\n        now - this.rateLimitState.firstAttemptTime > RATE_LIMIT_CONFIG.ATTEMPT_WINDOW_MS) {\n      this.resetRateLimit();\n    }\n\n    // Check if currently locked out\n    if (this.rateLimitState.lockedUntil !== null && now < this.rateLimitState.lockedUntil) {\n      return {\n        isLimited: true,\n        retryAfterMs: this.rateLimitState.lockedUntil - now,\n        attemptsRemaining: 0,\n      };\n    }\n\n    // If lockout has expired, reset\n    if (this.rateLimitState.lockedUntil !== null && now >= this.rateLimitState.lockedUntil) {\n      this.resetRateLimit();\n    }\n\n    return {\n      isLimited: false,\n      retryAfterMs: 0,\n      attemptsRemaining: RATE_LIMIT_CONFIG.MAX_ATTEMPTS - this.rateLimitState.failedAttempts,\n    };\n  }\n\n  /**\n   * Initialize the auth service by loading tokens from secure storage.\n   * This should be called once during app initialization.\n   *\n   * SECURITY: Also initializes session encryption key if not already done.\n   *\n   * @returns Promise that resolves when initialization is complete\n   */\n  async initialize(): Promise<void> {\n    // Ensure session security is initialized\n    getSessionEncryptionKey();\n    await this.loadTokensToCache();\n  }\n\n  /**\n   * Login with credentials using apiClient.\n   *\n   * SECURITY: Implements rate limiting with exponential backoff to prevent\n   * brute force attacks. If too many failed attempts occur, the user will\n   * be temporarily locked out.\n   *\n   * @throws {ApiError} When authentication fails\n   * @throws {Error} When rate limited\n   */\n  async login(credentials: LoginCredentials): Promise<{ user: User; tokens: AuthTokens }> {\n    // Check rate limiting before attempting login\n    const rateLimitStatus = this.checkRateLimit();\n    if (rateLimitStatus.isLimited) {\n      const retrySeconds = Math.ceil(rateLimitStatus.retryAfterMs / 1000);\n      throw new Error(\n        `Too many failed login attempts. Please try again in ${retrySeconds} seconds.`\n      );\n    }\n\n    try {\n      const response = await apiClient.post<{ user: User; tokens: AuthTokens }>(\n        '/auth/login',\n        credentials,\n        { meta: { skipAuth: true } }\n      );\n\n      // Successful login - reset rate limiting\n      this.resetRateLimit();\n\n      await this.storeTokens(response.data.tokens);\n      return response.data;\n    } catch (error) {\n      // Record failed attempt for rate limiting\n      this.recordFailedAttempt();\n      throw error;\n    }\n  }\n\n  /**\n   * Register a new user using apiClient\n   * @throws {ApiError} When registration fails\n   */\n  async register(credentials: RegisterCredentials): Promise<{ user: User; tokens: AuthTokens }> {\n    const response = await apiClient.post<{ user: User; tokens: AuthTokens }>(\n      '/auth/register',\n      credentials,\n      { meta: { skipAuth: true } }\n    );\n\n    await this.storeTokens(response.data.tokens);\n    return response.data;\n  }\n\n  /**\n   * Logout the current user using apiClient.\n   * Securely clears all tokens from encrypted storage and memory cache.\n   */\n  async logout(): Promise<void> {\n    try {\n      const refreshToken = this.getRefreshToken();\n      if (refreshToken !== undefined && refreshToken !== null) {\n        await apiClient.post('/auth/logout', { refreshToken });\n      }\n    } catch {\n      // Ignore errors during logout - we still want to clear local tokens\n    } finally {\n      this.clearTokens();\n    }\n  }\n\n  /**\n   * Refresh the access token using apiClient\n   * @throws {ApiError} When token refresh fails\n   */\n  async refreshTokens(): Promise<AuthTokens> {\n    // Prevent concurrent refresh requests\n    if (this.refreshPromise) {\n      return this.refreshPromise;\n    }\n\n    const refreshToken = this.getRefreshToken();\n    if (refreshToken === undefined || refreshToken === null) {\n      throw new Error('No refresh token available');\n    }\n\n    this.refreshPromise = apiClient\n      .post<AuthTokens>('/auth/refresh', { refreshToken }, { meta: { skipAuth: true } })\n      .then(async (response) => {\n        await this.storeTokens(response.data);\n        return response.data;\n      })\n      .finally(() => {\n        this.refreshPromise = null;\n      });\n\n    return this.refreshPromise;\n  }\n\n  /**\n   * Get the current user profile using apiClient\n   * @throws {ApiError} When fetching user profile fails\n   */\n  async getCurrentUser(): Promise<User> {\n    const response = await apiClient.get<User>('/auth/me');\n    return response.data;\n  }\n\n  /**\n   * Check if the user is authenticated (has valid tokens).\n   * Uses in-memory cache for synchronous access.\n   */\n  isAuthenticated(): boolean {\n    const token = this.getAccessToken();\n    if (token === undefined || token === null) return false;\n\n    const expiresAt = this.getTokenExpiry();\n    if (expiresAt === undefined || expiresAt === null) return false;\n\n    // Check if token is expired (with 1 minute buffer)\n    return Date.now() < expiresAt - 60000;\n  }\n\n  /**\n   * Get the access token from in-memory cache.\n   *\n   * SECURITY: Returns cached value for synchronous access.\n   * The actual token is stored encrypted in SecureStorage.\n   */\n  getAccessToken(): string | null {\n    return this.tokenCache.accessToken;\n  }\n\n  /**\n   * Get the refresh token from in-memory cache.\n   *\n   * SECURITY: Returns cached value for synchronous access.\n   * The actual token is stored encrypted in SecureStorage.\n   */\n  getRefreshToken(): string | null {\n    return this.tokenCache.refreshToken;\n  }\n\n  /**\n   * Get token expiry timestamp from in-memory cache.\n   */\n  getTokenExpiry(): number | null {\n    return this.tokenCache.expiresAt;\n  }\n\n  /**\n   * Retrieve access token asynchronously from secure storage.\n   * Use this when you need the freshest value from encrypted storage.\n   *\n   * SECURITY: Uses session-keyed encrypted storage.\n   *\n   * @returns Promise resolving to the access token or null\n   */\n  async getAccessTokenAsync(): Promise<string | null> {\n    const storage = this.getSecureStorage();\n    const result = await storage.getItem<string>(SECURE_TOKEN_KEYS.ACCESS_TOKEN);\n    if (result.success && result.data !== undefined && result.data !== null) {\n      this.tokenCache.accessToken = result.data;\n      return result.data;\n    }\n    return null;\n  }\n\n  /**\n   * Retrieve refresh token asynchronously from secure storage.\n   * Use this when you need the freshest value from encrypted storage.\n   *\n   * SECURITY: Uses session-keyed encrypted storage.\n   *\n   * @returns Promise resolving to the refresh token or null\n   */\n  async getRefreshTokenAsync(): Promise<string | null> {\n    const storage = this.getSecureStorage();\n    const result = await storage.getItem<string>(SECURE_TOKEN_KEYS.REFRESH_TOKEN);\n    if (result.success && result.data !== undefined && result.data !== null) {\n      this.tokenCache.refreshToken = result.data;\n      return result.data;\n    }\n    return null;\n  }\n\n  /**\n   * Gets the secure storage instance, initializing with session key if needed.\n   *\n   * SECURITY: Uses cryptographically secure session key for encryption.\n   */\n  private getSecureStorage(): SecureStorage {\n    if (!this.secureStorage) {\n      const encryptionKey = getSessionEncryptionKey();\n      this.secureStorage = createSecureLocalStorage(encryptionKey);\n    }\n    return this.secureStorage;\n  }\n\n  /**\n   * Records a failed authentication attempt and applies rate limiting.\n   *\n   * SECURITY: Implements exponential backoff to slow down brute force attacks.\n   */\n  private recordFailedAttempt(): void {\n    const now = Date.now();\n\n    // Start tracking if this is first attempt in window\n    if (this.rateLimitState.failedAttempts === 0) {\n      this.rateLimitState.firstAttemptTime = now;\n    }\n\n    this.rateLimitState.failedAttempts++;\n\n    // Apply exponential backoff after max attempts\n    if (this.rateLimitState.failedAttempts >= RATE_LIMIT_CONFIG.MAX_ATTEMPTS) {\n      // Calculate delay: baseDelay * 2^(attempts - maxAttempts)\n      const exponent = this.rateLimitState.failedAttempts - RATE_LIMIT_CONFIG.MAX_ATTEMPTS;\n      const delay = Math.min(\n        RATE_LIMIT_CONFIG.BASE_DELAY_MS * Math.pow(2, exponent),\n        RATE_LIMIT_CONFIG.MAX_DELAY_MS\n      );\n      this.rateLimitState.lockedUntil = now + delay;\n\n      console.warn(\n        `[AuthService] Rate limit applied. Too many failed attempts. ` +\n        `Locked for ${Math.ceil(delay / 1000)} seconds.`\n      );\n    }\n  }\n\n  /**\n   * Resets the rate limiting state after successful authentication.\n   *\n   * SECURITY: Called after successful login to clear failed attempt counter.\n   */\n  private resetRateLimit(): void {\n    this.rateLimitState = {\n      failedAttempts: 0,\n      firstAttemptTime: 0,\n      lockedUntil: null,\n    };\n  }\n\n  /**\n   * Load tokens from secure storage into memory cache.\n   * This enables synchronous access for performance-critical paths.\n   *\n   * SECURITY: Uses lazily-initialized secure storage with session key.\n   */\n  private async loadTokensToCache(): Promise<void> {\n    const storage = this.getSecureStorage();\n    const [accessResult, refreshResult, expiryResult] = await Promise.all([\n      storage.getItem<string>(SECURE_TOKEN_KEYS.ACCESS_TOKEN),\n      storage.getItem<string>(SECURE_TOKEN_KEYS.REFRESH_TOKEN),\n      storage.getItem<number>(SECURE_TOKEN_KEYS.TOKEN_EXPIRY),\n    ]);\n\n    this.tokenCache = {\n      accessToken: accessResult.success ? accessResult.data ?? null : null,\n      refreshToken: refreshResult.success ? refreshResult.data ?? null : null,\n      expiresAt: expiryResult.success ? expiryResult.data ?? null : null,\n    };\n  }\n\n  /**\n   * Store tokens in encrypted secure storage.\n   *\n   * SECURITY: Tokens are encrypted using AES-GCM before storage.\n   * The encryption key is derived using PBKDF2 with a high iteration count.\n   * Each token storage operation uses a unique IV for encryption.\n   * Session key is cryptographically random and stored only in memory.\n   *\n   * @param tokens - The auth tokens to store securely\n   */\n  private async storeTokens(tokens: AuthTokens): Promise<void> {\n    const storage = this.getSecureStorage();\n    // Store tokens in parallel for better performance\n    const [accessResult, refreshResult, expiryResult] = await Promise.all([\n      storage.setItem(SECURE_TOKEN_KEYS.ACCESS_TOKEN, tokens.accessToken),\n      storage.setItem(SECURE_TOKEN_KEYS.REFRESH_TOKEN, tokens.refreshToken),\n      storage.setItem(SECURE_TOKEN_KEYS.TOKEN_EXPIRY, tokens.expiresAt),\n    ]);\n\n    // Check for storage failures\n    if (!accessResult.success || !refreshResult.success || !expiryResult.success) {\n      const errors = [\n        !accessResult.success ? `Access token: ${accessResult.error}` : null,\n        !refreshResult.success ? `Refresh token: ${refreshResult.error}` : null,\n        !expiryResult.success ? `Expiry: ${expiryResult.error}` : null,\n      ].filter((error): error is string => error !== null).join('; ');\n\n      console.error('[AuthService] Failed to store tokens securely:', errors);\n      throw new Error('Failed to store authentication tokens securely');\n    }\n\n    // Update in-memory cache\n    this.tokenCache = {\n      accessToken: tokens.accessToken,\n      refreshToken: tokens.refreshToken,\n      expiresAt: tokens.expiresAt,\n    };\n  }\n\n  /**\n   * Clear tokens from encrypted storage and memory cache.\n   *\n   * SECURITY: This method ensures tokens are removed from both\n   * the encrypted storage and the in-memory cache to prevent\n   * any token leakage after logout.\n   */\n  private clearTokens(): void {\n    const storage = this.getSecureStorage();\n    // Clear from secure storage (fire and forget with void)\n    void storage.removeItem(SECURE_TOKEN_KEYS.ACCESS_TOKEN);\n    void storage.removeItem(SECURE_TOKEN_KEYS.REFRESH_TOKEN);\n    void storage.removeItem(SECURE_TOKEN_KEYS.TOKEN_EXPIRY);\n\n    // Clear in-memory cache\n    this.tokenCache = {\n      accessToken: null,\n      refreshToken: null,\n      expiresAt: null,\n    };\n  }\n}\n\nexport const authService = new AuthService();\n"],"names":["SECURE_TOKEN_KEYS","RATE_LIMIT_CONFIG","sessionEncryptionKey","generateSessionKey","keyBytes","initializeSessionSecurity","getSessionEncryptionKey","AuthService","now","credentials","rateLimitStatus","retrySeconds","response","apiClient","error","refreshToken","token","expiresAt","result","encryptionKey","createSecureLocalStorage","exponent","delay","storage","accessResult","refreshResult","expiryResult","tokens","errors","authService"],"mappings":"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"}