diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs index efb6dc991..c852c7843 100644 --- a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs +++ b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapter.cs @@ -82,6 +82,12 @@ protected override async Task RemoveKeyAsync(string cacheKey) catch (Exception ex) { _logger.LogError($"[IdWebCache] Connection issue encountered with Distributed cache. Currently using In Memory cache only. Error message: {ex.Message} "); + + if (_distributedCacheOptions.OnL2CacheFailure != null && _distributedCacheOptions.OnL2CacheFailure(ex)) + { + _logger.LogDebug($"[IdWebCache] DistributedCache: Retry to remove cacheKey {cacheKey}. "); + await _distributedCache.RemoveAsync(cacheKey).ConfigureAwait(false); + } } } diff --git a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapterOptions.cs b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapterOptions.cs index b15933f1c..42e202d47 100644 --- a/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapterOptions.cs +++ b/src/Microsoft.Identity.Web/TokenCacheProviders/Distributed/MsalDistributedTokenCacheAdapterOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using Microsoft.Extensions.Caching.Distributed; namespace Microsoft.Identity.Web.TokenCacheProviders.Distributed @@ -18,6 +19,17 @@ public class MsalDistributedTokenCacheAdapterOptions : DistributedCacheEntryOpti /// public long L1CacheSizeLimit { get; set; } = 500; + /// + /// Callback offered to the app to be notified when the L2 cache fails. + /// This way the app is given the possibility to act on the L2 cache, + /// for instance, in the case of Redis, to reconnect. This is left to the application as it's + /// the only one that knows about the real implementation of the L2 cache. + /// The handler should return true if the cache should try again the operation, and + /// false otherwise. When true is passed and the retry fails, an exception + /// will be thrown. + /// + public Func? OnL2CacheFailure { get; set; } + /// /// Value more than 0, less than 1, to set the In Memory (L1) cache /// expiration time values relative to the Distributed (L2) cache. diff --git a/tests/WebAppCallsWebApiCallsGraph/Client/Startup.cs b/tests/WebAppCallsWebApiCallsGraph/Client/Startup.cs index a6952921f..3a90a194e 100644 --- a/tests/WebAppCallsWebApiCallsGraph/Client/Startup.cs +++ b/tests/WebAppCallsWebApiCallsGraph/Client/Startup.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Identity.Web.UI; +using Microsoft.Identity.Web.TokenCacheProviders.Distributed; namespace WebApp_OpenIDConnect_DotNet { @@ -48,6 +49,18 @@ public void ConfigureServices(IServiceCollection services) options.Configuration = Configuration.GetConnectionString("Redis"); options.InstanceName = "RedisDemos_"; //should be unique to the app }); + services.Configure(options => + { + options.OnL2CacheFailure = (ex) => + { + if (ex is StackExchange.Redis.RedisConnectionException) + { + // action: try to reconnect or something + return true; //try to do the cache operation again + } + return false; + }; + }); #endif services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)