From 6b1cff85c10786244f186a48f5b25b78663dc578 Mon Sep 17 00:00:00 2001
From: pmaytak <34331512+pmaytak@users.noreply.github.com>
Date: Fri, 8 Jan 2021 23:44:15 -0800
Subject: [PATCH] Disable ADAL cache (#2309)
* Add public API method to disable ADAL cache. Add if checks around ADAL cache operations.
* Update if AdalCacheEnabled checks.
* Add benchmark for SaveToken method.
* Renaming and comment updates. Add app option.
* Add GetRefreshToken test.
* Fix FindToken test. Move helper methods into LegacyTokenCacheHelper for reusability.
* Cleanup.
* Add RemoveUser and GetAllUsers benchmarks.
* Add unit tests.
* Update XML comment.
* Rename AdalCache to LegacyCache
---
.../AppConfig/AbstractApplicationBuilder.cs | 19 ++
.../AppConfig/ApplicationConfiguration.cs | 2 +
.../AppConfig/ApplicationOptions.cs | 5 +
.../AppConfig/IAppConfig.cs | 4 +
.../TokenCache.ITokenCacheInternal.cs | 45 +++--
.../Microsoft.Identity.Client/TokenCache.cs | 19 +-
.../LegacyCacheOperationsTests.cs | 100 ++++++++++
.../Program.cs | 2 +-
.../Core/Mocks/LegacyTokenCacheHelper.cs | 141 ++++++++++++++
.../Core/Mocks/TokenCacheHelper.cs | 12 +-
.../TestCommon.cs | 6 +-
...nfidentialClientApplicationBuilderTests.cs | 12 ++
.../CacheFallbackOperationsTests.cs | 182 ++++--------------
.../CacheTests/TokenCacheTests.cs | 39 ++++
14 files changed, 419 insertions(+), 169 deletions(-)
create mode 100644 tests/Microsoft.Identity.Client.Performance/LegacyCacheOperationsTests.cs
create mode 100644 tests/Microsoft.Identity.Test.Common/Core/Mocks/LegacyTokenCacheHelper.cs
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs b/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
index 7b581100d6..df4b941544 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/AbstractApplicationBuilder.cs
@@ -190,6 +190,24 @@ internal T WithUserTokenCacheInternalForTest(ITokenCacheInternal tokenCacheInter
return (T)this;
}
+ ///
+ /// Enables legacy ADAL cache serialization and deserialization.
+ ///
+ /// Enable legacy ADAL cache compatibility.
+ /// The builder to chain the .With methods.
+ ///
+ /// ADAL is a previous legacy generation of MSAL.NET authentication library.
+ /// If you don't use .WithLegacyCacheCompatibility(false), then by default, the ADAL cache is used
+ /// (along with MSAL cache). true flag is only needed for specific migration scenarios
+ /// from ADAL.NET to MSAL.NET when both library versions are running side-by-side.
+ /// To improve performance add .WithLegacyCacheCompatibility(false) unless you care about migration scenarios.
+ ///
+ public T WithLegacyCacheCompatibility(bool enableLegacyCacheCompatibility = true)
+ {
+ Config.LegacyCacheCompatibilityEnabled = enableLegacyCacheCompatibility;
+ return (T)this;
+ }
+
///
/// Sets the logging callback. For details see https://aka.ms/msal-net-logging
///
@@ -356,6 +374,7 @@ protected T WithOptions(ApplicationOptions applicationOptions)
WithClientName(applicationOptions.ClientName);
WithClientVersion(applicationOptions.ClientVersion);
WithClientCapabilities(applicationOptions.ClientCapabilities);
+ WithLegacyCacheCompatibility(applicationOptions.LegacyCacheCompatibilityEnabled);
WithLogging(
null,
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
index d10e742f23..d025df9ddb 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationConfiguration.cs
@@ -81,6 +81,8 @@ public string ClientVersion
public bool MergeWithDefaultClaims { get; internal set; }
internal int ConfidentialClientCredentialCount;
+ public bool LegacyCacheCompatibilityEnabled { get; internal set; } = true;
+
#region Authority
public InstanceDiscoveryResponse CustomInstanceDiscoveryMetadata { get; set; }
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationOptions.cs b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationOptions.cs
index 728d2e4cd5..2824e6c056 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/ApplicationOptions.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/ApplicationOptions.cs
@@ -121,5 +121,10 @@ public abstract class ApplicationOptions
/// For more details see https://aka.ms/msal-net-claims-request
///
public IEnumerable ClientCapabilities { get; set; }
+
+ ///
+ /// Enables legacy ADAL cache serialization and deserialization.
+ ///
+ public bool LegacyCacheCompatibilityEnabled { get; set; }
}
}
diff --git a/src/client/Microsoft.Identity.Client/AppConfig/IAppConfig.cs b/src/client/Microsoft.Identity.Client/AppConfig/IAppConfig.cs
index 2ea2583a83..1e1d5dbdff 100644
--- a/src/client/Microsoft.Identity.Client/AppConfig/IAppConfig.cs
+++ b/src/client/Microsoft.Identity.Client/AppConfig/IAppConfig.cs
@@ -103,6 +103,10 @@ public interface IAppConfig
///
IEnumerable ClientCapabilities { get; }
+ ///
+ /// Enables legacy ADAL cache serialization and deserialization.
+ ///
+ bool LegacyCacheCompatibilityEnabled { get; }
///
///
diff --git a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs
index dad01e92fb..8cf4ce7b29 100644
--- a/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs
+++ b/src/client/Microsoft.Identity.Client/TokenCache.ITokenCacheInternal.cs
@@ -177,8 +177,9 @@ public sealed partial class TokenCache : ITokenCacheInternal
UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId);
- // Do not save RT in ADAL cache for confidential client or B2C
- if (!requestParams.IsClientCredentialRequest &&
+ // Do not save RT in ADAL cache for client credentials flow or B2C
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled &&
+ !requestParams.IsClientCredentialRequest &&
requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C)
{
var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment(
@@ -560,9 +561,10 @@ private MsalAccessTokenCacheItem FilterByKeyId(MsalAccessTokenCacheItem item, Au
requestParams.RequestContext.Logger.Info("Checking ADAL cache for matching RT. ");
// ADAL legacy cache does not store FRTs
- if (requestParams.Account != null && string.IsNullOrEmpty(familyId))
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled &&
+ requestParams.Account != null &&
+ string.IsNullOrEmpty(familyId))
{
-
return CacheFallbackOperations.GetRefreshToken(
Logger,
LegacyCachePersistence,
@@ -631,17 +633,22 @@ async Task> ITokenCacheInternal.GetAccountsAsync(Authentic
if (logger.IsLoggingEnabled(LogLevel.Verbose))
logger.Verbose($"GetAccounts found {rtCacheItems.Count()} RTs and {accountCacheItems.Count()} accounts in MSAL cache. ");
- AdalUsersForMsal adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal(
- Logger,
- LegacyCachePersistence,
- ClientId);
-
// Multi-cloud support - must filter by env.
ISet allEnvironmentsInCache = new HashSet(
accountCacheItems.Select(aci => aci.Environment),
StringComparer.OrdinalIgnoreCase);
allEnvironmentsInCache.UnionWith(rtCacheItems.Select(rt => rt.Environment));
- allEnvironmentsInCache.UnionWith(adalUsersResult.GetAdalUserEnviroments());
+
+ AdalUsersForMsal adalUsersResult = null;
+
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled)
+ {
+ adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal(
+ Logger,
+ LegacyCachePersistence,
+ ClientId);
+ allEnvironmentsInCache.UnionWith(adalUsersResult.GetAdalUserEnviroments());
+ }
var instanceMetadata = await ServiceBundle.InstanceDiscoveryManager.GetMetadataEntryTryAvoidNetworkAsync(
requestParameters.AuthorityInfo.CanonicalAuthority,
@@ -671,11 +678,14 @@ async Task> ITokenCacheInternal.GetAccountsAsync(Authentic
}
}
- UpdateMapWithAdalAccountsWithClientInfo(
- environment,
- instanceMetadata.Aliases,
- adalUsersResult,
- clientInfoToAccountMap);
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled)
+ {
+ UpdateMapWithAdalAccountsWithClientInfo(
+ environment,
+ instanceMetadata.Aliases,
+ adalUsersResult,
+ clientInfoToAccountMap);
+ }
// Add WAM accounts stored in MSAL's cache - for which we do not have an RT
if (requestParameters.IsBrokerConfigured && ServiceBundle.PlatformProxy.BrokerSupportsWamAccounts)
@@ -792,7 +802,10 @@ async Task ITokenCacheInternal.RemoveAccountAsync(IAccount account, RequestConte
}
tokenCacheInternal.RemoveMsalAccountWithNoLocks(account, requestContext);
- RemoveAdalUser(account);
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled)
+ {
+ RemoveAdalUser(account);
+ }
}
finally
{
diff --git a/src/client/Microsoft.Identity.Client/TokenCache.cs b/src/client/Microsoft.Identity.Client/TokenCache.cs
index 4bdd348c0f..7236d888b8 100644
--- a/src/client/Microsoft.Identity.Client/TokenCache.cs
+++ b/src/client/Microsoft.Identity.Client/TokenCache.cs
@@ -42,7 +42,7 @@ public sealed partial class TokenCache : ITokenCacheInternal
private ICoreLogger Logger => ServiceBundle.DefaultLogger;
internal IServiceBundle ServiceBundle { get; }
- internal ILegacyCachePersistence LegacyCachePersistence { get; }
+ internal ILegacyCachePersistence LegacyCachePersistence { get; set; }
internal string ClientId => ServiceBundle.Config.ClientId;
ITokenCacheAccessor ITokenCacheInternal.Accessor => _accessor;
@@ -206,23 +206,26 @@ private bool RtMatchesAccount(MsalRefreshTokenCacheItem rtItem, MsalAccountCache
}
}
- private static List UpdateWithAdalAccountsWithoutClientInfo(
+ private List UpdateWithAdalAccountsWithoutClientInfo(
string envFromRequest,
IEnumerable envAliases,
AdalUsersForMsal adalUsers,
IDictionary clientInfoToAccountMap)
{
var accounts = new List();
-
accounts.AddRange(clientInfoToAccountMap.Values);
- var uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList();
- foreach (AdalUserInfo user in adalUsers.GetUsersWithoutClientInfo(envAliases))
+ if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled)
{
- if (!string.IsNullOrEmpty(user.DisplayableId) && !uniqueUserNames.Contains(user.DisplayableId))
+ var uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList();
+
+ foreach (AdalUserInfo user in adalUsers.GetUsersWithoutClientInfo(envAliases))
{
- accounts.Add(new Account(null, user.DisplayableId, envFromRequest));
- uniqueUserNames.Add(user.DisplayableId);
+ if (!string.IsNullOrEmpty(user.DisplayableId) && !uniqueUserNames.Contains(user.DisplayableId))
+ {
+ accounts.Add(new Account(null, user.DisplayableId, envFromRequest));
+ uniqueUserNames.Add(user.DisplayableId);
+ }
}
}
diff --git a/tests/Microsoft.Identity.Client.Performance/LegacyCacheOperationsTests.cs b/tests/Microsoft.Identity.Client.Performance/LegacyCacheOperationsTests.cs
new file mode 100644
index 0000000000..f6778ec186
--- /dev/null
+++ b/tests/Microsoft.Identity.Client.Performance/LegacyCacheOperationsTests.cs
@@ -0,0 +1,100 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Engines;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.Instance;
+using Microsoft.Identity.Client.Instance.Discovery;
+using Microsoft.Identity.Client.Internal;
+using Microsoft.Identity.Client.Internal.Requests;
+using Microsoft.Identity.Client.OAuth2;
+using Microsoft.Identity.Test.Common;
+using Microsoft.Identity.Test.Common.Core.Mocks;
+using Microsoft.Identity.Test.Unit;
+
+namespace Microsoft.Identity.Test.Performance
+{
+ [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByMethod)]
+ public class LegacyCacheOperationsTests
+ {
+ private ITokenCacheInternal _cache;
+ private MsalTokenResponse _response;
+ private AuthenticationRequestParameters _requestParams;
+ private RequestContext _requestContext;
+ private readonly Consumer _consumer = new Consumer();
+
+ [Params(1, 100, 1000)]
+ public int TokenCacheSize { get; set; }
+
+ [ParamsAllValues]
+ public bool EnableLegacyCache { get; set; }
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ var serviceBundle = TestCommon.CreateServiceBundleWithCustomHttpManager(null, isLegacyCacheEnabled: EnableLegacyCache);
+
+ _requestContext = new RequestContext(serviceBundle, Guid.NewGuid());
+ _cache = new TokenCache(serviceBundle, false);
+ _response = TestConstants.CreateMsalTokenResponse();
+
+ _requestParams = TestCommon.CreateAuthenticationRequestParameters(serviceBundle);
+ _requestParams.TenantUpdatedCanonicalAuthority = Authority.CreateAuthorityWithTenant(
+ _requestParams.AuthorityInfo,
+ TestConstants.Utid);
+ _requestParams.Account = new Account(TestConstants.s_userIdentifier, $"1{TestConstants.DisplayableId}", TestConstants.ProductionPrefNetworkEnvironment);
+
+ AddHostToInstanceCache(serviceBundle, TestConstants.ProductionPrefCacheEnvironment);
+
+ LegacyTokenCacheHelper.PopulateLegacyCache(serviceBundle.DefaultLogger, _cache.LegacyPersistence, TokenCacheSize);
+ TokenCacheHelper.AddRefreshTokensToCache(_cache.Accessor, TokenCacheSize);
+ }
+
+ [Benchmark(Description = "SaveToken")]
+ public async Task SaveTokenResponseTestAsync()
+ {
+ var result = await _cache.SaveTokenResponseAsync(_requestParams, _response).ConfigureAwait(true);
+ return result.Item1.ClientId;
+ }
+
+ [Benchmark(Description = "FindToken")]
+ public async Task FindRefreshTokenTestAsync()
+ {
+ var result = await _cache.FindRefreshTokenAsync(_requestParams).ConfigureAwait(true);
+ return result?.ClientId;
+ }
+
+ [Benchmark(Description = "GetAllUsers")]
+ public async Task GetAllAdalUsersTestAsync()
+ {
+ var result = await _cache.GetAccountsAsync(_requestParams).ConfigureAwait(true);
+ result.Consume(_consumer);
+ }
+
+ [Benchmark(Description = "RemoveUser")]
+ public async Task RemoveAdalUserTestAsync()
+ {
+ await _cache.RemoveAccountAsync(_requestParams.Account, _requestContext).ConfigureAwait(true);
+ }
+
+ private void AddHostToInstanceCache(IServiceBundle serviceBundle, string host)
+ {
+ (serviceBundle.InstanceDiscoveryManager as InstanceDiscoveryManager)
+ .AddTestValueToStaticProvider(
+ host,
+ new InstanceDiscoveryMetadataEntry
+ {
+ PreferredNetwork = host,
+ PreferredCache = host,
+ Aliases = new string[]
+ {
+ host
+ }
+ });
+ }
+ }
+}
diff --git a/tests/Microsoft.Identity.Client.Performance/Program.cs b/tests/Microsoft.Identity.Client.Performance/Program.cs
index 0b6565a5ad..b7cb0f6d0a 100644
--- a/tests/Microsoft.Identity.Client.Performance/Program.cs
+++ b/tests/Microsoft.Identity.Client.Performance/Program.cs
@@ -12,7 +12,7 @@ class Program
{
static void Main(string[] args)
{
- BenchmarkRunner.Run(
+ BenchmarkRunner.Run(
DefaultConfig.Instance
.WithOptions(ConfigOptions.DontOverwriteResults)
.AddJob(
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/LegacyTokenCacheHelper.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/LegacyTokenCacheHelper.cs
new file mode 100644
index 0000000000..a45b31ea53
--- /dev/null
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/LegacyTokenCacheHelper.cs
@@ -0,0 +1,141 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Identity.Client.Cache;
+using Microsoft.Identity.Client.Cache.Items;
+using Microsoft.Identity.Client.Core;
+using Microsoft.Identity.Client.Internal;
+using Microsoft.Identity.Test.Unit;
+
+namespace Microsoft.Identity.Test.Common.Core.Mocks
+{
+ internal class LegacyTokenCacheHelper
+ {
+ internal static void PopulateLegacyCache(ICoreLogger logger, ILegacyCachePersistence legacyCachePersistence, int tokenQuantity = 1)
+ {
+ for (int i = 1; i <= tokenQuantity; i++)
+ {
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefCacheEnvironment,
+ TestConstants.Uid,
+ TestConstants.Utid,
+ $"{i}{TestConstants.DisplayableId}");
+ }
+ }
+
+ internal static void PopulateLegacyCache(ICoreLogger logger, ILegacyCachePersistence legacyCachePersistence)
+ {
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefNetworkEnvironment,
+ "uid1",
+ "tenantId1",
+ "user1");
+
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefNetworkEnvironment,
+ "uid2",
+ "tenantId2",
+ "user2");
+
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefNetworkEnvironment,
+ null,
+ null,
+ "no_client_info_user3");
+
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefNetworkEnvironment,
+ null,
+ null,
+ "no_client_info_user4");
+
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.SovereignNetworkEnvironment, // different env
+ "uid4",
+ "tenantId4",
+ "sovereign_user5");
+
+ PopulateLegacyWithRtAndId(
+ logger,
+ legacyCachePersistence,
+ "other_client_id", // different client id
+ TestConstants.SovereignNetworkEnvironment,
+ "uid5",
+ "tenantId5",
+ "user6");
+ }
+
+ internal static void PopulateLegacyWithRtAndId(
+ ICoreLogger logger,
+ ILegacyCachePersistence legacyCachePersistence,
+ string clientId,
+ string env,
+ string uid,
+ string uniqueTenantId,
+ string username)
+ {
+ PopulateLegacyWithRtAndId(logger, legacyCachePersistence, clientId, env, uid, uniqueTenantId, username, "scope1");
+ }
+
+ internal static void PopulateLegacyWithRtAndId(
+ ICoreLogger logger,
+ ILegacyCachePersistence legacyCachePersistence,
+ string clientId,
+ string env,
+ string uid,
+ string uniqueTenantId,
+ string username,
+ string scope)
+ {
+ string clientInfoString;
+ string homeAccountId;
+ if (string.IsNullOrEmpty(uid) || string.IsNullOrEmpty(uniqueTenantId))
+ {
+ clientInfoString = null;
+ homeAccountId = null;
+ }
+ else
+ {
+ clientInfoString = MockHelpers.CreateClientInfo(uid, uniqueTenantId);
+ homeAccountId = ClientInfo.CreateFromJson(clientInfoString).ToAccountIdentifier();
+ }
+
+ var rtItem = new MsalRefreshTokenCacheItem(env, clientId, "someRT", clientInfoString, null, homeAccountId);
+
+ var idTokenCacheItem = new MsalIdTokenCacheItem(
+ env,
+ clientId,
+ MockHelpers.CreateIdToken(uid, username),
+ clientInfoString,
+ homeAccountId,
+ tenantId: uniqueTenantId);
+
+ CacheFallbackOperations.WriteAdalRefreshToken(
+ logger,
+ legacyCachePersistence,
+ rtItem,
+ idTokenCacheItem,
+ "https://" + env + "/common",
+ uid,
+ scope);
+ }
+ }
+}
diff --git a/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs b/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
index 59549b2024..f2e8c9bda9 100644
--- a/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
+++ b/tests/Microsoft.Identity.Test.Common/Core/Mocks/TokenCacheHelper.cs
@@ -15,9 +15,9 @@ internal class TokenCacheHelper
public static long ValidExpiresIn = 28800;
public static long ValidExtendedExpiresIn = 57600;
- internal void PopulateCacheForClientCredential(ITokenCacheAccessor accessor, int tokensQuantity = 1)
+ internal void PopulateCacheForClientCredential(ITokenCacheAccessor accessor, int tokenQuantity = 1)
{
- for (int i = 1; i <= tokensQuantity; i++)
+ for (int i = 1; i <= tokenQuantity; i++)
{
var atItem = CreateAccessTokenItem(string.Format(System.Globalization.CultureInfo.InvariantCulture, TestConstants.ScopeStrFormat, i));
accessor.SaveAccessToken(atItem);
@@ -190,6 +190,14 @@ internal void PopulateCacheWithOneAccessToken(ITokenCacheAccessor accessor)
accessor.SaveRefreshToken(rtItem);
}
+ public static void AddRefreshTokensToCache(ITokenCacheAccessor cacheAccessor, int tokensQuantity = 1)
+ {
+ for (int i = 1; i <= tokensQuantity; i++)
+ {
+ AddRefreshTokenToCache(cacheAccessor, Guid.NewGuid().ToString(), TestConstants.Utid);
+ }
+ }
+
public static void AddAccountToCache(
ITokenCacheAccessor accessor,
string uid,
diff --git a/tests/Microsoft.Identity.Test.Common/TestCommon.cs b/tests/Microsoft.Identity.Test.Common/TestCommon.cs
index 938d84fe22..bfd97a92aa 100644
--- a/tests/Microsoft.Identity.Test.Common/TestCommon.cs
+++ b/tests/Microsoft.Identity.Test.Common/TestCommon.cs
@@ -56,7 +56,8 @@ public static object GetPropValue(object src, string propName)
bool enablePiiLogging = false,
string clientId = TestConstants.ClientId,
bool clearCaches = true,
- bool validateAuthority = true)
+ bool validateAuthority = true,
+ bool isLegacyCacheEnabled = true)
{
var appConfig = new ApplicationConfiguration()
@@ -69,7 +70,8 @@ public static object GetPropValue(object src, string propName)
LogLevel = LogLevel.Verbose,
EnablePiiLogging = enablePiiLogging,
IsExtendedTokenLifetimeEnabled = isExtendedTokenLifetimeEnabled,
- AuthorityInfo = AuthorityInfo.FromAuthorityUri(authority, validateAuthority)
+ AuthorityInfo = AuthorityInfo.FromAuthorityUri(authority, validateAuthority),
+ LegacyCacheCompatibilityEnabled = isLegacyCacheEnabled
};
return new ServiceBundle(appConfig, clearCaches);
}
diff --git a/tests/Microsoft.Identity.Test.Unit/AppConfigTests/ConfidentialClientApplicationBuilderTests.cs b/tests/Microsoft.Identity.Test.Unit/AppConfigTests/ConfidentialClientApplicationBuilderTests.cs
index 5282eeb865..06617dfcbe 100644
--- a/tests/Microsoft.Identity.Test.Unit/AppConfigTests/ConfidentialClientApplicationBuilderTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/AppConfigTests/ConfidentialClientApplicationBuilderTests.cs
@@ -338,5 +338,17 @@ public void TestConstructor_BadInstanceMetadata()
Assert.AreEqual(ex.ErrorCode, MsalError.InvalidUserInstanceMetadata);
}
+
+ [TestMethod]
+ public void TestConstructor_WithLegacyCacheCompatibility()
+ {
+ var cca = ConfidentialClientApplicationBuilder
+ .Create(TestConstants.ClientId)
+ .WithClientSecret(TestConstants.ClientSecret)
+ .WithLegacyCacheCompatibility(true)
+ .Build();
+
+ Assert.AreEqual(true, cca.AppConfig.LegacyCacheCompatibilityEnabled);
+ }
}
}
diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/CacheFallbackOperationsTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/CacheFallbackOperationsTests.cs
index ce400e4f58..4f7c815933 100644
--- a/tests/Microsoft.Identity.Test.Unit/CacheTests/CacheFallbackOperationsTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/CacheFallbackOperationsTests.cs
@@ -39,8 +39,7 @@ public void TestInitialize()
public void GetAllAdalUsersForMsal_ScopedBy_ClientIdAndEnv()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
-
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
// Act - query users by env and clientId
var adalUsers =
@@ -97,7 +96,7 @@ public void GetAllAdalUsersForMsal_ScopedBy_ClientIdAndEnv()
public void GetAllAdalEntriesForMsal_FilterBy_Upn()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
// Act - query Adal Entries For Msal with valid Upn as a filter
var rt =
@@ -109,7 +108,6 @@ public void GetAllAdalEntriesForMsal_FilterBy_Upn()
TestConstants.SovereignNetworkEnvironment },
TestConstants.ClientId,
new Account(null, "User1", null));
-
Assert.AreEqual("uid1.tenantId1", rt.HomeAccountId);
@@ -131,7 +129,7 @@ public void GetAllAdalEntriesForMsal_FilterBy_Upn()
public void GetAllAdalEntriesForMsal_FilterBy_UniqueId()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
// Act - query Adal Entries For Msal with valid UniqueId as a filter
var rt =
@@ -164,7 +162,7 @@ public void GetAllAdalEntriesForMsal_FilterBy_UniqueId()
public void GetAllAdalEntriesForMsal_NoFilter()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
// Act - query Adal Entries For Msal with valid Upn and UniqueId as a filter
var rt =
@@ -184,7 +182,7 @@ public void GetAllAdalEntriesForMsal_NoFilter()
public void GetAllAdalEntriesForMsal_FilterBy_UniqueIdAndUpn()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
// Act - query Adal Entries For Msal with valid Upn and UniqueId as a filter
var rt =
@@ -200,7 +198,7 @@ public void GetAllAdalEntriesForMsal_FilterBy_UniqueIdAndUpn()
Assert.AreEqual("uid1.tenantId1", rt.HomeAccountId);
// Act - query Adal Entries For Msal with invalid Upn and valid UniqueId as a filter
- rt =
+ rt =
CacheFallbackOperations.GetRefreshToken(
_logger,
_legacyCachePersistence,
@@ -230,15 +228,17 @@ public void GetAllAdalEntriesForMsal_FilterBy_UniqueIdAndUpn()
public void GetAllAdalEntriesForMsal_MultipleRTsPerEnv()
{
// Arrange
- PopulateLegacyWithRtAndId(
- _legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.ProductionPrefNetworkEnvironment,
- "uid",
- "tenantId1",
- "user1");
-
- PopulateLegacyWithRtAndId(
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
+ _legacyCachePersistence,
+ TestConstants.ClientId,
+ TestConstants.ProductionPrefNetworkEnvironment,
+ "uid",
+ "tenantId1",
+ "user1");
+
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
_legacyCachePersistence,
TestConstants.ClientId,
TestConstants.ProductionPrefNetworkEnvironment,
@@ -262,9 +262,10 @@ public void GetAllAdalEntriesForMsal_MultipleRTsPerEnv()
public void RemoveAdalUser_RemovesUserWithSameId()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
- PopulateLegacyWithRtAndId( // different clientId -> should not be deleted
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( // different clientId -> should not be deleted
+ _logger,
_legacyCachePersistence,
"other_client_id",
TestConstants.ProductionPrefNetworkEnvironment,
@@ -272,7 +273,8 @@ public void RemoveAdalUser_RemovesUserWithSameId()
"tenantId1",
"user1_other_client_id");
- PopulateLegacyWithRtAndId( // different env -> should be deleted
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( // different env -> should be deleted
+ _logger,
_legacyCachePersistence,
TestConstants.ClientId,
"other_env",
@@ -297,7 +299,7 @@ public void RemoveAdalUser_RemovesUserWithSameId()
AssertByUsername(
adalUsers,
- new[] { TestConstants.ProductionPrefNetworkEnvironment},
+ new[] { TestConstants.ProductionPrefNetworkEnvironment },
new[]
{
"user2",
@@ -313,9 +315,10 @@ public void RemoveAdalUser_RemovesUserWithSameId()
public void RemoveAdalUser_RemovesUserNoClientInfo()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
- PopulateLegacyWithRtAndId(
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
_legacyCachePersistence,
"other_client_id",
TestConstants.ProductionPrefNetworkEnvironment,
@@ -323,7 +326,8 @@ public void RemoveAdalUser_RemovesUserNoClientInfo()
null,
"no_client_info_user3"); // no client info, different client id -> won't be deleted
- PopulateLegacyWithRtAndId(
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
_legacyCachePersistence,
TestConstants.ClientId,
"other_env",
@@ -394,7 +398,7 @@ private void AssertCacheEntryCount(int expectedEntryCount)
public void RemoveAdalUser_RemovesUserNoClientInfo_And_NoDisplayName()
{
// Arrange
- PopulateLegacyCache(_legacyCachePersistence);
+ LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence);
IDictionary adalCacheBeforeDelete =
AdalCacheOperations.Deserialize(_logger, _legacyCachePersistence.LoadCache());
Assert.AreEqual(6, adalCacheBeforeDelete.Count);
@@ -420,7 +424,8 @@ public void RemoveAdalUser_RemovesAdalEntitiesWithClientInfoAndWithout()
// adal cache can have different cache entities for the
// same user/account with client info and wihout
// CacheFallbackOperations.RemoveAdalUser should remove both
- PopulateLegacyWithRtAndId(
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
_legacyCachePersistence,
TestConstants.ClientId,
TestConstants.ProductionPrefNetworkEnvironment,
@@ -431,7 +436,8 @@ public void RemoveAdalUser_RemovesAdalEntitiesWithClientInfoAndWithout()
AssertCacheEntryCount(1);
- PopulateLegacyWithRtAndId(
+ LegacyTokenCacheHelper.PopulateLegacyWithRtAndId(
+ _logger,
_legacyCachePersistence,
TestConstants.ClientId,
TestConstants.ProductionPrefNetworkEnvironment,
@@ -463,17 +469,17 @@ public void WriteAdalRefreshToken_ErrorLog()
var rtItem = new MsalRefreshTokenCacheItem(
TestConstants.ProductionPrefNetworkEnvironment,
TestConstants.ClientId,
- "someRT",
- clientInfo,
- null,
+ "someRT",
+ clientInfo,
+ null,
homeAccountId);
var idTokenCacheItem = new MsalIdTokenCacheItem(
TestConstants.ProductionPrefCacheEnvironment, // different env
TestConstants.ClientId,
MockHelpers.CreateIdToken("u1", "username"),
- clientInfo,
- tenantId: "ut1",
+ clientInfo,
+ tenantId: "ut1",
homeAccountId: homeAccountId);
// Act
@@ -505,16 +511,16 @@ public void DoNotWriteFRTs()
TestConstants.ProductionPrefNetworkEnvironment,
TestConstants.ClientId,
"someRT",
- clientInfo,
- "familyId",
+ clientInfo,
+ "familyId",
homeAccountId);
var idTokenCacheItem = new MsalIdTokenCacheItem(
TestConstants.ProductionPrefNetworkEnvironment, // different env
TestConstants.ClientId,
MockHelpers.CreateIdToken("u1", "username"),
- clientInfo,
- tenantId: "ut1",
+ clientInfo,
+ tenantId: "ut1",
homeAccountId: homeAccountId);
// Act
@@ -530,57 +536,6 @@ public void DoNotWriteFRTs()
AssertCacheEntryCount(0);
}
- private void PopulateLegacyCache(ILegacyCachePersistence legacyCachePersistence)
- {
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.ProductionPrefNetworkEnvironment,
- "uid1",
- "tenantId1",
- "user1");
-
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.ProductionPrefNetworkEnvironment,
- "uid2",
- "tenantId2",
- "user2");
-
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.ProductionPrefNetworkEnvironment,
- null,
- null,
- "no_client_info_user3");
-
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.ProductionPrefNetworkEnvironment,
- null,
- null,
- "no_client_info_user4");
-
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- TestConstants.ClientId,
- TestConstants.SovereignNetworkEnvironment, // different env
- "uid4",
- "tenantId4",
- "sovereign_user5");
-
- PopulateLegacyWithRtAndId(
- legacyCachePersistence,
- "other_client_id", // different client id
- TestConstants.SovereignNetworkEnvironment,
- "uid5",
- "tenantId5",
- "user6");
- }
-
private static void AssertUsersByDisplayName(
IEnumerable expectedUsernames,
IEnumerable adalUserInfos,
@@ -591,59 +546,6 @@ private void PopulateLegacyCache(ILegacyCachePersistence legacyCachePersistence)
CollectionAssert.AreEquivalent(expectedUsernames.ToArray(), actualUsernames, errorMessage);
}
- private void PopulateLegacyWithRtAndId(
- ILegacyCachePersistence legacyCachePersistence,
- string clientId,
- string env,
- string uid,
- string uniqueTenantId,
- string username)
- {
- PopulateLegacyWithRtAndId(legacyCachePersistence, clientId, env, uid, uniqueTenantId, username, "scope1");
- }
-
- private void PopulateLegacyWithRtAndId(
- ILegacyCachePersistence legacyCachePersistence,
- string clientId,
- string env,
- string uid,
- string uniqueTenantId,
- string username,
- string scope)
- {
- string clientInfoString;
- string homeAccountId;
- if (string.IsNullOrEmpty(uid) || string.IsNullOrEmpty(uniqueTenantId))
- {
- clientInfoString = null;
- homeAccountId = null;
- }
- else
- {
- clientInfoString = MockHelpers.CreateClientInfo(uid, uniqueTenantId);
- homeAccountId = ClientInfo.CreateFromJson(clientInfoString).ToAccountIdentifier();
- }
-
- var rtItem = new MsalRefreshTokenCacheItem(env, clientId, "someRT", clientInfoString, null, homeAccountId);
-
- var idTokenCacheItem = new MsalIdTokenCacheItem(
- env,
- clientId,
- MockHelpers.CreateIdToken(uid, username),
- clientInfoString,
- homeAccountId,
- tenantId: uniqueTenantId);
-
- CacheFallbackOperations.WriteAdalRefreshToken(
- _logger,
- legacyCachePersistence,
- rtItem,
- idTokenCacheItem,
- "https://" + env + "/common",
- uid,
- scope);
- }
-
private static void AssertByUsername(
AdalUsersForMsal adalUsers,
IEnumerable enviroments,
diff --git a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheTests.cs b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheTests.cs
index d69e2f93da..bde0199f9b 100644
--- a/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheTests.cs
+++ b/tests/Microsoft.Identity.Test.Unit/CacheTests/TokenCacheTests.cs
@@ -44,6 +44,45 @@ public override void TestInitialize()
base.TestInitialize();
}
+ [DataTestMethod]
+ [DataRow(true)]
+ [DataRow(false)]
+ public async Task WithLegacyCacheCompatibilityTest_Async(bool enableLegacyCacheCompatibility)
+ {
+ // Arrange
+ var legacyCachePersistence = Substitute.For();
+ var serviceBundle = TestCommon.CreateServiceBundleWithCustomHttpManager(null, isLegacyCacheEnabled: enableLegacyCacheCompatibility);
+ var requestContext = new RequestContext(serviceBundle, Guid.NewGuid());
+ var response = TestConstants.CreateMsalTokenResponse();
+
+ ITokenCacheInternal cache = new TokenCache(serviceBundle, false);
+ ((TokenCache)cache).LegacyCachePersistence = legacyCachePersistence;
+
+ var requestParams = TestCommon.CreateAuthenticationRequestParameters(serviceBundle);
+ requestParams.TenantUpdatedCanonicalAuthority = Authority.CreateAuthorityWithTenant(
+ requestParams.AuthorityInfo,
+ TestConstants.Utid);
+ requestParams.Account = new Account(TestConstants.s_userIdentifier, $"1{TestConstants.DisplayableId}", TestConstants.ProductionPrefNetworkEnvironment);
+
+ // Act
+ await cache.FindRefreshTokenAsync(requestParams).ConfigureAwait(true);
+ await cache.SaveTokenResponseAsync(requestParams, response).ConfigureAwait(true);
+ await cache.GetAccountsAsync(requestParams).ConfigureAwait(true);
+ await cache.RemoveAccountAsync(requestParams.Account, requestContext).ConfigureAwait(true);
+
+ // Assert
+ if (enableLegacyCacheCompatibility)
+ {
+ legacyCachePersistence.ReceivedWithAnyArgs().LoadCache();
+ legacyCachePersistence.ReceivedWithAnyArgs().WriteCache(Arg.Any());
+ }
+ else
+ {
+ legacyCachePersistence.DidNotReceiveWithAnyArgs().LoadCache();
+ legacyCachePersistence.DidNotReceiveWithAnyArgs().WriteCache(Arg.Any());
+ }
+ }
+
[TestMethod]
public void GetExactScopesMatchedAccessTokenTest()
{