From 734061031bae4e5fce38770eb9d0bf4d340a60f0 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Wed, 1 Apr 2026 12:52:15 +0200 Subject: [PATCH] better OIDC config cache scaling --- .../AutopilotMonitor.Agent/buildcounter.txt | 2 +- .../Middleware/AuthenticationMiddleware.cs | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt index e4978283..a38f20ae 100644 --- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt +++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt @@ -1 +1 @@ -759 +760 diff --git a/src/Backend/AutopilotMonitor.Functions/Middleware/AuthenticationMiddleware.cs b/src/Backend/AutopilotMonitor.Functions/Middleware/AuthenticationMiddleware.cs index b4764982..7223e63b 100644 --- a/src/Backend/AutopilotMonitor.Functions/Middleware/AuthenticationMiddleware.cs +++ b/src/Backend/AutopilotMonitor.Functions/Middleware/AuthenticationMiddleware.cs @@ -28,15 +28,11 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware private readonly IUserUsageRepository _userUsageRepo; // Cache configuration managers per tenant to avoid repeated OIDC metadata fetches - // Bounded to prevent memory exhaustion from malicious tenant ID flooding - private const int MaxCacheSize = 100; - private readonly Dictionary> _configManagerCache; + // Bounded with LRU eviction to prevent memory exhaustion from malicious tenant ID flooding + private const int MaxCacheSize = 500; + private readonly Dictionary Manager, DateTime LastAccessed)> _configManagerCache; private readonly SemaphoreSlim _cacheLock = new SemaphoreSlim(1, 1); - // Track if we're currently initializing a tenant to prevent race conditions - private readonly HashSet _initializingTenants = new HashSet(); - private readonly object _initLock = new object(); - /// /// Routes that do not require JWT authentication (exact match). /// Device-to-cloud routes use their own certificate/hardware validation via SecurityValidator. @@ -76,7 +72,7 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware _configuration = configuration; _userUsageRepo = userUsageRepo; _tokenHandler = new JwtSecurityTokenHandler(); - _configManagerCache = new Dictionary>(); + _configManagerCache = new Dictionary Manager, DateTime LastAccessed)>(); // Disable PII logging in production for security Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = false; @@ -143,7 +139,12 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware } try { - if (!_configManagerCache.TryGetValue(tenantSpecificAuthority, out tenantConfigManager)) + if (_configManagerCache.TryGetValue(tenantSpecificAuthority, out var cached)) + { + tenantConfigManager = cached.Manager; + _configManagerCache[tenantSpecificAuthority] = (cached.Manager, DateTime.UtcNow); + } + else { var tenantMetadataAddress = $"{tenantSpecificAuthority}/.well-known/openid-configuration"; tenantConfigManager = new ConfigurationManager( @@ -156,15 +157,16 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware RefreshInterval = TimeSpan.FromHours(24) }; - if (_configManagerCache.Count < MaxCacheSize) + if (_configManagerCache.Count >= MaxCacheSize) { - _configManagerCache[tenantSpecificAuthority] = tenantConfigManager; - _logger.LogDebug("[Auth Middleware] Created new config manager for {Authority}", tenantSpecificAuthority); - } - else - { - _logger.LogWarning("[Auth Middleware] Config manager cache full ({MaxSize}). Tenant {Authority} not cached.", MaxCacheSize, tenantSpecificAuthority); + // Evict least-recently-used entry + var lruKey = _configManagerCache.MinBy(kvp => kvp.Value.LastAccessed).Key; + _configManagerCache.Remove(lruKey); + _logger.LogDebug("[Auth Middleware] Evicted LRU config manager for {Authority}", lruKey); } + + _configManagerCache[tenantSpecificAuthority] = (tenantConfigManager, DateTime.UtcNow); + _logger.LogDebug("[Auth Middleware] Created new config manager for {Authority}", tenantSpecificAuthority); } } finally