From bdd3a5932af3796bca30bd6df7d5a74eb22a5015 Mon Sep 17 00:00:00 2001 From: Keegan Caruso Date: Mon, 5 Aug 2024 14:30:02 -0700 Subject: [PATCH] Revert "Remove SlimLock when updating metadata. (#2751)" This reverts commit bbc09a4811ba2fd4eb58edb693114c3f8a0fd0a1. --- .../OpenIdConnectConfigurationValidator.cs | 13 +- .../Configuration/ConfigurationManager.cs | 212 ++++------ .../Configuration/HttpDocumentRetriever.cs | 26 +- .../ConfigurationManagerTests.cs | 390 +++++++----------- .../OpenIdConfigData.cs | 215 ---------- .../OpenIdConnectSerializationTests.cs | 2 - .../ExtensibilityTests.cs | 11 +- .../InMemoryDocumentRetriever.cs | 38 -- .../SampleListener.cs | 2 +- 9 files changed, 225 insertions(+), 684 deletions(-) delete mode 100644 test/Microsoft.IdentityModel.TestUtils/InMemoryDocumentRetriever.cs diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationValidator.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationValidator.cs index ce3e3ba040..de72a9a519 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationValidator.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationValidator.cs @@ -39,20 +39,11 @@ public ConfigurationValidationResult Validate(OpenIdConnectConfiguration openIdC Succeeded = false }; } - - int numberOfValidKeys = 0; - for (int i = 0; i < openIdConnectConfiguration.JsonWebKeySet.Keys.Count; i++) - if (openIdConnectConfiguration.JsonWebKeySet.Keys[i].ConvertedSecurityKey != null) - numberOfValidKeys++; + var numberOfValidKeys = openIdConnectConfiguration.JsonWebKeySet.Keys.Where(key => key.ConvertedSecurityKey != null).Count(); if (numberOfValidKeys < MinimumNumberOfKeys) { - string convertKeyInfos = string.Join( - "\n", - openIdConnectConfiguration.JsonWebKeySet.Keys.Where( - key => !string.IsNullOrEmpty(key.ConvertKeyInfo)) - .Select(key => key.Kid.ToString() + ": " + key.ConvertKeyInfo)); - + var convertKeyInfos = string.Join("\n", openIdConnectConfiguration.JsonWebKeySet.Keys.Where(key => !string.IsNullOrEmpty(key.ConvertKeyInfo)).Select(key => key.Kid.ToString() + ": " + key.ConvertKeyInfo)); return new ConfigurationValidationResult { ErrorMessage = LogHelper.FormatInvariant( diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs index 8f1b527ad0..279e046687 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/ConfigurationManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Diagnostics.Contracts; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -19,22 +20,17 @@ namespace Microsoft.IdentityModel.Protocols public class ConfigurationManager : BaseConfigurationManager, IConfigurationManager where T : class { private DateTimeOffset _syncAfter = DateTimeOffset.MinValue; - private DateTimeOffset _lastRequestRefresh = DateTimeOffset.MinValue; + private DateTimeOffset _lastRefresh = DateTimeOffset.MinValue; private bool _isFirstRefreshRequest = true; + private readonly SemaphoreSlim _refreshLock; private readonly IDocumentRetriever _docRetriever; private readonly IConfigurationRetriever _configRetriever; private readonly IConfigurationValidator _configValidator; private T _currentConfiguration; + private Exception _fetchMetadataFailure; private TimeSpan _bootstrapRefreshInterval = TimeSpan.FromSeconds(1); - // task states are used to ensure the call to 'update config' (UpdateCurrentConfiguration) is a singleton. Uses Interlocked.CompareExchange. - // metadata is not being obtained - private const int ConfigurationRetrieverIdle = 0; - // metadata is being retrieved - private const int ConfigurationRetrieverRunning = 1; - private int _configurationRetrieverState = ConfigurationRetrieverIdle; - /// /// Instantiates a new that manages automatic and controls refreshing on configuration data. /// @@ -96,6 +92,7 @@ public ConfigurationManager(string metadataAddress, IConfigurationRetriever c MetadataAddress = metadataAddress; _docRetriever = docRetriever; _configRetriever = configRetriever; + _refreshLock = new SemaphoreSlim(1); } /// @@ -148,149 +145,83 @@ public async Task GetConfigurationAsync() public virtual async Task GetConfigurationAsync(CancellationToken cancel) { if (_currentConfiguration != null && _syncAfter > DateTimeOffset.UtcNow) + { return _currentConfiguration; + } - Exception fetchMetadataFailure = null; - - // LOGIC - // if configuration != null => configuration has been retrieved before - // reach out to the metadata endpoint - // else - // if task is running, return the current configuration - // else kick off task to update current configuration - if (_currentConfiguration == null) + await _refreshLock.WaitAsync(cancel).ConfigureAwait(false); + try { - try + if (_syncAfter <= DateTimeOffset.UtcNow) { - // Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation. - // The transport should have it's own timeouts, etc.. - var configuration = await _configRetriever.GetConfigurationAsync(MetadataAddress, _docRetriever, CancellationToken.None).ConfigureAwait(false); - if (_configValidator != null) + try { - ConfigurationValidationResult result = _configValidator.Validate(configuration); - // in this case we have never had a valid configuration, so we will throw an exception if the validation fails - if (!result.Succeeded) - throw LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage))); - } - - // Add a random amount between 0 and 5% of AutomaticRefreshInterval jitter to avoid spike traffic to IdentityProvider. - _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval + - TimeSpan.FromSeconds(new Random().Next((int)AutomaticRefreshInterval.TotalSeconds / 20))); - - _currentConfiguration = configuration; - } - catch (Exception ex) - { - fetchMetadataFailure = ex; + // Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation. + // The transport should have it's own timeouts, etc.. + var configuration = await _configRetriever.GetConfigurationAsync(MetadataAddress, _docRetriever, CancellationToken.None).ConfigureAwait(false); + if (_configValidator != null) + { + ConfigurationValidationResult result = _configValidator.Validate(configuration); + if (!result.Succeeded) + throw LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage))); + } - // In this case configuration was never obtained. - if (_currentConfiguration == null) + _lastRefresh = DateTimeOffset.UtcNow; + // Add a random amount between 0 and 5% of AutomaticRefreshInterval jitter to avoid spike traffic to IdentityProvider. + _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval + + TimeSpan.FromSeconds(new Random().Next((int)AutomaticRefreshInterval.TotalSeconds / 20))); + _currentConfiguration = configuration; + } + catch (Exception ex) { - if (_bootstrapRefreshInterval < RefreshInterval) + _fetchMetadataFailure = ex; + + if (_currentConfiguration == null) // Throw an exception if there's no configuration to return. { - // Adopt exponential backoff for bootstrap refresh interval with a decorrelated jitter if it is not longer than the refresh interval. - TimeSpan _bootstrapRefreshIntervalWithJitter = TimeSpan.FromSeconds(new Random().Next((int)_bootstrapRefreshInterval.TotalSeconds)); - _bootstrapRefreshInterval += _bootstrapRefreshInterval; - _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, _bootstrapRefreshIntervalWithJitter); - } + if (_bootstrapRefreshInterval < RefreshInterval) + { + // Adopt exponential backoff for bootstrap refresh interval with a decorrelated jitter if it is not longer than the refresh interval. + TimeSpan _bootstrapRefreshIntervalWithJitter = TimeSpan.FromSeconds(new Random().Next((int)_bootstrapRefreshInterval.TotalSeconds)); + _bootstrapRefreshInterval += _bootstrapRefreshInterval; + _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, _bootstrapRefreshIntervalWithJitter); + } + else + { + _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); + } + + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant(LogMessages.IDX20803, LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), LogHelper.MarkAsNonPII(_syncAfter), LogHelper.MarkAsNonPII(ex)), ex)); + } else { _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); - } - - throw LogHelper.LogExceptionMessage( - new InvalidOperationException( - LogHelper.FormatInvariant( - LogMessages.IDX20803, - LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), - LogHelper.MarkAsNonPII(_syncAfter), - LogHelper.MarkAsNonPII(ex)), - ex)); - } - else - { - _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); - LogHelper.LogExceptionMessage( - new InvalidOperationException( - LogHelper.FormatInvariant( - LogMessages.IDX20806, - LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), - LogHelper.MarkAsNonPII(ex)), - ex)); + LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant(LogMessages.IDX20806, LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), LogHelper.MarkAsNonPII(ex)), ex)); + } } } - } - else - { - if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverIdle, ConfigurationRetrieverRunning) != ConfigurationRetrieverRunning) - { - _ = Task.Run(UpdateCurrentConfiguration, CancellationToken.None); - } - } - - // If metadata exists return it. - if (_currentConfiguration != null) - return _currentConfiguration; - - throw LogHelper.LogExceptionMessage( - new InvalidOperationException( - LogHelper.FormatInvariant( - LogMessages.IDX20803, - LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), - LogHelper.MarkAsNonPII(_syncAfter), - LogHelper.MarkAsNonPII(fetchMetadataFailure)), - fetchMetadataFailure)); - } - - /// - /// This should be called when the configuration needs to be updated either from RequestRefresh or AutomaticRefresh, first checking the state checking state using: - /// if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverIdle, ConfigurationRetrieverRunning) != ConfigurationRetrieverRunning). - /// - private void UpdateCurrentConfiguration() - { -#pragma warning disable CA1031 // Do not catch general exception types - try - { - T configuration = _configRetriever.GetConfigurationAsync( - MetadataAddress, - _docRetriever, - CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - if (_configValidator == null) - { - _currentConfiguration = configuration; - } + // Stale metadata is better than no metadata + if (_currentConfiguration != null) + return _currentConfiguration; else - { - ConfigurationValidationResult result = _configValidator.Validate(configuration); - - if (!result.Succeeded) - LogHelper.LogExceptionMessage( - new InvalidConfigurationException( - LogHelper.FormatInvariant( - LogMessages.IDX20810, - result.ErrorMessage))); - else - _currentConfiguration = configuration; - } - } - catch (Exception ex) - { - LogHelper.LogExceptionMessage( - new InvalidOperationException( - LogHelper.FormatInvariant( - LogMessages.IDX20806, - LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), - ex), - ex)); + throw LogHelper.LogExceptionMessage( + new InvalidOperationException( + LogHelper.FormatInvariant( + LogMessages.IDX20803, + LogHelper.MarkAsNonPII(MetadataAddress ?? "null"), + LogHelper.MarkAsNonPII(_syncAfter), + LogHelper.MarkAsNonPII(_fetchMetadataFailure)), + _fetchMetadataFailure)); } finally { - _syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); - Interlocked.Exchange(ref _configurationRetrieverState, ConfigurationRetrieverIdle); + _refreshLock.Release(); } -#pragma warning restore CA1031 // Do not catch general exception types } /// @@ -301,8 +232,10 @@ private void UpdateCurrentConfiguration() /// If the time since the last call is less than then is not called and the current Configuration is returned. public override async Task GetBaseConfigurationAsync(CancellationToken cancel) { - T obj = await GetConfigurationAsync(cancel).ConfigureAwait(false); - return obj as BaseConfiguration; + var obj = await GetConfigurationAsync(cancel).ConfigureAwait(false); + if (obj is BaseConfiguration) + return obj as BaseConfiguration; + return null; } /// @@ -313,15 +246,14 @@ public override async Task GetBaseConfigurationAsync(Cancella public override void RequestRefresh() { DateTimeOffset now = DateTimeOffset.UtcNow; - - if (now >= DateTimeUtil.Add(_lastRequestRefresh.UtcDateTime, RefreshInterval) || _isFirstRefreshRequest ) + if (_isFirstRefreshRequest) { + _syncAfter = now; _isFirstRefreshRequest = false; - _lastRequestRefresh = now; - if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverIdle, ConfigurationRetrieverRunning) != ConfigurationRetrieverRunning) - { - _ = Task.Run(UpdateCurrentConfiguration, CancellationToken.None); - } + } + else if (now >= DateTimeUtil.Add(_lastRefresh.UtcDateTime, RefreshInterval)) + { + _syncAfter = now; } } diff --git a/src/Microsoft.IdentityModel.Protocols/Configuration/HttpDocumentRetriever.cs b/src/Microsoft.IdentityModel.Protocols/Configuration/HttpDocumentRetriever.cs index c69174c757..9eb4c2291b 100644 --- a/src/Microsoft.IdentityModel.Protocols/Configuration/HttpDocumentRetriever.cs +++ b/src/Microsoft.IdentityModel.Protocols/Configuration/HttpDocumentRetriever.cs @@ -84,22 +84,17 @@ public HttpDocumentRetriever(HttpClient httpClient) public async Task GetDocumentAsync(string address, CancellationToken cancel) { if (string.IsNullOrWhiteSpace(address)) - throw LogHelper.LogArgumentNullException(nameof(address)); + throw LogHelper.LogArgumentNullException("address"); if (!Utility.IsHttps(address) && RequireHttps) - throw LogHelper.LogExceptionMessage( - new ArgumentException( - LogHelper.FormatInvariant( - LogMessages.IDX20108, - LogHelper.MarkAsNonPII(address)), - nameof(address))); + throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX20108, address), nameof(address))); Exception unsuccessfulHttpResponseException; HttpResponseMessage response; try { if (LogHelper.IsEnabled(EventLogLevel.Verbose)) - LogHelper.LogVerbose(LogMessages.IDX20805, LogHelper.MarkAsNonPII(address)); + LogHelper.LogVerbose(LogMessages.IDX20805, address); var httpClient = _httpClient ?? _defaultHttpClient; var uri = new Uri(address, UriKind.RelativeOrAbsolute); @@ -109,24 +104,13 @@ public async Task GetDocumentAsync(string address, CancellationToken can if (response.IsSuccessStatusCode) return responseContent; - unsuccessfulHttpResponseException = new IOException( - LogHelper.FormatInvariant( - LogMessages.IDX20807, - LogHelper.MarkAsNonPII(address), - response, - responseContent)); - + unsuccessfulHttpResponseException = new IOException(LogHelper.FormatInvariant(LogMessages.IDX20807, address, response, responseContent)); unsuccessfulHttpResponseException.Data.Add(StatusCode, response.StatusCode); unsuccessfulHttpResponseException.Data.Add(ResponseContent, responseContent); } catch (Exception ex) { - throw LogHelper.LogExceptionMessage( - new IOException( - LogHelper.FormatInvariant( - LogMessages.IDX20804, - LogHelper.MarkAsNonPII(address)), - ex)); + throw LogHelper.LogExceptionMessage(new IOException(LogHelper.FormatInvariant(LogMessages.IDX20804, address), ex)); } throw LogHelper.LogExceptionMessage(unsuccessfulHttpResponseException); diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs index ccd9fc0ede..4fa6f57ca3 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/ConfigurationManagerTests.cs @@ -11,7 +11,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Protocols.Configuration; using Microsoft.IdentityModel.Protocols.OpenIdConnect.Configuration; using Microsoft.IdentityModel.TestUtils; @@ -291,245 +290,163 @@ public void GetSets() TestUtilities.AssertFailIfErrors("ConfigurationManager_GetSets", context.Errors); } - [Theory, MemberData(nameof(AutomaticIntervalTestCases), DisableDiscoveryEnumeration = true)] - public async Task AutomaticRefreshInterval(ConfigurationManagerTheoryData theoryData) + [Fact] + public void GetConfiguration() { - var context = new CompareContext($"{this}.AutomaticRefreshInterval"); - - try - { - - var configuration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - IdentityComparer.AreEqual(configuration, theoryData.ExpectedConfiguration, context); - - theoryData.ConfigurationManager.MetadataAddress = theoryData.UpdatedMetadataAddress; - TestUtilities.SetField(theoryData.ConfigurationManager, "_syncAfter", theoryData.SyncAfter); - var updatedConfiguration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - // we wait 50 ms here to make the task is finished. - Thread.Sleep(50); - updatedConfiguration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - IdentityComparer.AreEqual(updatedConfiguration, theoryData.ExpectedUpdatedConfiguration, context); + var docRetriever = new FileDocumentRetriever(); + var configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); + var context = new CompareContext($"{this}.GetConfiguration"); - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } + // AutomaticRefreshInterval interval should return same config. + var configuration = configManager.GetConfigurationAsync().Result; + configManager.MetadataAddress = "OpenIdConnectMetadata2.json"; + var configuration2 = configManager.GetConfigurationAsync().Result; + IdentityComparer.AreEqual(configuration, configuration2, context); + if (!object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("!object.ReferenceEquals(configuration, configuration2)"); - TestUtilities.AssertFailIfErrors(context); - } + // AutomaticRefreshInterval should pick up new bits. + configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); + configManager.RequestRefresh(); + configuration = configManager.GetConfigurationAsync().Result; + TestUtilities.SetField(configManager, "_lastRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); + configManager.MetadataAddress = "OpenIdConnectMetadata2.json"; + configManager.RequestRefresh(); + configuration2 = configManager.GetConfigurationAsync().Result; + if (IdentityComparer.AreEqual(configuration, configuration2)) + context.Diffs.Add("IdentityComparer.AreEqual(configuration, configuration2)"); - public static TheoryData> AutomaticIntervalTestCases - { - get - { - var theoryData = new TheoryData>(); + if (object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("object.ReferenceEquals(configuration, configuration2) (2)"); - // Failing to get metadata returns existing. - theoryData.Add(new ConfigurationManagerTheoryData("HttpFault_ReturnExisting") - { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config, - SyncAfter = DateTime.UtcNow - TimeSpan.FromDays(2), - UpdatedMetadataAddress = "https://httpstat.us/429" - }); + // RefreshInterval is set to MaxValue + configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); + configuration = configManager.GetConfigurationAsync().Result; + configManager.RefreshInterval = TimeSpan.MaxValue; + configManager.MetadataAddress = "OpenIdConnectMetadata2.json"; + configuration2 = configManager.GetConfigurationAsync().Result; + IdentityComparer.AreEqual(configuration, configuration2, context); + if (!object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("!object.ReferenceEquals(configuration, configuration2) (3)"); - // AutomaticRefreshInterval interval should return same config. - theoryData.Add(new ConfigurationManagerTheoryData("AutomaticRefreshIntervalNotHit") - { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config, - SyncAfter = DateTime.UtcNow + TimeSpan.FromDays(2), - UpdatedMetadataAddress = "AADCommonV2Json" - }); - - // AutomaticRefreshInterval should pick up new bits. - theoryData.Add(new ConfigurationManagerTheoryData("AutomaticRefreshIntervalHit") - { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config, - SyncAfter = DateTime.UtcNow, - UpdatedMetadataAddress = "AADCommonV2Json" - }); + configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); + configuration = configManager.GetConfigurationAsync().Result; + // First force refresh should pickup new config + configManager.RequestRefresh(); + configManager.MetadataAddress = "OpenIdConnectMetadata2.json"; + configuration2 = configManager.GetConfigurationAsync().Result; + if (IdentityComparer.AreEqual(configuration, configuration2)) + context.Diffs.Add("IdentityComparer.AreEqual(configuration, configuration2), should be different"); + if (object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("object.ReferenceEquals(configuration, configuration2) (4)"); + // Next force refresh shouldn't pickup new config, as RefreshInterval hasn't passed + configManager.RequestRefresh(); + configManager.MetadataAddress = "OpenIdConnectMetadata.json"; + var configuration3 = configManager.GetConfigurationAsync().Result; + IdentityComparer.AreEqual(configuration2, configuration3, context); + if (!object.ReferenceEquals(configuration2, configuration3)) + context.Diffs.Add("!object.ReferenceEquals(configuration2, configuration3) (5)"); + // Next force refresh should pickup config since, RefreshInterval is set to 1s + configManager.RefreshInterval = TimeSpan.FromSeconds(1); + Thread.Sleep(1000); + configManager.RequestRefresh(); + var configuration4 = configManager.GetConfigurationAsync().Result; + if (IdentityComparer.AreEqual(configuration2, configuration4)) + context.Diffs.Add("IdentityComparer.AreEqual(configuration2, configuration4), should be different"); + if (object.ReferenceEquals(configuration2, configuration4)) + context.Diffs.Add("object.ReferenceEquals(configuration2, configuration4) (6)"); - return theoryData; - } - } + // Refresh should force pickup of new config + configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); + configuration = configManager.GetConfigurationAsync().Result; + TestUtilities.SetField(configManager, "_lastRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); + configManager.RequestRefresh(); + configManager.MetadataAddress = "OpenIdConnectMetadata2.json"; + configuration2 = configManager.GetConfigurationAsync().Result; + if (IdentityComparer.AreEqual(configuration, configuration2)) + context.Diffs.Add("IdentityComparer.AreEqual(configuration, configuration2), should be different"); - [Theory, MemberData(nameof(RequestRefreshTestCases), DisableDiscoveryEnumeration = true)] - public async Task RequestRefresh(ConfigurationManagerTheoryData theoryData) - { - var context = new CompareContext($"{this}.RequestRefresh"); + if (object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("object.ReferenceEquals(configuration, configuration2)"); - var configuration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - IdentityComparer.AreEqual(configuration, theoryData.ExpectedConfiguration, context); + // Refresh set to MaxValue + configManager.RefreshInterval = TimeSpan.MaxValue; + configuration = configManager.GetConfigurationAsync().Result; + IdentityComparer.AreEqual(configuration, configuration2, context); + if (!object.ReferenceEquals(configuration, configuration2)) + context.Diffs.Add("!object.ReferenceEquals(configuration, configuration2)"); - // the first call to RequestRefresh will trigger a refresh with ConfigurationManager.RefreshInterval being ignored. - // Testing RefreshInterval requires a two calls, the second call will trigger a refresh with ConfigurationManager.RefreshInterval being used. - if (theoryData.RequestRefresh) + // get configuration from http address, should throw + configManager = new ConfigurationManager("http://127.0.0.1", new OpenIdConnectConfigurationRetriever()); + var ee = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(ArgumentException)); + try { - theoryData.ConfigurationManager.RequestRefresh(); - configuration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + configuration = configManager.GetConfigurationAsync().Result; + ee.ProcessNoException(context); } - - theoryData.ConfigurationManager.RefreshInterval = theoryData.RefreshInterval; - theoryData.ConfigurationManager.MetadataAddress = theoryData.UpdatedMetadataAddress; - if (theoryData.SleepTimeInMs > 0) - Thread.Sleep(theoryData.SleepTimeInMs); - - theoryData.ConfigurationManager.RequestRefresh(); - - if (theoryData.SleepTimeInMs > 0) - Thread.Sleep(theoryData.SleepTimeInMs); - - var updatedConfiguration = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - - IdentityComparer.AreEqual(updatedConfiguration, theoryData.ExpectedUpdatedConfiguration, context); - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData> RequestRefreshTestCases - { - get + catch (AggregateException ex) { - var theoryData = new TheoryData>(); + // this should throw, because last configuration retrived was null + Assert.Throws(() => configuration = configManager.GetConfigurationAsync().Result); - // RefreshInterval set to 1 sec should return new config. - theoryData.Add(new ConfigurationManagerTheoryData("RequestRefresh_TimeSpan_1000ms") - { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config, - RefreshInterval = TimeSpan.FromSeconds(1), - RequestRefresh = true, - SleepTimeInMs = 1000, - UpdatedMetadataAddress = "AADCommonV2Json" - }); - - // RefreshInterval set to TimeSpan.MaxValue should return same config. - theoryData.Add(new ConfigurationManagerTheoryData("RequestRefresh_TimeSpan_MaxValue") - { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV1Config, - RefreshInterval = TimeSpan.MaxValue, - RequestRefresh = true, - UpdatedMetadataAddress = "AADCommonV2Json" - }); - - // First RequestRefresh should pickup new config - theoryData.Add(new ConfigurationManagerTheoryData("RequestRefresh_FirstRefresh") + ex.Handle((x) => { - ConfigurationManager = new ConfigurationManager( - "AADCommonV1Json", - new OpenIdConnectConfigurationRetriever(), - InMemoryDocumentRetriever), - ExpectedConfiguration = OpenIdConfigData.AADCommonV1Config, - ExpectedUpdatedConfiguration = OpenIdConfigData.AADCommonV2Config, - SleepTimeInMs = 100, - UpdatedMetadataAddress = "AADCommonV2Json" + ee.ProcessException(x, context); + return true; }); - - return theoryData; } - } - - [Theory, MemberData(nameof(HttpFailuresTestCases), DisableDiscoveryEnumeration = true)] - public async Task HttpFailures(ConfigurationManagerTheoryData theoryData) - { - var context = new CompareContext($"{this}.HttpFailures"); + // get configuration from https address, should throw + configManager = new ConfigurationManager("https://127.0.0.1", new OpenIdConnectConfigurationRetriever()); + ee = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(IOException)); try { - _ = await theoryData.ConfigurationManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); + configuration = configManager.GetConfigurationAsync().Result; + ee.ProcessNoException(context); } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData> HttpFailuresTestCases - { - get + catch (AggregateException ex) { - var theoryData = new TheoryData>(); + // this should throw, because last configuration retrived was null + Assert.Throws(() => configuration = configManager.GetConfigurationAsync().Result); - theoryData.Add(new ConfigurationManagerTheoryData("LocalHost_HTTPS_Status_Error") + ex.Handle((x) => { - ConfigurationManager = new ConfigurationManager( - "https://httpstat.us/429", - new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever()), - ExpectedException = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(IOException)), + ee.ProcessException(x, context); + return true; }); + } - theoryData.Add(new ConfigurationManagerTheoryData("LocalHost_HTTPS_Error") - { - ConfigurationManager = new ConfigurationManager( - "https://127.0.0.1", - new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever()), - ExpectedException = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(IOException)), - }); + // get configuration with unsuccessful HTTP response status code + configManager = new ConfigurationManager("https://httpstat.us/429", new OpenIdConnectConfigurationRetriever()); + ee = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(IOException)); + try + { + configuration = configManager.GetConfigurationAsync().Result; + ee.ProcessNoException(context); + } + catch (AggregateException ex) + { + // this should throw, because last configuration retrived was null + Assert.Throws(() => configuration = configManager.GetConfigurationAsync().Result); - theoryData.Add(new ConfigurationManagerTheoryData("LocalHost_HTTP_ArgumentError") + ex.Handle((x) => { - ConfigurationManager = new ConfigurationManager( - "http://127.0.0.1", - new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever()), - ExpectedException = new ExpectedException(typeof(InvalidOperationException), "IDX20803:", typeof(ArgumentException)), + ee.ProcessException(x, context); + return true; }); - - return theoryData; } - } - - [Fact] - public async Task GetConfigurationAsync() - { - var docRetriever = new FileDocumentRetriever(); - var configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); - var context = new CompareContext($"{this}.GetConfiguration"); // Unable to obtain a new configuration, but _currentConfiguration is not null so it should be returned. configManager = new ConfigurationManager("OpenIdConnectMetadata.json", new OpenIdConnectConfigurationRetriever(), docRetriever); - var configuration = await configManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - TestUtilities.SetField(configManager, "_lastRequestRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); + configuration = configManager.GetConfigurationAsync().Result; + TestUtilities.SetField(configManager, "_lastRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); configManager.RequestRefresh(); configManager.MetadataAddress = "http://127.0.0.1"; - var configuration2 = await configManager.GetConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + configuration2 = configManager.GetConfigurationAsync().Result; IdentityComparer.AreEqual(configuration, configuration2, context); if (!object.ReferenceEquals(configuration, configuration2)) context.Diffs.Add("!object.ReferenceEquals(configuration, configuration2)"); - - // get configuration from http address, should throw - // get configuration with unsuccessful HTTP response status code TestUtilities.AssertFailIfErrors(context); } @@ -628,10 +545,8 @@ public void ValidateOpenIdConnectConfigurationTests(ConfigurationManagerTheoryDa { //create a listener and enable it for logs var listener = TestUtils.SampleListener.CreateLoggerListener(EventLevel.Warning); - configuration = configurationManager.GetConfigurationAsync().Result; - // we need to sleep here to make sure the task that updates configuration is finished. - Thread.Sleep(250); + configuration = configurationManager.GetConfigurationAsync().Result; if (!string.IsNullOrEmpty(theoryData.ExpectedErrorMessage) && !listener.TraceBuffer.Contains(theoryData.ExpectedErrorMessage)) context.AddDiff($"Expected exception to contain: '{theoryData.ExpectedErrorMessage}'.{Environment.NewLine}Log is:{Environment.NewLine}'{listener.TraceBuffer}'"); @@ -661,25 +576,25 @@ public static TheoryData>(); - //theoryData.Add(new ConfigurationManagerTheoryData - //{ - // ConfigurationRetreiver = new OpenIdConnectConfigurationRetriever(), - // ConfigurationValidator = openIdConnectConfigurationValidator, - // DocumentRetriever = new FileDocumentRetriever(), - // First = true, - // MetadataAddress = "OpenIdConnectMetadata.json", - // TestId = "ValidConfiguration" - //}); - - //theoryData.Add(new ConfigurationManagerTheoryData - //{ - // ConfigurationRetreiver = new OpenIdConnectConfigurationRetriever(), - // ConfigurationValidator = openIdConnectConfigurationValidator2, - // DocumentRetriever = new FileDocumentRetriever(), - // ExpectedException = new ExpectedException(typeof(InvalidOperationException), "IDX21818:", typeof(InvalidConfigurationException)), - // MetadataAddress = "OpenIdConnectMetadata.json", - // TestId = "ValidConfiguration_NotEnoughKey" - //}); + theoryData.Add(new ConfigurationManagerTheoryData + { + ConfigurationRetreiver = new OpenIdConnectConfigurationRetriever(), + ConfigurationValidator = openIdConnectConfigurationValidator, + DocumentRetriever = new FileDocumentRetriever(), + First = true, + MetadataAddress = "OpenIdConnectMetadata.json", + TestId = "ValidConfiguration" + }); + + theoryData.Add(new ConfigurationManagerTheoryData + { + ConfigurationRetreiver = new OpenIdConnectConfigurationRetriever(), + ConfigurationValidator = openIdConnectConfigurationValidator2, + DocumentRetriever = new FileDocumentRetriever(), + ExpectedException = new ExpectedException(typeof(InvalidOperationException), "IDX21818:", typeof(InvalidConfigurationException)), + MetadataAddress = "OpenIdConnectMetadata.json", + TestId = "ValidConfiguration_NotEnoughKey" + }); theoryData.Add(new ConfigurationManagerTheoryData { @@ -759,19 +674,8 @@ public static TheoryData new InMemoryDocumentRetriever( - new Dictionary - { - { "AADCommonV1Json", OpenIdConfigData.AADCommonV1Json }, - { "https://login.microsoftonline.com/common/discovery/keys", OpenIdConfigData.AADCommonV1JwksString }, - { "AADCommonV2Json", OpenIdConfigData.AADCommonV2Json }, - { "https://login.microsoftonline.com/common/discovery/v2.0/keys", OpenIdConfigData.AADCommonV2JwksString } - }); - - public class ConfigurationManagerTheoryData : TheoryDataBase where T : class + public class ConfigurationManagerTheoryData : TheoryDataBase { - public ConfigurationManager ConfigurationManager { get; set; } - public ConfigurationManagerTheoryData() {} public ConfigurationManagerTheoryData(string testId) : base(testId) {} @@ -780,7 +684,7 @@ public ConfigurationManagerTheoryData(string testId) : base(testId) {} public IConfigurationRetriever ConfigurationRetreiver { get; set; } - public IConfigurationValidator ConfigurationValidator { get; set; } + public IConfigurationValidator ConfigurationValidator { get; set; } public IDocumentRetriever DocumentRetriever { get; set; } @@ -788,30 +692,18 @@ public ConfigurationManagerTheoryData(string testId) : base(testId) {} public string ExpectedErrorMessage { get; set; } - public T ExpectedConfiguration { get; set; } - - public T ExpectedUpdatedConfiguration { get; set; } - - public DateTimeOffset LastRefreshTime { get; set; } = DateTime.MinValue; - public string MetadataAddress { get; set; } public bool PresetCurrentConfiguration { get; set; } - public TimeSpan RefreshInterval { get; set; } = BaseConfigurationManager.DefaultRefreshInterval; + public TimeSpan RefreshInterval { get; set; } public bool RequestRefresh { get; set; } - public int SleepTimeInMs { get; set; } = 0; - - public DateTimeOffset SyncAfter { get; set; } = DateTime.UtcNow; - public override string ToString() { return $"{TestId}, {MetadataAddress}, {ExpectedException}"; } - - public string UpdatedMetadataAddress { get; set; } } } } diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs index 5708633a8e..3de8987f4f 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs @@ -272,92 +272,6 @@ public static OpenIdConnectConfiguration AccountsGoogleComConfig } """; - // 7/22/2024 - public static string AADCommonV1JwksString => - """ - { - "keys": [ - { - "kty": "RSA", - "use": "sig", - "kid": "23ntaxfH9Gk-8D_USzoAJgwjyt8", - "x5t": "23ntaxfH9Gk-8D_USzoAJgwjyt8", - "n": "hu2SJrLlDOUtU2s9T6-6OVGEPaba2zIT2_Jl50f4NGG-r-GyQdaOzTFASfAfMkMfMQMRnabqd-dp_Ooqha473bw6DMbM23nv2uhBn5Afp-S1W_d4NxEhfNlN1Tgjx3Sh6UblBSFCE4JGkugSkLi2SVouy43seskesQotXGVNv4iboFm4yO_twlMCG9EDwza32y6WZtV8i9gkQP42OfK0X1qy6EUz2DN7cpfZtmkNtsFJhFf9waOvNCR95LVCPGafeCOMAQEvu1VO3mrBSIg7Izu0CzvuaBQTwnGv29Ggxc3GO4gvb_OStkkmfIwchu3A8F6e0aJ4Ys8PFP7z7Z8lqQ", - "e": "AQAB", - "x5c": [ - "MIIC/jCCAeagAwIBAgIJAJB4tJM2GkZjMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMjQwNTIyMTg1ODQwWhcNMjkwNTIyMTg1ODQwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhu2SJrLlDOUtU2s9T6+6OVGEPaba2zIT2/Jl50f4NGG+r+GyQdaOzTFASfAfMkMfMQMRnabqd+dp/Ooqha473bw6DMbM23nv2uhBn5Afp+S1W/d4NxEhfNlN1Tgjx3Sh6UblBSFCE4JGkugSkLi2SVouy43seskesQotXGVNv4iboFm4yO/twlMCG9EDwza32y6WZtV8i9gkQP42OfK0X1qy6EUz2DN7cpfZtmkNtsFJhFf9waOvNCR95LVCPGafeCOMAQEvu1VO3mrBSIg7Izu0CzvuaBQTwnGv29Ggxc3GO4gvb/OStkkmfIwchu3A8F6e0aJ4Ys8PFP7z7Z8lqQIDAQABoyEwHzAdBgNVHQ4EFgQUeGPdsxkVp8lIRku0u41SCzqW7LIwDQYJKoZIhvcNAQELBQADggEBAHMJCPO473QQJtTXJ49OhZ48kVCiVgbut+xElHxvBWQrfJ4Zb6WAi2RudjwrpwchVBciwjIelp/3Ryp5rVL94D479Ta/C5BzWNm9LsZCw3rPrsIvUdx26GmfQomHyL18AJQyBj8jZ+pVvdprvbV7v586TcgY24pW018IiYGQEO/fR8DSO4eN8ekTvT8hODBoKiJ9NFy+BruqW1AbMDptH12uzpU/N9bftysnWeDJEVZd5Rj8u8F9MRbB6V7dzxdoswaKkiJbxt+JrZgdtHSFqz6rDypIkumYwUkyiwH4/GQGPiyBLFbRp1EYVa3SFwAEmhl4a7On05aHVnOfCoyj/qA=" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "MGLqj98VNLoXaFfpJCBpgB4JaKs", - "x5t": "MGLqj98VNLoXaFfpJCBpgB4JaKs", - "n": "yfNcG8Ka_b4R7niLqdzlFvzRjrTdl2wTVEtqRWXqDhJAt_KIizVrqe0x3E1tNohmySHAMz3IS4llC41ML5YUDZVg33XR0RMc6UntOq-qCSOkPXdliC3QkwxspGAaJxVzhO5OZuQlMoQNL6_1FgdaR62gfayjLSJepB8M-o7yC8sOtRhatwe9kbO_5QJC54B8ni0ge5i9nANMln-9ZCHeRQYkgl0RSvR_KtfpWrEqAa4K2cyPaDqejOs8G8V0kM_8CLtDWi5diKpO_fvzRJwparEB5hfMdjAyJgdTOqCVUulZdL7tsoHzb8-_ufq-5QFJkyNUpYB9R1mVQwmRGdY0nQ", - "e": "AQAB", - "x5c": [ - "MIIC/jCCAeagAwIBAgIJAJtuCSyF4i1FMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMjQwNjA5MTkxNzM5WhcNMjkwNjA5MTkxNzM5WjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyfNcG8Ka/b4R7niLqdzlFvzRjrTdl2wTVEtqRWXqDhJAt/KIizVrqe0x3E1tNohmySHAMz3IS4llC41ML5YUDZVg33XR0RMc6UntOq+qCSOkPXdliC3QkwxspGAaJxVzhO5OZuQlMoQNL6/1FgdaR62gfayjLSJepB8M+o7yC8sOtRhatwe9kbO/5QJC54B8ni0ge5i9nANMln+9ZCHeRQYkgl0RSvR/KtfpWrEqAa4K2cyPaDqejOs8G8V0kM/8CLtDWi5diKpO/fvzRJwparEB5hfMdjAyJgdTOqCVUulZdL7tsoHzb8+/ufq+5QFJkyNUpYB9R1mVQwmRGdY0nQIDAQABoyEwHzAdBgNVHQ4EFgQUzF0gtMcVDEn4JoNlDOxvhM8IHBswDQYJKoZIhvcNAQELBQADggEBAJe2muR0H2h3phiZ/v6FD8Yio6niulN9jr7+eC/UJV1M7l5xdHgVL83JbNZjUECDrJ/m+ICY1NbEXfv4fo3sfpU1AwG5GXAhxTrS4zMhH7Hvir3800wCd3ByJ/2vQW1y3orlqR8Q65BN9ayub6BCBTNmtUAOpAWcnP3FnGtIDmAL4APcacK92ZTg8ayVX586U7DDWmI4l7X6xCruK0ic5W2b13k2cay0EalHNWHl+gikqQg6tTGSvM295P6Xy5bQ1I5QtHjVCbm0315T/FylvR8fZhVD+AUCc1DwtOr3Yhm3EXftDb6hP08C4yDhGIDH3Q3+xuWlIA7KQjgljuiT67U=" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "inEVM76gXEQEonQ0PSQXBO_7HfU", - "x5t": "inEVM76gXEQEonQ0PSQXBO_7HfU", - "n": "q0sct8P8TxXmXX2QXzIhMnwZHdCO96SMFCMtfswF1TpxYqaIFObIhT_zxxpBTsvkYHAxG7CUQ6qVgd_TQhMx0TSZq_X3_0NG6cIRik0g-Woe0gT6tUJ-o6zdtO-6EvoOXovT3YMh8vN1Q5UJV6dudDqjnlTNHH1OxFcU4U6no1R6iILDMci_TGq7I2AJS5i_O9Ptp5NmgDT_kbwZHJz1Abbw4VuOPMFJ2Q1rN9odV9YHKjjowqa3BULVyTvP8FoGUzhoopu6O7oA-ehlO9fhEoSS0zNn0lWXQMZXUF7GSyui12121kIXyll2KlvuETQNdVkeXu0m95g_pnX-8iZ_cw", - "e": "AQAB", - "x5c": [ - "MIIC/TCCAeWgAwIBAgIINBmSj+3xdxwwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDAeFw0yNDA2MTIxNjA3MjBaFw0yOTA2MTIxNjA3MjBaMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrSxy3w/xPFeZdfZBfMiEyfBkd0I73pIwUIy1+zAXVOnFipogU5siFP/PHGkFOy+RgcDEbsJRDqpWB39NCEzHRNJmr9ff/Q0bpwhGKTSD5ah7SBPq1Qn6jrN2077oS+g5ei9PdgyHy83VDlQlXp250OqOeVM0cfU7EVxThTqejVHqIgsMxyL9MarsjYAlLmL870+2nk2aANP+RvBkcnPUBtvDhW448wUnZDWs32h1X1gcqOOjCprcFQtXJO8/wWgZTOGiim7o7ugD56GU71+EShJLTM2fSVZdAxldQXsZLK6LXbXbWQhfKWXYqW+4RNA11WR5e7Sb3mD+mdf7yJn9zAgMBAAGjITAfMB0GA1UdDgQWBBSZbhe/r/sxfv0nYlyrwjx+b6W2RTANBgkqhkiG9w0BAQsFAAOCAQEAoWZ+C/snZySK1KiOsrn1iq7wrVzkuModPMZEshR3SuDIB6+C76fmP42I3UtDVIY5EeE79YjdwDwy86dPZjKVNbP7yUSbJC8uPM1TNMA9s8QpO6RZ63ZZ4i8hcgk6PXgi0PPjX2cmzUSNUa4gS8ibhf7JDu4aF9lUceBsNQghNQfz3tBs1ksJoW3WY5EfW6yMCv1Vim0uBlpnYdlynAd8O+9N2JR9wC+12PwPGrdGQDX3pos8bnmBxM55ueiGoqDH5yGI1h63POlGnpEdqOONT8N4cZNazQ1NswbBQuZMZSfbPXjiiFQ4bktyiXr421KbknQrkRYogi1F2Cjrd4SZJg==" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "KQ2tAcrE7lBaVVGBmc5FobgdJo4", - "x5t": "KQ2tAcrE7lBaVVGBmc5FobgdJo4", - "n": "08xqQ-OBv9jvWmtvWw8g3IkcuDHVOAGCn3K6TXyKie0L6cAyQWNX4vqxbt0cHdaLunrzaFJ2mIGj_qfor8KR_FOFVFOF24FAakB5El96LvsTwlWJNIw4kpf1O_xibycZ_UcDAEqABJfe51JSPh-PxI2sXt0UMapSjvTdnps0Conp11Ay_yupl_h7nawVg0kzw3QDX5-vKTruiHAHr845YwDRW1yJLEgkUPYXdM8d_SrRgqb2RKJEN8D1c4-SUpFHKwGAwLgVYH1cqwADX9el857z_2uKqJoP48l8WqUOfNGdvx79RCgF1NzzRh07EQrk0GJ_EB8eO-EF4YHLPImVtQ", - "e": "AQAB", - "x5c": [ - "MIIC/TCCAeWgAwIBAgIIRf5MUh/1XVIwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDAeFw0yNDA3MTAxNjA0NTBaFw0yOTA3MTAxNjA0NTBaMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTzGpD44G/2O9aa29bDyDciRy4MdU4AYKfcrpNfIqJ7QvpwDJBY1fi+rFu3Rwd1ou6evNoUnaYgaP+p+ivwpH8U4VUU4XbgUBqQHkSX3ou+xPCVYk0jDiSl/U7/GJvJxn9RwMASoAEl97nUlI+H4/Ejaxe3RQxqlKO9N2emzQKienXUDL/K6mX+HudrBWDSTPDdANfn68pOu6IcAevzjljANFbXIksSCRQ9hd0zx39KtGCpvZEokQ3wPVzj5JSkUcrAYDAuBVgfVyrAANf16XznvP/a4qomg/jyXxapQ580Z2/Hv1EKAXU3PNGHTsRCuTQYn8QHx474QXhgcs8iZW1AgMBAAGjITAfMB0GA1UdDgQWBBQGoURL0sKGdYALEdvfObZ6NEgmJTANBgkqhkiG9w0BAQsFAAOCAQEAl/UkmIa4OvsgULkBmGIZ6HeyJDvVuORphBK9/vpxEFsgnlitwMncBO54uMjJVr63baV490ODSI+ZTiCh7WGM+zrSjllCbVWDxjrdXA1ygHnXX7bXecIQyDmVb5/Hfb7DmQ4MHa3lEwf+pNS5XJeOhPoduRsfYCdD0QbxEADDgqV4FtgYx4I+iAoqbPDPou7wchEu9d3MuFuTMorkTvDLCyTHi2rgBnk9GBf2rArCGyTpvVPGXmxBttqgm9krFRujLj00u9jKUx4YkmAhS9YRddME8+gh6X4qFMxQMhyzkaBxjLs/E+pwMJaUwBqvostwt9+52qrMSUo+jkFgiGCe4Q==" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "EHu9neGZBCDyv2IYq8U5JiRMFng", - "x5t": "EHu9neGZBCDyv2IYq8U5JiRMFng", - "n": "w1kH9dFGdaJS8fvQulDssuuNhkczzy1Mo6IiNoC3ih3K-L_VF5TQmSkqXrovWCUlhBCfc1VPR9Cn2G4UP7Sygn0nTqXBY1NFQQZecqwGESJFIuonRqjdlDhNYXjSF_eg63KyuyLV8A-Sn05Ufuc8ax0tyrxPbkOql0pB2hmRhj94iDAFB2LBoxfEgxCG3VT0ascVYW6voTCChs2P65-4RLC-ib1w1FjuACDwsB7KZDxxaUGLfnIoLWUjmw1zCaDRiRvhxB4jQXpB64IFxaYsqxA_x8bj2JEE7qALZ2dZ3fPy9yYSAnRfaTMetgouR9x4SKy4HxUxsADMm_7p9LiRZQ", - "e": "AQAB", - "x5c": [ - "MIIC6jCCAdKgAwIBAgIJAO8yTjZIibNNMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czAeFw0yNDA1MDYyMzA5MDJaFw0yOTA1MDYyMzA5MDJaMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMNZB/XRRnWiUvH70LpQ7LLrjYZHM88tTKOiIjaAt4odyvi/1ReU0JkpKl66L1glJYQQn3NVT0fQp9huFD+0soJ9J06lwWNTRUEGXnKsBhEiRSLqJ0ao3ZQ4TWF40hf3oOtysrsi1fAPkp9OVH7nPGsdLcq8T25DqpdKQdoZkYY/eIgwBQdiwaMXxIMQht1U9GrHFWFur6EwgobNj+ufuESwvom9cNRY7gAg8LAeymQ8cWlBi35yKC1lI5sNcwmg0Ykb4cQeI0F6QeuCBcWmLKsQP8fG49iRBO6gC2dnWd3z8vcmEgJ0X2kzHrYKLkfceEisuB8VMbAAzJv+6fS4kWUCAwEAAaMhMB8wHQYDVR0OBBYEFJ4xtCt3JpPxlUVH7ATgJGM4ofg7MA0GCSqGSIb3DQEBCwUAA4IBAQB9WAEvE3VtO5wIOtN5N/QbIU63H5QPgMW3M9nOs43AhLgwvWupxaiATyMqtK53RPvcxYPe7QwSw/xH9McXii1bOBVmc71AcjlXYfuMJ/0IMEFEUQwZDEwj+vIlg07gWh0hleehyAgMblDUQRRN+b5J+soa9LBBAooY/48F/++y4DiTzKyoWn5cV4H2kdIFVyB43XzJRqDoK1ZhplVLTc1a3K1NL1/qP9rhvtx62YDzfNh4+FTJLu31ALcUbD+Qx2m0U9wuWq3EdUzEen5DeLvhx55YD7V1BASHNYBd8lGhHk97aTw53CMGAuTELvWO+4x7dFM9autw2KvSn76n/4Ql" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "FB8_wii85nv_UW3qrldTvWwg-rE", - "x5t": "FB8_wii85nv_UW3qrldTvWwg-rE", - "n": "vusbA5UBNtCB0U2RmyQOCE-8fWl8bzCQXm3V5Nd7oockcyCpqXOWfhVNJD-Ifb5_zAmxRgHvRdfpA2btaqZiit5XaFYngtRK6mVxCcnOEgwxQGX9DLM5plXWtGTf_DF1FATBidFlM8KgicTS3MTyKZNrnTz0JD7ISxwV0TgSEiRrsm7eVsumuNYNW30Yb38DRDTei9U1YR0YDmdZyuf-OKTllxKH_BO-aj8Gkxcnkdriih2CINF6M6oASOHTJYO7P8CQE1DX2y2Zq7xxVvzm4IClk7WDdzuAoC-ZiKvDaU5plSyrnH3_VgjJrzXtuGN-HEd4Vg89h_2rE74cN5KRtQ", - "e": "AQAB", - "x5c": [ - "MIIC6jCCAdKgAwIBAgIJAJOO92n+BJBLMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czAeFw0yNDA2MTAxODA3NDVaFw0yOTA2MTAxODA3NDVaMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7rGwOVATbQgdFNkZskDghPvH1pfG8wkF5t1eTXe6KHJHMgqalzln4VTSQ/iH2+f8wJsUYB70XX6QNm7WqmYoreV2hWJ4LUSuplcQnJzhIMMUBl/QyzOaZV1rRk3/wxdRQEwYnRZTPCoInE0tzE8imTa5089CQ+yEscFdE4EhIka7Ju3lbLprjWDVt9GG9/A0Q03ovVNWEdGA5nWcrn/jik5ZcSh/wTvmo/BpMXJ5Ha4oodgiDRejOqAEjh0yWDuz/AkBNQ19stmau8cVb85uCApZO1g3c7gKAvmYirw2lOaZUsq5x9/1YIya817bhjfhxHeFYPPYf9qxO+HDeSkbUCAwEAAaMhMB8wHQYDVR0OBBYEFLBW6P0A+qHESOFg8Rgxqp38myYtMA0GCSqGSIb3DQEBCwUAA4IBAQAsZzkzk8w7RR3KCHOY+XLn3R2NanL/j+WILdOHnJn9Ot1VbG868MFQgwMp8Y2y7Kj5RekknY6EGcNuJi4rLgq5u1LSB/IoNPCs7l3MhRQqoedJX4sDNf4NfTVHK+4GNSQqP60eBoxClRexIbKcHJ0x57Ww/S9NNWtldBIfB7egoSj6UVcTHRLWZyPoZsOXHY4bYOf8ANNg21jT1KWwOXSWUx60v7tVxEXs8XAEUnmuMbuh3yAnjv3UoRdl7wcaQ5jq2/+vaAWZm0WlWN3CCY3y2mE0OZZg9HRCQu+o+58wt658sDIpP7PXGjyA5h23W9+i8QtyQ1PtqCXKj8zktivW" - ] - }, - { - "kty": "RSA", - "use": "sig", - "kid": "ZZ8JkzXRCgYMWcMYdOn9sdDBeUA", - "x5t": "ZZ8JkzXRCgYMWcMYdOn9sdDBeUA", - "n": "iJd0N795eVyYQvWe417HOF_GHlRgOsPZRh1KwNHyWP_WKrjlOl8ftPAs-Sspv-s8v68TilHYkY9pjUdEkxBvBolJiPP6ntAKIKk_Fa4K7sutgQdyxKehhRnk4hAIc-mUM9ROrkyr4dIi-Au9T7aWaBSG5dCffXQBQ1DBVbIUMNOwr4ewQYeb49ujxzE6dPiCB-uDX2Z_hjy9M8wMrHS8e2vKDYqx3AJ3xyiDjIDB-wBEe-SF5bKVNSfExcsiL0KzV_iQkKQNALJrakX4Mw-hC3ssv7q3NQcza9kpyew0TKytSOcJcIheX9Cse22F-y1h873iOkYWiebNIu5TeTjVww", - "e": "AQAB", - "x5c": [ - "MIIC6TCCAdGgAwIBAgIIHxgoSKq7mWgwDQYJKoZIhvcNAQELBQAwIzEhMB8GA1UEAxMYbG9naW4ubWljcm9zb2Z0b25saW5lLnVzMB4XDTI0MDYxMDE4MjgyOFoXDTI5MDYxMDE4MjgyOFowIzEhMB8GA1UEAxMYbG9naW4ubWljcm9zb2Z0b25saW5lLnVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiJd0N795eVyYQvWe417HOF/GHlRgOsPZRh1KwNHyWP/WKrjlOl8ftPAs+Sspv+s8v68TilHYkY9pjUdEkxBvBolJiPP6ntAKIKk/Fa4K7sutgQdyxKehhRnk4hAIc+mUM9ROrkyr4dIi+Au9T7aWaBSG5dCffXQBQ1DBVbIUMNOwr4ewQYeb49ujxzE6dPiCB+uDX2Z/hjy9M8wMrHS8e2vKDYqx3AJ3xyiDjIDB+wBEe+SF5bKVNSfExcsiL0KzV/iQkKQNALJrakX4Mw+hC3ssv7q3NQcza9kpyew0TKytSOcJcIheX9Cse22F+y1h873iOkYWiebNIu5TeTjVwwIDAQABoyEwHzAdBgNVHQ4EFgQUcLvbIYVCbexuF1KXcKysM8kS6EMwDQYJKoZIhvcNAQELBQADggEBAF95Wf/yAfmHksmL42JiCemjsHN0KlZ2NsGTj2+zbDXbttj8zm+ZA74bPlAWI5aFvKfxxpC3Chfi26+GhKVeVRA65KyokTulQzE+BWbqphQZoH6Iz07J3GB3uUthPQbedtj6SDD/zE4jcmhmrY8o0lU5zJhkp9T5f8644ZR6rJRIXpFbDwmbsFM5H4Nz7D5FG+A4uYumICoTaiQjJ+cu/k8sDM8ut6R2cGmwlRMIGzD8HzNeGuaRtXsFqCGAI+qRbW29hJoFNZxhQBeFRDdBvwbNIa/o6ZAzKq81E4SdV1d33oM3vWDMBlR3b46a1d+Unm1Ou8uJ2yDfqMrZ7/NGNV8=" - ] - } - ] - } - """; - public static OpenIdConnectConfiguration AADCommonV1Config { get @@ -390,7 +304,6 @@ public static OpenIdConnectConfiguration AADCommonV1Config config.AdditionalData.Add("cloud_graph_host_name", "graph.windows.net"); config.AdditionalData.Add("msgraph_host", "graph.microsoft.com"); config.AdditionalData.Add("rbac_url", "https://pas.windows.net"); - config.JsonWebKeySet = JsonWebKeySet.Create(AADCommonV1JwksString); return config; } @@ -427,133 +340,6 @@ public static OpenIdConnectConfiguration AADCommonV1Config } """; - public static string AADCommonV2JwksString => - """ - { - "keys": [ - { - "kty": "RSA", - "use": "sig", - "kid": "23ntaxfH9Gk-8D_USzoAJgwjyt8", - "x5t": "23ntaxfH9Gk-8D_USzoAJgwjyt8", - "n": "hu2SJrLlDOUtU2s9T6-6OVGEPaba2zIT2_Jl50f4NGG-r-GyQdaOzTFASfAfMkMfMQMRnabqd-dp_Ooqha473bw6DMbM23nv2uhBn5Afp-S1W_d4NxEhfNlN1Tgjx3Sh6UblBSFCE4JGkugSkLi2SVouy43seskesQotXGVNv4iboFm4yO_twlMCG9EDwza32y6WZtV8i9gkQP42OfK0X1qy6EUz2DN7cpfZtmkNtsFJhFf9waOvNCR95LVCPGafeCOMAQEvu1VO3mrBSIg7Izu0CzvuaBQTwnGv29Ggxc3GO4gvb_OStkkmfIwchu3A8F6e0aJ4Ys8PFP7z7Z8lqQ", - "e": "AQAB", - "x5c": [ - "MIIC/jCCAeagAwIBAgIJAJB4tJM2GkZjMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMjQwNTIyMTg1ODQwWhcNMjkwNTIyMTg1ODQwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhu2SJrLlDOUtU2s9T6+6OVGEPaba2zIT2/Jl50f4NGG+r+GyQdaOzTFASfAfMkMfMQMRnabqd+dp/Ooqha473bw6DMbM23nv2uhBn5Afp+S1W/d4NxEhfNlN1Tgjx3Sh6UblBSFCE4JGkugSkLi2SVouy43seskesQotXGVNv4iboFm4yO/twlMCG9EDwza32y6WZtV8i9gkQP42OfK0X1qy6EUz2DN7cpfZtmkNtsFJhFf9waOvNCR95LVCPGafeCOMAQEvu1VO3mrBSIg7Izu0CzvuaBQTwnGv29Ggxc3GO4gvb/OStkkmfIwchu3A8F6e0aJ4Ys8PFP7z7Z8lqQIDAQABoyEwHzAdBgNVHQ4EFgQUeGPdsxkVp8lIRku0u41SCzqW7LIwDQYJKoZIhvcNAQELBQADggEBAHMJCPO473QQJtTXJ49OhZ48kVCiVgbut+xElHxvBWQrfJ4Zb6WAi2RudjwrpwchVBciwjIelp/3Ryp5rVL94D479Ta/C5BzWNm9LsZCw3rPrsIvUdx26GmfQomHyL18AJQyBj8jZ+pVvdprvbV7v586TcgY24pW018IiYGQEO/fR8DSO4eN8ekTvT8hODBoKiJ9NFy+BruqW1AbMDptH12uzpU/N9bftysnWeDJEVZd5Rj8u8F9MRbB6V7dzxdoswaKkiJbxt+JrZgdtHSFqz6rDypIkumYwUkyiwH4/GQGPiyBLFbRp1EYVa3SFwAEmhl4a7On05aHVnOfCoyj/qA=" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "MGLqj98VNLoXaFfpJCBpgB4JaKs", - "x5t": "MGLqj98VNLoXaFfpJCBpgB4JaKs", - "n": "yfNcG8Ka_b4R7niLqdzlFvzRjrTdl2wTVEtqRWXqDhJAt_KIizVrqe0x3E1tNohmySHAMz3IS4llC41ML5YUDZVg33XR0RMc6UntOq-qCSOkPXdliC3QkwxspGAaJxVzhO5OZuQlMoQNL6_1FgdaR62gfayjLSJepB8M-o7yC8sOtRhatwe9kbO_5QJC54B8ni0ge5i9nANMln-9ZCHeRQYkgl0RSvR_KtfpWrEqAa4K2cyPaDqejOs8G8V0kM_8CLtDWi5diKpO_fvzRJwparEB5hfMdjAyJgdTOqCVUulZdL7tsoHzb8-_ufq-5QFJkyNUpYB9R1mVQwmRGdY0nQ", - "e": "AQAB", - "x5c": [ - "MIIC/jCCAeagAwIBAgIJAJtuCSyF4i1FMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMjQwNjA5MTkxNzM5WhcNMjkwNjA5MTkxNzM5WjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyfNcG8Ka/b4R7niLqdzlFvzRjrTdl2wTVEtqRWXqDhJAt/KIizVrqe0x3E1tNohmySHAMz3IS4llC41ML5YUDZVg33XR0RMc6UntOq+qCSOkPXdliC3QkwxspGAaJxVzhO5OZuQlMoQNL6/1FgdaR62gfayjLSJepB8M+o7yC8sOtRhatwe9kbO/5QJC54B8ni0ge5i9nANMln+9ZCHeRQYkgl0RSvR/KtfpWrEqAa4K2cyPaDqejOs8G8V0kM/8CLtDWi5diKpO/fvzRJwparEB5hfMdjAyJgdTOqCVUulZdL7tsoHzb8+/ufq+5QFJkyNUpYB9R1mVQwmRGdY0nQIDAQABoyEwHzAdBgNVHQ4EFgQUzF0gtMcVDEn4JoNlDOxvhM8IHBswDQYJKoZIhvcNAQELBQADggEBAJe2muR0H2h3phiZ/v6FD8Yio6niulN9jr7+eC/UJV1M7l5xdHgVL83JbNZjUECDrJ/m+ICY1NbEXfv4fo3sfpU1AwG5GXAhxTrS4zMhH7Hvir3800wCd3ByJ/2vQW1y3orlqR8Q65BN9ayub6BCBTNmtUAOpAWcnP3FnGtIDmAL4APcacK92ZTg8ayVX586U7DDWmI4l7X6xCruK0ic5W2b13k2cay0EalHNWHl+gikqQg6tTGSvM295P6Xy5bQ1I5QtHjVCbm0315T/FylvR8fZhVD+AUCc1DwtOr3Yhm3EXftDb6hP08C4yDhGIDH3Q3+xuWlIA7KQjgljuiT67U=" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "inEVM76gXEQEonQ0PSQXBO_7HfU", - "x5t": "inEVM76gXEQEonQ0PSQXBO_7HfU", - "n": "q0sct8P8TxXmXX2QXzIhMnwZHdCO96SMFCMtfswF1TpxYqaIFObIhT_zxxpBTsvkYHAxG7CUQ6qVgd_TQhMx0TSZq_X3_0NG6cIRik0g-Woe0gT6tUJ-o6zdtO-6EvoOXovT3YMh8vN1Q5UJV6dudDqjnlTNHH1OxFcU4U6no1R6iILDMci_TGq7I2AJS5i_O9Ptp5NmgDT_kbwZHJz1Abbw4VuOPMFJ2Q1rN9odV9YHKjjowqa3BULVyTvP8FoGUzhoopu6O7oA-ehlO9fhEoSS0zNn0lWXQMZXUF7GSyui12121kIXyll2KlvuETQNdVkeXu0m95g_pnX-8iZ_cw", - "e": "AQAB", - "x5c": [ - "MIIC/TCCAeWgAwIBAgIINBmSj+3xdxwwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDAeFw0yNDA2MTIxNjA3MjBaFw0yOTA2MTIxNjA3MjBaMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrSxy3w/xPFeZdfZBfMiEyfBkd0I73pIwUIy1+zAXVOnFipogU5siFP/PHGkFOy+RgcDEbsJRDqpWB39NCEzHRNJmr9ff/Q0bpwhGKTSD5ah7SBPq1Qn6jrN2077oS+g5ei9PdgyHy83VDlQlXp250OqOeVM0cfU7EVxThTqejVHqIgsMxyL9MarsjYAlLmL870+2nk2aANP+RvBkcnPUBtvDhW448wUnZDWs32h1X1gcqOOjCprcFQtXJO8/wWgZTOGiim7o7ugD56GU71+EShJLTM2fSVZdAxldQXsZLK6LXbXbWQhfKWXYqW+4RNA11WR5e7Sb3mD+mdf7yJn9zAgMBAAGjITAfMB0GA1UdDgQWBBSZbhe/r/sxfv0nYlyrwjx+b6W2RTANBgkqhkiG9w0BAQsFAAOCAQEAoWZ+C/snZySK1KiOsrn1iq7wrVzkuModPMZEshR3SuDIB6+C76fmP42I3UtDVIY5EeE79YjdwDwy86dPZjKVNbP7yUSbJC8uPM1TNMA9s8QpO6RZ63ZZ4i8hcgk6PXgi0PPjX2cmzUSNUa4gS8ibhf7JDu4aF9lUceBsNQghNQfz3tBs1ksJoW3WY5EfW6yMCv1Vim0uBlpnYdlynAd8O+9N2JR9wC+12PwPGrdGQDX3pos8bnmBxM55ueiGoqDH5yGI1h63POlGnpEdqOONT8N4cZNazQ1NswbBQuZMZSfbPXjiiFQ4bktyiXr421KbknQrkRYogi1F2Cjrd4SZJg==" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "KQ2tAcrE7lBaVVGBmc5FobgdJo4", - "x5t": "KQ2tAcrE7lBaVVGBmc5FobgdJo4", - "n": "08xqQ-OBv9jvWmtvWw8g3IkcuDHVOAGCn3K6TXyKie0L6cAyQWNX4vqxbt0cHdaLunrzaFJ2mIGj_qfor8KR_FOFVFOF24FAakB5El96LvsTwlWJNIw4kpf1O_xibycZ_UcDAEqABJfe51JSPh-PxI2sXt0UMapSjvTdnps0Conp11Ay_yupl_h7nawVg0kzw3QDX5-vKTruiHAHr845YwDRW1yJLEgkUPYXdM8d_SrRgqb2RKJEN8D1c4-SUpFHKwGAwLgVYH1cqwADX9el857z_2uKqJoP48l8WqUOfNGdvx79RCgF1NzzRh07EQrk0GJ_EB8eO-EF4YHLPImVtQ", - "e": "AQAB", - "x5c": [ - "MIIC/TCCAeWgAwIBAgIIRf5MUh/1XVIwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDAeFw0yNDA3MTAxNjA0NTBaFw0yOTA3MTAxNjA0NTBaMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTzGpD44G/2O9aa29bDyDciRy4MdU4AYKfcrpNfIqJ7QvpwDJBY1fi+rFu3Rwd1ou6evNoUnaYgaP+p+ivwpH8U4VUU4XbgUBqQHkSX3ou+xPCVYk0jDiSl/U7/GJvJxn9RwMASoAEl97nUlI+H4/Ejaxe3RQxqlKO9N2emzQKienXUDL/K6mX+HudrBWDSTPDdANfn68pOu6IcAevzjljANFbXIksSCRQ9hd0zx39KtGCpvZEokQ3wPVzj5JSkUcrAYDAuBVgfVyrAANf16XznvP/a4qomg/jyXxapQ580Z2/Hv1EKAXU3PNGHTsRCuTQYn8QHx474QXhgcs8iZW1AgMBAAGjITAfMB0GA1UdDgQWBBQGoURL0sKGdYALEdvfObZ6NEgmJTANBgkqhkiG9w0BAQsFAAOCAQEAl/UkmIa4OvsgULkBmGIZ6HeyJDvVuORphBK9/vpxEFsgnlitwMncBO54uMjJVr63baV490ODSI+ZTiCh7WGM+zrSjllCbVWDxjrdXA1ygHnXX7bXecIQyDmVb5/Hfb7DmQ4MHa3lEwf+pNS5XJeOhPoduRsfYCdD0QbxEADDgqV4FtgYx4I+iAoqbPDPou7wchEu9d3MuFuTMorkTvDLCyTHi2rgBnk9GBf2rArCGyTpvVPGXmxBttqgm9krFRujLj00u9jKUx4YkmAhS9YRddME8+gh6X4qFMxQMhyzkaBxjLs/E+pwMJaUwBqvostwt9+52qrMSUo+jkFgiGCe4Q==" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "EHu9neGZBCDyv2IYq8U5JiRMFng", - "x5t": "EHu9neGZBCDyv2IYq8U5JiRMFng", - "n": "w1kH9dFGdaJS8fvQulDssuuNhkczzy1Mo6IiNoC3ih3K-L_VF5TQmSkqXrovWCUlhBCfc1VPR9Cn2G4UP7Sygn0nTqXBY1NFQQZecqwGESJFIuonRqjdlDhNYXjSF_eg63KyuyLV8A-Sn05Ufuc8ax0tyrxPbkOql0pB2hmRhj94iDAFB2LBoxfEgxCG3VT0ascVYW6voTCChs2P65-4RLC-ib1w1FjuACDwsB7KZDxxaUGLfnIoLWUjmw1zCaDRiRvhxB4jQXpB64IFxaYsqxA_x8bj2JEE7qALZ2dZ3fPy9yYSAnRfaTMetgouR9x4SKy4HxUxsADMm_7p9LiRZQ", - "e": "AQAB", - "x5c": [ - "MIIC6jCCAdKgAwIBAgIJAO8yTjZIibNNMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czAeFw0yNDA1MDYyMzA5MDJaFw0yOTA1MDYyMzA5MDJaMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMNZB/XRRnWiUvH70LpQ7LLrjYZHM88tTKOiIjaAt4odyvi/1ReU0JkpKl66L1glJYQQn3NVT0fQp9huFD+0soJ9J06lwWNTRUEGXnKsBhEiRSLqJ0ao3ZQ4TWF40hf3oOtysrsi1fAPkp9OVH7nPGsdLcq8T25DqpdKQdoZkYY/eIgwBQdiwaMXxIMQht1U9GrHFWFur6EwgobNj+ufuESwvom9cNRY7gAg8LAeymQ8cWlBi35yKC1lI5sNcwmg0Ykb4cQeI0F6QeuCBcWmLKsQP8fG49iRBO6gC2dnWd3z8vcmEgJ0X2kzHrYKLkfceEisuB8VMbAAzJv+6fS4kWUCAwEAAaMhMB8wHQYDVR0OBBYEFJ4xtCt3JpPxlUVH7ATgJGM4ofg7MA0GCSqGSIb3DQEBCwUAA4IBAQB9WAEvE3VtO5wIOtN5N/QbIU63H5QPgMW3M9nOs43AhLgwvWupxaiATyMqtK53RPvcxYPe7QwSw/xH9McXii1bOBVmc71AcjlXYfuMJ/0IMEFEUQwZDEwj+vIlg07gWh0hleehyAgMblDUQRRN+b5J+soa9LBBAooY/48F/++y4DiTzKyoWn5cV4H2kdIFVyB43XzJRqDoK1ZhplVLTc1a3K1NL1/qP9rhvtx62YDzfNh4+FTJLu31ALcUbD+Qx2m0U9wuWq3EdUzEen5DeLvhx55YD7V1BASHNYBd8lGhHk97aTw53CMGAuTELvWO+4x7dFM9autw2KvSn76n/4Ql" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "FB8_wii85nv_UW3qrldTvWwg-rE", - "x5t": "FB8_wii85nv_UW3qrldTvWwg-rE", - "n": "vusbA5UBNtCB0U2RmyQOCE-8fWl8bzCQXm3V5Nd7oockcyCpqXOWfhVNJD-Ifb5_zAmxRgHvRdfpA2btaqZiit5XaFYngtRK6mVxCcnOEgwxQGX9DLM5plXWtGTf_DF1FATBidFlM8KgicTS3MTyKZNrnTz0JD7ISxwV0TgSEiRrsm7eVsumuNYNW30Yb38DRDTei9U1YR0YDmdZyuf-OKTllxKH_BO-aj8Gkxcnkdriih2CINF6M6oASOHTJYO7P8CQE1DX2y2Zq7xxVvzm4IClk7WDdzuAoC-ZiKvDaU5plSyrnH3_VgjJrzXtuGN-HEd4Vg89h_2rE74cN5KRtQ", - "e": "AQAB", - "x5c": [ - "MIIC6jCCAdKgAwIBAgIJAJOO92n+BJBLMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czAeFw0yNDA2MTAxODA3NDVaFw0yOTA2MTAxODA3NDVaMCMxITAfBgNVBAMTGGxvZ2luLm1pY3Jvc29mdG9ubGluZS51czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7rGwOVATbQgdFNkZskDghPvH1pfG8wkF5t1eTXe6KHJHMgqalzln4VTSQ/iH2+f8wJsUYB70XX6QNm7WqmYoreV2hWJ4LUSuplcQnJzhIMMUBl/QyzOaZV1rRk3/wxdRQEwYnRZTPCoInE0tzE8imTa5089CQ+yEscFdE4EhIka7Ju3lbLprjWDVt9GG9/A0Q03ovVNWEdGA5nWcrn/jik5ZcSh/wTvmo/BpMXJ5Ha4oodgiDRejOqAEjh0yWDuz/AkBNQ19stmau8cVb85uCApZO1g3c7gKAvmYirw2lOaZUsq5x9/1YIya817bhjfhxHeFYPPYf9qxO+HDeSkbUCAwEAAaMhMB8wHQYDVR0OBBYEFLBW6P0A+qHESOFg8Rgxqp38myYtMA0GCSqGSIb3DQEBCwUAA4IBAQAsZzkzk8w7RR3KCHOY+XLn3R2NanL/j+WILdOHnJn9Ot1VbG868MFQgwMp8Y2y7Kj5RekknY6EGcNuJi4rLgq5u1LSB/IoNPCs7l3MhRQqoedJX4sDNf4NfTVHK+4GNSQqP60eBoxClRexIbKcHJ0x57Ww/S9NNWtldBIfB7egoSj6UVcTHRLWZyPoZsOXHY4bYOf8ANNg21jT1KWwOXSWUx60v7tVxEXs8XAEUnmuMbuh3yAnjv3UoRdl7wcaQ5jq2/+vaAWZm0WlWN3CCY3y2mE0OZZg9HRCQu+o+58wt658sDIpP7PXGjyA5h23W9+i8QtyQ1PtqCXKj8zktivW" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "ZZ8JkzXRCgYMWcMYdOn9sdDBeUA", - "x5t": "ZZ8JkzXRCgYMWcMYdOn9sdDBeUA", - "n": "iJd0N795eVyYQvWe417HOF_GHlRgOsPZRh1KwNHyWP_WKrjlOl8ftPAs-Sspv-s8v68TilHYkY9pjUdEkxBvBolJiPP6ntAKIKk_Fa4K7sutgQdyxKehhRnk4hAIc-mUM9ROrkyr4dIi-Au9T7aWaBSG5dCffXQBQ1DBVbIUMNOwr4ewQYeb49ujxzE6dPiCB-uDX2Z_hjy9M8wMrHS8e2vKDYqx3AJ3xyiDjIDB-wBEe-SF5bKVNSfExcsiL0KzV_iQkKQNALJrakX4Mw-hC3ssv7q3NQcza9kpyew0TKytSOcJcIheX9Cse22F-y1h873iOkYWiebNIu5TeTjVww", - "e": "AQAB", - "x5c": [ - "MIIC6TCCAdGgAwIBAgIIHxgoSKq7mWgwDQYJKoZIhvcNAQELBQAwIzEhMB8GA1UEAxMYbG9naW4ubWljcm9zb2Z0b25saW5lLnVzMB4XDTI0MDYxMDE4MjgyOFoXDTI5MDYxMDE4MjgyOFowIzEhMB8GA1UEAxMYbG9naW4ubWljcm9zb2Z0b25saW5lLnVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiJd0N795eVyYQvWe417HOF/GHlRgOsPZRh1KwNHyWP/WKrjlOl8ftPAs+Sspv+s8v68TilHYkY9pjUdEkxBvBolJiPP6ntAKIKk/Fa4K7sutgQdyxKehhRnk4hAIc+mUM9ROrkyr4dIi+Au9T7aWaBSG5dCffXQBQ1DBVbIUMNOwr4ewQYeb49ujxzE6dPiCB+uDX2Z/hjy9M8wMrHS8e2vKDYqx3AJ3xyiDjIDB+wBEe+SF5bKVNSfExcsiL0KzV/iQkKQNALJrakX4Mw+hC3ssv7q3NQcza9kpyew0TKytSOcJcIheX9Cse22F+y1h873iOkYWiebNIu5TeTjVwwIDAQABoyEwHzAdBgNVHQ4EFgQUcLvbIYVCbexuF1KXcKysM8kS6EMwDQYJKoZIhvcNAQELBQADggEBAF95Wf/yAfmHksmL42JiCemjsHN0KlZ2NsGTj2+zbDXbttj8zm+ZA74bPlAWI5aFvKfxxpC3Chfi26+GhKVeVRA65KyokTulQzE+BWbqphQZoH6Iz07J3GB3uUthPQbedtj6SDD/zE4jcmhmrY8o0lU5zJhkp9T5f8644ZR6rJRIXpFbDwmbsFM5H4Nz7D5FG+A4uYumICoTaiQjJ+cu/k8sDM8ut6R2cGmwlRMIGzD8HzNeGuaRtXsFqCGAI+qRbW29hJoFNZxhQBeFRDdBvwbNIa/o6ZAzKq81E4SdV1d33oM3vWDMBlR3b46a1d+Unm1Ou8uJ2yDfqMrZ7/NGNV8=" - ], - "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "dGtHQMhGltJUCcH_SQW64nEUoYE", - "x5t": "dGtHQMhGltJUCcH_SQW64nEUoYE", - "n": "io_Qh_kyYBnXCXPV54XbUZheP_fpWo5M0-_aWJQ6i-CebDHQVpxHurahUYj446qEvUBFK-goUEDU1Ah87F_KXNDQhsJq0F422joJPIzsHSsed_k0KlYnkJgCeUC8yHmtgSnNjH7jCnnBZ6Oznt0rdEw9MVd_2ofWgoA28XRUQ_arpXgGo8EWSPWuLGsG3cKTsSVW-1d_JSZ56S73j5YBDQz11ZPVm13nWohrGEgPBgswCCLUsZod0t1oTiRmKihRom-FhWvsfFixUZ4D39XSk51UjWttu1gnhhxhV7PVqlaqbvQ1D2urlpgMnAgyeQrIUC-3L-fN6hwD_1NZCaQdeQ", - "e": "AQAB", - "x5c": [ - "MIIDCzCCAfOgAwIBAgIRAKdbZc1Eb0WDFn5HsLQuwwYwDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAxMeTGl2ZSBJRCBTVFMgU2lnbmluZyBQdWJsaWMgS2V5MB4XDTI0MDcxNDE1MDI1MFoXDTI5MDcxNDE1MDI1MFowKTEnMCUGA1UEAxMeTGl2ZSBJRCBTVFMgU2lnbmluZyBQdWJsaWMgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAio/Qh/kyYBnXCXPV54XbUZheP/fpWo5M0+/aWJQ6i+CebDHQVpxHurahUYj446qEvUBFK+goUEDU1Ah87F/KXNDQhsJq0F422joJPIzsHSsed/k0KlYnkJgCeUC8yHmtgSnNjH7jCnnBZ6Oznt0rdEw9MVd/2ofWgoA28XRUQ/arpXgGo8EWSPWuLGsG3cKTsSVW+1d/JSZ56S73j5YBDQz11ZPVm13nWohrGEgPBgswCCLUsZod0t1oTiRmKihRom+FhWvsfFixUZ4D39XSk51UjWttu1gnhhxhV7PVqlaqbvQ1D2urlpgMnAgyeQrIUC+3L+fN6hwD/1NZCaQdeQIDAQABoy4wLDAdBgNVHQ4EFgQUS8ytdfEdWAsFJApZy/6lA7wlfgowCwYDVR0PBAQDAgLEMA0GCSqGSIb3DQEBCwUAA4IBAQBDnyg3sAC3S64Pm4xA81r2kts96usCRu7tF34f3RJX7Met+rJMrllpRT8zVTzFTaPjHsJvhl5F/ApD0lZN6noy7UwNjbnMoC/lYluPLDuQE4ClstsgpNBdSNF0l+tWk085sIM7LF3wAuf17Yp5jIXCyokbbDJb5+XpNGZm4ukTLADajk/jk76z7p94shgV1XMla3fV+1d7jDL6UlbIvXNUSp3swvSLQPv90sSI2OUwTTulNZDokmeWtLUedTTIpnu9y+vLJWbKiwtenYbj3zM7VN/Qr5aXl4w3Ajx+QKnRydv1se8ycMabu28OFXgP92AsY1/NW4BF6321OOq2OmbC" - ], - "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "pb_TKRXVJY-27vIG_A81PMH-cdY", - "x5t": "pb_TKRXVJY-27vIG_A81PMH-cdY", - "n": "iM24cYc714exgvGQeAuw6pqYqkSf7NEuLug5jCYcGqa2APSSjzks5h7en-uEEnE0q03VeaJB6PWxaE3GTfGKXzr-sPudGCTTOsgnY3t4ms3DLeyhZvWi5ADc4JtpLBQOxYm1f4ReGwryZqsOHdvqNiYn7B7PyN_3dVbUuXWaueCJ3hhW5JyXkRGD75cOsgOm7GU3tYtOcxm29yjOzNcQXOiL_fChEz6G6bjOHzFYISgv5m7TffaOEFF4T4RoP4AQ35zvxjHx8XkBQPTz661TjTN1h_mYsFEwa2cDcErjJ4dJTdKSkM-VFPDklcSXsrDhkOw42ZeuKAoQTVep5EJ71w", - "e": "AQAB", - "x5c": [ - "MIIDCjCCAfKgAwIBAgIQNas2IybvbYSgWXpOzctlSDANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDEx5MaXZlIElEIFNUUyBTaWduaW5nIFB1YmxpYyBLZXkwHhcNMjQwNzAxMTgzMzA1WhcNMjkwNzAxMTgzMzA1WjApMScwJQYDVQQDEx5MaXZlIElEIFNUUyBTaWduaW5nIFB1YmxpYyBLZXkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCIzbhxhzvXh7GC8ZB4C7DqmpiqRJ/s0S4u6DmMJhwaprYA9JKPOSzmHt6f64QScTSrTdV5okHo9bFoTcZN8YpfOv6w+50YJNM6yCdje3iazcMt7KFm9aLkANzgm2ksFA7FibV/hF4bCvJmqw4d2+o2JifsHs/I3/d1VtS5dZq54IneGFbknJeREYPvlw6yA6bsZTe1i05zGbb3KM7M1xBc6Iv98KETPobpuM4fMVghKC/mbtN99o4QUXhPhGg/gBDfnO/GMfHxeQFA9PPrrVONM3WH+ZiwUTBrZwNwSuMnh0lN0pKQz5UU8OSVxJeysOGQ7DjZl64oChBNV6nkQnvXAgMBAAGjLjAsMB0GA1UdDgQWBBRsbUU+6mjS1sX/3Ek+xKEA6JTeLDALBgNVHQ8EBAMCAsQwDQYJKoZIhvcNAQELBQADggEBACO1MS24nE70L0Tcw4NRv80uZ8b5OWAfsAO+AN7zwXeo6J7TN/sslMuQ9FtL3Coot2ItdYaFHmfzKijuCV17EiWdXXccwoGEZqp3y2gvyYCof2OVQK4/KZVPUhI8wg2kR8dn09B9fdiMmwqd2+ZezWbgvSz1fQz5gZCg5FbFFojYvQL65bIq3tUZBtAT7ixrcGOfFbYzZbpi4mJdJItidd3Oh+TXfexzRL5Cw7Zn4LGlUVUOwildBfYtB+Fr022wutr/adxjJV7wgr6AxaTlls/hQz6+TOs8Vmyeb8KsU9CJZRXPIBKvZwAyMJsDZ3l4x+XPAZYQo3i6Oa4F5ROR9ZM=" - ], - "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0" - }, - { - "kty": "RSA", - "use": "sig", - "kid": "yBm7yTZhZ3SWV3-9inQcQk1X51U", - "x5t": "yBm7yTZhZ3SWV3-9inQcQk1X51U", - "n": "nwNwsp54S9SUESzWUZXc0dY19bOVn4smmRSxANxPblU0nQEBpDPumlBVYmHI3XXVIshrh2DAl4BSVfQhVKLCu35Vyv7_P9cLvmqM_dvIHEjtrQPPFIBlH6fitB4v5zs7i7_zV-mTteGsNoUWg-TtHHKekJBrrBxoJ633vvaZ9AEFP8OdZoVGjXW1Wb76nczV8uhjgF9u69XrOPVrYB7YcxtiA-jRzn8AQRt8SfkrIvEjDL5ejtxRNyucz8dFzmbrCazoUY3oeei6UHjdtFgiODs4KE29e6p1Lm4CexjkcIrFWXkoxytOKEsB5zCGq8pQeI-tGmoCBhVnhNw7u5okjQ", - "e": "AQAB", - "x5c": [ - "MIIDCzCCAfOgAwIBAgIRAMtLWPmqFNLXNg6BbZWr9EAwDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAxMeTGl2ZSBJRCBTVFMgU2lnbmluZyBQdWJsaWMgS2V5MB4XDTI0MDYxODIyMzE1MFoXDTI5MDYxODIyMzE1MFowKTEnMCUGA1UEAxMeTGl2ZSBJRCBTVFMgU2lnbmluZyBQdWJsaWMgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnwNwsp54S9SUESzWUZXc0dY19bOVn4smmRSxANxPblU0nQEBpDPumlBVYmHI3XXVIshrh2DAl4BSVfQhVKLCu35Vyv7/P9cLvmqM/dvIHEjtrQPPFIBlH6fitB4v5zs7i7/zV+mTteGsNoUWg+TtHHKekJBrrBxoJ633vvaZ9AEFP8OdZoVGjXW1Wb76nczV8uhjgF9u69XrOPVrYB7YcxtiA+jRzn8AQRt8SfkrIvEjDL5ejtxRNyucz8dFzmbrCazoUY3oeei6UHjdtFgiODs4KE29e6p1Lm4CexjkcIrFWXkoxytOKEsB5zCGq8pQeI+tGmoCBhVnhNw7u5okjQIDAQABoy4wLDAdBgNVHQ4EFgQUFGb4FaXDu89wqG9A6JK1xLUMKPUwCwYDVR0PBAQDAgLEMA0GCSqGSIb3DQEBCwUAA4IBAQAw3cpDyl03Kgka9P2BJR6xU+C+IiWpJVLbLbdvBepqGI1/NqXCrG7E4INS5oRsFVO8DuYXws7Ko5kKCTV+iqkGngtG9b/JFP8QBcRrhngHTnE8EevwLkDtqFvpBdNnzTmOJDP4FdtYRuucJqx7aLE1MXr2jEkKfY7YLu2YEmOG6hnZfqWeCRm+g9eUolbhexllsdtj3bi9V9c8anXPLUsEeY/BRT7n4TBGJBWDD9kYEgoMKPLp58Om8aY6BucKN6vjf/v9RR//2ggCXX+qrZP3ebj9cXI6dWtgn5WwkBTfufIXnbbrzyCp/jdCP7q8SXbG2MeFiqKLKul5q5neiIdm" - ], - "issuer": "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0" - } - ] - } - """; public static OpenIdConnectConfiguration AADCommonV2Config { get @@ -584,7 +370,6 @@ public static OpenIdConnectConfiguration AADCommonV2Config config.AdditionalData.Add("cloud_graph_host_name", "graph.windows.net"); config.AdditionalData.Add("msgraph_host", "graph.microsoft.com"); config.AdditionalData.Add("rbac_url", "https://pas.windows.net"); - config.JsonWebKeySet = JsonWebKeySet.Create(AADCommonV2JwksString); return config; } diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs index 2e3c38f305..22454614f5 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; using Microsoft.IdentityModel.TestUtils; using Microsoft.IdentityModel.Tokens.Json.Tests; using Xunit; @@ -24,7 +23,6 @@ public void Deserialize(OpenIdConnectTheoryData theoryData) OpenIdConnectConfiguration configuration = new OpenIdConnectConfiguration(theoryData.Json); OpenIdConnectConfiguration configurationUpperCase = new OpenIdConnectConfiguration(JsonUtilities.SetPropertiesToUpperCase(theoryData.Json)); theoryData.ExpectedException.ProcessNoException(context); - context.PropertiesToIgnoreWhenComparing.Add(typeof(OpenIdConnectConfiguration), new List { "JsonWebKeySet" }); IdentityComparer.AreEqual(configuration, theoryData.CompareTo, context); IdentityComparer.AreEqual(configurationUpperCase, theoryData.CompareTo, context); diff --git a/test/Microsoft.IdentityModel.Protocols.Tests/ExtensibilityTests.cs b/test/Microsoft.IdentityModel.Protocols.Tests/ExtensibilityTests.cs index d633eb48fa..7d71601577 100644 --- a/test/Microsoft.IdentityModel.Protocols.Tests/ExtensibilityTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.Tests/ExtensibilityTests.cs @@ -73,27 +73,24 @@ public void ConfigurationManagerUsingCustomClass() { var docRetriever = new FileDocumentRetriever(); var configManager = new ConfigurationManager("IssuerMetadata.json", new IssuerConfigurationRetriever(), docRetriever); - var context = new CompareContext($"{this}.ConfigurationManagerUsingCustomClass"); + var context = new CompareContext($"{this}.GetConfiguration"); var configuration = configManager.GetConfigurationAsync().Result; configManager.MetadataAddress = "IssuerMetadata.json"; var configuration2 = configManager.GetConfigurationAsync().Result; - if (!IdentityComparer.AreEqual(configuration.Issuer, configuration2.Issuer, context)) + if (!IdentityComparer.AreEqual(configuration.Issuer, configuration2.Issuer)) context.Diffs.Add("!IdentityComparer.AreEqual(configuration, configuration2)"); // AutomaticRefreshInterval should pick up new bits. configManager = new ConfigurationManager("IssuerMetadata.json", new IssuerConfigurationRetriever(), docRetriever); configManager.RequestRefresh(); configuration = configManager.GetConfigurationAsync().Result; - TestUtilities.SetField(configManager, "_lastRequestRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); + TestUtilities.SetField(configManager, "_lastRefresh", DateTimeOffset.UtcNow - TimeSpan.FromHours(1)); configManager.MetadataAddress = "IssuerMetadata2.json"; configManager.RequestRefresh(); - - // Wait for the refresh to complete. - Thread.Sleep(50); configuration2 = configManager.GetConfigurationAsync().Result; if (IdentityComparer.AreEqual(configuration.Issuer, configuration2.Issuer)) - context.Diffs.Add("IdentityComparer.AreEqual(configuration.Issuer, configuration2.Issuer)"); + context.Diffs.Add("IdentityComparer.AreEqual(configuration, configuration2)"); TestUtilities.AssertFailIfErrors(context); } diff --git a/test/Microsoft.IdentityModel.TestUtils/InMemoryDocumentRetriever.cs b/test/Microsoft.IdentityModel.TestUtils/InMemoryDocumentRetriever.cs deleted file mode 100644 index 363ddb1da6..0000000000 --- a/test/Microsoft.IdentityModel.TestUtils/InMemoryDocumentRetriever.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.IdentityModel.Protocols; - -namespace Microsoft.IdentityModel.TestUtils -{ - /// - /// Returns a string set in the constructor. - /// Simplifies testing. - /// - public class InMemoryDocumentRetriever : IDocumentRetriever - { - private readonly IDictionary _configurations; - - /// - /// Initializes a new instance of the class. - /// - public InMemoryDocumentRetriever(IDictionary configuration) - { - _configurations = configuration; - } - - /// - /// Returns the document passed in constructor or set on . - /// - /// Fully qualified path to a file. Ignored for now. - /// Ignored for now. - /// UTF8 decoding of bytes in the file. - public async Task GetDocumentAsync(string address, CancellationToken cancel) - { - return await Task.FromResult(_configurations[address]).ConfigureAwait(false); - } - } -} diff --git a/test/Microsoft.IdentityModel.TestUtils/SampleListener.cs b/test/Microsoft.IdentityModel.TestUtils/SampleListener.cs index 76dee7c906..dd02c50729 100644 --- a/test/Microsoft.IdentityModel.TestUtils/SampleListener.cs +++ b/test/Microsoft.IdentityModel.TestUtils/SampleListener.cs @@ -8,7 +8,7 @@ namespace Microsoft.IdentityModel.TestUtils { public class SampleListener : EventListener { - public string TraceBuffer { get; set; } = string.Empty; + public string TraceBuffer { get; set; } protected override void OnEventWritten(EventWrittenEventArgs eventData) {