diff --git a/src/client/Microsoft.Identity.Client/AuthenticationResultMetadata.cs b/src/client/Microsoft.Identity.Client/AuthenticationResultMetadata.cs index 58fce3acbf..ec07ecb171 100644 --- a/src/client/Microsoft.Identity.Client/AuthenticationResultMetadata.cs +++ b/src/client/Microsoft.Identity.Client/AuthenticationResultMetadata.cs @@ -97,5 +97,10 @@ public AuthenticationResultMetadata(TokenSource tokenSource) /// See https://aka.ms/msal-net-logging for more details about logging. /// public string Telemetry { get; set; } + + /// + /// The number of access tokens in the cache. + /// + public int CachedAccessTokenCount { get; set; } } } diff --git a/src/client/Microsoft.Identity.Client/Cache/CacheSessionManager.cs b/src/client/Microsoft.Identity.Client/Cache/CacheSessionManager.cs index d4b74143ae..d7c1334385 100644 --- a/src/client/Microsoft.Identity.Client/Cache/CacheSessionManager.cs +++ b/src/client/Microsoft.Identity.Client/Cache/CacheSessionManager.cs @@ -48,9 +48,11 @@ public async Task FindAccessTokenAsync() return await TokenCacheInternal.FindAccessTokenAsync(_requestParams).ConfigureAwait(false); } - public Task> SaveTokenResponseAsync(MsalTokenResponse tokenResponse) + public async Task> SaveTokenResponseAsync(MsalTokenResponse tokenResponse) { - return TokenCacheInternal.SaveTokenResponseAsync(_requestParams, tokenResponse); + var result = await TokenCacheInternal.SaveTokenResponseAsync(_requestParams, tokenResponse).ConfigureAwait(false); + RequestContext.ApiEvent.CachedAccessTokenCount = TokenCacheInternal.Accessor.EntryCount; + return result; } public async Task GetAccountAssociatedWithAccessTokenAsync(MsalAccessTokenCacheItem msalAccessTokenCacheItem) @@ -181,6 +183,8 @@ private async Task RefreshCacheForReadOperationsAsync() { RequestContext.ApiEvent.CacheLevel = CacheLevel.L1Cache; } + + RequestContext.ApiEvent.CachedAccessTokenCount = TokenCacheInternal.Accessor.EntryCount; } } } diff --git a/src/client/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs b/src/client/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs index 5efa34cb7c..1d73a84dcf 100644 --- a/src/client/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs +++ b/src/client/Microsoft.Identity.Client/Cache/ITokenCacheAccessor.cs @@ -10,6 +10,8 @@ namespace Microsoft.Identity.Client.Cache { internal interface ITokenCacheAccessor { + int EntryCount { get; } + void SaveAccessToken(MsalAccessTokenCacheItem item); void SaveRefreshToken(MsalRefreshTokenCacheItem item); diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs index 69a3cbb876..0ac5bd3627 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs @@ -223,6 +223,7 @@ private void UpdateTelemetry(long elapsedMilliseconds, ApiEvent apiEvent, Authen authenticationResult.AuthenticationResultMetadata.CacheLevel = GetCacheLevel(authenticationResult); authenticationResult.AuthenticationResultMetadata.Telemetry = apiEvent.MsalRuntimeTelemetry; authenticationResult.AuthenticationResultMetadata.RegionDetails = CreateRegionDetails(apiEvent); + authenticationResult.AuthenticationResultMetadata.CachedAccessTokenCount = apiEvent.CachedAccessTokenCount; Metrics.IncrementTotalDurationInMs(authenticationResult.AuthenticationResultMetadata.DurationTotalInMs); } diff --git a/src/client/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs b/src/client/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs index 47eb369779..cf501fe35f 100644 --- a/src/client/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs +++ b/src/client/Microsoft.Identity.Client/Platforms/Android/AndroidTokenCacheAccessor.cs @@ -161,6 +161,8 @@ public List GetAllAccounts(string optionalPartitionKey = n } #endregion + public int EntryCount { get; } = 0; // not implemented for Android + public MsalAccountCacheItem GetAccount(MsalAccountCacheItem accountCacheItem) { return MsalAccountCacheItem.FromJsonString(_accountSharedPreference.GetString(accountCacheItem.CacheKey, null)); diff --git a/src/client/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs b/src/client/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs index 47dddc5cdb..89892e874a 100644 --- a/src/client/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs +++ b/src/client/Microsoft.Identity.Client/Platforms/iOS/iOSTokenCacheAccessor.cs @@ -167,7 +167,10 @@ public List GetAllAccounts(string optionalPartitionKey = n .Select(x => MsalAccountCacheItem.FromJsonString(x)) .ToList(); } -#endregion + #endregion + + public int EntryCount { get; } = 0; // not implemented for iOS + internal SecStatusCode TryGetBrokerApplicationToken(string clientId, out string appToken) { diff --git a/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedAppTokenCacheAccessor.cs b/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedAppTokenCacheAccessor.cs index 8ae392d260..1a732eb856 100644 --- a/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedAppTokenCacheAccessor.cs +++ b/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedAppTokenCacheAccessor.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; using Microsoft.Identity.Client.Cache; using Microsoft.Identity.Client.Cache.Items; using Microsoft.Identity.Client.Core; @@ -34,6 +35,11 @@ internal class InMemoryPartitionedAppTokenCacheAccessor : ITokenCacheAccessor protected readonly ILoggerAdapter _logger; private readonly CacheOptions _tokenCacheAccessorOptions; + private int _entryCount = 0; + private static int s_entryCount = 0; + + public int EntryCount => GetEntryCountRef(); + public InMemoryPartitionedAppTokenCacheAccessor( ILoggerAdapter logger, CacheOptions tokenCacheAccessorOptions) @@ -59,9 +65,18 @@ public void SaveAccessToken(MsalAccessTokenCacheItem item) string itemKey = item.CacheKey; string partitionKey = CacheKeyFactory.GetAppTokenCacheItemKey(item.ClientId, item.TenantId, item.KeyId, item.AdditionalCacheKeyComponents); - // if a conflict occurs, pick the latest value - AccessTokenCacheDictionary - .GetOrAdd(partitionKey, new ConcurrentDictionary())[itemKey] = item; + var partition = AccessTokenCacheDictionary.GetOrAdd(partitionKey, _ => new ConcurrentDictionary()); + bool added = partition.TryAdd(itemKey, item); + + // only increment the entry count if the item was added, not updated + if (added) + { + Interlocked.Increment(ref GetEntryCountRef()); + } + else + { + partition[itemKey] = item; + } } /// @@ -129,12 +144,19 @@ public void DeleteAccessToken(MsalAccessTokenCacheItem item) { var partitionKey = CacheKeyFactory.GetAppTokenCacheItemKey(item.ClientId, item.TenantId, item.KeyId); - AccessTokenCacheDictionary.TryGetValue(partitionKey, out var partition); - if (partition == null || !partition.TryRemove(item.CacheKey, out _)) + if (AccessTokenCacheDictionary.TryGetValue(partitionKey, out var partition)) { - _logger.InfoPii( - () => $"[Internal cache] Cannot delete access token because it was not found in the cache. Key {item.CacheKey}.", - () => "[Internal cache] Cannot delete access token because it was not found in the cache."); + bool removed = partition.TryRemove(item.CacheKey, out _); + if (removed) + { + Interlocked.Decrement(ref GetEntryCountRef()); + } + else + { + _logger.InfoPii( + () => $"[Internal cache] Cannot delete access token because it was not found in the cache. Key {item.CacheKey}.", + () => "[Internal cache] Cannot delete access token because it was not found in the cache."); + } } } @@ -219,6 +241,7 @@ public virtual void Clear(ILoggerAdapter requestlogger = null) { var logger = requestlogger ?? _logger; AccessTokenCacheDictionary.Clear(); + Interlocked.Exchange(ref GetEntryCountRef(), 0); logger.Always("[Internal cache] Clearing app token cache accessor."); // app metadata isn't removable } @@ -227,5 +250,11 @@ public virtual bool HasAccessOrRefreshTokens() { return AccessTokenCacheDictionary.Any(partition => partition.Value.Any(token => !token.Value.IsExpiredWithBuffer())); } + + private ref int GetEntryCountRef() + { + return ref _tokenCacheAccessorOptions.UseSharedCache ? ref s_entryCount : ref _entryCount; + } + } } diff --git a/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedUserTokenCacheAccessor.cs b/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedUserTokenCacheAccessor.cs index 4e3800129b..0d23372704 100644 --- a/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedUserTokenCacheAccessor.cs +++ b/src/client/Microsoft.Identity.Client/PlatformsCommon/Shared/InMemoryPartitionedUserTokenCacheAccessor.cs @@ -41,9 +41,15 @@ internal class InMemoryPartitionedUserTokenCacheAccessor : ITokenCacheAccessor private static readonly ConcurrentDictionary s_appMetadataDictionary = new ConcurrentDictionary(); + private static int s_entryCount = 0; + protected readonly ILoggerAdapter _logger; private readonly CacheOptions _tokenCacheAccessorOptions; + private int _entryCount = 0; + + public int EntryCount => GetEntryCountRef(); + public InMemoryPartitionedUserTokenCacheAccessor(ILoggerAdapter logger, CacheOptions tokenCacheAccessorOptions) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -73,8 +79,17 @@ public void SaveAccessToken(MsalAccessTokenCacheItem item) string itemKey = item.CacheKey; string partitionKey = CacheKeyFactory.GetKeyFromCachedItem(item); - AccessTokenCacheDictionary - .GetOrAdd(partitionKey, new ConcurrentDictionary())[itemKey] = item; // if a conflict occurs, pick the latest value + var partition = AccessTokenCacheDictionary.GetOrAdd(partitionKey, _ => new ConcurrentDictionary()); + bool added = partition.TryAdd(itemKey, item); + // only increment the entry count if this is a new item + if (added) + { + System.Threading.Interlocked.Increment(ref GetEntryCountRef()); + } + else + { + partition[itemKey] = item; + } } public void SaveRefreshToken(MsalRefreshTokenCacheItem item) @@ -151,12 +166,19 @@ public void DeleteAccessToken(MsalAccessTokenCacheItem item) { string partitionKey = CacheKeyFactory.GetKeyFromCachedItem(item); - AccessTokenCacheDictionary.TryGetValue(partitionKey, out var partition); - if (partition == null || !partition.TryRemove(item.CacheKey, out _)) + if (AccessTokenCacheDictionary.TryGetValue(partitionKey, out var partition)) { - _logger.InfoPii( - () => $"[Internal cache] Cannot delete access token because it was not found in the cache. Key {item.CacheKey}.", - () => "[Internal cache] Cannot delete access token because it was not found in the cache."); + bool removed = partition.TryRemove(item.CacheKey, out _); + if (removed) + { + System.Threading.Interlocked.Decrement(ref GetEntryCountRef()); + } + else + { + _logger.InfoPii( + () => $"[Internal cache] Cannot delete access token because it was not found in the cache. Key {item.CacheKey}.", + () => "[Internal cache] Cannot delete access token because it was not found in the cache."); + } } } @@ -246,7 +268,7 @@ public virtual List GetAllRefreshTokens(string partit if (RefreshTokenCacheDictionary.Count == 1 && result.Count == 0) { logger.VerbosePii( - () => $"[Internal cache] 0 RTs and 1 partition. Partition in cache is {RefreshTokenCacheDictionary.Keys.First()}", + () => $"[Internal cache] 0 RTs and 1 partition. Partition in cache is {RefreshTokenCacheDictionary.Keys.First()}", () => "[Internal cache] 0 RTs and 1 partition] 0 RTs and 1 partition."); } } @@ -290,7 +312,7 @@ public virtual List GetAllAccounts(string partitionKey = n { AccountCacheDictionary.TryGetValue(partitionKey, out ConcurrentDictionary partition); result = partition?.Select(kv => kv.Value)?.ToList() ?? CollectionHelpers.GetEmptyList(); - + if (logger.IsLoggingEnabled(LogLevel.Verbose)) { logger.Verbose(() => $"[Internal cache] GetAllAccounts (with partition - exists? {partition != null}) found {result.Count} accounts."); @@ -327,6 +349,8 @@ public virtual void Clear(ILoggerAdapter requestlogger = null) IdTokenCacheDictionary.Clear(); AccountCacheDictionary.Clear(); // app metadata isn't removable + System.Threading.Interlocked.Exchange(ref GetEntryCountRef(), 0); + } /// WARNING: this API is slow as it loads all tokens, not just from 1 partition. @@ -336,5 +360,10 @@ public virtual bool HasAccessOrRefreshTokens() return RefreshTokenCacheDictionary.Any(partition => partition.Value.Count > 0) || AccessTokenCacheDictionary.Any(partition => partition.Value.Any(token => !token.Value.IsExpiredWithBuffer())); } + + private ref int GetEntryCountRef() + { + return ref _tokenCacheAccessorOptions.UseSharedCache ? ref s_entryCount : ref _entryCount; + } } } diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt index 1164619fd4..9b7aaf512c 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -5,3 +5,5 @@ Microsoft.Identity.Client.Utils.MacMainThreadScheduler.IsRunning() -> bool Microsoft.Identity.Client.Utils.MacMainThreadScheduler.Stop() -> void Microsoft.Identity.Client.Utils.MacMainThreadScheduler.RunOnMainThreadAsync(System.Func asyncAction) -> System.Threading.Tasks.Task Microsoft.Identity.Client.Utils.MacMainThreadScheduler.StartMessageLoop() -> void +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.get -> int +Microsoft.Identity.Client.AuthenticationResultMetadata.CachedAccessTokenCount.set -> void \ No newline at end of file diff --git a/src/client/Microsoft.Identity.Client/TelemetryCore/Internal/Events/ApiEvent.cs b/src/client/Microsoft.Identity.Client/TelemetryCore/Internal/Events/ApiEvent.cs index 301a196c7b..abd5bac980 100644 --- a/src/client/Microsoft.Identity.Client/TelemetryCore/Internal/Events/ApiEvent.cs +++ b/src/client/Microsoft.Identity.Client/TelemetryCore/Internal/Events/ApiEvent.cs @@ -62,6 +62,8 @@ public string ApiIdString public string ApiErrorCode { get; set; } + public int CachedAccessTokenCount { get; set; } + #region Region public string RegionUsed { get; set; } diff --git a/tests/Microsoft.Identity.Test.Performance/AcquireTokenForClientCacheTests.cs b/tests/Microsoft.Identity.Test.Performance/AcquireTokenForClientCacheTests.cs index 360466710c..548d6a330d 100644 --- a/tests/Microsoft.Identity.Test.Performance/AcquireTokenForClientCacheTests.cs +++ b/tests/Microsoft.Identity.Test.Performance/AcquireTokenForClientCacheTests.cs @@ -46,7 +46,6 @@ public class AcquireTokenForClientCacheTests [ParamsAllValues] public bool EnableCacheSerialization { get; set; } - //[Params(false)] public bool UseMicrosoftIdentityWebCache { get; set; } diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/InternalCacheOptionsTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/InternalCacheOptionsTests.cs index de87882209..333255bedd 100644 --- a/tests/Microsoft.Identity.Test.Unit/CacheTests/InternalCacheOptionsTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/InternalCacheOptionsTests.cs @@ -96,13 +96,13 @@ public async Task ClientCreds_StaticCache_Async() .BuildConcrete(); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); - await ClientCredsAcquireAndAssertTokenSourceAsync(app1, "S1", TokenSource.IdentityProvider).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app1, "S1", TokenSource.IdentityProvider, 1).ConfigureAwait(false); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); - await ClientCredsAcquireAndAssertTokenSourceAsync(app1, "S2", TokenSource.IdentityProvider).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app1, "S2", TokenSource.IdentityProvider, 2).ConfigureAwait(false); - await ClientCredsAcquireAndAssertTokenSourceAsync(app2, "S1", TokenSource.Cache).ConfigureAwait(false); - await ClientCredsAcquireAndAssertTokenSourceAsync(app2, "S2", TokenSource.Cache).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app2, "S1", TokenSource.Cache, 2).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app2, "S2", TokenSource.Cache, 2).ConfigureAwait(false); ConfidentialClientApplication app3 = ConfidentialClientApplicationBuilder.Create(TestConstants.ClientId) @@ -111,14 +111,16 @@ public async Task ClientCreds_StaticCache_Async() .WithCacheOptions(CacheOptions.EnableSharedCacheOptions) .BuildConcrete(); - await ClientCredsAcquireAndAssertTokenSourceAsync(app3, "S1", TokenSource.Cache).ConfigureAwait(false); - await ClientCredsAcquireAndAssertTokenSourceAsync(app3, "S2", TokenSource.Cache).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app3, "S1", TokenSource.Cache, 2).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app3, "S2", TokenSource.Cache, 2).ConfigureAwait(false); + httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); + await ClientCredsAcquireAndAssertTokenSourceAsync(app3, "S3", TokenSource.IdentityProvider, 3).ConfigureAwait(false); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); - await ClientCredsAcquireAndAssertTokenSourceAsync(app_withoutStaticCache, "S1", TokenSource.IdentityProvider).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app_withoutStaticCache, "S1", TokenSource.IdentityProvider, 1).ConfigureAwait(false); httpManager.AddMockHandlerSuccessfulClientCredentialTokenResponseMessage(); - await ClientCredsAcquireAndAssertTokenSourceAsync(app_withoutStaticCache, "S2", TokenSource.IdentityProvider).ConfigureAwait(false); + await ClientCredsAcquireAndAssertTokenSourceAsync(app_withoutStaticCache, "S2", TokenSource.IdentityProvider, 2).ConfigureAwait(false); } } @@ -145,9 +147,12 @@ public async Task PublicClient_StaticCache_Async() .AcquireTokenInteractive(TestConstants.s_scope) .ExecuteAsync().ConfigureAwait(false); + Assert.AreEqual(1, result.AuthenticationResultMetadata.CachedAccessTokenCount); + var accounts = await app1.GetAccountsAsync().ConfigureAwait(false); Assert.AreEqual(1, accounts.Count()); result = await app1.AcquireTokenSilent(TestConstants.s_scope, accounts.Single()).ExecuteAsync().ConfigureAwait(false); + Assert.AreEqual(1, result.AuthenticationResultMetadata.CachedAccessTokenCount); var app2 = PublicClientApplicationBuilder .Create(TestConstants.ClientId) @@ -159,17 +164,29 @@ public async Task PublicClient_StaticCache_Async() accounts = await app2.GetAccountsAsync().ConfigureAwait(false); Assert.AreEqual(1, accounts.Count()); result = await app2.AcquireTokenSilent(TestConstants.s_scope, accounts.Single()).ExecuteAsync().ConfigureAwait(false); + Assert.AreEqual(1, result.AuthenticationResultMetadata.CachedAccessTokenCount); } } - private async Task ClientCredsAcquireAndAssertTokenSourceAsync(IConfidentialClientApplication app, string scope, TokenSource expectedSource) + private async Task ClientCredsAcquireAndAssertTokenSourceAsync( + IConfidentialClientApplication app, + string scope, + TokenSource expectedSource, + int expectedAccessTokenCount) { var result = await app.AcquireTokenForClient(new[] { scope }) .WithTenantId(TestConstants.Utid) .ExecuteAsync().ConfigureAwait(false); + Assert.AreEqual( expectedSource, result.AuthenticationResultMetadata.TokenSource); + + + Assert.AreEqual(expectedAccessTokenCount, + result.AuthenticationResultMetadata.CachedAccessTokenCount); + + return result; } } } diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/LoadingProjectsTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/LoadingProjectsTests.cs index 4f7fc5b791..c22bda6088 100644 --- a/tests/Microsoft.Identity.Test.Unit/CacheTests/LoadingProjectsTests.cs +++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/LoadingProjectsTests.cs @@ -8,7 +8,6 @@ namespace Microsoft.Identity.Test.Unit.CacheTests { -#if !ANDROID && !iOS // custom token cache serialization not available [TestClass] public class LoadingProjectsTests { @@ -27,5 +26,4 @@ public void CanDeserializeTokenCache() }; } } -#endif }