-
Notifications
You must be signed in to change notification settings - Fork 395
Description
Library version used
4.66.1
.NET version
.Net6.0
Scenario
ConfidentialClient - service to service (AcquireTokenForClient)
Is this a new or an existing app?
The app is in production, I haven't upgraded MSAL, but started seeing this issue
Issue description and reproduction steps
We have enabled ESTS-r globally. And we are seeing the following errors across the board. Can you help to take a look?
2025-04-23T12:45:47 [msg:1bd9][deq:998c] MsalUiRequiredException ERROR: RegionalClientCertificateCredential acquire token for tenant "" with parent requst ID "" failed. Exception: 'Microsoft.Identity.Client.MsalUiRequiredException' occured: AADSTS5000224: We are sorry, this resource is not available. If you are seeing this message by mistake, please contact Microsoft support. Trace ID: 22c5042e-23b3-4157-a5d5-14e787cec700 Correlation ID: 3aefdc8a-a61a-42d6-9da2-d5df9639826f Timestamp: 2025-04-23 12:45:47Z.
Stack: at Microsoft.Identity.Client.OAuth2.OAuth2Client.ThrowServerException(HttpResponse response, RequestContext requestContext)
at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T](HttpResponse response, RequestContext requestContext)
at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders, Func2 onBeforePostRequestData) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.SendTokenRequestAsync(IDictionary2 additionalBodyParameters, CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.GetAccessTokenAsync(CancellationToken cancellationToken, ILoggerAdapter logger) at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.<>c__DisplayClass11_1.<<RunAsync>b__1>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(Func1 codeBlock)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenForClientParameters clientParameters, CancellationToken cancellationToken)
at AzureSearch.Identity.RegionalClientCertificateCredential.<>c__DisplayClass4_0.<b__0>d.MoveNext() in F:\dbs\el\mbas\Source\Common\Product\AzureSearch.Identity\RegionalClientCertificateCredential.cs:line 69
Relevant code snippets
public sealed class RegionalClientCertificateCredential : TokenCredential
{
private readonly IConfidentialClientApplication _confidentialClient;
private readonly bool _sendCertificateChain;
public RegionalClientCertificateCredential(
string authority, string tenantId, string clientId, X509Certificate2 clientCertificate, string region, bool sendCertificateChain)
{
_confidentialClient = ConfidentialClientApplicationBuilder.Create(clientId)
.WithAuthority(authority, tenantId)
.WithCertificate(clientCertificate)
.WithAzureRegion(region)
.Build();
Log.TraceVerbose($"{nameof(RegionalClientCertificateCredential)} is initialized with azure region {region}.");
_sendCertificateChain = sendCertificateChain;
}
public override AccessToken GetToken(
TokenRequestContext requestContext, CancellationToken cancellationToken) =>
GetTokenAsync(requestContext, cancellationToken).GetAwaiter().GetResult();
public override async ValueTask<AccessToken> GetTokenAsync(
TokenRequestContext requestContext, CancellationToken cancellationToken)
{
AcquireTokenForClientParameterBuilder request = _confidentialClient.AcquireTokenForClient(requestContext.Scopes)
.WithSendX5C(_sendCertificateChain);
if (!String.IsNullOrEmpty(requestContext.TenantId))
{
request.WithTenantId(requestContext.TenantId);
}
if (!String.IsNullOrEmpty(requestContext.ParentRequestId) && Guid.TryParse(requestContext.ParentRequestId, out Guid guid))
{
request.WithCorrelationId(guid);
}
if (!String.IsNullOrEmpty(requestContext.Claims))
{
request.WithClaims(requestContext.Claims);
}
int maxRetries = 3;
AuthenticationResult result = await Retry.ExecuteAsync<AuthenticationResult,Exception>(
nameof(GetTokenAsync),
async (attempt) =>
{
Log.TraceVerbose($"Attempt {attempt}: {nameof(RegionalClientCertificateCredential)} start to acquire token for tenant \"{requestContext.TenantId}\" with parent requst ID \"{requestContext.ParentRequestId}\"");
try
{
return await request.ExecuteAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
Log.TraceException(ex, TraceEventType.Error, $"ERROR: {nameof(RegionalClientCertificateCredential)} acquire token for tenant \"{requestContext.TenantId}\" with parent requst ID \"{requestContext.ParentRequestId}\" failed. Exception: '{ex.GetType().FullName}' occured");
throw ex;
}
},
maxAttempts: maxRetries,
shouldRetry: ex => {
return IsMsalRetryableException(ex);
},
retryDelayEvaluator: Retry.ExponentialDelay(initialDelay: TimeSpan.FromMilliseconds(500)),
cancellationToken: cancellationToken).ConfigureAwait(false);
return new AccessToken(result.AccessToken, result.ExpiresOn);
}
//Following example from https://learn.microsoft.com/en-us/entra/msal/dotnet/advanced/exceptions/retry-policy#example-retry-policy to retry on retryable exceptions
private bool IsMsalRetryableException(Exception ex)
{
bool isRetryable = false;
if (ex is HttpRequestException)
{
isRetryable = true;
} else if (ex is MsalException msalException && msalException.IsRetryable)
{
isRetryable = true;
}
if (isRetryable)
{
Log.TraceVerbose($"Will retry accquiring token later.");
} else
{
Log.TraceVerbose($"Non-Retryable exception {ex.GetType().FullName}.");
}
return isRetryable;
}Expected behavior
No response
Identity provider
Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)
Regression
No response
Solution and workarounds
No response