diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs index 97205a9d67..8be4efe1d8 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs @@ -96,11 +96,14 @@ protected override async Task ExecuteAsync(CancellationTok SilentRequestHelper.ProcessFetchInBackground( cachedAccessTokenItem, - () => + async () => { // Use a linked token source, in case the original cancellation token source is disposed before this background task completes. + // IMPORTANT: The lambda must be async and await the inner call. Without async/await, `using var` disposes the linked CTS + // before the async operation completes, breaking cancellation propagation and causing unbounded SemaphoreSlim convoy. + // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/6053 using var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - return GetAccessTokenAsync(tokenSource.Token, logger); + return await GetAccessTokenAsync(tokenSource.Token, logger).ConfigureAwait(false); }, logger, ServiceBundle, AuthenticationRequestParameters.RequestContext.ApiEvent, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkApiId, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkVersion); diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/ManagedIdentityAuthRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/ManagedIdentityAuthRequest.cs index eec0728a53..9fce54ffd4 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/ManagedIdentityAuthRequest.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/ManagedIdentityAuthRequest.cs @@ -123,11 +123,14 @@ protected override async Task ExecuteAsync(CancellationTok SilentRequestHelper.ProcessFetchInBackground( cachedAccessTokenItem, - () => + async () => { // Use a linked token source, in case the original cancellation token source is disposed before this background task completes. + // IMPORTANT: The lambda must be async and await the inner call. Without async/await, `using var` disposes the linked CTS + // before the async operation completes, breaking cancellation propagation and causing unbounded SemaphoreSlim convoy. + // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/6053 using var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - return GetAccessTokenAsync(tokenSource.Token, logger); + return await GetAccessTokenAsync(tokenSource.Token, logger).ConfigureAwait(false); }, logger, ServiceBundle, AuthenticationRequestParameters.RequestContext.ApiEvent, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkApiId, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkVersion); diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/OnBehalfOfRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/OnBehalfOfRequest.cs index 109a27458b..3d37501e82 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/OnBehalfOfRequest.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/OnBehalfOfRequest.cs @@ -155,11 +155,14 @@ protected override async Task ExecuteAsync(CancellationTok SilentRequestHelper.ProcessFetchInBackground( cachedAccessToken, - () => + async () => { // Use a linked token source, in case the original cancellation token source is disposed before this background task completes. + // IMPORTANT: The lambda must be async and await the inner call. Without async/await, `using var` disposes the linked CTS + // before the async operation completes, breaking cancellation propagation and causing unbounded SemaphoreSlim convoy. + // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/6053 using var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - return RefreshRtOrFetchNewAccessTokenAsync(tokenSource.Token); + return await RefreshRtOrFetchNewAccessTokenAsync(tokenSource.Token).ConfigureAwait(false); }, logger, ServiceBundle, AuthenticationRequestParameters.RequestContext.ApiEvent, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkApiId, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkVersion); diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs index fbc46acf24..9e61b7a21d 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs @@ -106,11 +106,14 @@ public async Task ExecuteAsync(CancellationToken cancellat SilentRequestHelper.ProcessFetchInBackground( cachedAccessTokenItem, - () => + async () => { // Use a linked token source, in case the original cancellation token source is disposed before this background task completes. + // IMPORTANT: The lambda must be async and await the inner call. Without async/await, `using var` disposes the linked CTS + // before the async operation completes, breaking cancellation propagation and causing unbounded SemaphoreSlim convoy. + // See https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/6053 using var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - return RefreshRtOrFailAsync(tokenSource.Token); + return await RefreshRtOrFailAsync(tokenSource.Token).ConfigureAwait(false); }, logger, ServiceBundle, AuthenticationRequestParameters.RequestContext.ApiEvent, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkApiId, AuthenticationRequestParameters.RequestContext.ApiEvent.CallerSdkVersion);