[Bug] OBO token is not refreshed when it has expired
See original GitHub issueLogs and Network traces Without logs or traces, it is unlikely that the team can investigate your issue. Capturing logs and network traces is described at https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/logging
Which Version of MSAL are you using ? MSAL.NET 4.29
Platform .NET Core
What authentication flow has the issue?
- Web API
- [ x ] OBO
Repro
See https://github.com/AzureAD/microsoft-identity-web/compare/jmprieur/repro_obo_rt. This is simulating a long running process in a web API, or a feature like OneDrive that creates albums for the user who is no longer signed-in. This requires the OBO token to be refreshed, (which is possible until the refresh token expires).
A controller action calls RegisterPeriodicCallbackForLongProcessing()
which itself registers a callback (with a timer)
/// <summary>
/// This methods the processing of user data where the web API periodically checks the user
/// date (think of OneDrive producing albums)
/// </summary>
private void RegisterPeriodicCallbackForLongProcessing()
{
// Get the token incoming to the web API - we could do better here.
var token = (HttpContext.Items["JwtSecurityTokenUsedToCallWebAPI"] as JwtSecurityToken).RawData;
// Build the URL to the callback controller, based on the request.
var request = HttpContext.Request;
string url = request.Scheme + "://" + request.Host + request.Path.Value.Replace("todolist", "callback");
// Setup a timer so that the API calls back the callback every 10 mins.
Timer timer = new Timer(async (state) =>
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", $"bearer {token}");
var message = await httpClient.GetAsync(url);
}, null, 1000, 1000 * 60 * 10);
}
The callback is defined in another controller CallbackController
[HttpGet]
public async Task GetAsync()
{
_logger.LogWarning($"Callback called {DateTime.Now}");
string owner = User.GetDisplayName();
// Below is for testing multi-tenants
var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(new string[] { "user.read" }).ConfigureAwait(false); // for testing OBO
if (result.AuthenticationResultMetadata.TokenSource == Microsoft.Identity.Client.TokenSource.IdentityProvider)
{
_logger.LogWarning($"OBO access token refreshed {DateTime.Now}");
}
}
Expected behavior Passing the same (expired) incoming token (as a key), given that there is a refresh token associated to the (expired) OBO access token that was generated from the incoming (expired) token, this OBO token should be refreshed, and the long running process should happen for ever (as the refresh token will also be refreshed). See the protocol documentation for On behalf of. The refresh token is returned (when offline_access is requested), for this scenario.
Therefore , with the repro code above, we should see, after 1h, a log warning “OBO access token refreshed <date>”
Actual behavior EVO does not accept the incoming (expired) token, of course, but MSAL.NET doesn’t even attempt to refresh the OBO access token, and there is an exception.
Microsoft.Identity.Client.MsalUiRequiredException
HResult=0x80131500
Message=AADSTS500133: Assertion is not within its valid time range. Ensure that the access token is not expired before using it for user assertion, or request a new token. Current time: 2021-04-17T19:50:21.6663073Z, expiry time of assertion 2021-04-17T19:50:15.0000000Z.
Trace ID: ee96f1de-d755-46e7-811e-aba122bd4700
Correlation ID: 1a64be83-e614-444a-a7ce-7c2f570cf35a
Timestamp: 2021-04-17 19:50:21Z
Source=Microsoft.Identity.Client
StackTrace:
at Microsoft.Identity.Client.Internal.Requests.RequestBase.HandleTokenRefreshError(MsalServiceException e, MsalAccessTokenCacheItem cachedAccessTokenItem)
at Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.<ExecuteAsync>d__2.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__13.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable
1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.<ExecuteAsync>d__4.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Identity.Web.TokenAcquisition.<GetAuthenticationResultForWebApiToCallDownstreamApiAsync>d__26.MoveNext() in C:\gh\microsoft-identity-web\src\Microsoft.Identity.Web\TokenAcquisition.cs:line 631
This exception was originally thrown at this call stack: Microsoft.Identity.Client.Internal.Requests.RequestBase.HandleTokenRefreshError(Microsoft.Identity.Client.MsalServiceException, Microsoft.Identity.Client.Cache.Items.MsalAccessTokenCacheItem) Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.ExecuteAsync(System.Threading.CancellationToken) System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task) System.Runtime.CompilerServices.ConfiguredTaskAwaitable<TResult>.ConfiguredTaskAwaiter.GetResult() Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(System.Threading.CancellationToken) System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task) System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task) … [Call Stack Truncated]
Issue Analytics
- State:
- Created 2 years ago
- Comments:63 (22 by maintainers)
Top GitHub Comments
Found that TTL was set on the cosmos cache container, have removed this so expect it to work now.
I have just tried using “AddDistributedSqlServerCache” instead of “AddCosmosCache” and it seems to have worked. I will double check this after a longer time period has elapsed tomorrow. Maybe the bug is with the Microsoft.Extensions.Caching.Cosmos package