UNPKG

@clduab11/gemini-flow

Version:

Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.

871 lines (761 loc) 22.8 kB
/** * Unified Authentication Manager * * Central authentication coordinator that manages multiple authentication providers, * credential storage, token caching, and provides a unified interface for all auth operations */ import { EventEmitter } from "events"; import { Logger } from "../../utils/logger.js"; import { OAuth2Provider } from "./oauth2-provider.js"; import { VertexAIProvider } from "./vertex-ai-provider.js"; import { createCredentialStorage } from "./credential-storage.js"; import { createTokenCache } from "./token-cache.js"; import { UnifiedAuthConfig, AuthProvider, AuthCredentials, AuthenticationResult, RefreshTokenResult, ValidationResult, AuthContext, SecurityContext, CredentialStorage, TokenCache, AuthError, AuthEvent, AuthEventHandler, AuthMetrics, AuthProviderType, AuthStatus, } from "../../types/auth.js"; /** * Authentication session */ interface AuthSession { id: string; context: AuthContext; securityContext?: SecurityContext; createdAt: number; lastActivity: number; refreshCount: number; status: AuthStatus; } /** * Provider registration */ interface ProviderRegistration { provider: AuthProvider; config: any; enabled: boolean; priority: number; } /** * Unified Authentication Manager */ export class UnifiedAuthManager extends EventEmitter { private config: UnifiedAuthConfig; private logger: Logger; private storage: CredentialStorage; private cache: TokenCache; // Provider management private providers = new Map<AuthProviderType, ProviderRegistration>(); private sessions = new Map<string, AuthSession>(); private eventHandlers = new Set<AuthEventHandler>(); // Background tasks private tokenRefreshInterval?: ReturnType<typeof setInterval>; private sessionCleanupInterval?: ReturnType<typeof setInterval>; private metricsInterval?: ReturnType<typeof setInterval>; // Metrics private metrics: AuthMetrics = { totalAuthentications: 0, successfulAuthentications: 0, failedAuthentications: 0, tokenRefreshes: 0, tokenValidations: 0, averageAuthTime: 0, errorsByType: {}, activeContexts: 0, cacheHitRate: 0, }; constructor(config: UnifiedAuthConfig) { super(); this.config = config; this.logger = new Logger("UnifiedAuthManager"); // Initialize storage and cache this.storage = createCredentialStorage(config.storage); this.cache = createTokenCache({ maxSize: config.cache.maxSize || 1000, defaultTTL: config.cache.ttl, enableMetrics: true, enableEvents: true, }); // Set up event forwarding this.setupEventForwarding(); // Initialize providers this.initializeProviders(); // Start background tasks this.startBackgroundTasks(); this.logger.info("Unified Auth Manager initialized", { providers: Array.from(this.providers.keys()), storageType: config.storage.type, cacheType: config.cache.type, enableMetrics: config.logging.level, }); } /** * Authenticate using specified provider */ async authenticate( providerType: AuthProviderType, options: any = {}, ): Promise<AuthenticationResult> { const startTime = Date.now(); try { this.logger.info("Starting authentication", { provider: providerType, options: Object.keys(options), }); const registration = this.providers.get(providerType); if (!registration || !registration.enabled) { throw new Error(`Provider not available: ${providerType}`); } // Attempt authentication const result = await registration.provider.authenticate(); if (result.success && result.credentials && result.context) { // Create session const session = await this.createSession(result.context); // Store credentials await this.storeCredentials(session.id, result.credentials); // Cache credentials await this.cacheCredentials(session.id, result.credentials); // Update metrics this.updateAuthMetrics(true, Date.now() - startTime); // Emit events this.emitAuthEvent({ type: "authentication", timestamp: Date.now(), provider: providerType, sessionId: session.id, success: true, }); this.logger.info("Authentication successful", { provider: providerType, sessionId: session.id, duration: Date.now() - startTime, }); return { ...result, context: { ...result.context, sessionId: session.id, }, }; } else { this.updateAuthMetrics(false, Date.now() - startTime); this.emitAuthEvent({ type: "authentication", timestamp: Date.now(), provider: providerType, success: false, error: result.error?.message, }); return result; } } catch (error) { const authError = this.createAuthError( "authentication", "AUTH_FAILED", `Authentication failed for provider ${providerType}`, error as Error, ); this.updateAuthMetrics(false, Date.now() - startTime); this.emitAuthEvent({ type: "error", timestamp: Date.now(), provider: providerType, success: false, error: authError.message, }); this.logger.error("Authentication failed", { provider: providerType, error: authError, }); return { success: false, error: authError }; } } /** * Refresh credentials for a session */ async refreshCredentials(sessionId: string): Promise<RefreshTokenResult> { try { this.logger.info("Refreshing credentials", { sessionId }); const session = this.sessions.get(sessionId); if (!session) { return { success: false, requiresReauth: true, error: this.createAuthError( "authentication", "SESSION_NOT_FOUND", "Session not found", ), }; } const provider = this.getProviderForCredentials( session.context.credentials, ); if (!provider) { return { success: false, requiresReauth: true, error: this.createAuthError( "authentication", "PROVIDER_NOT_FOUND", "Provider not found", ), }; } // Attempt refresh const result = await provider.refresh(session.context.credentials); if (result.success && result.credentials) { // Update session session.context.credentials = result.credentials; session.refreshCount++; session.lastActivity = Date.now(); // Update storage and cache await this.storeCredentials(sessionId, result.credentials); await this.cacheCredentials(sessionId, result.credentials); this.metrics.tokenRefreshes++; this.emitAuthEvent({ type: "refresh", timestamp: Date.now(), provider: session.context.credentials.provider, sessionId, success: true, }); this.logger.info("Credentials refreshed successfully", { sessionId }); } else if (result.requiresReauth) { // Mark session as requiring re-authentication session.status = "expired"; this.emitAuthEvent({ type: "refresh", timestamp: Date.now(), provider: session.context.credentials.provider, sessionId, success: false, error: "Requires re-authentication", }); } return result; } catch (error) { const authError = this.createAuthError( "authentication", "REFRESH_FAILED", "Failed to refresh credentials", error as Error, ); this.logger.error("Credential refresh failed", { sessionId, error: authError, }); return { success: false, error: authError }; } } /** * Validate credentials for a session */ async validateCredentials(sessionId: string): Promise<ValidationResult> { try { this.logger.debug("Validating credentials", { sessionId }); const session = this.sessions.get(sessionId); if (!session) { return { valid: false, error: "Session not found" }; } const provider = this.getProviderForCredentials( session.context.credentials, ); if (!provider) { return { valid: false, error: "Provider not found" }; } const result = await provider.validate(session.context.credentials); // Update session activity session.lastActivity = Date.now(); this.metrics.tokenValidations++; if (!result.valid && result.expired) { session.status = "expired"; } return result; } catch (error) { this.logger.error("Credential validation failed", { sessionId, error }); return { valid: false, error: error instanceof Error ? error.message : "Unknown error", }; } } /** * Get credentials for a session (with automatic refresh if needed) */ async getCredentials( sessionId: string, autoRefresh = true, ): Promise<AuthCredentials | null> { try { // Check cache first const cached = await this.cache.get(sessionId); if (cached) { // Validate cached credentials const validation = await this.validateCredentials(sessionId); if (validation.valid) { return cached; } // Try auto-refresh if expired and enabled if (validation.expired && autoRefresh) { const refreshResult = await this.refreshCredentials(sessionId); if (refreshResult.success && refreshResult.credentials) { return refreshResult.credentials; } } } // Fall back to storage const stored = await this.storage.retrieve(sessionId); if (stored) { // Update cache await this.cache.set(sessionId, stored); return stored; } return null; } catch (error) { this.logger.error("Failed to get credentials", { sessionId, error }); return null; } } /** * Revoke credentials and end session */ async revokeCredentials(sessionId: string): Promise<void> { try { this.logger.info("Revoking credentials", { sessionId }); const session = this.sessions.get(sessionId); if (!session) { throw new Error("Session not found"); } const provider = this.getProviderForCredentials( session.context.credentials, ); if (provider) { try { await provider.revoke(session.context.credentials); } catch (error) { this.logger.warn("Provider revocation failed", { sessionId, error }); // Continue with cleanup even if provider revocation fails } } // Clean up storage and cache await this.storage.delete(sessionId); await this.cache.delete(sessionId); // Remove session this.sessions.delete(sessionId); this.emitAuthEvent({ type: "revocation", timestamp: Date.now(), provider: session.context.credentials.provider, sessionId, success: true, }); this.logger.info("Credentials revoked successfully", { sessionId }); } catch (error) { const authError = this.createAuthError( "authentication", "REVOCATION_FAILED", "Failed to revoke credentials", error as Error, ); this.logger.error("Credential revocation failed", { sessionId, error: authError, }); throw authError; } } /** * List active sessions */ getActiveSessions(): string[] { return Array.from(this.sessions.keys()); } /** * Get session information */ getSession(sessionId: string): AuthSession | null { const session = this.sessions.get(sessionId); return session ? { ...session } : null; // Return copy } /** * Register custom authentication provider */ registerProvider( type: AuthProviderType, provider: AuthProvider, config: any = {}, options: { enabled?: boolean; priority?: number } = {}, ): void { this.providers.set(type, { provider, config, enabled: options.enabled ?? true, priority: options.priority ?? 100, }); this.logger.info("Provider registered", { type, enabled: options.enabled }); } /** * Unregister authentication provider */ unregisterProvider(type: AuthProviderType): void { const removed = this.providers.delete(type); if (removed) { this.logger.info("Provider unregistered", { type }); } } /** * Enable/disable provider */ setProviderEnabled(type: AuthProviderType, enabled: boolean): void { const registration = this.providers.get(type); if (registration) { registration.enabled = enabled; this.logger.info("Provider status changed", { type, enabled }); } } /** * Get available providers */ getAvailableProviders(): AuthProviderType[] { return Array.from(this.providers.keys()).filter((type) => { const registration = this.providers.get(type); return registration?.enabled; }); } /** * Add event handler */ addEventHandler(handler: AuthEventHandler): void { this.eventHandlers.add(handler); } /** * Remove event handler */ removeEventHandler(handler: AuthEventHandler): void { this.eventHandlers.delete(handler); } /** * Get authentication metrics */ getMetrics(): AuthMetrics { const cacheMetrics = this.cache.getMetrics ? this.cache.getMetrics() : { hitRate: 0 }; return { ...this.metrics, activeContexts: this.sessions.size, cacheHitRate: cacheMetrics.hitRate, }; } /** * Force cleanup of expired sessions */ async cleanup(): Promise<number> { const now = Date.now(); let cleanedCount = 0; for (const [sessionId, session] of this.sessions.entries()) { // Check if session is expired const maxAge = this.config.security.maxSessionAge; const sessionAge = now - session.createdAt; if (sessionAge > maxAge || session.status === "expired") { try { await this.revokeCredentials(sessionId); cleanedCount++; } catch (error) { this.logger.warn("Failed to cleanup expired session", { sessionId, error, }); } } } if (cleanedCount > 0) { this.logger.info("Session cleanup completed", { cleanedSessions: cleanedCount, }); } return cleanedCount; } /** * Shutdown auth manager and cleanup resources */ async shutdown(): Promise<void> { this.logger.info("Shutting down Unified Auth Manager"); // Stop background tasks if (this.tokenRefreshInterval) { clearInterval(this.tokenRefreshInterval); } if (this.sessionCleanupInterval) { clearInterval(this.sessionCleanupInterval); } if (this.metricsInterval) { clearInterval(this.metricsInterval); } // Cleanup active sessions const activeSessionIds = Array.from(this.sessions.keys()); for (const sessionId of activeSessionIds) { try { await this.revokeCredentials(sessionId); } catch (error) { this.logger.warn("Failed to cleanup session during shutdown", { sessionId, error, }); } } // Cleanup cache if (this.cache.destroy) { this.cache.destroy(); } this.logger.info("Unified Auth Manager shutdown complete"); } /** * Initialize built-in providers */ private initializeProviders(): void { // Initialize OAuth2 provider if configured if (this.config.providers.oauth2) { const oauth2Provider = new OAuth2Provider(this.config.providers.oauth2); this.registerProvider( "oauth2", oauth2Provider, this.config.providers.oauth2, ); } // Initialize Vertex AI provider if configured if (this.config.providers.vertexAI) { const vertexAIProvider = new VertexAIProvider( this.config.providers.vertexAI, ); this.registerProvider( "vertex-ai", vertexAIProvider, this.config.providers.vertexAI, ); } // Add more providers as needed } /** * Set up event forwarding from storage and cache */ private setupEventForwarding(): void { // Forward storage events this.storage.on("stored", (data) => this.emit("credentials_stored", data)); this.storage.on("retrieved", (data) => this.emit("credentials_retrieved", data), ); this.storage.on("deleted", (data) => this.emit("credentials_deleted", data), ); // Forward cache events this.cache.on("cache_hit", (data) => this.emit("cache_hit", data)); this.cache.on("cache_miss", (data) => this.emit("cache_miss", data)); this.cache.on("cache_set", (data) => this.emit("cache_set", data)); } /** * Start background maintenance tasks */ private startBackgroundTasks(): void { // Token refresh check every 5 minutes this.tokenRefreshInterval = setInterval( () => { this.checkTokenRefresh().catch((error) => { this.logger.error("Token refresh check failed", { error }); }); }, 5 * 60 * 1000, ); // Session cleanup every hour this.sessionCleanupInterval = setInterval( () => { this.cleanup().catch((error) => { this.logger.error("Session cleanup failed", { error }); }); }, 60 * 60 * 1000, ); // Metrics update every minute (if enabled) if (this.config.logging.level === "debug") { this.metricsInterval = setInterval(() => { this.updateMetrics(); }, 60 * 1000); } } /** * Check for tokens that need refresh */ private async checkTokenRefresh(): Promise<void> { const refreshBuffer = this.config.security.tokenRefreshBuffer; const now = Date.now(); for (const [sessionId, session] of this.sessions.entries()) { const credentials = session.context.credentials; if (credentials.expiresAt && credentials.refreshToken) { const timeUntilExpiry = credentials.expiresAt - now; if (timeUntilExpiry <= refreshBuffer) { try { await this.refreshCredentials(sessionId); } catch (error) { this.logger.warn("Background token refresh failed", { sessionId, error, }); } } } } } /** * Update internal metrics */ private updateMetrics(): void { this.logger.debug("Auth metrics", this.getMetrics()); } /** * Create new authentication session */ private async createSession(context: AuthContext): Promise<AuthSession> { const sessionId = context.sessionId || this.generateSessionId(); const session: AuthSession = { id: sessionId, context: { ...context, sessionId, }, createdAt: Date.now(), lastActivity: Date.now(), refreshCount: 0, status: "authenticated", }; this.sessions.set(sessionId, session); return session; } /** * Store credentials using storage backend */ private async storeCredentials( sessionId: string, credentials: AuthCredentials, ): Promise<void> { await this.storage.store(sessionId, credentials); } /** * Cache credentials for fast access */ private async cacheCredentials( sessionId: string, credentials: AuthCredentials, ): Promise<void> { const ttl = credentials.expiresAt ? credentials.expiresAt - Date.now() : this.config.cache.ttl; await this.cache.set(sessionId, credentials, ttl); } /** * Get provider for specific credentials */ private getProviderForCredentials( credentials: AuthCredentials, ): AuthProvider | null { const providerType = this.getProviderTypeFromCredentials(credentials); const registration = this.providers.get(providerType); return registration?.provider || null; } /** * Map credentials to provider type */ private getProviderTypeFromCredentials( credentials: AuthCredentials, ): AuthProviderType { // This is a simplified mapping - in practice you'd have more sophisticated logic switch (credentials.provider) { case "oauth2": return "oauth2"; case "vertex-ai": return "vertex-ai"; case "google-ai": return "google-ai"; default: return credentials.provider as AuthProviderType; } } /** * Generate unique session ID */ private generateSessionId(): string { return `auth_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`; } /** * Update authentication metrics */ private updateAuthMetrics(success: boolean, duration: number): void { this.metrics.totalAuthentications++; if (success) { this.metrics.successfulAuthentications++; } else { this.metrics.failedAuthentications++; } // Update average auth time const totalTime = this.metrics.averageAuthTime * (this.metrics.totalAuthentications - 1) + duration; this.metrics.averageAuthTime = totalTime / this.metrics.totalAuthentications; } /** * Emit authentication event to handlers */ private emitAuthEvent(event: AuthEvent): void { // Emit on EventEmitter this.emit("auth_event", event); // Call registered handlers this.eventHandlers.forEach((handler) => { try { const result = handler(event); if (result instanceof Promise) { result.catch((error) => { this.logger.error("Auth event handler error", { error }); }); } } catch (error) { this.logger.error("Auth event handler error", { error }); } }); } /** * Create standardized auth error */ private createAuthError( type: AuthError["type"], code: string, message: string, originalError?: Error, ): AuthError { const error = new Error(message) as AuthError; error.code = code; error.type = type; error.retryable = type === "network"; error.originalError = originalError; error.context = { manager: "unified", timestamp: Date.now(), }; // Track error in metrics this.metrics.errorsByType[type] = (this.metrics.errorsByType[type] || 0) + 1; return error; } }