question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Bug] DistributedSqlServerCache GetAccounts returns null despite having Accounts in TokenCache

See original GitHub issue

Which Version of MSAL are you using ? Microsoft.Identity.Client 1.1.0

Platform netcoreapp3.1

What authentication flow has the issue? Other: not directly related to authentication flow but rather reading value from a populated TokenCache

Is this a new or existing app? New test app to test update path for existing app

Repro

  • Prime the TokenCache with a successfully authenticated user flow (following standard Sample).
  • In my case I used .AddDistributedTokenCaches() with .AddDistributedSqlServerCache().
  • Database now shows one (non-expired) record in the TokenCache with let’s say Id = 123.456 (userId and tenantId combo)
// _applicationOptions injected as IOptions<ConfidentialClientApplicationOptions>
// _httpClientFactory injected as IHttpClientFactory (and wrapped in MsalAspNetCoreHttpClientFactory)
// _tokenCacheProvider injected as IMsalTokenCacheProvider 
var confidentialClient = ConfidentialClientApplicationBuilder
                .CreateWithApplicationOptions(_applicationOptions)
                .WithHttpClientFactory(_httpClientFactory)
                .WithAuthority($"{_applicationOptions.Instance}{_applicationOptions.TenantId}/")
                .Build();

await _tokenCacheProvider.InitializeAsync(confidentialClient.AppTokenCache).ConfigureAwait(false);
await _tokenCacheProvider.InitializeAsync(confidentialClient.UserTokenCache).ConfigureAwait(false);

// this returns 0 accounts
var accounts= await confidentialClient.GetAccountsAsync();
// where this returns the account and proves there is an account in the TokenCache
var account = await confidentialClient.GetAccountAsync("123.456"); // replace with actual id from tokenCache 

Expected behavior GetAccountsAsync should return the accounts in the TokenCache.

Actual behavior GetAccountsAsync returns an empty array

Possible Solution SuggestedWebCacheKeyFactory should not return null for ApiId==ApiIds.GetAccounts

Additional context/ Logs / Screenshots The problem occurs because GetAccounts, initiates a Read from the TokenCache but SuggestedWebCacheKeyFactory returns null for ApiIds.GetAccounts, which causes TokenCache not to read anything.

More verbose:

  • Starting in Microsoft.Identity.Client/ClientApplicationBase.cs
    • GetAccountsAsync passes ApiIds.GetAccounts to
    • GetAccountsInternalAsync passes it to
    • GetAccountsFromCacheAsync to create the AuthenticationRequestParameters and set ApiId there.
    • Instantiates CacheSessionManager with these params (at ClientApplicationBase.cs:205)
    • then calls CacheSessionManager.GetAccountsAsync()
    • which in turn calls RefreshCacheForReadOperationsAsync
    • where the cache key is generated using:
    • SuggestedWebCacheKeyFactory.GetKeyFromRequest(_requestParams); (_requestParams is the above AuthenticationRequestParameters) which returns null because there is no handling when ApiId==ApiIds.GetAccounts
    • The MsalDistributedTokenCacheAdapter correctly checks for a null cacheKey and does not read from TokenCache.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:9

github_iconTop GitHub Comments

1reaction
adugglebycommented, Oct 9, 2020
  • Do I understand that we’d want to have a Microsoft.Identity.AzureFunction library ?

Yes that would be a great addition.

1reaction
jmprieurcommented, Oct 9, 2020

@aduggleby in confidential client scenarios, you need to use one cache per account (for performance and security), and therefore GetAccountsAsync() does not make sense when you are not in the context of an account.

In general, given you are on asp.net core, we strongly recommend that you use Microsoft.Identity.Web which GAed last week. This takes care of the dirty details for you, brings tons of features on top of MSAL.NET and will evolve to support more. See https://github.com/AzureAD/microsoft-identity-web/wiki/1.0.0 If you really need to use MSAL.NET directly, we want to know, as this might mean that we are missing features you need, and we are willing to provide them.

@bgavrilMS : as discussed, I propose that we remove GetAccountsAsync() from IConfidentialClientApplication in MSAL.NET 5.x (as this is a breaking change, but really is useless if customers follow the recommended guidance)

Read more comments on GitHub >

github_iconTop Results From Across the Web

MSAL TokenAcquisition GetAccessTokenOnBehalfOfUser ...
returns an empty lists/IEnumerable. I would expect to retrieve an account from application.GetAccountAsync(accountIdentifier). and a list from
Read more >
Get & remove accounts from the token cache (MSAL4j)
Learn how to view and remove accounts from the token cache using the Microsoft Authentication Library for Java.
Read more >
ConfidentialClientApplication GetAccountsAsync returns ...
This works fine in first pass and I assume the token gets stored in cache for subsequent uses. ... The account is always...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found