Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update locking and caching for OpenIdConnectCachingSecurityTokenProvider #3118

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

msbw2
Copy link
Contributor

@msbw2 msbw2 commented Oct 31, 2024

Update locking and caching for OpenIdConnectCachingSecurityTokenProvider. Fixes #3078.

@msbw2 msbw2 requested a review from a team as a code owner October 31, 2024 05:36
@msbw2 msbw2 force-pushed the brettwhite/oidcsecuritytokenprovider-cache branch from 99cfb8d to 7652290 Compare October 31, 2024 05:43
Copy link

Summary

Summary
Generated on: 10/31/2024 - 5:49:17 AM
Coverage date: 10/31/2024 - 5:49:13 AM
Parser: Cobertura
Assemblies: 9
Classes: 158
Files: 166
Line coverage: 51.4% (3177 of 6180)
Covered lines: 3177
Uncovered lines: 3003
Coverable lines: 6180
Total lines: 19826
Branch coverage: 48.4% (1137 of 2348)
Covered branches: 1137
Total branches: 2348
Method coverage: Feature is only available for sponsors

Coverage

Microsoft.Identity.Web - 63.6%
Name Line Branch
Microsoft.Identity.Web 63.6% 56.9%
Microsoft.Identity.Web.AadIssuerValidatorOptions 100%
Microsoft.Identity.Web.AccountExtensions 100% 100%
Microsoft.Identity.Web.AppContextSwitches 100% 100%
Microsoft.Identity.Web.AppServicesAuthenticationBuilderExtensions 100%
Microsoft.Identity.Web.AppServicesAuthenticationHandler 0% 0%
Microsoft.Identity.Web.AppServicesAuthenticationInformation 78.8% 59%
Microsoft.Identity.Web.AppServicesAuthenticationTokenAcquisition 0% 0%
Microsoft.Identity.Web.AuthorityHelpers 100% 92.3%
Microsoft.Identity.Web.AuthorizeForScopesAttribute 8.3% 7.1%
Microsoft.Identity.Web.AzureADB2COpenIDConnectEventHandlers 97.5% 87.5%
Microsoft.Identity.Web.AzureFunctionsAuthenticationHttpContextExtension 0% 0%
Microsoft.Identity.Web.ClaimsPrincipalFactory 100% 100%
Microsoft.Identity.Web.CookiePolicyOptionsExtensions 95.8% 90%
Microsoft.Identity.Web.DefaultMicrosoftIdentityAuthenticationDelegatingHand
lerFactory
100%
Microsoft.Identity.Web.DownstreamWebApi 10.8% 0%
Microsoft.Identity.Web.DownstreamWebApiExtensions 100%
Microsoft.Identity.Web.DownstreamWebApiGenericExtensions 0% 0%
Microsoft.Identity.Web.DownstreamWebApiOptions 94.7% 50%
Microsoft.Identity.Web.Extensions 100% 100%
Microsoft.Identity.Web.IDownstreamWebApi 0%
Microsoft.Identity.Web.IncrementalConsentAndConditionalAccessHelper 95.8% 87.5%
Microsoft.Identity.Web.MergedOptionsValidation 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppAuthenticationMessageHandler 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder
Extension
0% 0%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseMessageHandler 96% 62.5%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseOptions 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerHttpCli
entBuilderExtensions
91.3%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerOptions 90%
Microsoft.Identity.Web.MicrosoftIdentityBlazorServiceCollectionExtensions 100%
Microsoft.Identity.Web.MicrosoftIdentityConsentAndConditionalAccessHandler 4.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityServiceHandler 0%
Microsoft.Identity.Web.MicrosoftIdentityUserAuthenticationMessageHandler 100% 66.6%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilder 78.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensio
ns
62.2% 27.7%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderWithConf
iguration
100%
Microsoft.Identity.Web.MicrosoftIdentityWebApiServiceCollectionExtensions 0%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder 94.6% 83.3%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderExtensio
ns
97% 88.7%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderWithConf
iguration
81.2%
Microsoft.Identity.Web.MicrosoftIdentityWebAppServiceCollectionExtensions 100%
Microsoft.Identity.Web.PolicyBuilderExtensions 100%
Microsoft.Identity.Web.RequiredScopeExtensions 40%
Microsoft.Identity.Web.RequiredScopeOrAppPermissionExtensions 33.3%
Microsoft.Identity.Web.RequireScopeOptions 100% 50%
Microsoft.Identity.Web.RequireScopeOrAppPermissionOptions 100% 50%
Microsoft.Identity.Web.Resource.JwtBearerMiddlewareDiagnostics 100% 100%
Microsoft.Identity.Web.Resource.MicrosoftIdentityIssuerValidatorFactory 90% 62.5%
Microsoft.Identity.Web.Resource.OpenIdConnectMiddlewareDiagnostics 100% 83.3%
Microsoft.Identity.Web.Resource.RegisterValidAudience 95.6% 90%
Microsoft.Identity.Web.Resource.RequiredScopeAttribute 0%
Microsoft.Identity.Web.Resource.RequiredScopeOrAppPermissionAttribute 0%
Microsoft.Identity.Web.Resource.RolesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.Resource.ScopesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.ScopeAuthorizationHandler 90.3% 73%
Microsoft.Identity.Web.ScopeAuthorizationRequirement 100% 50%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationHandler 90.2% 72.9%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationRequirement 78.9% 60%
Microsoft.Identity.Web.TempDataLoginErrorAccessor 15% 25%
Microsoft.Identity.Web.TokenAcquisitionAppTokenCredential 0%
Microsoft.Identity.Web.TokenAcquisitionTokenCredential 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.MsalSessionTokenCachePro
vider
0% 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.SessionTokenCacheProvide
rExtension
0% 0%
Microsoft.Identity.Web.Certificate - 41.4%
Name Line Branch
Microsoft.Identity.Web.Certificate 41.4% 26.5%
Microsoft.Identity.Web.Base64EncodedCertificateLoader 90.9% 66.6%
Microsoft.Identity.Web.CertificateDescription 87.9%
Microsoft.Identity.Web.CertificateLoaderHelper 29.1% 20%
Microsoft.Identity.Web.DefaultCertificateLoader 45.6% 31.8%
Microsoft.Identity.Web.DefaultCredentialsLoader 54.7% 50%
Microsoft.Identity.Web.FromPathCertificateLoader 0%
Microsoft.Identity.Web.KeyVaultCertificateLoader 0% 0%
Microsoft.Identity.Web.SignedAssertionFilePathCredentialsLoader 93.7% 100%
Microsoft.Identity.Web.SignedAssertionFromManagedIdentityCredentialLoader 15.7% 0%
Microsoft.Identity.Web.StoreWithDistinguishedNameCertificateLoader 0%
Microsoft.Identity.Web.StoreWithThumbprintCertificateLoader 0%
Microsoft.Identity.Web.Certificateless - 40.1%
Name Line Branch
Microsoft.Identity.Web.Certificateless 40.1% 43.2%
Microsoft.Identity.Web.AzureIdentityForKubernetesClientAssertion 58.1% 63.1%
Microsoft.Identity.Web.CertificatelessOptions 100%
Microsoft.Identity.Web.ClientAssertion 100%
Microsoft.Identity.Web.ClientAssertionProviderBase 100% 80%
Microsoft.Identity.Web.ManagedIdentityClientAssertion 0% 0%
Microsoft.Identity.Web.Diagnostics - 10.2%
Name Line Branch
Microsoft.Identity.Web.Diagnostics 10.2% 7.1%
Microsoft.Identity.Web.Diagnostics.OsHelper 33.3%
Microsoft.Identity.Web.Throws 8.6% 7.1%
Microsoft.Identity.Web.DownstreamApi - 14.5%
Name Line Branch
Microsoft.Identity.Web.DownstreamApi 14.5% 16.1%
Microsoft.Extensions.Configuration.Binder.SourceGeneration 0% 0%
Microsoft.Identity.Web.DownstreamApi 15.4% 21.6%
Microsoft.Identity.Web.DownstreamApiExtensions 51.4% 80%
Microsoft.Identity.Web.DownstreamApiLoggingEventId 100%
System.Runtime.CompilerServices 0%
Microsoft.Identity.Web.MicrosoftGraph - 42%
Name Line Branch
Microsoft.Identity.Web.MicrosoftGraph 42% 4.5%
Microsoft.Identity.Web.BaseRequestExtensions 0% 0%
Microsoft.Identity.Web.GraphServiceCollectionExtensions 82.7% 50%
Microsoft.Identity.Web.MicrosoftGraphExtensions 67.7%
Microsoft.Identity.Web.MicrosoftGraphOptions 100%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider 13.5% 0%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProviderOption 16.6%
Microsoft.Identity.Web.Test.Common - 69.3%
Name Line Branch
Microsoft.Identity.Web.Test.Common 69.3% 64.5%
Microsoft.Identity.Web.Test.Common.Asserts 100%
Microsoft.Identity.Web.Test.Common.Mocks.LoggerMock`1 50% 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpClientFactory 100% 33.3%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpContextAccessor 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpCreator 60.8%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpMessageHandler 76.4% 75%
Microsoft.Identity.Web.Test.Common.Mocks.QueryStringParser 86.8% 85.7%
Microsoft.Identity.Web.Test.Common.Mocks.QueueHttpMessageHandler 57.1% 37.5%
Microsoft.Identity.Web.Test.Common.TestConstants 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.ExternalApp 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.HttpContextUtilities 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.InMemoryTokenCache 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.MsalTestTokenCacheProvider 26.6%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestMsalDistributedTokenCach
eAdapter
100%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestOptionsMonitor`1 50% 0%
Microsoft.Identity.Web.TokenAcquisition - 53.2%
Name Line Branch
Microsoft.Identity.Web.TokenAcquisition 53.2% 55.1%
Microsoft.Identity.Web.ApplicationBuilderExtensions 0%
Microsoft.Identity.Web.AuthCodeRedemptionParameters 100%
Microsoft.Identity.Web.CiamAuthorityHelper 100% 92.8%
Microsoft.Identity.Web.ClientInfo 100% 87.5%
Microsoft.Identity.Web.ConfidentialClientApplicationBuilderExtension 79% 60.5%
Microsoft.Identity.Web.ConfidentialClientApplicationOptionsMerger 100% 50%
Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider 54.9% 26.6%
Microsoft.Identity.Web.DefaultTokenAcquirerFactoryImplementation 1.6% 0%
Microsoft.Identity.Web.Experimental.CertificateChangeEventArg 0%
Microsoft.Identity.Web.Extensibility.BaseAuthorizationHeaderProvider 45.4%
Microsoft.Identity.Web.Hosts.DefaultTokenAcquisitionHost 38.4% 0%
Microsoft.Identity.Web.HttpContextExtensions 100%
Microsoft.Identity.Web.IdHelper 95.8% 50%
Microsoft.Identity.Web.Internal.WebApiBuilders 100% 100%
Microsoft.Identity.Web.ITokenAcquisition 0%
Microsoft.Identity.Web.JwtBearerOptionsMerger 0% 0%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.LoggingOptions 100%
Microsoft.Identity.Web.MergedOptions 93.3% 92%
Microsoft.Identity.Web.MergedOptionsStore 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder 83.3% 100%
Microsoft.Identity.Web.MicrosoftIdentityApplicationOptionsMerger 60% 0%
Microsoft.Identity.Web.MicrosoftIdentityBaseAuthenticationBuilder 85.1% 78.5%
Microsoft.Identity.Web.MicrosoftIdentityOptions 100% 75%
Microsoft.Identity.Web.MicrosoftIdentityOptionsMerger 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException 0%
Microsoft.Identity.Web.MsalAspNetCoreHttpClientFactory 50%
Microsoft.Identity.Web.MsAuth10AtPop 100% 100%
Microsoft.Identity.Web.PrincipalExtensionsForSecurityTokens 100% 70%
Microsoft.Identity.Web.ServiceCollectionExtensions 66.6% 64.2%
Microsoft.Identity.Web.TokenAcquirer 0% 0%
Microsoft.Identity.Web.TokenAcquirerFactory 55.4% 50%
Microsoft.Identity.Web.TokenAcquisition 22.9% 17.2%
Microsoft.Identity.Web.TokenAcquisitionAspNetCore 4.6% 0%
Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost 40.4% 23.6%
Microsoft.Identity.Web.TokenAcquisitionExtensionOptions 100% 100%
Microsoft.Identity.Web.TokenAcquisitionOptions 100%
Microsoft.Identity.Web.Util.Base64UrlHelpers 93.4% 81.4%
Microsoft.Identity.Web.TokenCache - 80.8%
Name Line Branch
Microsoft.Identity.Web.TokenCache 80.8% 82.6%
Microsoft.Identity.Web.ClaimsPrincipalExtensions 96.7% 100%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.TokenCacheExtensions 100%
Microsoft.Identity.Web.TokenCacheProviders.CacheSerializerHints 100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.DistributedTokenCach
eAdapterExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapter
83% 83.3%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapterOptions
88.8%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.InMemoryTokenCacheProvi
derExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCacheOpt
ions
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCachePro
vider
83.8% 81.8%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult 100%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult`1 0%
Microsoft.Identity.Web.TokenCacheProviders.MsalAbstractTokenCacheProvider 50.7% 66.6%
Microsoft.Identity.Web.TokenCacheProviders.Utility 54.5%

Copy link

Summary

Summary
Generated on: 10/31/2024 - 5:56:04 AM
Coverage date: 10/31/2024 - 5:56:01 AM
Parser: Cobertura
Assemblies: 9
Classes: 158
Files: 166
Line coverage: 51.4% (3177 of 6180)
Covered lines: 3177
Uncovered lines: 3003
Coverable lines: 6180
Total lines: 19826
Branch coverage: 48.4% (1137 of 2348)
Covered branches: 1137
Total branches: 2348
Method coverage: Feature is only available for sponsors

Coverage

Microsoft.Identity.Web - 63.6%
Name Line Branch
Microsoft.Identity.Web 63.6% 56.9%
Microsoft.Identity.Web.AadIssuerValidatorOptions 100%
Microsoft.Identity.Web.AccountExtensions 100% 100%
Microsoft.Identity.Web.AppContextSwitches 100% 100%
Microsoft.Identity.Web.AppServicesAuthenticationBuilderExtensions 100%
Microsoft.Identity.Web.AppServicesAuthenticationHandler 0% 0%
Microsoft.Identity.Web.AppServicesAuthenticationInformation 78.8% 59%
Microsoft.Identity.Web.AppServicesAuthenticationTokenAcquisition 0% 0%
Microsoft.Identity.Web.AuthorityHelpers 100% 92.3%
Microsoft.Identity.Web.AuthorizeForScopesAttribute 8.3% 7.1%
Microsoft.Identity.Web.AzureADB2COpenIDConnectEventHandlers 97.5% 87.5%
Microsoft.Identity.Web.AzureFunctionsAuthenticationHttpContextExtension 0% 0%
Microsoft.Identity.Web.ClaimsPrincipalFactory 100% 100%
Microsoft.Identity.Web.CookiePolicyOptionsExtensions 95.8% 90%
Microsoft.Identity.Web.DefaultMicrosoftIdentityAuthenticationDelegatingHand
lerFactory
100%
Microsoft.Identity.Web.DownstreamWebApi 10.8% 0%
Microsoft.Identity.Web.DownstreamWebApiExtensions 100%
Microsoft.Identity.Web.DownstreamWebApiGenericExtensions 0% 0%
Microsoft.Identity.Web.DownstreamWebApiOptions 94.7% 50%
Microsoft.Identity.Web.Extensions 100% 100%
Microsoft.Identity.Web.IDownstreamWebApi 0%
Microsoft.Identity.Web.IncrementalConsentAndConditionalAccessHelper 95.8% 87.5%
Microsoft.Identity.Web.MergedOptionsValidation 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppAuthenticationMessageHandler 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder
Extension
0% 0%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseMessageHandler 96% 62.5%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseOptions 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerHttpCli
entBuilderExtensions
91.3%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerOptions 90%
Microsoft.Identity.Web.MicrosoftIdentityBlazorServiceCollectionExtensions 100%
Microsoft.Identity.Web.MicrosoftIdentityConsentAndConditionalAccessHandler 4.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityServiceHandler 0%
Microsoft.Identity.Web.MicrosoftIdentityUserAuthenticationMessageHandler 100% 66.6%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilder 78.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensio
ns
62.2% 27.7%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderWithConf
iguration
100%
Microsoft.Identity.Web.MicrosoftIdentityWebApiServiceCollectionExtensions 0%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder 94.6% 83.3%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderExtensio
ns
97% 88.7%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderWithConf
iguration
81.2%
Microsoft.Identity.Web.MicrosoftIdentityWebAppServiceCollectionExtensions 100%
Microsoft.Identity.Web.PolicyBuilderExtensions 100%
Microsoft.Identity.Web.RequiredScopeExtensions 40%
Microsoft.Identity.Web.RequiredScopeOrAppPermissionExtensions 33.3%
Microsoft.Identity.Web.RequireScopeOptions 100% 50%
Microsoft.Identity.Web.RequireScopeOrAppPermissionOptions 100% 50%
Microsoft.Identity.Web.Resource.JwtBearerMiddlewareDiagnostics 100% 100%
Microsoft.Identity.Web.Resource.MicrosoftIdentityIssuerValidatorFactory 90% 62.5%
Microsoft.Identity.Web.Resource.OpenIdConnectMiddlewareDiagnostics 100% 83.3%
Microsoft.Identity.Web.Resource.RegisterValidAudience 95.6% 90%
Microsoft.Identity.Web.Resource.RequiredScopeAttribute 0%
Microsoft.Identity.Web.Resource.RequiredScopeOrAppPermissionAttribute 0%
Microsoft.Identity.Web.Resource.RolesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.Resource.ScopesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.ScopeAuthorizationHandler 90.3% 73%
Microsoft.Identity.Web.ScopeAuthorizationRequirement 100% 50%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationHandler 90.2% 72.9%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationRequirement 78.9% 60%
Microsoft.Identity.Web.TempDataLoginErrorAccessor 15% 25%
Microsoft.Identity.Web.TokenAcquisitionAppTokenCredential 0%
Microsoft.Identity.Web.TokenAcquisitionTokenCredential 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.MsalSessionTokenCachePro
vider
0% 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.SessionTokenCacheProvide
rExtension
0% 0%
Microsoft.Identity.Web.Certificate - 41.4%
Name Line Branch
Microsoft.Identity.Web.Certificate 41.4% 26.5%
Microsoft.Identity.Web.Base64EncodedCertificateLoader 90.9% 66.6%
Microsoft.Identity.Web.CertificateDescription 87.9%
Microsoft.Identity.Web.CertificateLoaderHelper 29.1% 20%
Microsoft.Identity.Web.DefaultCertificateLoader 45.6% 31.8%
Microsoft.Identity.Web.DefaultCredentialsLoader 54.7% 50%
Microsoft.Identity.Web.FromPathCertificateLoader 0%
Microsoft.Identity.Web.KeyVaultCertificateLoader 0% 0%
Microsoft.Identity.Web.SignedAssertionFilePathCredentialsLoader 93.7% 100%
Microsoft.Identity.Web.SignedAssertionFromManagedIdentityCredentialLoader 15.7% 0%
Microsoft.Identity.Web.StoreWithDistinguishedNameCertificateLoader 0%
Microsoft.Identity.Web.StoreWithThumbprintCertificateLoader 0%
Microsoft.Identity.Web.Certificateless - 40.1%
Name Line Branch
Microsoft.Identity.Web.Certificateless 40.1% 43.2%
Microsoft.Identity.Web.AzureIdentityForKubernetesClientAssertion 58.1% 63.1%
Microsoft.Identity.Web.CertificatelessOptions 100%
Microsoft.Identity.Web.ClientAssertion 100%
Microsoft.Identity.Web.ClientAssertionProviderBase 100% 80%
Microsoft.Identity.Web.ManagedIdentityClientAssertion 0% 0%
Microsoft.Identity.Web.Diagnostics - 10.2%
Name Line Branch
Microsoft.Identity.Web.Diagnostics 10.2% 7.1%
Microsoft.Identity.Web.Diagnostics.OsHelper 33.3%
Microsoft.Identity.Web.Throws 8.6% 7.1%
Microsoft.Identity.Web.DownstreamApi - 14.5%
Name Line Branch
Microsoft.Identity.Web.DownstreamApi 14.5% 16.1%
Microsoft.Extensions.Configuration.Binder.SourceGeneration 0% 0%
Microsoft.Identity.Web.DownstreamApi 15.4% 21.6%
Microsoft.Identity.Web.DownstreamApiExtensions 51.4% 80%
Microsoft.Identity.Web.DownstreamApiLoggingEventId 100%
System.Runtime.CompilerServices 0%
Microsoft.Identity.Web.MicrosoftGraph - 42%
Name Line Branch
Microsoft.Identity.Web.MicrosoftGraph 42% 4.5%
Microsoft.Identity.Web.BaseRequestExtensions 0% 0%
Microsoft.Identity.Web.GraphServiceCollectionExtensions 82.7% 50%
Microsoft.Identity.Web.MicrosoftGraphExtensions 67.7%
Microsoft.Identity.Web.MicrosoftGraphOptions 100%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider 13.5% 0%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProviderOption 16.6%
Microsoft.Identity.Web.Test.Common - 69.3%
Name Line Branch
Microsoft.Identity.Web.Test.Common 69.3% 64.5%
Microsoft.Identity.Web.Test.Common.Asserts 100%
Microsoft.Identity.Web.Test.Common.Mocks.LoggerMock`1 50% 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpClientFactory 100% 33.3%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpContextAccessor 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpCreator 60.8%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpMessageHandler 76.4% 75%
Microsoft.Identity.Web.Test.Common.Mocks.QueryStringParser 86.8% 85.7%
Microsoft.Identity.Web.Test.Common.Mocks.QueueHttpMessageHandler 57.1% 37.5%
Microsoft.Identity.Web.Test.Common.TestConstants 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.ExternalApp 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.HttpContextUtilities 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.InMemoryTokenCache 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.MsalTestTokenCacheProvider 26.6%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestMsalDistributedTokenCach
eAdapter
100%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestOptionsMonitor`1 50% 0%
Microsoft.Identity.Web.TokenAcquisition - 53.2%
Name Line Branch
Microsoft.Identity.Web.TokenAcquisition 53.2% 55.1%
Microsoft.Identity.Web.ApplicationBuilderExtensions 0%
Microsoft.Identity.Web.AuthCodeRedemptionParameters 100%
Microsoft.Identity.Web.CiamAuthorityHelper 100% 92.8%
Microsoft.Identity.Web.ClientInfo 100% 87.5%
Microsoft.Identity.Web.ConfidentialClientApplicationBuilderExtension 79% 60.5%
Microsoft.Identity.Web.ConfidentialClientApplicationOptionsMerger 100% 50%
Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider 54.9% 26.6%
Microsoft.Identity.Web.DefaultTokenAcquirerFactoryImplementation 1.6% 0%
Microsoft.Identity.Web.Experimental.CertificateChangeEventArg 0%
Microsoft.Identity.Web.Extensibility.BaseAuthorizationHeaderProvider 45.4%
Microsoft.Identity.Web.Hosts.DefaultTokenAcquisitionHost 38.4% 0%
Microsoft.Identity.Web.HttpContextExtensions 100%
Microsoft.Identity.Web.IdHelper 95.8% 50%
Microsoft.Identity.Web.Internal.WebApiBuilders 100% 100%
Microsoft.Identity.Web.ITokenAcquisition 0%
Microsoft.Identity.Web.JwtBearerOptionsMerger 0% 0%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.LoggingOptions 100%
Microsoft.Identity.Web.MergedOptions 93.3% 92%
Microsoft.Identity.Web.MergedOptionsStore 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder 83.3% 100%
Microsoft.Identity.Web.MicrosoftIdentityApplicationOptionsMerger 60% 0%
Microsoft.Identity.Web.MicrosoftIdentityBaseAuthenticationBuilder 85.1% 78.5%
Microsoft.Identity.Web.MicrosoftIdentityOptions 100% 75%
Microsoft.Identity.Web.MicrosoftIdentityOptionsMerger 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException 0%
Microsoft.Identity.Web.MsalAspNetCoreHttpClientFactory 50%
Microsoft.Identity.Web.MsAuth10AtPop 100% 100%
Microsoft.Identity.Web.PrincipalExtensionsForSecurityTokens 100% 70%
Microsoft.Identity.Web.ServiceCollectionExtensions 66.6% 64.2%
Microsoft.Identity.Web.TokenAcquirer 0% 0%
Microsoft.Identity.Web.TokenAcquirerFactory 55.4% 50%
Microsoft.Identity.Web.TokenAcquisition 22.9% 17.2%
Microsoft.Identity.Web.TokenAcquisitionAspNetCore 4.6% 0%
Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost 40.4% 23.6%
Microsoft.Identity.Web.TokenAcquisitionExtensionOptions 100% 100%
Microsoft.Identity.Web.TokenAcquisitionOptions 100%
Microsoft.Identity.Web.Util.Base64UrlHelpers 93.4% 81.4%
Microsoft.Identity.Web.TokenCache - 80.8%
Name Line Branch
Microsoft.Identity.Web.TokenCache 80.8% 82.6%
Microsoft.Identity.Web.ClaimsPrincipalExtensions 96.7% 100%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.TokenCacheExtensions 100%
Microsoft.Identity.Web.TokenCacheProviders.CacheSerializerHints 100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.DistributedTokenCach
eAdapterExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapter
83% 83.3%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapterOptions
88.8%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.InMemoryTokenCacheProvi
derExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCacheOpt
ions
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCachePro
vider
83.8% 81.8%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult 100%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult`1 0%
Microsoft.Identity.Web.TokenCacheProviders.MsalAbstractTokenCacheProvider 50.7% 66.6%
Microsoft.Identity.Web.TokenCacheProviders.Utility 54.5%

