[Bug] DistributedSqlServerCache GetAccounts returns null despite having Accounts in TokenCache
See original GitHub issueWhich 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
passesApiIds.GetAccounts
toGetAccountsInternalAsync
passes it toGetAccountsFromCacheAsync
to create theAuthenticationRequestParameters
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 aboveAuthenticationRequestParameters
) which returns null because there is no handling whenApiId==ApiIds.GetAccounts
- The
MsalDistributedTokenCacheAdapter
correctly checks for a null cacheKey and does not read from TokenCache.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:9
Top GitHub Comments
Yes that would be a great addition.
@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)