UNPKG

@jhzhu89/azure-client-pool

Version:

Azure client lifecycle management with intelligent caching and authentication

977 lines (961 loc) 31.2 kB
// src/types.ts var AuthMode = { Application: "application", Delegated: "delegated", Composite: "composite" }; var CredentialType = { Application: "application", Delegated: "delegated" }; var ApplicationAuthStrategy = { Cli: "cli", ManagedIdentity: "mi", Chain: "chain" }; // src/utils/cache.ts import TTLCache from "@isaacs/ttlcache"; import { createHash } from "crypto"; // src/utils/logging.ts import { pino } from "pino"; function createPinoAdapter(pinoLogger) { return { debug: (message, context) => pinoLogger.debug(context, message), info: (message, context) => pinoLogger.info(context, message), warn: (message, context) => pinoLogger.warn(context, message), error: (message, context) => pinoLogger.error(context, message), child: (context) => createPinoAdapter(pinoLogger.child(context)) }; } var rootLogger; function initializeLogger() { const pinoLogger = pino({ level: process.env.LOG_LEVEL || "info", ...process.env.NODE_ENV === "development" && { transport: { target: "pino-pretty", options: { colorize: true, translateTime: "HH:MM:ss", ignore: "pid,hostname" } } } }); return createPinoAdapter(pinoLogger); } function getRootLogger() { if (!rootLogger) { rootLogger = initializeLogger(); } return rootLogger; } function getLogger(component) { const logger8 = getRootLogger(); return component ? logger8.child?.({ component }) || logger8 : logger8; } function setRootLogger(logger8) { rootLogger = logger8; } // src/utils/cache.ts var logger = getLogger("cache-manager"); var CACHE_KEY_TRUNCATE_LENGTH = 50; function hasDispose(obj) { return obj != null && typeof obj === "object" && // eslint-disable-next-line @typescript-eslint/no-explicit-any (typeof obj.dispose === "function" || // eslint-disable-next-line @typescript-eslint/no-explicit-any typeof obj[Symbol.asyncDispose] === "function" || // eslint-disable-next-line @typescript-eslint/no-explicit-any typeof obj[Symbol.dispose] === "function"); } var CacheManager = class { constructor(config, cacheType) { this.cacheType = cacheType; this.pendingRequests = /* @__PURE__ */ new Map(); this.absoluteTtl = config.absoluteTtl; this.slidingTtl = config.slidingTtl; this.cache = new TTLCache({ max: config.maxSize, ttl: config.slidingTtl, updateAgeOnGet: true, checkAgeOnGet: true, dispose: (entry, key, reason) => { this.disposeEntry(entry, key, reason).catch((error) => { logger.error(`Error disposing ${this.cacheType} cache entry`, { cacheType: this.cacheType, cacheKey: this.createLoggableKey(key), error: error instanceof Error ? error.message : String(error), reason }); }); } }); } cache; pendingRequests; absoluteTtl; slidingTtl; async disposeEntry(entry, cacheKey, reason) { if (!hasDispose(entry.value)) { return; } try { logger.debug(`Disposing ${this.cacheType} cache entry`, { cacheType: this.cacheType, cacheKey: this.createLoggableKey(cacheKey), reason, valueType: entry.value?.constructor?.name || "Unknown" }); const value = entry.value; if (value[Symbol.asyncDispose]) { await value[Symbol.asyncDispose](); } else if (value[Symbol.dispose]) { value[Symbol.dispose](); } else if (value.dispose) { await value.dispose(); } logger.debug(`${this.cacheType} cache entry disposed successfully`, { cacheType: this.cacheType, cacheKey: this.createLoggableKey(cacheKey), reason }); } catch (error) { logger.warn( `${this.cacheType} cache entry dispose failed, continuing cleanup`, { cacheType: this.cacheType, cacheKey: this.createLoggableKey(cacheKey), error: error instanceof Error ? error.message : String(error), reason } ); } } createCacheEntry(value) { const entry = { value }; if (this.absoluteTtl) { entry.absoluteExpiresAt = Date.now() + this.absoluteTtl; } return entry; } async getOrCreate(cacheKey, factory, contextInfo, customTtl) { const cached = this.cache.get(cacheKey); if (cached) { if (cached.absoluteExpiresAt && cached.absoluteExpiresAt <= Date.now()) { return this.createInternal(cacheKey, factory, contextInfo, customTtl); } logger.debug(`${this.cacheType} cache hit`, { cacheKey: this.createLoggableKey(cacheKey), ...contextInfo }); return cached.value; } const existingPromise = this.pendingRequests.get(cacheKey); if (existingPromise) { logger.debug(`Found pending ${this.cacheType} request`, { cacheKey: this.createLoggableKey(cacheKey), ...contextInfo }); return existingPromise; } logger.debug(`${this.cacheType} cache miss, creating new entry`, { cacheKey: this.createLoggableKey(cacheKey), ...contextInfo }); const promise = this.createInternal( cacheKey, factory, contextInfo, customTtl ); this.pendingRequests.set(cacheKey, promise); try { return await promise; } finally { this.pendingRequests.delete(cacheKey); } } async createInternal(cacheKey, factory, contextInfo, customTtl) { const value = await factory(); const entry = this.createCacheEntry(value); const ttl = customTtl ?? this.slidingTtl; this.cache.set(cacheKey, entry, { ttl }); logger.debug(`${this.cacheType} entry created and cached`, { cacheKey: this.createLoggableKey(cacheKey), slidingTTL: ttl, ...contextInfo }); return value; } clear() { this.cache.clear(); this.pendingRequests.clear(); logger.debug(`${this.cacheType} cache cleared`); } delete(cacheKey) { const deleted = this.cache.delete(cacheKey); logger.debug(`${this.cacheType} cache entry removed`, { cacheKey: this.createLoggableKey(cacheKey), deleted, cacheSize: this.cache.size }); return deleted; } getStats() { return { size: this.cache.size, maxSize: this.cache.max, pendingRequests: this.pendingRequests.size }; } createLoggableKey(rawKey) { return rawKey.length > CACHE_KEY_TRUNCATE_LENGTH ? rawKey.substring(0, CACHE_KEY_TRUNCATE_LENGTH) + "..." : rawKey; } }; function createStableCacheKey(rawKey) { return createHash("md5").update(rawKey, "utf8").digest("base64url"); } // src/client-pool/pool.ts var logger2 = getLogger("client-pool"); var ClientPool = class { constructor(clientFactory, credentialManager, config) { this.clientFactory = clientFactory; this.credentialManager = credentialManager; this.config = config; this.clientCache = new CacheManager( { maxSize: config.clientCache.maxSize, slidingTtl: config.clientCache.slidingTtl }, "client-pool" ); } clientCache; async getClient(authContext, options) { const rawCacheKey = this.generateRawCacheKey(authContext, options); const cacheKey = createStableCacheKey(rawCacheKey); logger2.debug("Getting client from cache", { rawCacheKey }); let customTtl; if (authContext.mode !== AuthMode.Application) { const tokenContext = authContext; const now = Date.now(); const tokenRemainingTime = tokenContext.expiresAt - now; const bufferMs = this.config.clientCache.bufferMs; customTtl = Math.max(tokenRemainingTime - bufferMs, 0); logger2.debug("Using dynamic TTL for token-based auth", { tokenExpiresAt: new Date(tokenContext.expiresAt).toISOString(), tokenRemainingTime: Math.floor(tokenRemainingTime / 1e3), bufferMs: Math.floor(bufferMs / 1e3), customTtl: Math.floor(customTtl / 1e3) }); } return this.clientCache.getOrCreate( cacheKey, async () => { logger2.debug("Creating new client", { authMode: authContext.mode, userObjectId: "userObjectId" in authContext ? authContext.userObjectId : void 0, tenantId: "tenantId" in authContext ? authContext.tenantId : void 0 }); const credentialProvider = { getCredential: (authType) => this.credentialManager.getCredential(authContext, authType) }; return this.clientFactory.createClient(credentialProvider, options); }, { authMode: authContext.mode, userObjectId: "userObjectId" in authContext ? authContext.userObjectId : void 0, tenantId: "tenantId" in authContext ? authContext.tenantId : void 0 }, customTtl ); } generateRawCacheKey(authContext, options) { const parts = [this.config.cacheKeyPrefix, authContext.mode]; if (authContext.mode !== AuthMode.Application) { const tokenContext = authContext; parts.push( tokenContext.tenantId || "unknown", tokenContext.userObjectId || "unknown" ); } const clientFingerprint = this.clientFactory.getClientFingerprint?.(options); if (clientFingerprint) { parts.push(clientFingerprint); } else if (options !== void 0) { const optionsHash = createStableCacheKey(JSON.stringify(options)); parts.push(optionsHash); } return parts.join("::"); } async clearCache() { this.clientCache.clear(); logger2.debug("Client pool cache cleared"); } async removeCachedClient(authContext, options) { const rawCacheKey = this.generateRawCacheKey(authContext, options); const cacheKey = createStableCacheKey(rawCacheKey); const removed = this.clientCache.delete(cacheKey); logger2.debug("Removed specific client from cache", { rawCacheKey, removed }); return removed; } getCacheStats() { return this.clientCache.getStats(); } }; // src/credentials/application-strategy.ts import { AzureCliCredential, ManagedIdentityCredential, ChainedTokenCredential } from "@azure/identity"; var logger3 = getLogger("application-strategy"); var ApplicationCredentialStrategy = class { constructor(config) { this.config = config; } createCredential() { logger3.debug("Creating application credential", { strategy: this.config.strategy, hasManagedIdentityClientId: !!this.config.managedIdentityClientId }); switch (this.config.strategy) { case ApplicationAuthStrategy.Cli: logger3.debug("Using Azure CLI credential"); return new AzureCliCredential(); case ApplicationAuthStrategy.ManagedIdentity: logger3.debug("Using Managed Identity credential", { clientId: this.config.managedIdentityClientId }); return new ManagedIdentityCredential( this.config.managedIdentityClientId ? { clientId: this.config.managedIdentityClientId } : void 0 ); case ApplicationAuthStrategy.Chain: logger3.debug("Using chained credential (CLI -> ManagedIdentity)", { clientId: this.config.managedIdentityClientId }); return new ChainedTokenCredential( new AzureCliCredential(), new ManagedIdentityCredential( this.config.managedIdentityClientId ? { clientId: this.config.managedIdentityClientId } : void 0 ) ); default: logger3.error("Unsupported application strategy", { strategy: this.config.strategy }); throw new Error( `Unsupported application strategy: ${this.config.strategy}` ); } } }; // src/credentials/delegated-strategy.ts import { OnBehalfOfCredential } from "@azure/identity"; var logger4 = getLogger("delegated-strategy"); var DelegatedCredentialStrategy = class { constructor(config) { this.config = config; this.usesCertificate = !!config.certificatePath; if (!this.usesCertificate && !config.clientSecret) { throw new Error( "Azure authentication requires either client certificate path or client secret" ); } if (this.usesCertificate && config.clientSecret) { throw new Error( "Only one of certificatePath or clientSecret should be provided" ); } } usesCertificate; createOBOCredential(context) { const now = Date.now(); if (context.expiresAt <= now) { const expiredAt = new Date(context.expiresAt).toISOString(); logger4.error("User assertion token has expired", { tenantId: context.tenantId, userObjectId: context.userObjectId, expiredAt }); throw new Error( `User assertion token expired at ${expiredAt}. Please refresh the token and try again.` ); } logger4.debug("Creating OBO credential", { tenantId: context.tenantId, clientId: this.config.clientId, usesCertificate: this.usesCertificate }); const baseOptions = { tenantId: context.tenantId, clientId: this.config.clientId, userAssertionToken: context.accessToken }; if (this.usesCertificate) { logger4.debug("Using certificate-based OBO credential"); return new OnBehalfOfCredential( this.createCertificateOptions(baseOptions) ); } logger4.debug("Using secret-based OBO credential"); return new OnBehalfOfCredential(this.createSecretOptions(baseOptions)); } createCertificateOptions(baseOptions) { if (!this.config.certificatePath) { logger4.error( "Certificate path is missing for certificate-based authentication" ); throw new Error( "Certificate path is required for certificate-based authentication" ); } logger4.debug("Building certificate options", { certificatePath: this.config.certificatePath }); return { ...baseOptions, certificatePath: this.config.certificatePath }; } createSecretOptions(baseOptions) { if (!this.config.clientSecret) { logger4.error("Client secret is missing for secret-based authentication"); throw new Error( "Client secret is required for secret-based authentication" ); } logger4.debug("Building secret options"); return { ...baseOptions, clientSecret: this.config.clientSecret }; } }; // src/credentials/credential-factory.ts var CredentialFactory = class { applicationStrategy; delegatedStrategy; constructor(applicationConfig, delegatedConfig) { this.applicationStrategy = new ApplicationCredentialStrategy( applicationConfig ); this.delegatedStrategy = new DelegatedCredentialStrategy(delegatedConfig); } createApplicationCredential() { return this.applicationStrategy.createCredential(); } createDelegatedCredential(context) { return this.delegatedStrategy.createOBOCredential(context); } }; // src/credentials/manager.ts var logger5 = getLogger("credential-manager"); var CredentialManager = class { constructor(applicationConfig, delegatedConfig, config) { this.config = config; this.credentialFactory = new CredentialFactory( applicationConfig, delegatedConfig ); this.applicationCredentialCache = this.createCredentialCache( "application-credential" ); } applicationCredentialCache; credentialFactory; createCredentialCache(cacheType) { return new CacheManager( { maxSize: this.config.credentialCache.maxSize, slidingTtl: this.config.credentialCache.slidingTtl, absoluteTtl: this.config.credentialCache.absoluteTtl }, cacheType ); } async getCredential(authContext, authType) { switch (authType) { case CredentialType.Application: return this.getApplicationCredential(); case CredentialType.Delegated: return this.getDelegatedCredential(authContext); default: throw new Error(`Unsupported auth type: ${authType}`); } } async getApplicationCredential() { const rawCacheKey = this.createApplicationRawCacheKey(); const cacheKey = createStableCacheKey(rawCacheKey); logger5.debug("Getting application credential from cache", { rawCacheKey }); return this.applicationCredentialCache.getOrCreate( cacheKey, async () => this.credentialFactory.createApplicationCredential(), { authType: CredentialType.Application } ); } async getDelegatedCredential(authContext) { const tokenContext = this.validateAndGetTokenContext(authContext); const now = Date.now(); if (tokenContext.expiresAt <= now) { const expiredAt = new Date(tokenContext.expiresAt).toISOString(); logger5.error("User assertion token has expired", { tenantId: tokenContext.tenantId, userObjectId: tokenContext.userObjectId, expiredAt }); throw new Error( `User assertion token expired at ${expiredAt}. Please refresh the token and try again.` ); } logger5.debug("Creating delegated credential without caching", { tenantId: tokenContext.tenantId, userObjectId: tokenContext.userObjectId, tokenExpiresAt: new Date(tokenContext.expiresAt).toISOString() }); return this.credentialFactory.createDelegatedCredential(tokenContext); } validateAndGetTokenContext(authContext) { if (authContext.mode === AuthMode.Application) { throw new Error( "Cannot provide delegated credentials with ApplicationAuthContext" ); } return authContext; } createApplicationRawCacheKey() { const parts = [this.config.cacheKeyPrefix, CredentialType.Application]; return parts.join("::"); } clearCache() { this.applicationCredentialCache.clear(); logger5.debug("Application credential cache cleared"); } getCacheStats() { return { applicationCredentials: this.applicationCredentialCache.getStats() }; } }; // src/auth/context.ts var AuthContextFactory = { application: () => ({ mode: AuthMode.Application }), delegated: (token) => ({ mode: AuthMode.Delegated, tenantId: token.tenantId, userObjectId: token.userObjectId, accessToken: token.rawToken, expiresAt: token.expiresAt }), composite: (token) => ({ mode: AuthMode.Composite, tenantId: token.tenantId, userObjectId: token.userObjectId, accessToken: token.rawToken, expiresAt: token.expiresAt }) }; // src/auth/jwt/validator.ts import jwt from "jsonwebtoken"; import jwksClient from "jwks-rsa"; // src/auth/jwt/token.ts var ParsedJwtToken = class { constructor(claims, rawToken) { this.claims = claims; this.rawToken = rawToken; } get userObjectId() { return this.claims.userObjectId; } get tenantId() { return this.claims.tenantId; } get expiresAt() { return this.claims.expiresAt; } }; // src/utils/errors.ts var AUTH_ERROR_CODES = { jwt_validation_failed: "jwt_validation_failed", token_exchange_failed: "token_exchange_failed", invalid_access_token: "invalid_access_token" }; var AuthError = class _AuthError extends Error { constructor(code, message, context) { super(message); this.code = code; this.context = context; this.name = "AuthError"; this.timestamp = /* @__PURE__ */ new Date(); if (Error.captureStackTrace) { Error.captureStackTrace(this, _AuthError); } } timestamp; }; // src/auth/jwt/validator.ts var logger6 = getLogger("jwt-validator"); var JwtHandler = class { jwksClient; config; constructor(config) { this.config = { clientId: config.clientId, tenantId: config.tenantId, audience: config.audience ?? config.clientId, issuer: config.issuer ?? `https://sts.windows.net/${config.tenantId}/`, clockTolerance: config.clockTolerance }; this.jwksClient = jwksClient({ jwksUri: `https://login.microsoftonline.com/${config.tenantId}/discovery/v2.0/keys`, cache: true, cacheMaxAge: config.cacheMaxAge, rateLimit: true, jwksRequestsPerMinute: config.jwksRequestsPerMinute }); } async validateToken(accessToken) { try { logger6.debug("Validating JWT token", { tenantId: this.config.tenantId }); const payload = await this.verifyToken(accessToken); const claims = this.extractClaims(payload); logger6.debug("JWT token validated", { tenantId: claims.tenantId, userObjectId: claims.userObjectId, expiresAt: new Date(claims.expiresAt).toISOString() }); return new ParsedJwtToken(claims, accessToken); } catch (error) { logger6.error("Token validation failed", { tenantId: this.config.tenantId, error: error instanceof Error ? error.message : String(error) }); throw new AuthError( AUTH_ERROR_CODES.jwt_validation_failed, `Token validation failed: ${error instanceof Error ? error.message : error}` ); } } async isValid(accessToken) { try { await this.validateToken(accessToken); return true; } catch { return false; } } verifyToken(accessToken) { return new Promise((resolve, reject) => { const getKey = (header, callback) => { this.jwksClient.getSigningKey(header.kid, (err, key) => { callback(err, key?.getPublicKey()); }); }; jwt.verify( accessToken, getKey, { audience: this.config.audience, issuer: this.config.issuer, algorithms: ["RS256"], clockTolerance: this.config.clockTolerance }, (err, decoded) => { if (err) return reject(err); const payload = decoded; if (payload.tid !== this.config.tenantId) { return reject(new Error("Invalid tenant")); } resolve(payload); } ); }); } extractClaims(payload) { if (!payload.oid || !payload.tid || !payload.exp) { throw new Error("Missing required claims"); } return { userObjectId: payload.oid, tenantId: payload.tid, expiresAt: payload.exp * 1e3 }; } }; // src/config/configuration.ts var logger7 = getLogger("configuration"); function loadRawEnvironmentConfig() { logger7.debug("Loading environment configuration"); const nodeEnv = process.env.NODE_ENV || "development"; logger7.debug("Configuration loaded", { nodeEnv, tenantId: process.env.AZURE_TENANT_ID }); return { azure: { clientId: process.env.AZURE_CLIENT_ID || "", ...process.env.AZURE_CLIENT_SECRET && { clientSecret: process.env.AZURE_CLIENT_SECRET }, ...process.env.AZURE_CLIENT_CERTIFICATE_PATH && { certificatePath: process.env.AZURE_CLIENT_CERTIFICATE_PATH }, ...process.env.AZURE_CLIENT_CERTIFICATE_PASSWORD && { certificatePassword: process.env.AZURE_CLIENT_CERTIFICATE_PASSWORD }, tenantId: process.env.AZURE_TENANT_ID || "", ...process.env.AZURE_MANAGED_IDENTITY_CLIENT_ID && { managedIdentityClientId: process.env.AZURE_MANAGED_IDENTITY_CLIENT_ID }, ...process.env.AZURE_APPLICATION_AUTH_STRATEGY && { applicationAuthStrategy: process.env.AZURE_APPLICATION_AUTH_STRATEGY } }, jwt: { ...process.env.JWT_AUDIENCE && { audience: process.env.JWT_AUDIENCE }, ...process.env.JWT_ISSUER && { issuer: process.env.JWT_ISSUER }, clockTolerance: parseInt(process.env.JWT_CLOCK_TOLERANCE || "300"), cacheMaxAge: parseInt(process.env.JWT_CACHE_MAX_AGE || "86400000"), jwksRequestsPerMinute: parseInt( process.env.JWKS_REQUESTS_PER_MINUTE || "10" ) }, cache: { keyPrefix: process.env.CACHE_KEY_PREFIX || "client", ...process.env.CACHE_CLIENT_SLIDING_TTL && { clientCacheSlidingTtl: parseInt(process.env.CACHE_CLIENT_SLIDING_TTL) }, ...process.env.CACHE_CLIENT_MAX_SIZE && { clientCacheMaxSize: parseInt(process.env.CACHE_CLIENT_MAX_SIZE) }, ...process.env.CACHE_CLIENT_BUFFER_MS && { clientCacheBufferMs: parseInt(process.env.CACHE_CLIENT_BUFFER_MS) }, ...process.env.CACHE_CREDENTIAL_SLIDING_TTL && { credentialCacheSlidingTtl: parseInt( process.env.CACHE_CREDENTIAL_SLIDING_TTL ) }, ...process.env.CACHE_CREDENTIAL_MAX_SIZE && { credentialCacheMaxSize: parseInt(process.env.CACHE_CREDENTIAL_MAX_SIZE) }, ...process.env.CACHE_CREDENTIAL_ABSOLUTE_TTL && { credentialCacheAbsoluteTtl: parseInt( process.env.CACHE_CREDENTIAL_ABSOLUTE_TTL ) } } }; } function validateRawEnvironmentConfig(config) { if (config.jwt.clockTolerance < 0 || config.jwt.cacheMaxAge <= 0 || config.jwt.jwksRequestsPerMinute <= 0) { throw new Error("JWT configuration must have valid values"); } } function createApplicationAuthConfig(raw) { const strategyString = raw.azure.applicationAuthStrategy || ApplicationAuthStrategy.Chain; const strategy = Object.values(ApplicationAuthStrategy).includes( strategyString ) ? strategyString : ApplicationAuthStrategy.Chain; return { ...raw.azure.managedIdentityClientId && { managedIdentityClientId: raw.azure.managedIdentityClientId }, strategy }; } function createDelegatedAuthConfig(raw) { const { clientId, tenantId, clientSecret, certificatePath, certificatePassword } = raw.azure; if (!clientId || !tenantId) { throw new Error( "AZURE_CLIENT_ID and AZURE_TENANT_ID must be set for delegated authentication" ); } const hasSecret = !!clientSecret; const hasCertificate = !!certificatePath; if (!hasSecret && !hasCertificate) { throw new Error( "Either AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH must be set for delegated authentication" ); } if (hasSecret && hasCertificate) { throw new Error( "Only one of AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH should be set" ); } return { clientId, tenantId, ...clientSecret && { clientSecret }, ...certificatePath && { certificatePath }, ...certificatePassword && { certificatePassword } }; } function createJwtConfig(raw) { const { clientId, tenantId } = raw.azure; if (!clientId || !tenantId) { throw new Error( "AZURE_CLIENT_ID and AZURE_TENANT_ID must be set for JWT validation" ); } return { clientId, tenantId, ...raw.jwt }; } function createClientManagerConfig(raw) { return { cacheKeyPrefix: raw.cache.keyPrefix, clientCache: { slidingTtl: raw.cache.clientCacheSlidingTtl ?? 45 * 60 * 1e3, maxSize: raw.cache.clientCacheMaxSize ?? 100, bufferMs: raw.cache.clientCacheBufferMs ?? 60 * 1e3 }, credentialCache: { slidingTtl: raw.cache.credentialCacheSlidingTtl ?? 2 * 60 * 60 * 1e3, maxSize: raw.cache.credentialCacheMaxSize ?? 200, absoluteTtl: raw.cache.credentialCacheAbsoluteTtl ?? 8 * 60 * 60 * 1e3 } }; } var cachedRawConfig = null; function getRawConfig() { if (!cachedRawConfig) { cachedRawConfig = loadRawEnvironmentConfig(); validateRawEnvironmentConfig(cachedRawConfig); } return cachedRawConfig; } function getApplicationAuthConfig() { return createApplicationAuthConfig(getRawConfig()); } function getDelegatedAuthConfig() { return createDelegatedAuthConfig(getRawConfig()); } function getJwtConfig() { return createJwtConfig(getRawConfig()); } function getClientManagerConfig() { return createClientManagerConfig(getRawConfig()); } // src/client-pool/provider.ts var ClientProviderImpl = class { constructor(clientPool, jwtHandler) { this.clientPool = clientPool; this.jwtHandler = jwtHandler; } async getClient(authRequest, options) { const context = await this.createAuthContext(authRequest); return await this.clientPool.getClient(context, options); } async invalidateClientCache(authRequest, options) { const context = await this.createAuthContext(authRequest); return await this.clientPool.removeCachedClient(context, options); } async createAuthContext(authRequest) { switch (authRequest.mode) { case AuthMode.Application: { return AuthContextFactory.application(); } case AuthMode.Delegated: { if (!this.jwtHandler) { throw new Error( "JwtHandler is required for delegated authentication" ); } const parsedToken = await this.jwtHandler.validateToken( authRequest.accessToken ); return AuthContextFactory.delegated(parsedToken); } case AuthMode.Composite: { if (!this.jwtHandler) { throw new Error( "JwtHandler is required for composite authentication" ); } const compositeToken = await this.jwtHandler.validateToken( authRequest.accessToken ); return AuthContextFactory.composite(compositeToken); } default: { const _exhaustive = authRequest; throw new Error(`Unknown auth mode: ${_exhaustive}`); } } } }; async function createClientProvider(clientFactory) { const applicationConfig = getApplicationAuthConfig(); const delegatedConfig = getDelegatedAuthConfig(); const clientManagerConfig = getClientManagerConfig(); const credentialManager = new CredentialManager( applicationConfig, delegatedConfig, clientManagerConfig ); const clientPool = new ClientPool( clientFactory, credentialManager, clientManagerConfig ); const jwtConfig = getJwtConfig(); const jwtHandler = new JwtHandler(jwtConfig); return new ClientProviderImpl(clientPool, jwtHandler); } async function createClientProviderWithMapper(clientFactory, requestMapper, authRequestFactory) { const clientProvider = await createClientProvider(clientFactory); return { getClient: async (request) => { const authData = requestMapper.extractAuthData(request); const authRequest = authRequestFactory(authData); const options = requestMapper.extractOptions ? requestMapper.extractOptions(request) : void 0; return await clientProvider.getClient(authRequest, options); }, invalidateClientCache: async (request) => { const authData = requestMapper.extractAuthData(request); const authRequest = authRequestFactory(authData); const options = requestMapper.extractOptions ? requestMapper.extractOptions(request) : void 0; return await clientProvider.invalidateClientCache(authRequest, options); } }; } // src/client-pool/request-mapper.ts var McpRequestMapper = class { extractAuthData(request) { const params = request.params; const arguments_ = params?.arguments; const authData = {}; if (arguments_?.access_token && typeof arguments_.access_token === "string") { authData.accessToken = arguments_.access_token; } return authData; } }; export { AuthMode, CredentialType, McpRequestMapper, createClientProvider, createClientProviderWithMapper, getLogger, setRootLogger }; //# sourceMappingURL=index.mjs.map