{"version":3,"file":"ad-sso.mjs","sources":["../../../../src/lib/auth/active-directory/ad-sso.ts"],"sourcesContent":["/**\n * Active Directory Single Sign-On (SSO) Support\n *\n * Provides cross-tab session synchronization, SSO detection,\n * and seamless authentication across applications.\n *\n * @module auth/active-directory/ad-sso\n */\n\nimport type {\n  SSOSession,\n  SSOConfig,\n  ADTokens,\n  ADUser,\n} from './types';\nimport { DEFAULT_SSO_CONFIG } from '@/lib/auth';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SSO_BROADCAST_CHANNEL = 'ad_sso_sync';\nconst SESSION_CHECK_INTERVAL = 60000; // 1 minute\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * SSO event types for cross-tab communication.\n */\nexport type SSOEventType =\n  | 'session_created'\n  | 'session_updated'\n  | 'session_expired'\n  | 'session_ended'\n  | 'activity_ping'\n  | 'token_refreshed';\n\n/**\n * SSO broadcast message structure.\n */\nexport interface SSOBroadcastMessage {\n  type: SSOEventType;\n  sessionId: string;\n  timestamp: number;\n  payload?: {\n    tokens?: ADTokens;\n    user?: Partial<ADUser>;\n    source?: string;\n  };\n}\n\n/**\n * SSO event handler function type.\n */\nexport type SSOEventHandler = (event: SSOBroadcastMessage) => void;\n\n/**\n * SSO Manager options.\n */\nexport interface SSOManagerOptions extends SSOConfig {\n  /** Callback when session changes */\n  onSessionChange?: (session: SSOSession | null) => void;\n  /** Callback when tokens are synced */\n  onTokenSync?: (tokens: ADTokens) => void;\n  /** Callback when session expires */\n  onSessionExpired?: () => void;\n  /** Enable debug logging */\n  debug?: boolean;\n}\n\n// =============================================================================\n// SSO Manager Class\n// =============================================================================\n\n/**\n * SSO Manager for cross-tab session synchronization.\n *\n * Manages Single Sign-On sessions across browser tabs and windows,\n * providing seamless authentication state synchronization.\n *\n * @example\n * ```typescript\n * const ssoManager = new SSOManager({\n *   crossTabSync: true,\n *   sessionTimeout: 8 * 60 * 60 * 1000, // 8 hours\n *   onSessionChange: (session) => {\n *     if (session) {\n *       console.log('Session active:', session.upn);\n *     } else {\n *       console.log('No active session');\n *     }\n *   },\n * });\n *\n * // Start a new SSO session\n * await ssoManager.startSession(user, tokens);\n *\n * // Check for existing session on load\n * const existingSession = await ssoManager.detectSession();\n * ```\n */\nexport class SSOManager {\n  private options: Required<SSOManagerOptions>;\n  private broadcastChannel: BroadcastChannel | null = null;\n  private activityTimer: ReturnType<typeof setInterval> | null = null;\n  private sessionCheckTimer: ReturnType<typeof setInterval> | null = null;\n  private eventHandlers: Set<SSOEventHandler> = new Set();\n  private readonly tabId: string;\n  private currentSession: SSOSession | null = null;\n\n  /**\n   * Create a new SSO manager.\n   *\n   * @param options - SSO configuration options\n   */\n  constructor(options: Partial<SSOManagerOptions> = {}) {\n    this.options = {\n      crossTabSync: (options.crossTabSync ?? DEFAULT_SSO_CONFIG.crossTabSync) as boolean,\n      storagePrefix: (options.storagePrefix ?? DEFAULT_SSO_CONFIG.storagePrefix) as string,\n      sessionTimeout: (options.sessionTimeout ?? DEFAULT_SSO_CONFIG.sessionTimeout) as number,\n      trackActivity: (options.trackActivity ?? DEFAULT_SSO_CONFIG.trackActivity) as boolean,\n      activityCheckInterval: (options.activityCheckInterval ?? DEFAULT_SSO_CONFIG.activityCheckInterval) as number,\n      autoExtendSession: (options.autoExtendSession ?? DEFAULT_SSO_CONFIG.autoExtendSession) as boolean,\n      allowedDomains: options.allowedDomains ?? [],\n      onSessionChange: options.onSessionChange ?? (() => {}),\n      onTokenSync: options.onTokenSync ?? (() => {}),\n      onSessionExpired: options.onSessionExpired ?? (() => {}),\n      debug: options.debug ?? false,\n    };\n\n    this.tabId = this.generateTabId();\n\n    if (this.options.crossTabSync) {\n      this.initializeBroadcastChannel();\n    }\n\n    if (this.options.trackActivity) {\n      this.initializeActivityTracking();\n    }\n\n    this.startSessionCheck();\n  }\n\n  // ===========================================================================\n  // Session Management\n  // ===========================================================================\n\n  /**\n   * Start a new SSO session.\n   *\n   * @param user - Authenticated user\n   * @param tokens - Authentication tokens\n   * @returns The created session\n   */\n  startSession(user: ADUser, tokens: ADTokens): SSOSession {\n    const now = Date.now();\n    const session: SSOSession = {\n      sessionId: this.generateSessionId(),\n      upn: user.adAttributes.upn,\n      createdAt: now,\n      lastActivity: now,\n      expiresAt: now + this.options.sessionTimeout,\n      isValid: true,\n      domain: window.location.hostname,\n      metadata: {\n        userId: user.id,\n        displayName: user.displayName,\n        email: user.email,\n      },\n    };\n\n    this.currentSession = session;\n    this.persistSession(session);\n    this.persistTokens(tokens);\n\n    this.log('Session started:', session.sessionId);\n\n    // Notify other tabs\n    this.broadcast({\n      type: 'session_created',\n      sessionId: session.sessionId,\n      timestamp: now,\n      payload: { user: { id: user.id, email: user.email }, tokens },\n    });\n\n    this.options.onSessionChange(session);\n\n    return session;\n  }\n\n  /**\n   * End the current SSO session.\n   */\n  endSession(): void {\n    if (!this.currentSession) {\n      return;\n    }\n\n    const {sessionId} = this.currentSession;\n\n    // Clear session data\n    this.clearSessionData();\n    this.currentSession = null;\n\n    this.log('Session ended:', sessionId);\n\n    // Notify other tabs\n    this.broadcast({\n      type: 'session_ended',\n      sessionId,\n      timestamp: Date.now(),\n    });\n\n    this.options.onSessionChange(null);\n  }\n\n  /**\n   * Detect and restore existing SSO session.\n   *\n   * @returns Existing session if found and valid, null otherwise\n   */\n  detectSession(): SSOSession | null {\n    const session = this.loadSession();\n\n    if (!session) {\n      this.log('No existing session found');\n      return null;\n    }\n\n    // Check if session is still valid\n    if (!this.isSessionValid(session)) {\n      this.log('Found expired session, cleaning up');\n      this.clearSessionData();\n      this.options.onSessionExpired();\n      return null;\n    }\n\n    this.currentSession = session;\n    this.log('Restored existing session:', session.sessionId);\n    this.options.onSessionChange(session);\n\n    return session;\n  }\n\n  /**\n   * Get the current active session.\n   */\n  getCurrentSession(): SSOSession | null {\n    return this.currentSession;\n  }\n\n  /**\n   * Check if there's an active SSO session.\n   */\n  hasActiveSession(): boolean {\n    return this.currentSession !== null && this.isSessionValid(this.currentSession);\n  }\n\n  /**\n   * Update session activity timestamp.\n   */\n  recordActivity(): void {\n    if (!this.currentSession) {\n      return;\n    }\n\n    const now = Date.now();\n    this.currentSession.lastActivity = now;\n\n    // Extend session if auto-extend is enabled\n    if (this.options.autoExtendSession) {\n      this.currentSession.expiresAt = now + this.options.sessionTimeout;\n    }\n\n    this.persistSession(this.currentSession);\n\n    // Notify other tabs of activity\n    this.broadcast({\n      type: 'activity_ping',\n      sessionId: this.currentSession.sessionId,\n      timestamp: now,\n    });\n  }\n\n  // ===========================================================================\n  // Token Management\n  // ===========================================================================\n\n  /**\n   * Get stored tokens for the current session.\n   */\n  getSessionTokens(): ADTokens | null {\n    const stored = this.getStorage().getItem(\n      `${this.options.storagePrefix}tokens`\n    );\n\n    if (stored == null || stored === '') {\n      return null;\n    }\n\n    try {\n      return JSON.parse(stored) as ADTokens;\n    } catch {\n      return null;\n    }\n  }\n\n  /**\n   * Update tokens and sync across tabs.\n   *\n   * @param tokens - New tokens\n   */\n  syncTokens(tokens: ADTokens): void {\n    this.persistTokens(tokens);\n\n    if (this.currentSession) {\n      this.broadcast({\n        type: 'token_refreshed',\n        sessionId: this.currentSession.sessionId,\n        timestamp: Date.now(),\n        payload: { tokens },\n      });\n    }\n  }\n\n  // ===========================================================================\n  // Cross-Tab Synchronization\n  // ===========================================================================\n\n  /**\n   * Subscribe to SSO events.\n   *\n   * @param handler - Event handler function\n   * @returns Unsubscribe function\n   */\n  onEvent(handler: SSOEventHandler): () => void {\n    this.eventHandlers.add(handler);\n    return () => this.eventHandlers.delete(handler);\n  }\n\n  /**\n   * Cleanup resources.\n   */\n  dispose(): void {\n    if (this.broadcastChannel) {\n      this.broadcastChannel.close();\n      this.broadcastChannel = null;\n    }\n\n    if (this.activityTimer) {\n      clearInterval(this.activityTimer);\n      this.activityTimer = null;\n    }\n\n    if (this.sessionCheckTimer) {\n      clearInterval(this.sessionCheckTimer);\n      this.sessionCheckTimer = null;\n    }\n\n    this.eventHandlers.clear();\n  }\n\n  /**\n   * Initialize BroadcastChannel for cross-tab communication.\n   */\n  private initializeBroadcastChannel(): void {\n    if (typeof BroadcastChannel === 'undefined') {\n      this.log('BroadcastChannel not supported, using storage events');\n      window.addEventListener('storage', this.handleStorageEvent.bind(this));\n      return;\n    }\n\n    this.broadcastChannel = new BroadcastChannel(SSO_BROADCAST_CHANNEL);\n    this.broadcastChannel.onmessage = (event) => {\n      this.handleBroadcastMessage(event.data as SSOBroadcastMessage);\n    };\n\n    this.log('BroadcastChannel initialized');\n  }\n\n  /**\n   * Broadcast a message to other tabs.\n   */\n  private broadcast(message: SSOBroadcastMessage): void {\n    if (this.broadcastChannel) {\n      this.broadcastChannel.postMessage(message);\n    } else {\n      // Fallback to localStorage events\n      const key = `${this.options.storagePrefix}broadcast`;\n      localStorage.setItem(key, JSON.stringify({ ...message, tabId: this.tabId }));\n      localStorage.removeItem(key);\n    }\n  }\n\n  /**\n   * Handle incoming broadcast messages.\n   */\n  private handleBroadcastMessage(message: SSOBroadcastMessage): void {\n    this.log('Received broadcast:', message.type);\n\n    // Notify event handlers\n    this.eventHandlers.forEach(handler => handler(message));\n\n    switch (message.type) {\n      case 'session_created':\n      case 'session_updated': {\n        // Another tab created/updated a session\n        const session = this.loadSession();\n        if (session && session.sessionId === message.sessionId) {\n          this.currentSession = session;\n          this.options.onSessionChange(session);\n        }\n        break;\n      }\n\n      case 'session_ended':\n        // Another tab ended the session\n        if (this.currentSession?.sessionId === message.sessionId) {\n          this.currentSession = null;\n          this.clearSessionData();\n          this.options.onSessionChange(null);\n        }\n        break;\n\n      case 'token_refreshed':\n        // Tokens were refreshed in another tab\n        if (message.payload?.tokens) {\n          this.options.onTokenSync(message.payload.tokens);\n        }\n        break;\n\n      case 'activity_ping':\n        // Activity in another tab\n        if (this.currentSession?.sessionId === message.sessionId) {\n          this.currentSession.lastActivity = message.timestamp;\n        }\n        break;\n    }\n  }\n\n  // ===========================================================================\n  // Activity Tracking\n  // ===========================================================================\n\n  /**\n   * Handle storage events for fallback sync.\n   */\n  private handleStorageEvent(event: StorageEvent): void {\n    if (event.key !== `${this.options.storagePrefix}broadcast` || event.newValue == null || event.newValue === '') {\n      return;\n    }\n\n    try {\n      const message = JSON.parse(event.newValue) as { tabId?: string } & SSOBroadcastMessage;\n      // Ignore messages from this tab\n      if (message.tabId != null && message.tabId === this.tabId) {\n        return;\n      }\n      this.handleBroadcastMessage(message);\n    } catch {\n      // Invalid message, ignore\n    }\n  }\n\n  /**\n   * Initialize activity tracking.\n   */\n  private initializeActivityTracking(): void {\n    const activityEvents = ['mousedown', 'keydown', 'scroll', 'touchstart'];\n    let lastActivity = Date.now();\n\n    const trackActivity = (): void => {\n      const now = Date.now();\n      // Debounce activity updates\n      if (now - lastActivity > 10000) { // 10 seconds\n        lastActivity = now;\n        this.recordActivity();\n      }\n    };\n\n    activityEvents.forEach(event => {\n      document.addEventListener(event, trackActivity, { passive: true });\n    });\n\n    // Periodic activity check\n    this.activityTimer = setInterval(() => {\n      if (this.currentSession != null && this.options.trackActivity) {\n        const inactiveTime = Date.now() - this.currentSession.lastActivity;\n        // If inactive for more than session timeout, expire the session\n        if (inactiveTime > this.options.sessionTimeout) {\n          this.log('Session expired due to inactivity');\n          void this.endSession();\n          this.options.onSessionExpired();\n        }\n      }\n    }, this.options.activityCheckInterval);\n  }\n\n  // ===========================================================================\n  // Storage Operations\n  // ===========================================================================\n\n  /**\n   * Start periodic session validity check.\n   */\n  private startSessionCheck(): void {\n    this.sessionCheckTimer = setInterval(() => {\n      if (this.currentSession != null && !this.isSessionValid(this.currentSession)) {\n        this.log('Session expired during check');\n        void this.endSession();\n        this.options.onSessionExpired();\n      }\n    }, SESSION_CHECK_INTERVAL);\n  }\n\n  /**\n   * Persist session to storage.\n   */\n  private persistSession(session: SSOSession): void {\n    this.getStorage().setItem(\n      `${this.options.storagePrefix}session`,\n      JSON.stringify(session)\n    );\n  }\n\n  /**\n   * Load session from storage.\n   */\n  private loadSession(): SSOSession | null {\n    const stored = this.getStorage().getItem(\n      `${this.options.storagePrefix}session`\n    );\n\n    if (stored == null || stored === '') {\n      return null;\n    }\n\n    try {\n      return JSON.parse(stored) as SSOSession;\n    } catch {\n      return null;\n    }\n  }\n\n  /**\n   * Persist tokens to storage.\n   */\n  private persistTokens(tokens: ADTokens): void {\n    this.getStorage().setItem(\n      `${this.options.storagePrefix}tokens`,\n      JSON.stringify(tokens)\n    );\n  }\n\n  /**\n   * Clear all session data.\n   */\n  private clearSessionData(): void {\n    const storage = this.getStorage();\n    const keysToRemove: string[] = [];\n\n    for (let i = 0; i < storage.length; i++) {\n      const key = storage.key(i);\n      if (key?.startsWith(this.options.storagePrefix) === true) {\n        keysToRemove.push(key);\n      }\n    }\n\n    keysToRemove.forEach(key => storage.removeItem(key));\n  }\n\n  // ===========================================================================\n  // Utility Methods\n  // ===========================================================================\n\n  /**\n   * Get the storage object.\n   */\n  private getStorage(): Storage {\n    return sessionStorage;\n  }\n\n  /**\n   * Check if a session is still valid.\n   */\n  private isSessionValid(session: SSOSession): boolean {\n    if (!session.isValid) {\n      return false;\n    }\n\n    const now = Date.now();\n\n    // Check expiration\n    if (now >= session.expiresAt) {\n      return false;\n    }\n\n    // Check domain\n    return !(this.options.allowedDomains != null &&\n      this.options.allowedDomains.length > 0 &&\n      !this.options.allowedDomains.includes(session.domain));\n\n\n  }\n\n  /**\n   * Generate unique session ID.\n   */\n  private generateSessionId(): string {\n    const array = new Uint8Array(16);\n    crypto.getRandomValues(array);\n    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n  }\n\n  /**\n   * Generate unique tab ID.\n   */\n  private generateTabId(): string {\n    const array = new Uint8Array(8);\n    crypto.getRandomValues(array);\n    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n  }\n\n  /**\n   * Log debug message.\n   */\n  private log(message: string, ...args: unknown[]): void {\n    if (this.options.debug) {\n      // eslint-disable-next-line no-console\n      console.log(`[SSOManager] ${message}`, ...args);\n    }\n  }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a new SSO manager.\n *\n * @param options - SSO configuration options\n * @returns Configured SSOManager\n */\nexport function createSSOManager(options?: Partial<SSOManagerOptions>): SSOManager {\n  return new SSOManager(options);\n}\n\n// =============================================================================\n// Cross-Domain SSO Support\n// =============================================================================\n\n/**\n * Cross-domain SSO helper for applications on different subdomains.\n *\n * Uses postMessage for secure cross-origin communication.\n */\nexport class CrossDomainSSO {\n  private allowedOrigins: string[];\n  private messageHandler: ((event: MessageEvent) => void) | null = null;\n\n  constructor(allowedOrigins: string[]) {\n    this.allowedOrigins = allowedOrigins;\n  }\n\n  /**\n   * Initialize cross-domain SSO listener.\n   *\n   * @param onSessionReceived - Callback when session is received\n   */\n  listen(onSessionReceived: (session: SSOSession, tokens: ADTokens) => void): void {\n    this.messageHandler = (event: MessageEvent) => {\n      if (!this.allowedOrigins.includes(event.origin)) {\n        return;\n      }\n\n      const data = event.data as { type?: string; session?: SSOSession; tokens?: ADTokens };\n      if (data.type === 'sso_session_share' && data.session != null && data.tokens != null) {\n        onSessionReceived(data.session, data.tokens);\n      }\n    };\n\n    window.addEventListener('message', this.messageHandler);\n  }\n\n  /**\n   * Share session with another domain.\n   *\n   * @param targetOrigin - Target origin URL\n   * @param targetWindow\n   * @param session - Session to share\n   * @param tokens - Tokens to share\n   */\n  shareSession(\n    targetOrigin: string,\n    targetWindow: Window,\n    session: SSOSession,\n    tokens: ADTokens\n  ): void {\n    if (!this.allowedOrigins.includes(targetOrigin)) {\n      throw new Error(`Origin ${targetOrigin} is not allowed`);\n    }\n\n    targetWindow.postMessage(\n      {\n        type: 'sso_session_share',\n        session,\n        tokens,\n      },\n      targetOrigin\n    );\n  }\n\n  /**\n   * Request session from parent window (for iframes).\n   *\n   * @param parentOrigin - Parent window origin\n   */\n  requestSession(parentOrigin: string): void {\n    if (window.parent === window) {\n      return; // Not in an iframe\n    }\n\n    window.parent.postMessage(\n      { type: 'sso_session_request' },\n      parentOrigin\n    );\n  }\n\n  /**\n   * Stop listening for cross-domain messages.\n   */\n  dispose(): void {\n    if (this.messageHandler) {\n      window.removeEventListener('message', this.messageHandler);\n      this.messageHandler = null;\n    }\n  }\n}\n\n/**\n * Create a cross-domain SSO helper.\n *\n * @param allowedOrigins - List of allowed origins\n * @returns CrossDomainSSO instance\n */\nexport function createCrossDomainSSO(allowedOrigins: string[]): CrossDomainSSO {\n  return new CrossDomainSSO(allowedOrigins);\n}\n"],"names":["SSO_BROADCAST_CHANNEL","SESSION_CHECK_INTERVAL","SSOManager","options","DEFAULT_SSO_CONFIG","user","tokens","now","session","sessionId","stored","handler","event","message","key","activityEvents","lastActivity","trackActivity","storage","keysToRemove","i","array","byte","args","createSSOManager","CrossDomainSSO","allowedOrigins","onSessionReceived","data","targetOrigin","targetWindow","parentOrigin","createCrossDomainSSO"],"mappings":"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"}