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