import { OidcProvider } from "./OidcProvider"; import { OidcProviderConfig } from "../OidcProviderConfig"; import { createOidcError, OidcErrorCodes } from "../utils/error-utils"; /** * Provider registry for managing OIDC provider instances * * Handles: * - Static provider configuration * - Dynamic provider loading from database * - Provider instance caching * - Lazy initialization */ export class ProviderRegistry { private staticProviders: Record; private providerInstances: Map = new Map(); private providerLoader?: (providerName: string) => Promise; constructor( staticProviders: Record, providerLoader?: (providerName: string) => Promise ) { this.staticProviders = staticProviders; this.providerLoader = providerLoader; } /** * Get provider instance by name * * Looks up provider in the following order: * 1. Cached instance * 2. Static configuration * 3. Dynamic loader (if configured) * * Providers are lazy-initialized on first use and cached. * * @param providerName - Provider identifier * @returns Initialized OIDC provider instance * @throws Error if provider not found or initialization fails */ async getProvider(providerName: string): Promise { // Check cache first const cachedProvider = this.providerInstances.get(providerName); if (cachedProvider) { return cachedProvider; } // Try static configuration let config: OidcProviderConfig | null = this.staticProviders[providerName] || null; // Try dynamic loader if not in static config if (!config && this.providerLoader) { config = await this.providerLoader(providerName); } if (!config) { throw createOidcError(OidcErrorCodes.PROVIDER_NOT_CONFIGURED, `OIDC provider '${providerName}' is not configured`, { providerName, availableProviders: Object.keys(this.staticProviders), }); } // Create and initialize provider const provider = new OidcProvider(config); await provider.initialize(); // Cache the instance this.providerInstances.set(providerName, provider); return provider; } /** * Check if provider exists in static configuration or can be loaded dynamically * * @param providerName - Provider identifier * @returns true if provider exists, false otherwise */ hasProvider(providerName: string): boolean { return providerName in this.staticProviders || !!this.providerLoader; } /** * Clear provider cache * * Forces re-initialization of providers on next access. * Useful when provider configurations change. * * @param providerName - Optional provider to clear (clears all if not specified) */ clearCache(providerName?: string): void { if (providerName) { this.providerInstances.delete(providerName); } else { this.providerInstances.clear(); } } /** * Get list of configured provider names * * @returns Array of provider names from static configuration */ getProviderNames(): string[] { return Object.keys(this.staticProviders); } }