@jmprieur
Copy link
Collaborator

jmprieur commented Nov 1, 2024

@alex-set and @mderriey.
Do you want to try this branch and check if your issues are fixed?

{
_synclock.EnterWriteLock();
try
_syncLock.EnterReadLock();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't ConfigurationManagerBase already control the threadings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for reading/writing the fields in this class. This is to ensure they aren't changed in the middle of reading leading to unexpected behavior. The ConfigurationManager handles all the threading for making the actual metadata refresh call as needed internally, but that won't help here unless we change this to not cache the issuer and keys and instead just always call into the config manager (e.g., public string? Issuer => _configManager.GetConfigurationAsync().Result.Issuer), but it's my understanding that that is the issue being reported in the first place.

Copy link

github-actions bot commented Nov 1, 2024

Summary

Summary
Generated on: 11/1/2024 - 3:32:57 AM
Coverage date: 11/1/2024 - 3:32:54 AM
Parser: Cobertura
Assemblies: 9
Classes: 158
Files: 166
Line coverage: 51.4% (3177 of 6180)
Covered lines: 3177
Uncovered lines: 3003
Coverable lines: 6180
Total lines: 19826
Branch coverage: 48.4% (1137 of 2348)
Covered branches: 1137
Total branches: 2348
Method coverage: Feature is only available for sponsors

Coverage

Microsoft.Identity.Web - 63.6%
Name Line Branch
Microsoft.Identity.Web 63.6% 56.9%
Microsoft.Identity.Web.AadIssuerValidatorOptions 100%
Microsoft.Identity.Web.AccountExtensions 100% 100%
Microsoft.Identity.Web.AppContextSwitches 100% 100%
Microsoft.Identity.Web.AppServicesAuthenticationBuilderExtensions 100%
Microsoft.Identity.Web.AppServicesAuthenticationHandler 0% 0%
Microsoft.Identity.Web.AppServicesAuthenticationInformation 78.8% 59%
Microsoft.Identity.Web.AppServicesAuthenticationTokenAcquisition 0% 0%
Microsoft.Identity.Web.AuthorityHelpers 100% 92.3%
Microsoft.Identity.Web.AuthorizeForScopesAttribute 8.3% 7.1%
Microsoft.Identity.Web.AzureADB2COpenIDConnectEventHandlers 97.5% 87.5%
Microsoft.Identity.Web.AzureFunctionsAuthenticationHttpContextExtension 0% 0%
Microsoft.Identity.Web.ClaimsPrincipalFactory 100% 100%
Microsoft.Identity.Web.CookiePolicyOptionsExtensions 95.8% 90%
Microsoft.Identity.Web.DefaultMicrosoftIdentityAuthenticationDelegatingHand
lerFactory
100%
Microsoft.Identity.Web.DownstreamWebApi 10.8% 0%
Microsoft.Identity.Web.DownstreamWebApiExtensions 100%
Microsoft.Identity.Web.DownstreamWebApiGenericExtensions 0% 0%
Microsoft.Identity.Web.DownstreamWebApiOptions 94.7% 50%
Microsoft.Identity.Web.Extensions 100% 100%
Microsoft.Identity.Web.IDownstreamWebApi 0%
Microsoft.Identity.Web.IncrementalConsentAndConditionalAccessHelper 95.8% 87.5%
Microsoft.Identity.Web.MergedOptionsValidation 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppAuthenticationMessageHandler 100% 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder
Extension
0% 0%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseMessageHandler 96% 62.5%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationBaseOptions 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerHttpCli
entBuilderExtensions
91.3%
Microsoft.Identity.Web.MicrosoftIdentityAuthenticationMessageHandlerOptions 90%
Microsoft.Identity.Web.MicrosoftIdentityBlazorServiceCollectionExtensions 100%
Microsoft.Identity.Web.MicrosoftIdentityConsentAndConditionalAccessHandler 4.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityServiceHandler 0%
Microsoft.Identity.Web.MicrosoftIdentityUserAuthenticationMessageHandler 100% 66.6%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilder 78.9% 0%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensio
ns
62.2% 27.7%
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderWithConf
iguration
100%
Microsoft.Identity.Web.MicrosoftIdentityWebApiServiceCollectionExtensions 0%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder 94.6% 83.3%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderExtensio
ns
97% 88.7%
Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderWithConf
iguration
81.2%
Microsoft.Identity.Web.MicrosoftIdentityWebAppServiceCollectionExtensions 100%
Microsoft.Identity.Web.PolicyBuilderExtensions 100%
Microsoft.Identity.Web.RequiredScopeExtensions 40%
Microsoft.Identity.Web.RequiredScopeOrAppPermissionExtensions 33.3%
Microsoft.Identity.Web.RequireScopeOptions 100% 50%
Microsoft.Identity.Web.RequireScopeOrAppPermissionOptions 100% 50%
Microsoft.Identity.Web.Resource.JwtBearerMiddlewareDiagnostics 100% 100%
Microsoft.Identity.Web.Resource.MicrosoftIdentityIssuerValidatorFactory 90% 62.5%
Microsoft.Identity.Web.Resource.OpenIdConnectMiddlewareDiagnostics 100% 83.3%
Microsoft.Identity.Web.Resource.RegisterValidAudience 95.6% 90%
Microsoft.Identity.Web.Resource.RequiredScopeAttribute 0%
Microsoft.Identity.Web.Resource.RequiredScopeOrAppPermissionAttribute 0%
Microsoft.Identity.Web.Resource.RolesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.Resource.ScopesRequiredHttpContextExtensions 100% 100%
Microsoft.Identity.Web.ScopeAuthorizationHandler 90.3% 73%
Microsoft.Identity.Web.ScopeAuthorizationRequirement 100% 50%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationHandler 90.2% 72.9%
Microsoft.Identity.Web.ScopeOrAppPermissionAuthorizationRequirement 78.9% 60%
Microsoft.Identity.Web.TempDataLoginErrorAccessor 15% 25%
Microsoft.Identity.Web.TokenAcquisitionAppTokenCredential 0%
Microsoft.Identity.Web.TokenAcquisitionTokenCredential 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.MsalSessionTokenCachePro
vider
0% 0%
Microsoft.Identity.Web.TokenCacheProviders.Session.SessionTokenCacheProvide
rExtension
0% 0%
Microsoft.Identity.Web.Certificate - 41.4%
Name Line Branch
Microsoft.Identity.Web.Certificate 41.4% 26.5%
Microsoft.Identity.Web.Base64EncodedCertificateLoader 90.9% 66.6%
Microsoft.Identity.Web.CertificateDescription 87.9%
Microsoft.Identity.Web.CertificateLoaderHelper 29.1% 20%
Microsoft.Identity.Web.DefaultCertificateLoader 45.6% 31.8%
Microsoft.Identity.Web.DefaultCredentialsLoader 54.7% 50%
Microsoft.Identity.Web.FromPathCertificateLoader 0%
Microsoft.Identity.Web.KeyVaultCertificateLoader 0% 0%
Microsoft.Identity.Web.SignedAssertionFilePathCredentialsLoader 93.7% 100%
Microsoft.Identity.Web.SignedAssertionFromManagedIdentityCredentialLoader 15.7% 0%
Microsoft.Identity.Web.StoreWithDistinguishedNameCertificateLoader 0%
Microsoft.Identity.Web.StoreWithThumbprintCertificateLoader 0%
Microsoft.Identity.Web.Certificateless - 40.1%
Name Line Branch
Microsoft.Identity.Web.Certificateless 40.1% 43.2%
Microsoft.Identity.Web.AzureIdentityForKubernetesClientAssertion 58.1% 63.1%
Microsoft.Identity.Web.CertificatelessOptions 100%
Microsoft.Identity.Web.ClientAssertion 100%
Microsoft.Identity.Web.ClientAssertionProviderBase 100% 80%
Microsoft.Identity.Web.ManagedIdentityClientAssertion 0% 0%
Microsoft.Identity.Web.Diagnostics - 10.2%
Name Line Branch
Microsoft.Identity.Web.Diagnostics 10.2% 7.1%
Microsoft.Identity.Web.Diagnostics.OsHelper 33.3%
Microsoft.Identity.Web.Throws 8.6% 7.1%
Microsoft.Identity.Web.DownstreamApi - 14.5%
Name Line Branch
Microsoft.Identity.Web.DownstreamApi 14.5% 16.1%
Microsoft.Extensions.Configuration.Binder.SourceGeneration 0% 0%
Microsoft.Identity.Web.DownstreamApi 15.4% 21.6%
Microsoft.Identity.Web.DownstreamApiExtensions 51.4% 80%
Microsoft.Identity.Web.DownstreamApiLoggingEventId 100%
System.Runtime.CompilerServices 0%
Microsoft.Identity.Web.MicrosoftGraph - 42%
Name Line Branch
Microsoft.Identity.Web.MicrosoftGraph 42% 4.5%
Microsoft.Identity.Web.BaseRequestExtensions 0% 0%
Microsoft.Identity.Web.GraphServiceCollectionExtensions 82.7% 50%
Microsoft.Identity.Web.MicrosoftGraphExtensions 67.7%
Microsoft.Identity.Web.MicrosoftGraphOptions 100%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider 13.5% 0%
Microsoft.Identity.Web.TokenAcquisitionAuthenticationProviderOption 16.6%
Microsoft.Identity.Web.Test.Common - 69.3%
Name Line Branch
Microsoft.Identity.Web.Test.Common 69.3% 64.5%
Microsoft.Identity.Web.Test.Common.Asserts 100%
Microsoft.Identity.Web.Test.Common.Mocks.LoggerMock`1 50% 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpClientFactory 100% 33.3%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpContextAccessor 100%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpCreator 60.8%
Microsoft.Identity.Web.Test.Common.Mocks.MockHttpMessageHandler 76.4% 75%
Microsoft.Identity.Web.Test.Common.Mocks.QueryStringParser 86.8% 85.7%
Microsoft.Identity.Web.Test.Common.Mocks.QueueHttpMessageHandler 57.1% 37.5%
Microsoft.Identity.Web.Test.Common.TestConstants 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.ExternalApp 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.HttpContextUtilities 100%
Microsoft.Identity.Web.Test.Common.TestHelpers.InMemoryTokenCache 0% 0%
Microsoft.Identity.Web.Test.Common.TestHelpers.MsalTestTokenCacheProvider 26.6%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestMsalDistributedTokenCach
eAdapter
100%
Microsoft.Identity.Web.Test.Common.TestHelpers.TestOptionsMonitor`1 50% 0%
Microsoft.Identity.Web.TokenAcquisition - 53.2%
Name Line Branch
Microsoft.Identity.Web.TokenAcquisition 53.2% 55.1%
Microsoft.Identity.Web.ApplicationBuilderExtensions 0%
Microsoft.Identity.Web.AuthCodeRedemptionParameters 100%
Microsoft.Identity.Web.CiamAuthorityHelper 100% 92.8%
Microsoft.Identity.Web.ClientInfo 100% 87.5%
Microsoft.Identity.Web.ConfidentialClientApplicationBuilderExtension 79% 60.5%
Microsoft.Identity.Web.ConfidentialClientApplicationOptionsMerger 100% 50%
Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider 54.9% 26.6%
Microsoft.Identity.Web.DefaultTokenAcquirerFactoryImplementation 1.6% 0%
Microsoft.Identity.Web.Experimental.CertificateChangeEventArg 0%
Microsoft.Identity.Web.Extensibility.BaseAuthorizationHeaderProvider 45.4%
Microsoft.Identity.Web.Hosts.DefaultTokenAcquisitionHost 38.4% 0%
Microsoft.Identity.Web.HttpContextExtensions 100%
Microsoft.Identity.Web.IdHelper 95.8% 50%
Microsoft.Identity.Web.Internal.WebApiBuilders 100% 100%
Microsoft.Identity.Web.ITokenAcquisition 0%
Microsoft.Identity.Web.JwtBearerOptionsMerger 0% 0%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.LoggingOptions 100%
Microsoft.Identity.Web.MergedOptions 93.3% 92%
Microsoft.Identity.Web.MergedOptionsStore 100%
Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder 83.3% 100%
Microsoft.Identity.Web.MicrosoftIdentityApplicationOptionsMerger 60% 0%
Microsoft.Identity.Web.MicrosoftIdentityBaseAuthenticationBuilder 85.1% 78.5%
Microsoft.Identity.Web.MicrosoftIdentityOptions 100% 75%
Microsoft.Identity.Web.MicrosoftIdentityOptionsMerger 100% 50%
Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException 0%
Microsoft.Identity.Web.MsalAspNetCoreHttpClientFactory 50%
Microsoft.Identity.Web.MsAuth10AtPop 100% 100%
Microsoft.Identity.Web.PrincipalExtensionsForSecurityTokens 100% 70%
Microsoft.Identity.Web.ServiceCollectionExtensions 66.6% 64.2%
Microsoft.Identity.Web.TokenAcquirer 0% 0%
Microsoft.Identity.Web.TokenAcquirerFactory 55.4% 50%
Microsoft.Identity.Web.TokenAcquisition 22.9% 17.2%
Microsoft.Identity.Web.TokenAcquisitionAspNetCore 4.6% 0%
Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost 40.4% 23.6%
Microsoft.Identity.Web.TokenAcquisitionExtensionOptions 100% 100%
Microsoft.Identity.Web.TokenAcquisitionOptions 100%
Microsoft.Identity.Web.Util.Base64UrlHelpers 93.4% 81.4%
Microsoft.Identity.Web.TokenCache - 80.8%
Name Line Branch
Microsoft.Identity.Web.TokenCache 80.8% 82.6%
Microsoft.Identity.Web.ClaimsPrincipalExtensions 96.7% 100%
Microsoft.Identity.Web.LoggingEventId 100%
Microsoft.Identity.Web.TokenCacheExtensions 100%
Microsoft.Identity.Web.TokenCacheProviders.CacheSerializerHints 100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.DistributedTokenCach
eAdapterExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapter
83% 83.3%
Microsoft.Identity.Web.TokenCacheProviders.Distributed.MsalDistributedToken
CacheAdapterOptions
88.8%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.InMemoryTokenCacheProvi
derExtension
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCacheOpt
ions
100%
Microsoft.Identity.Web.TokenCacheProviders.InMemory.MsalMemoryTokenCachePro
vider
83.8% 81.8%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult 100%
Microsoft.Identity.Web.TokenCacheProviders.MeasureDurationResult`1 0%
Microsoft.Identity.Web.TokenCacheProviders.MsalAbstractTokenCacheProvider 50.7% 66.6%
Microsoft.Identity.Web.TokenCacheProviders.Utility 54.5%

Copy link

@mderriey mderriey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer: I don't have much experience with multi-threading programming, locking, or synchronization.

First, thanks a lot for looking at this quickly after the issue was reported, it's much appreciated.

Overall, I think this version will improve the situation, since it heavily reduces the number of times we'll enter in a write lock.

I still have some concerns / questions.

Read lock

This is to ensure they aren't changed in the middle of reading leading to unexpected behavior.

I was thinking about this; is there a possibility that a thread reads the value of a variable while it's being written to by another thread, leading to a corrupt value being assigned?

I was under the impression that both variable assignment and read are atomic operations in .NET.

If they are, I'd question the need for a read lock altogether.

Caching

I see this is managing a cache of its own through syncAfter to minimize the number of times we're calling _configManager.GetConfigurationAsync.

It feels to me that we're trying to recreate what ConfigurationManager already implements, since most calls to GetConfigurationAsync will return cached data.

Kind of related, @msbw2 also mentioned (emphasis mine):

[...] won't help here unless we change this to not cache the issuer and keys and instead just always call into the config manager (e.g., public string? Issuer => _configManager.GetConfigurationAsync().Result.Issuer), but it's my understanding that that is the issue being reported in the first place.

I don't think that's what @alex-set and myself reported. The root issue is that under sufficient sustained concurrency, the original code leads to deadlock-style situations because of excessive locking and unbound synchronization, in the sense that threads will wait forever to acquire a read or write lock, without a timeout.

Calling GetConfigurationAsync on a threadpool thread every time is not ideal, I agree. However I'm not entirely sold on building our own cache on top of ConfigurationManager to avoid it.

Proposal / Request for review

We had to push a hand-rolled fix for this, and landed on a solution that has the following characteristics:

  1. Lightweight write lock through Interlocked.Exchange.
  2. If a thread can't acquire a write lock, it will not wait, and will return the current issuer and signing keys.
  3. No read lock.

The rationale behind this is that:

  1. My understanding is that for a given tenant, the issuer never changes.
  2. New signing keys are advertised way before they're being actually used to sign new tokens.

Here's the implementation (let me know if you'd like me to open a PR for a more practical discussion):

Click to expand
public class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
{
    private int _lock = 0;

    public ConfigurationManager<OpenIdConnectConfiguration> _configManager;
    private string _issuer;
    private IEnumerable<SecurityKey> _keys;

    public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
    {
        _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());

        RetrieveMetadata();
    }

    /// <summary>
    /// Gets the issuer the credentials are for.
    /// </summary>
    /// <value>
    /// The issuer the credentials are for.
    /// </value>
    public string Issuer
    {
        get
        {
            RetrieveMetadata();
            return _issuer;
        }
    }

    /// <summary>
    /// Gets all known security keys.
    /// </summary>
    /// <value>
    /// All known security keys.
    /// </value>
    public IEnumerable<SecurityKey> SecurityKeys
    {
        get
        {
            RetrieveMetadata();
            return _keys;
        }
    }

    private void RetrieveMetadata()
    {
        // Try to acquire the lock
        //
        // Interlocked.Exchange returns the original value of _lock before it was swapped.
        // If it's 0, it means it went from 0 to 1, so we did acquire the lock.
        // If it's 1, then the lock was already acquired by another thread.
        //
        // See the example in the Exchange(Int32, Int32) overload: https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.exchange?view=netframework-4.7.2
        if (Interlocked.Exchange(ref _lock, 1) == 0)
        {
            OpenIdConnectConfiguration config = Task.Run(_configManager.GetConfigurationAsync).Result;
            _issuer = config.Issuer;
            _keys = config.SigningKeys;

            // Release the lock
            Interlocked.Exchange(ref _lock, 0);
        }
    }
}

It's still early days, but we've been running this in production for a few days now, it's been OK under concurrency levels that brought the application down before.

I'm super interested in your opinion, feel free to point flaws / gaps in this implementation.

@msbw2
Copy link
Contributor Author

msbw2 commented Nov 1, 2024

Disclaimer: I don't have much experience with multi-threading programming, locking, or synchronization.

Likewise.

is there a possibility that a thread reads the value of a variable while it's being written to by another thread, leading to a corrupt value being assigned?
I was under the impression that both variable assignment and read are atomic operations in .NET.
If they are, I'd question the need for a read lock altogether.

Looking into this it looks like reading and writing for both _issuer and _keys is not atomic based on this StackOverflow post. string? and IEnumerable<SecurityKey>/ICollection<SecurityKey> would not fit into the <=32-bit size category that's guaranteed to be atomic.

It feels to me that we're trying to recreate what ConfigurationManager already implements, since most calls to GetConfigurationAsync will return cached data.

Yes basically.

The root issue is that under sufficient sustained concurrency, the original code leads to deadlock-style situations because of excessive locking and unbound synchronization, in the sense that threads will wait forever to acquire a read or write lock, without a timeout.

I see what you mean. Tying this back to the question of atomicity, I think we need some lock.

Calling GetConfigurationAsync on a threadpool thread every time is not ideal, I agree. However I'm not entirely sold on building our own cache on top of ConfigurationManager to avoid it.

Sure. My change was mainly in the spirit of the intent behind the original implementation, but I do agree with what you're stating. It makes sense that the ConfigurationManager should manage the configuration and not this class.

Proposal / Request for review

We had to push a hand-rolled fix for this, and landed on a solution that has the following characteristics:

  1. Lightweight write lock through Interlocked.Exchange.
  2. If a thread can't acquire a write lock, it will not wait, and will return the current issuer and signing keys.
  3. No read lock.

The rationale behind this is that:

  1. My understanding is that for a given tenant, the issuer never changes.
  2. New signing keys are advertised way before they're being actually used to sign new tokens.

The rationales make sense. I'm just unsure on the atomicity of reads and writes since the StackOverflow post seems to suggest it's not guaranteed in this case hence a read and write lock is necessary.

@msbw2
Copy link
Contributor Author

msbw2 commented Nov 1, 2024

is there a possibility that a thread reads the value of a variable while it's being written to by another thread, leading to a corrupt value being assigned?
I was under the impression that both variable assignment and read are atomic operations in .NET.
If they are, I'd question the need for a read lock altogether.

Looking into this it looks like reading and writing for both _issuer and _keys is not atomic based on this StackOverflow post. string? and IEnumerable<SecurityKey>/ICollection<SecurityKey> would not fit into the <=32-bit size category that's guaranteed to be atomic.

Never mind. All of these are reference types, and the references should fit into a native word, so the locks aren't necessary. Your implementation is what I would do to remove the locks, so you can create a PR with your changes. Thanks @keegan-caruso for pointing out the reference types and sizes.

@mderriey
Copy link

mderriey commented Nov 2, 2024

is there a possibility that a thread reads the value of a variable while it's being written to by another thread, leading to a corrupt value being assigned?
I was under the impression that both variable assignment and read are atomic operations in .NET.
If they are, I'd question the need for a read lock altogether.

Looking into this it looks like reading and writing for both _issuer and _keys is not atomic based on this StackOverflow post. string? and IEnumerable<SecurityKey>/ICollection<SecurityKey> would not fit into the <=32-bit size category that's guaranteed to be atomic.

Never mind. All of these are reference types, and the references should fit into a native word, so the locks aren't necessary. Your implementation is what I would do to remove the locks, so you can create a PR with your changes. Thanks @keegan-caruso for pointing out the reference types and sizes.

Thanks a lot for verifying this, it's much appreciated.

I opened a PR at #3124.

@alex-set
Copy link

alex-set commented Nov 2, 2024

To be clear, I think the biggest problem here is that ConfigurationManager is an async class and the IIssuerSecurityKeyProvider interface to which the OpenIdConnectCachingSecurityTokenProvider has to adhere is not async. If the interface had async get methods, this would be a non-issue. Under the current, larger design, no matter how you get this class to work, it will violate someone's rules for task concurrency. It's probably not realistic to address those issues in the short term, so I'll go back to talking about this specific class.

I agree with everything @mderriey stated. Though one thing that gives me hesitation about the implementation he suggests is the use of Task.Run in RetrieveMetadata. It would mean that every call to either of the properties will put work on a separate thread even though the called async method will very rarely ever do any async work since it usually just returns the cached config. However, the passive locking on calling the task ensures that it will never enter the thread starvation issue from which I'm still traumatized.

Here's what I used and it's been working fine for us:

    internal class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
    {
        private readonly ConfigurationManager<OpenIdConnectConfiguration> _configManager;
        private readonly Task<OpenIdConnectConfiguration> _initialRetrieval;

        public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
        {
            _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                metadataEndpoint, new OpenIdConnectConfigurationRetriever());

            _initialRetrieval = Task.Run(_configManager.GetConfigurationAsync);
        }

        public string? Issuer => GetConfig().Issuer;

        public IEnumerable<SecurityKey>? SecurityKeys => GetConfig().SigningKeys;

        private OpenIdConnectConfiguration GetConfig()
        {
            if (!_initialRetrieval.IsCompleted)
            {
#pragma warning disable VSTHRD002
                return _initialRetrieval.Result;
            }

            return _configManager.GetConfigurationAsync().Result;
#pragma warning restore VSTHRD002
        }
    }

One thing that's important to note is that the ConfigManager.GetConfigurationAsync method will only ever do async task work during the initial config retrieval. All other calls to the method will return the current config, though if it's time to refresh the config, it will start that on a separate thread, but it will still return the existing config rather than waiting for the new one. This means that GetConfigurationAsync will always returns a completed task after the first call to it completes. So when the class is constructed, we can just preserve the initial retrieval task. When the config is needed later, if the initial task is still running, we know that we have to wait for that to complete. If that task has already completed, we can just call GetConfigurationAsync again and know that it will return a completed task from which can just return the result.

The risk here is that it depends on the internal functionality of the ConfigurationManager and I don't think there's a guarantee that it wouldn't change in future versions. If it did change and the call to GetConfigurationAsync returned a task that is still running when we expect it to return a completed task, calling Result on it could theoretically cause a deadlock. However, I would argue that this class is likely only accessed from deep on async-heavy library call stacks, and it's highly unlikely that it could be called while attached to a synchronization context that could cause a deadlock.

If you want to stick with the Task.Run-always version instead of something like what I'm doing here, I have no problem with that. Like I said, it fixes the major bug that started the whole conversation. My remaining concerns are trivial by comparison.

@msbw2
Copy link
Contributor Author

msbw2 commented Nov 2, 2024

Though one thing that gives me hesitation about the implementation he suggests is the use of Task.Run in RetrieveMetadata. It would mean that every call to either of the properties will put work on a separate thread even though the called async method will very rarely ever do any async work since it usually just returns the cached config. However, the passive locking on calling the task ensures that it will never enter the thread starvation issue from which I'm still traumatized.

This makes sense to me.

Here's what I used and it's been working fine for us:

    internal class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
    {
        private readonly ConfigurationManager<OpenIdConnectConfiguration> _configManager;
        private readonly Task<OpenIdConnectConfiguration> _initialRetrieval;

        public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
        {
            _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                metadataEndpoint, new OpenIdConnectConfigurationRetriever());

            _initialRetrieval = Task.Run(_configManager.GetConfigurationAsync);
        }

        public string? Issuer => GetConfig().Issuer;

        public IEnumerable<SecurityKey>? SecurityKeys => GetConfig().SigningKeys;

        private OpenIdConnectConfiguration GetConfig()
        {
            if (!_initialRetrieval.IsCompleted)
            {
#pragma warning disable VSTHRD002
                return _initialRetrieval.Result;
            }

            return _configManager.GetConfigurationAsync().Result;
#pragma warning restore VSTHRD002
        }
    }

The only thing I don't like here is the reference to the initial task that is kept for the entire lifetime of the class. Once _initialRetrieval.IsCompleted returns true, it can never change back to false, so we could theoretically just store that to avoid accessing the original task in the future, and, at some point, remove the reference. I don't know if it's worth the hassle to make this change, though.

@alex-set
Copy link

alex-set commented Nov 5, 2024

One thing that finally occurred to me about the proposed implementation is that it calls RetrieveMetadata from the constructor which will block until the initial configuration loads. I think that's probably fine, and as a helpful side effect, it also makes the ConfigManager much easier to work with because the call to GetConfigurationAsync will always return a completed task without any async blocking. It handles keeping the configuration up to date separately, though it always returns the most recently cached version.

I think this could be an incredibly simple class. It shouldn't ever need to cache config requests, because that's exactly what the ConfigurationManager is already doing. It doesn't need to be concerned with concurrency for the reasons I already mentioned. With caching and concurrency no longer factors, there's no reason to worry about locking either. Obviously, it doesn’t need to maintain a reference to the initial retrieval task either, because that completes in the class constructor.

Here's what I have running in our moderately high-volume production environment:

internal class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
{
	private readonly ConfigurationManager<OpenIdConnectConfiguration> _configManager;

	public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
	{
		_configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
			metadataEndpoint, new OpenIdConnectConfigurationRetriever());

#pragma warning disable VSTHRD002
		Task.Run(_configManager.GetConfigurationAsync).Wait();
#pragma warning restore VSTHRD002
	}

	public string? Issuer => RetrieveMetadata().Issuer;

	public IEnumerable<SecurityKey>? SecurityKeys => RetrieveMetadata().SigningKeys;

#pragma warning disable VSTHRD002
	private OpenIdConnectConfiguration RetrieveMetadata() =>
		_configManager.GetConfigurationAsync().Result;
#pragma warning restore VSTHRD002
}

There still may be a valid question about whether this relies too much on the internal functionality of a separate library. Specifically, what if a future version of the ConfigurationManager’s GetConfigurationAsync method could occasionally block to do async work after the first retrieval? In that instance, calling Result on the task could potentially deadlock if a certain type of synchronization context were in effect. Here’s a potential way to safeguard against that without adding much overhead or complexity.

private OpenIdConnectConfiguration RetrieveMetadata()
{
    Task<OpenIdConnectConfiguration> task = _configManager.GetConfigurationAsync();
    if (task.IsCompleted)
    {
        return task.Result;
    }

    var sideTask = Task.Run(() => _configManager.GetConfigurationAsync());
    return sideTask.Result;
}

I know it looks a little hacky. Keep in mind that with the current implementation of ConfigurationManager, the returned task is always completed, so the second half of this method is effectively unreachable. Even if the GetConfigurationAsync implementation changed, I think it would be safe to assume it would still almost always return a completed task with the current configuration. As an added precaution, we can check if the returned task is still running in which case we would just want to restart the call from a separate thread using Task.Run, from which we can safely collect the configuration result. The important part is that we only call Task.Run when it’s truly necessary.

Sorry if I’m still laboring over some unimportant details. Hopefully this is helpful in some way, but I can drop it now that I’ve said my piece.

@mderriey
Copy link

mderriey commented Nov 5, 2024

From my perspective, the more people chiming in the better, and I appreciate you thinking about potential ways to make this simpler.

I like your thinking, especially the perspective of having this class not use any locks or synchronization.

I can't speak about the dependency on the internals of ConfigurationManager, that'll be for the maintaining team to assess.

It's probably not important for the team here, but I'm in a position where we don't use Microsoft.Identity.Web.OWIN because reasons, and we vendored in the original implementation in our project. If your proposal gets in, I'm not certain we're in a position to use it as-is because the version of ConfigurationManager we use doesn't work this way, as we're still on v6.23.1.
I thought I'd mention it, but again, the responsibility of the team here is to get Microsoft.Identity.Web.OWIN working, not satisfy the expectation of people cherry-picking individual classes and have them work 😇

Thanks again.

@alex-set
Copy link

alex-set commented Nov 5, 2024

I didn't even consider the functionality of an older version of ConfigManager. I just looked at the history and you're totally right; the functionality on which I was relying is a newer development. With that in mind, I think we have to use locking and caching to safely avoid thread pool starvation. In fact, the only thing I would really change about your original proposal is the use of Task.Run in RetrieveMetadata (which also means additional considerations for exception handling).

This is what I have running now:

    internal class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityKeyProvider
    {
        private readonly ConfigurationManager<OpenIdConnectConfiguration> _configManager;

        private OpenIdConnectConfiguration _lastConfig;
        private int _lock = 0;
        private Exception? _lastException;

        public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
        {
            _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
                metadataEndpoint, new OpenIdConnectConfigurationRetriever());

#pragma warning disable VSTHRD002
            _lastConfig = Task.Run(_configManager.GetConfigurationAsync).Result;
#pragma warning restore VSTHRD002
        }

        public string? Issuer => RetrieveMetadata().Issuer;

        public IEnumerable<SecurityKey>? SecurityKeys => RetrieveMetadata().SigningKeys;

        private OpenIdConnectConfiguration RetrieveMetadata()
        {
            if (Interlocked.Exchange(ref _lock, 1) == 0)
            {
                if (_lastException != null)
                {
                    Exception exc = _lastException;
                    _lastException = null;
                    _lock = 0;
                    throw exc;
                }

                Task<OpenIdConnectConfiguration> task = _configManager.GetConfigurationAsync();
                if (task.IsCompleted)
                {
                    UpdateConfig(task);
                }
                else
                {
                    _ = task.ContinueWith(UpdateConfig, TaskScheduler.Default);
                }
            }

            return _lastConfig;
        }

        private void UpdateConfig(Task<OpenIdConnectConfiguration> task)
        {
            if (task.IsFaulted)
            {
                _lastException = task.Exception;
            }
            else
            {
#pragma warning disable VSTHRD002 // This should always be a completed task
                _lastConfig = task.Result;
#pragma warning restore VSTHRD002
            }

            _lock = 0;
        }
    }

The call to GetConfigurationAsync still usually returns a completed task even with older versions (I think the default update interval is 12 hours). When it doesn't return a completed task, we can just schedule the continuation on a thread pool task and return the cached config without waiting for the result. That also means that if the async continuation has an exception, we have to preserve it and throw it on the next call to one of the getters.

I admit that this adds some non-trivial logic just to avoid Task.Run which may be overkill when passive locking is used to prevent overlapping calls. My only argument is that Task.Run is still really expensive to use in most calls to a property getter. I won't be offended if I don't persuade anyone.

@msbw2
Copy link
Contributor Author

msbw2 commented Nov 8, 2024

@alex-set Which of the two PRs do you think is closer to your proposal? You could leave comments to try to move it in your direction. Or, you could create your own PR with your suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants