Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ private async Task<T> GetConfigurationNonBlockingAsync(CancellationToken cancel)
// If provided configuration is valid, skip regular retriaval process and update current configuration.
if (ConfigurationEventHandler != null)
{
var configurationRetrieved = await HandleBeforeRetrieveAsync(cancel).ConfigureAwait(false);
var configurationRetrieved = await HandleBeforeRetrieveAsync(bypassCache: false, cancel).ConfigureAwait(false);

// replicate the behavior of successful retrieval from endpoint
if (configurationRetrieved != null && configurationRetrieved.Configuration != null)
Expand Down Expand Up @@ -331,7 +331,10 @@ private async Task<T> GetConfigurationNonBlockingAsync(CancellationToken cancel)
/// The Caller should first check the state checking state using:
/// if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverRunning, ConfigurationRetrieverIdle) == ConfigurationRetrieverIdle).
/// </summary>
private async Task UpdateCurrentConfigurationAsync()
private async Task UpdateCurrentConfigurationAsync() =>
await UpdateCurrentConfigurationAsync(bypassCache: false).ConfigureAwait(false);
Comment thread
westin-m marked this conversation as resolved.
Outdated

private async Task UpdateCurrentConfigurationAsync(bool bypassCache)
Comment thread
westin-m marked this conversation as resolved.
{
long startTimestamp = TimeProvider.GetTimestamp();

Expand All @@ -341,7 +344,7 @@ private async Task UpdateCurrentConfigurationAsync()
// If provided configuration is valid, skip regular retriaval process and update current configuration.
if (ConfigurationEventHandler != null)
{
var configurationRetrieved = await HandleBeforeRetrieveAsync().ConfigureAwait(false);
ConfigurationEventHandlerResult<T> configurationRetrieved = await HandleBeforeRetrieveAsync(bypassCache).ConfigureAwait(false);
if (configurationRetrieved != null && configurationRetrieved.Configuration != null)
{
UpdateConfiguration(configurationRetrieved.Configuration, configurationRetrieved.RetrievalTime);
Expand Down Expand Up @@ -481,19 +484,31 @@ private void RequestRefreshBackgroundThread()
_isFirstRefreshRequest = false;
if (Interlocked.CompareExchange(ref _configurationRetrieverState, ConfigurationRetrieverRunning, ConfigurationRetrieverIdle) == ConfigurationRetrieverIdle)
{
_ = Task.Run(UpdateCurrentConfigurationAsync, CancellationToken.None);
_ = Task.Run(() => UpdateCurrentConfigurationAsync(bypassCache: true), CancellationToken.None);
Comment thread
westin-m marked this conversation as resolved.
Outdated
Comment thread
westin-m marked this conversation as resolved.
Outdated
_lastRequestRefresh = now;
}
}
}

private async Task<ConfigurationEventHandlerResult<T>> HandleBeforeRetrieveAsync(CancellationToken cancellationToken = default)
private async Task<ConfigurationEventHandlerResult<T>> HandleBeforeRetrieveAsync(bool bypassCache, CancellationToken cancellationToken = default)
Comment thread
westin-m marked this conversation as resolved.
Outdated
{
long beforeHandlerTimestamp = TimeProvider.GetTimestamp();

try
{
var handlerResult = await ConfigurationEventHandler.BeforeRetrieveAsync(MetadataAddress, cancellationToken).ConfigureAwait(false);
ConfigurationEventHandlerResult<T> handlerResult;
if (ConfigurationEventHandler is IConfigurationEventHandlerContextAware<T> contextAware)
{
var context = new ConfigurationRetrievalContext() { BypassCache = bypassCache };
handlerResult = await contextAware.BeforeRetrieveAsync(
MetadataAddress, context, cancellationToken).ConfigureAwait(false);
}
else
{
handlerResult = await ConfigurationEventHandler.BeforeRetrieveAsync(
MetadataAddress, cancellationToken).ConfigureAwait(false);
}

if (handlerResult != null && handlerResult.Configuration != null)
{
var handlerElapsedTime = TimeProvider.GetElapsedTime(beforeHandlerTimestamp);
Expand Down Expand Up @@ -523,6 +538,7 @@ private async Task<ConfigurationEventHandlerResult<T>> HandleBeforeRetrieveAsync
return handlerResult;
}
}
#pragma warning disable CA1031 // Do not catch general exception types
Comment thread
westin-m marked this conversation as resolved.
Outdated
catch (Exception ex)
{
var handlerErrorElapsedTime = TimeProvider.GetElapsedTime(beforeHandlerTimestamp);
Expand All @@ -540,6 +556,7 @@ private async Task<ConfigurationEventHandlerResult<T>> HandleBeforeRetrieveAsync
ex),
ex));
}
#pragma warning restore CA1031 // Do not catch general exception types

return ConfigurationEventHandlerResult<T>.NoResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public partial class ConfigurationManager<T> where T : class
private TimeSpan _bootstrapRefreshInterval = TimeSpan.FromSeconds(1);

/// <summary>
/// Only used to track the type of request for telemetry.
/// Used to track the type of request for signaling the event handler and for telemetry.
/// </summary>
private bool _refreshRequested;

Expand All @@ -38,7 +38,7 @@ private async Task<T> GetConfigurationWithBlockingAsync(CancellationToken cancel
// If provided configuration is valid, skip regular retriaval process and update current configuration.
if (ConfigurationEventHandler != null)
{
var configurationRetrieved = await HandleBeforeRetrieveAsync(cancel).ConfigureAwait(false);
ConfigurationEventHandlerResult<T> configurationRetrieved = await HandleBeforeRetrieveAsync(bypassCache: _refreshRequested, cancel).ConfigureAwait(false);

// replicate the behavior of successful retrieval from endpoint
if (configurationRetrieved != null && configurationRetrieved.Configuration != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.IdentityModel.Protocols.Configuration;

/// <summary>
/// Defines the context for configuration retrieval operations.
/// </summary>
public class ConfigurationRetrievalContext
{
/// <summary>
/// Gets or sets a value indicating whether to bypass the cache when retrieving configuration.
/// </summary>
public bool BypassCache { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.IdentityModel.Protocols.Configuration;

/// <summary>
/// Defines event handlers for configuration retrieval and update operations.
/// </summary>
/// <typeparam name="T">The type of configuration.</typeparam>
public interface IConfigurationEventHandlerContextAware<T> : IConfigurationEventHandler<T> where T : class
{
/// <summary>
/// Called before retrieving configuration from the metadata endpoint.
/// </summary>
/// <param name="metadataAddress">The metadata endpoint address.</param>
/// <param name="context">The context for the configuration retrieval operation, providing additional information and control.</param>
/// <param name="cancellationToken">A cancellation token to observe while waiting for the task to complete.</param>
/// <returns>
/// A <see cref="ConfigurationEventHandlerResult{T}"/> if valid and available, or <see cref="ConfigurationEventHandlerResult{T}.NoResult"/> to proceed with normal retrieval.
/// </returns>
Task<ConfigurationEventHandlerResult<T>> BeforeRetrieveAsync(string metadataAddress, ConfigurationRetrievalContext context, CancellationToken cancellationToken = default);

// AfterUpdateAsync does not require context, but can be added here if needed in the future.
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>.Configuration.get -> T
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>.ConfigurationEventHandlerResult(T configuration, System.DateTimeOffset retrievalTime) -> void
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>.RetrievalTime.get -> System.DateTimeOffset
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationRetrievalContext
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationRetrievalContext.BypassCache.get -> bool
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationRetrievalContext.BypassCache.set -> void
Microsoft.IdentityModel.Protocols.Configuration.ConfigurationRetrievalContext.ConfigurationRetrievalContext() -> void
Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandler<T>
Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandler<T>.AfterUpdateAsync(string metadataAddress, T configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandler<T>.BeforeRetrieveAsync(string metadataAddress, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>>
Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandlerContextAware<T>
Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandlerContextAware<T>.BeforeRetrieveAsync(string metadataAddress, Microsoft.IdentityModel.Protocols.Configuration.ConfigurationRetrievalContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>>
Comment thread
westin-m marked this conversation as resolved.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.ConfigurationEventHandler.get -> Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandler<T>
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.ConfigurationEventHandler.set -> void
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.ConfigurationManager(string metadataAddress, Microsoft.IdentityModel.Protocols.IConfigurationRetriever<T> configRetriever, Microsoft.IdentityModel.Protocols.IDocumentRetriever docRetriever, Microsoft.IdentityModel.Protocols.IConfigurationValidator<T> configValidator, Microsoft.IdentityModel.Protocols.Configuration.LastKnownGoodConfigurationCacheOptions lkgCacheOptions, Microsoft.IdentityModel.Protocols.Configuration.IConfigurationEventHandler<T> configurationEventHandler) -> void
static readonly Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>.NoResult -> Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>
static readonly Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>.NoResult -> Microsoft.IdentityModel.Protocols.Configuration.ConfigurationEventHandlerResult<T>
Loading
Loading