Closed jmprieur closed 3 years ago
Would it be possible to schedule a call to go through this to see where the problem is? This has been ongoing for a long time
Yes, let's meet
@SirElTomato which access token are you using as the cache key for the long running process? Is it the token that you got from AcquireTokenOnBehalfOfUser or the token sent to your web API by the client? nevermind, see you answered this above. what do you do with the user object? how is it stored between calls to the api?
I have tried it with both. Which should I be using? Currently I am updating the user object with the new access token which I get from the AcquireTokenOnBehalfOf call.
You should be using the token sent to your web API by the client as the cache key. Using the token from AcquireTokenOnBehalfOfUser will not work.
@bgavrilMS thanks for the chat yesterday. I'm just going to update the thread here. Unfortunately the cache table is empty again. Something seems to be cleaning it up.
I was under the impression that you are not even running your asp.net website after 1h. Could it be some Cosmos cleanup policy that gets rid of it?
Can someone provide a summary of the discussion yesterday? thanks. @bgavrilMS @SirElTomato @lukaspechar
Sure.
Switch provider which is used to create the Graph Client. I am now using a DelegateAuthenticationProvider instead of OnBehalfOfProvider. There seemed to be an issue with OnBehalfOfProvider although I'm not sure what:
var authProvider = new DelegateAuthenticationProvider((request) =>
{
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.AccessToken);
return Task.CompletedTask;
});
var graphClient = new GraphServiceClient(authProvider);
Ensure original jwt assertion from client is used as the token stored on our user object.
public async Task GetAccessTokenWithJWT(string userId, IEnumerable<string> scopes, string jwt)
{
await AquireTokenOnBehalfOf(scopes, jwt);
var user = _userRepo.GetByIdAndPartitionKeyOrDefault(userId, userId);
user.MicrosoftIdentity = jwt;
_userRepo.Update(user);
}
Ensure the correct scope(s) is used in the GetAccessTokenWithJWT (and subsequently the AcquireTokenOnBehalfOf) call. In this case it was "mail.read".
We then executed the code with the changes.
This all worked great and proved that the caching and refreshing of tokens was working correctly.
Lukas then tried it again yesterday (the following day) and found that the cache was empty and therefore it failed when trying to retrieve a token.
We also discussed:
Found that TTL was set on the cosmos cache container, have removed this so expect it to work now.
Thanks for the update @SirElTomato
Trying to test it but running into issues with various microsoft nuget packages, currently I am experiencing this
All working, forgot to say you can close this. Thanks for all your help.
Logs 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?
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)The callback is defined in another controller
CallbackController
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"
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.d2.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.Internal.Requests.RequestBase.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.ConfiguredTaskAwaitabled__26.MoveNext() in C:\gh\microsoft-identity-web\src\Microsoft.Identity.Web\TokenAcquisition.cs:line 631
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.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.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]