From 7b2ae23c0be8e7be40c963352c44c3a415b18037 Mon Sep 17 00:00:00 2001 From: siddhijain Date: Mon, 3 Nov 2025 21:25:14 -0600 Subject: [PATCH 1/6] Use in-memory cache for accounts and credentials --- changelog.txt | 1 + .../identity/common/java/cache/MsalOAuth2TokenCache.java | 6 +++++- .../identity/common/java/flighting/CommonFlight.java | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 7904fbed39..9d0997fd3c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,6 @@ vNext ---------- +- [MINOR] Use SharedPreferencesInMemoryCache implementation in Broker (#2803) - [MINOR] Add new span name for DELEGATION_CERT_INSTALL's telemetry (#2790) - [MINOR] Refactor getAccountByLocalAccountId (#2781) - [MINOR] Add OTel Benchmarker (#2786) diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java index 432f9a979c..b876fc819e 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java @@ -42,6 +42,8 @@ import com.microsoft.identity.common.java.dto.IdTokenRecord; import com.microsoft.identity.common.java.dto.RefreshTokenRecord; import com.microsoft.identity.common.java.exception.ClientException; +import com.microsoft.identity.common.java.flighting.CommonFlight; +import com.microsoft.identity.common.java.flighting.CommonFlightsManager; import com.microsoft.identity.common.java.interfaces.INameValueStorage; import com.microsoft.identity.common.java.interfaces.IPlatformComponents; import com.microsoft.identity.common.java.logging.Logger; @@ -161,7 +163,9 @@ MicrosoftRefreshToken> create(@NonNull final IPlatformComponents components, boo String.class ); final IAccountCredentialCache accountCredentialCache; - if (useInMemoryCache) { + if (useInMemoryCache || CommonFlightsManager.INSTANCE + .getFlightsProvider() + .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS)) { accountCredentialCache = new SharedPreferencesAccountCredentialCacheWithMemoryCache( cacheKeyValueDelegate, sharedPreferencesFileManager diff --git a/common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java b/common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java index de4e15841b..bdf418f499 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java +++ b/common4j/src/main/com/microsoft/identity/common/java/flighting/CommonFlight.java @@ -181,7 +181,12 @@ public enum CommonFlight implements IFlightConfig { * Flight to enable OpenID issuer validation code which validates issuer against the open id well known * config endpoint and only reports the failure result. */ - ENABLE_OPENID_ISSUER_VALIDATION_REPORTING("EnableOpenIdIssuerValidationReporting", true); + ENABLE_OPENID_ISSUER_VALIDATION_REPORTING("EnableOpenIdIssuerValidationReporting", true), + + /** + * Flight to control whether or not to use in memory cache for accounts and credentials. + */ + USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS("UseInMemoryCacheForAccountsAndCredentials", false); private String key; private Object defaultValue; From b9a26f60baef03938fa5f8a659b642e35d4a9a4d Mon Sep 17 00:00:00 2001 From: Siddhi Date: Mon, 3 Nov 2025 21:27:37 -0600 Subject: [PATCH 2/6] Update changelog for Broker implementation change --- changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 9d0997fd3c..8550daa3c0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ vNext ---------- -- [MINOR] Use SharedPreferencesInMemoryCache implementation in Broker (#2803) +- [MINOR] Use SharedPreferencesInMemoryCache implementation in Broker (#2802) - [MINOR] Add new span name for DELEGATION_CERT_INSTALL's telemetry (#2790) - [MINOR] Refactor getAccountByLocalAccountId (#2781) - [MINOR] Add OTel Benchmarker (#2786) @@ -1190,4 +1190,4 @@ Version 0.0.3 * Separate methods for PII/OII logging - Initial Exception model implemented * BaseException + Client & Service subclasses -- Substantial portions of HTTP/S networking code migrated from ADAL & MSAL to this module \ No newline at end of file +- Substantial portions of HTTP/S networking code migrated from ADAL & MSAL to this module From 00da88b0396646f8a58a4b098bacc4f3d9f8d17b Mon Sep 17 00:00:00 2001 From: siddhijain Date: Mon, 3 Nov 2025 21:35:14 -0600 Subject: [PATCH 3/6] Added telemetry to see if in-memory cache was used --- .../identity/common/java/cache/MsalOAuth2TokenCache.java | 8 ++++++-- .../identity/common/java/opentelemetry/AttributeName.java | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java index b876fc819e..cfaa0e0c97 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java @@ -47,6 +47,8 @@ import com.microsoft.identity.common.java.interfaces.INameValueStorage; import com.microsoft.identity.common.java.interfaces.IPlatformComponents; import com.microsoft.identity.common.java.logging.Logger; +import com.microsoft.identity.common.java.opentelemetry.AttributeName; +import com.microsoft.identity.common.java.opentelemetry.SpanExtension; import com.microsoft.identity.common.java.providers.microsoft.MicrosoftAccount; import com.microsoft.identity.common.java.providers.microsoft.MicrosoftRefreshToken; import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsAuthorizationRequest; @@ -163,9 +165,11 @@ MicrosoftRefreshToken> create(@NonNull final IPlatformComponents components, boo String.class ); final IAccountCredentialCache accountCredentialCache; - if (useInMemoryCache || CommonFlightsManager.INSTANCE + final boolean isFlightEnabled = CommonFlightsManager.INSTANCE .getFlightsProvider() - .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS)) { + .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS); + if (useInMemoryCache || isFlightEnabled) { + SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), isFlightEnabled); accountCredentialCache = new SharedPreferencesAccountCredentialCacheWithMemoryCache( cacheKeyValueDelegate, sharedPreferencesFileManager diff --git a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java index 69780d9fca..99694c6c15 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java +++ b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java @@ -487,5 +487,10 @@ public enum AttributeName { /** * Records if current flow is in webcp flow. */ - is_in_web_cp_flow + is_in_web_cp_flow, + + /** + * Indicates whether or not in memory cache is used for accounts and credentials. + */ + in_memory_cache_used_for_accounts_and_credentials } From 46213abe8a7057493db5fe62c7b10a833f054982 Mon Sep 17 00:00:00 2001 From: siddhijain Date: Mon, 10 Nov 2025 13:22:18 -0600 Subject: [PATCH 4/6] Move the logic from MsalCache to BrokerCache --- .../java/cache/BrokerOAuth2TokenCache.java | 28 +++++++++++++++---- .../java/cache/MsalOAuth2TokenCache.java | 8 +----- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java index 91cf2b29ea..a66081b041 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java @@ -24,8 +24,12 @@ import com.microsoft.identity.common.java.authscheme.AbstractAuthenticationScheme; +import com.microsoft.identity.common.java.flighting.CommonFlight; +import com.microsoft.identity.common.java.flighting.CommonFlightsManager; import com.microsoft.identity.common.java.interfaces.INameValueStorage; import com.microsoft.identity.common.java.interfaces.IPlatformComponents; +import com.microsoft.identity.common.java.opentelemetry.AttributeName; +import com.microsoft.identity.common.java.opentelemetry.SpanExtension; import com.microsoft.identity.common.java.providers.oauth2.OAuth2Strategy; import com.microsoft.identity.common.java.providers.oauth2.OAuth2TokenCache; import com.microsoft.identity.common.java.WarningType; @@ -343,7 +347,7 @@ public synchronized List saveAndLoadAggregatedAccountData( } @SuppressWarnings("unchecked") - private List loadAggregatedAccountData(final @NonNull AbstractAuthenticationScheme authScheme, + public List loadAggregatedAccountData(final @NonNull AbstractAuthenticationScheme authScheme, final @NonNull ICacheRecord cacheRecord) { final String methodName = ":loadAggregatedAccountData"; @@ -1638,11 +1642,23 @@ private static T getTokenCache(@NonNull final I @NonNull final INameValueStorage spfm, boolean isFoci) { final ICacheKeyValueDelegate cacheKeyValueDelegate = new CacheKeyValueDelegate(); - final IAccountCredentialCache accountCredentialCache = - new SharedPreferencesAccountCredentialCache( - cacheKeyValueDelegate, - spfm - ); + final boolean isFlightEnabled = CommonFlightsManager.INSTANCE + .getFlightsProvider() + .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS); + final IAccountCredentialCache accountCredentialCache; + if (isFlightEnabled) { + accountCredentialCache = new SharedPreferencesAccountCredentialCacheWithMemoryCache( + cacheKeyValueDelegate, + spfm + ); + SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), true); + } else { + accountCredentialCache = new SharedPreferencesAccountCredentialCache( + cacheKeyValueDelegate, + spfm + ); + SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), false); + } final MicrosoftStsAccountCredentialAdapter accountCredentialAdapter = new MicrosoftStsAccountCredentialAdapter(); diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java index cfaa0e0c97..c0411503f8 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java @@ -42,8 +42,6 @@ import com.microsoft.identity.common.java.dto.IdTokenRecord; import com.microsoft.identity.common.java.dto.RefreshTokenRecord; import com.microsoft.identity.common.java.exception.ClientException; -import com.microsoft.identity.common.java.flighting.CommonFlight; -import com.microsoft.identity.common.java.flighting.CommonFlightsManager; import com.microsoft.identity.common.java.interfaces.INameValueStorage; import com.microsoft.identity.common.java.interfaces.IPlatformComponents; import com.microsoft.identity.common.java.logging.Logger; @@ -165,11 +163,7 @@ MicrosoftRefreshToken> create(@NonNull final IPlatformComponents components, boo String.class ); final IAccountCredentialCache accountCredentialCache; - final boolean isFlightEnabled = CommonFlightsManager.INSTANCE - .getFlightsProvider() - .isFlightEnabled(CommonFlight.USE_IN_MEMORY_CACHE_FOR_ACCOUNTS_AND_CREDENTIALS); - if (useInMemoryCache || isFlightEnabled) { - SpanExtension.current().setAttribute(AttributeName.in_memory_cache_used_for_accounts_and_credentials.name(), isFlightEnabled); + if (useInMemoryCache) { accountCredentialCache = new SharedPreferencesAccountCredentialCacheWithMemoryCache( cacheKeyValueDelegate, sharedPreferencesFileManager From 5addf5696a0721bdcab9e7f69e80b2ca59d6cd6d Mon Sep 17 00:00:00 2001 From: siddhijain Date: Mon, 10 Nov 2025 13:23:05 -0600 Subject: [PATCH 5/6] Removing unused imports --- .../identity/common/java/cache/MsalOAuth2TokenCache.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java index c0411503f8..432f9a979c 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/MsalOAuth2TokenCache.java @@ -45,8 +45,6 @@ import com.microsoft.identity.common.java.interfaces.INameValueStorage; import com.microsoft.identity.common.java.interfaces.IPlatformComponents; import com.microsoft.identity.common.java.logging.Logger; -import com.microsoft.identity.common.java.opentelemetry.AttributeName; -import com.microsoft.identity.common.java.opentelemetry.SpanExtension; import com.microsoft.identity.common.java.providers.microsoft.MicrosoftAccount; import com.microsoft.identity.common.java.providers.microsoft.MicrosoftRefreshToken; import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsAuthorizationRequest; From 381a40c2b4e5d587ea7d647bf6736777bd8a211f Mon Sep 17 00:00:00 2001 From: siddhijain Date: Tue, 11 Nov 2025 22:20:38 -0600 Subject: [PATCH 6/6] Removed unintentional change + add unit tests --- ...OAuth2TokenCacheWithInMemoryCacheTest.java | 1240 +++++++++++++++++ .../java/cache/BrokerOAuth2TokenCache.java | 2 +- 2 files changed, 1241 insertions(+), 1 deletion(-) create mode 100644 common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheWithInMemoryCacheTest.java diff --git a/common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheWithInMemoryCacheTest.java b/common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheWithInMemoryCacheTest.java new file mode 100644 index 0000000000..97fbecfc70 --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/BrokerOAuth2TokenCacheWithInMemoryCacheTest.java @@ -0,0 +1,1240 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common; + +import static com.microsoft.identity.common.MicrosoftStsAccountCredentialAdapterTest.MOCK_ID_TOKEN_WITH_CLAIMS; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.APPLICATION_IDENTIFIER_SHA512; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.BEARER_AUTHENTICATION_SCHEME; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.CACHED_AT; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.CLIENT_ID; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.ENVIRONMENT; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.EXPIRES_ON; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.HOME_ACCOUNT_ID; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.LOCAL_ACCOUNT_ID; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.MAM_ENROLLMENT_IDENTIFIER; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.REALM; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.SECRET; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.SESSION_KEY; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.TARGET; +import static com.microsoft.identity.common.SharedPreferencesAccountCredentialCacheTest.USERNAME; +import static com.microsoft.identity.common.java.cache.SharedPreferencesAccountCredentialCache.BROKER_FOCI_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES; +import static com.microsoft.identity.common.java.cache.SharedPreferencesAccountCredentialCache.getBrokerUidSequesteredFilename; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import androidx.test.core.app.ApplicationProvider; + +import com.microsoft.identity.common.components.MockPlatformComponentsFactory; +import com.microsoft.identity.common.internal.platform.AndroidPlatformUtil; +import com.microsoft.identity.common.java.cache.BrokerApplicationMetadata; +import com.microsoft.identity.common.java.cache.BrokerOAuth2TokenCache; +import com.microsoft.identity.common.java.cache.CacheKeyValueDelegate; +import com.microsoft.identity.common.java.cache.IAccountCredentialAdapter; +import com.microsoft.identity.common.java.cache.IAccountCredentialCache; +import com.microsoft.identity.common.java.cache.IBrokerApplicationMetadataCache; +import com.microsoft.identity.common.java.cache.MicrosoftFamilyOAuth2TokenCache; +import com.microsoft.identity.common.java.cache.MsalOAuth2TokenCache; +import com.microsoft.identity.common.java.cache.NameValueStorageBrokerApplicationMetadataCache; +import com.microsoft.identity.common.java.cache.AccountDeletionRecord; +import com.microsoft.identity.common.java.cache.ICacheRecord; +import com.microsoft.identity.common.java.cache.SharedPreferencesAccountCredentialCacheWithMemoryCache; +import com.microsoft.identity.common.java.dto.AccountRecord; +import com.microsoft.identity.common.java.dto.Credential; +import com.microsoft.identity.common.java.dto.CredentialType; +import com.microsoft.identity.common.java.exception.ClientException; +import com.microsoft.identity.common.java.interfaces.INameValueStorage; +import com.microsoft.identity.common.java.interfaces.IPlatformComponents; +import com.microsoft.identity.common.java.providers.microsoft.MicrosoftAccount; +import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsAuthorizationRequest; +import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsOAuth2Strategy; +import com.microsoft.identity.common.java.providers.microsoft.microsoftsts.MicrosoftStsTokenResponse; +import com.microsoft.identity.common.java.providers.oauth2.OAuth2TokenCache; +import com.microsoft.identity.common.shadows.ShadowAndroidSdkStorageEncryptionManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@SuppressWarnings({"rawtypes", "unchecked"}) +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowAndroidSdkStorageEncryptionManager.class}) +public class BrokerOAuth2TokenCacheWithInMemoryCacheTest { + + private static final int TEST_APP_UID = 1337; + + private IPlatformComponents mPlatformComponents; + + private MicrosoftStsOAuth2Strategy mockStrategy; + private MicrosoftStsAuthorizationRequest mockRequest; + private MicrosoftStsTokenResponse mockResponse; + private IAccountCredentialAdapter mMockCredentialAdapter; + + private MicrosoftFamilyOAuth2TokenCache mFociCache; + private IAccountCredentialCache mFociCredentialCache; + private IAccountCredentialCache mAppUidCredentialCache; + private List mOtherAppTokenCaches; + private List mOtherAppCredentialCaches; + private BrokerOAuth2TokenCache mBrokerOAuth2TokenCache; + + private MsalOAuth2TokenCacheTest.AccountCredentialTestBundle mDefaultFociTestBundle; + private MsalOAuth2TokenCacheTest.AccountCredentialTestBundle mDefaultAppUidTestBundle; + private List mOtherCacheTestBundles; + + private IBrokerApplicationMetadataCache mApplicationMetadataCache; + private int[] testAppUids; + + @Before + public void setUp() { + + mockStrategy = PowerMockito.mock(MicrosoftStsOAuth2Strategy.class); + mockRequest = PowerMockito.mock(MicrosoftStsAuthorizationRequest.class); + mockResponse = PowerMockito.mock(MicrosoftStsTokenResponse.class); + mMockCredentialAdapter = PowerMockito.mock(IAccountCredentialAdapter.class); + + mPlatformComponents = MockPlatformComponentsFactory.getNonFunctionalBuilder() + .platformUtil(new AndroidPlatformUtil(ApplicationProvider.getApplicationContext(), null)) + .build(); + + mApplicationMetadataCache = new NameValueStorageBrokerApplicationMetadataCache(mPlatformComponents); + + initFociCache(mPlatformComponents); + initOtherCaches(mPlatformComponents); + + mBrokerOAuth2TokenCache = new BrokerOAuth2TokenCache( + mPlatformComponents, + TEST_APP_UID, + mApplicationMetadataCache, + new BrokerOAuth2TokenCache.ProcessUidCacheFactory() { + @Override + public MsalOAuth2TokenCache getTokenCache(final IPlatformComponents context, + final int bindingProcessUid) { + return initAppUidCache(context, bindingProcessUid); + } + }, + mFociCache + ); + + mDefaultFociTestBundle = new MsalOAuth2TokenCacheTest.AccountCredentialTestBundle( + MicrosoftAccount.AUTHORITY_TYPE_MS_STS, + LOCAL_ACCOUNT_ID, + USERNAME, + HOME_ACCOUNT_ID, + ENVIRONMENT, + REALM, + TARGET, + CACHED_AT, + EXPIRES_ON, + SECRET, + CLIENT_ID, + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + SECRET, + MOCK_ID_TOKEN_WITH_CLAIMS, + "1", + SESSION_KEY, + CredentialType.IdToken + ); + + mDefaultAppUidTestBundle = new MsalOAuth2TokenCacheTest.AccountCredentialTestBundle( + MicrosoftAccount.AUTHORITY_TYPE_MS_STS, + LOCAL_ACCOUNT_ID, + USERNAME, + HOME_ACCOUNT_ID, + ENVIRONMENT, + REALM, + TARGET, + CACHED_AT, + EXPIRES_ON, + SECRET, + CLIENT_ID, + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + SECRET, + MOCK_ID_TOKEN_WITH_CLAIMS, + null, + SESSION_KEY, + CredentialType.IdToken + ); + + mOtherCacheTestBundles = new ArrayList<>(); + + for (int ii = 0; ii < mOtherAppTokenCaches.size(); ii++) { + mOtherCacheTestBundles.add( + new MsalOAuth2TokenCacheTest.AccountCredentialTestBundle( + MicrosoftAccount.AUTHORITY_TYPE_MS_STS, + UUID.randomUUID().toString(), + "test.user@tenant.onmicrosoft.com", + HOME_ACCOUNT_ID, + ENVIRONMENT, + UUID.randomUUID().toString(), + TARGET, + CACHED_AT, + EXPIRES_ON, + SECRET, + UUID.randomUUID().toString(), + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + SECRET, + MOCK_ID_TOKEN_WITH_CLAIMS, + null, + SESSION_KEY, + CredentialType.IdToken + ) + ); + } + } + + @After + public void tearDown() throws Exception { + if (null != mAppUidCredentialCache) { + mAppUidCredentialCache.clearAll(); + } + + if (null != mFociCredentialCache) { + mFociCredentialCache.clearAll(); + } + + for (final IAccountCredentialCache cache : mOtherAppCredentialCaches) { + cache.clearAll(); + } + + mApplicationMetadataCache.clear(); + } + + private void initOtherCaches(final IPlatformComponents components) { + testAppUids = new int[]{ + 1338, + 1339, + 1340, + 1341 + }; + + final List> fileManagers = getAppUidFileManagers( + components, + testAppUids + ); + + mOtherAppCredentialCaches = getAccountCredentialCaches( + fileManagers + ); + + mOtherAppTokenCaches = new ArrayList<>(); + + for (final IAccountCredentialCache cache : mOtherAppCredentialCaches) { + mOtherAppTokenCaches.add( + getTokenCache( + components, + cache, + false + ) + ); + } + } + + private List getAccountCredentialCaches(final List> fileManagers) { + final List accountCredentialCaches = new ArrayList<>(); + + for (final INameValueStorage fileManager : fileManagers) { + accountCredentialCaches.add( + getAccountCredentialCache(fileManager) + ); + } + + return accountCredentialCaches; + } + + private List> getAppUidFileManagers(final IPlatformComponents components, + final int[] testAppUids) { + final List> fileManagers = new ArrayList<>(); + + for (final int currentAppUid : testAppUids) { + fileManagers.add( + getAppUidFileManager( + components, + currentAppUid + ) + ); + } + + return fileManagers; + } + + private INameValueStorage getAppUidFileManager(final IPlatformComponents components, + final int appUid) { + return components.getStorageSupplier().getEncryptedNameValueStore( + getBrokerUidSequesteredFilename(appUid), + String.class); + } + + private INameValueStorage getFociFileManager(final IPlatformComponents components) { + return components.getStorageSupplier().getEncryptedNameValueStore( + BROKER_FOCI_ACCOUNT_CREDENTIAL_SHARED_PREFERENCES, + String.class + ); + } + + private SharedPreferencesAccountCredentialCacheWithMemoryCache getAccountCredentialCache( + final INameValueStorage fm) { + return new SharedPreferencesAccountCredentialCacheWithMemoryCache( + new CacheKeyValueDelegate(), + fm + ); + } + + @SuppressWarnings("unchecked") + private T getTokenCache(final IPlatformComponents components, + final IAccountCredentialCache cache, + boolean isFoci) { + return (T) (isFoci ? + new MicrosoftFamilyOAuth2TokenCache<>( + components, + cache, + mMockCredentialAdapter + ) : + new MsalOAuth2TokenCache( + components, + cache, + mMockCredentialAdapter + ) + ); + } + + + private MsalOAuth2TokenCache initAppUidCache(final IPlatformComponents components, final int uid) { + final INameValueStorage appUidCacheFileManager = getAppUidFileManager( + components, + uid + ); + + mAppUidCredentialCache = getAccountCredentialCache(appUidCacheFileManager); + + return getTokenCache(components, mAppUidCredentialCache, false); + } + + private void initFociCache(final IPlatformComponents components) { + @SuppressWarnings("unchecked") + final INameValueStorage fociCacheFileManager = getFociFileManager(components); + + mFociCredentialCache = getAccountCredentialCache(fociCacheFileManager); + + mFociCache = getTokenCache(components, mFociCredentialCache, true); + } + + @SuppressWarnings("unchecked") + private void configureMocks(final MsalOAuth2TokenCacheTest.AccountCredentialTestBundle testBundle) { + when( + mMockCredentialAdapter.createAccount( + mockStrategy, + mockRequest, + mockResponse + ) + ).thenReturn(testBundle.mGeneratedAccount); + + when( + mMockCredentialAdapter.createAccessToken( + mockStrategy, + mockRequest, + mockResponse + ) + ).thenReturn(testBundle.mGeneratedAccessToken); + + when( + mMockCredentialAdapter.createRefreshToken( + mockStrategy, + mockRequest, + mockResponse + ) + ).thenReturn(testBundle.mGeneratedRefreshToken); + + when( + mMockCredentialAdapter.createIdToken( + mockStrategy, + mockRequest, + mockResponse + ) + ).thenReturn(testBundle.mGeneratedIdToken); + } + + private void configureMocksForFoci() { + configureMocks(mDefaultFociTestBundle); + when(mockResponse.getFamilyId()).thenReturn("1"); + } + + private void configureMocksForAppUid() { + configureMocks(mDefaultAppUidTestBundle); + } + + @Test + @SuppressWarnings("unchecked") + public void testKnownClientIdsNonFoci() throws ClientException { + configureMocksForAppUid(); + + final ICacheRecord result = mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final String targetClientId = result.getRefreshToken().getClientId(); + assertTrue(mBrokerOAuth2TokenCache.isClientIdKnownToCache(targetClientId)); + } + + @Test + @SuppressWarnings("unchecked") + public void testKnownClientIdsFoci() throws ClientException { + configureMocksForFoci(); + + final ICacheRecord result = mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final String targetClientId = result.getRefreshToken().getClientId(); + assertTrue(mBrokerOAuth2TokenCache.isClientIdKnownToCache(targetClientId)); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetFociCacheRecords() throws ClientException { + configureMocksForFoci(); + + final ICacheRecord result = mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final List fociCacheRecords = mBrokerOAuth2TokenCache.getFociCacheRecords(); + + assertNotNull(fociCacheRecords); + assertFalse(fociCacheRecords.isEmpty()); + assertEquals( + result.getRefreshToken(), + fociCacheRecords.get(0).getRefreshToken() + ); + assertEquals( + result.getIdToken(), + fociCacheRecords.get(0).getIdToken() + ); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetFociCacheRecordsEmpty() throws ClientException { + configureMocksForAppUid(); + + final ICacheRecord result = mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final List fociCacheRecords = mBrokerOAuth2TokenCache.getFociCacheRecords(); + + assertNotNull(fociCacheRecords); + assertTrue(fociCacheRecords.isEmpty()); + } + + @Test + @SuppressWarnings("unchecked") + public void testCanSaveIntoAppUidCache() throws ClientException { + configureMocksForAppUid(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final List accounts = mAppUidCredentialCache.getAccounts(); + assertEquals(1, accounts.size()); + assertEquals(mDefaultAppUidTestBundle.mGeneratedAccount, accounts.get(0)); + + final List credentials = mAppUidCredentialCache.getCredentials(); + assertEquals(3, credentials.size()); + + final List rts = new ArrayList<>(); + final List ats = new ArrayList<>(); + final List ids = new ArrayList<>(); + + for (final Credential credential : credentials) { + if (credential.getCredentialType().equalsIgnoreCase(CredentialType.AccessToken.name())) { + ats.add(credential); + } else if (credential.getCredentialType().equalsIgnoreCase(CredentialType.RefreshToken.name())) { + rts.add(credential); + } else if (credential.getCredentialType().equalsIgnoreCase(CredentialType.IdToken.name())) { + ids.add(credential); + } else { + fail(); + } + } + + assertEquals(mDefaultAppUidTestBundle.mGeneratedAccessToken, ats.get(0)); + assertEquals(mDefaultAppUidTestBundle.mGeneratedRefreshToken, rts.get(0)); + assertEquals(mDefaultAppUidTestBundle.mGeneratedIdToken, ids.get(0)); + } + + @Test + public void testCanSaveIntoFociCache() throws ClientException { + configureMocksForFoci(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final List accounts = mFociCredentialCache.getAccounts(); + assertEquals(1, accounts.size()); + assertEquals(mDefaultFociTestBundle.mGeneratedAccount, accounts.get(0)); + + final List credentials = mFociCredentialCache.getCredentials(); + assertEquals(3, credentials.size()); + + final List rts = new ArrayList<>(); + final List ats = new ArrayList<>(); + final List ids = new ArrayList<>(); + + for (final Credential credential : credentials) { + if (credential.getCredentialType().equalsIgnoreCase(CredentialType.AccessToken.name())) { + ats.add(credential); + } else if (credential.getCredentialType().equalsIgnoreCase(CredentialType.RefreshToken.name())) { + rts.add(credential); + } else if (credential.getCredentialType().equalsIgnoreCase(CredentialType.IdToken.name())) { + ids.add(credential); + } else { + fail(); + } + } + + assertEquals(mDefaultFociTestBundle.mGeneratedAccessToken, ats.get(0)); + assertEquals(mDefaultFociTestBundle.mGeneratedRefreshToken, rts.get(0)); + assertEquals(mDefaultFociTestBundle.mGeneratedIdToken, ids.get(0)); + } + + @Test + public void testCacheMiss() { + final ICacheRecord cacheRecord = mBrokerOAuth2TokenCache.load( + CLIENT_ID, + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + TARGET, + mDefaultAppUidTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertNotNull(cacheRecord); + assertNotNull(cacheRecord.getAccount()); + assertNull(cacheRecord.getAccessToken()); + assertNull(cacheRecord.getRefreshToken()); + assertNull(cacheRecord.getIdToken()); + } + + @Test + public void testRemoveCredentialAppUidCache() throws ClientException { + configureMocksForAppUid(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final ICacheRecord cacheRecord = mBrokerOAuth2TokenCache.load( + CLIENT_ID, + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + TARGET, + mDefaultAppUidTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertTrue( + mBrokerOAuth2TokenCache.removeCredential( + mDefaultAppUidTestBundle.mGeneratedAccessToken + ) + ); + } + + @Test + public void testRemoveCredentialFociCache() throws ClientException { + configureMocksForFoci(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final ICacheRecord cacheRecord = mBrokerOAuth2TokenCache.load( + CLIENT_ID, + APPLICATION_IDENTIFIER_SHA512, + MAM_ENROLLMENT_IDENTIFIER, + TARGET, + mDefaultFociTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertTrue( + mBrokerOAuth2TokenCache.removeCredential( + mDefaultFociTestBundle.mGeneratedAccessToken + ) + ); + } + + @Test + public void testRemoveCredentialMiss() { + assertFalse( + mBrokerOAuth2TokenCache.removeCredential( + mDefaultFociTestBundle.mGeneratedAccessToken + ) + ); + } + + @Test + public void testGetAccountAppUidCache() throws ClientException { + configureMocksForAppUid(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + assertNotNull( + mBrokerOAuth2TokenCache.getAccount( + ENVIRONMENT, + CLIENT_ID, + HOME_ACCOUNT_ID, + REALM + ) + ); + + assertNull( + mFociCache.getAccount( + ENVIRONMENT, + CLIENT_ID, + HOME_ACCOUNT_ID, + REALM + ) + ); + } + + @Test + public void testGetAccountFociCache() throws ClientException { + configureMocksForFoci(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + assertNotNull( + mBrokerOAuth2TokenCache.getAccount( + ENVIRONMENT, + CLIENT_ID, + HOME_ACCOUNT_ID, + REALM + ) + ); + } + + @Test + public void testGetAccountWithLocalAccountIdAppUidCache() throws ClientException { + configureMocksForAppUid(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final AccountRecord account = mBrokerOAuth2TokenCache.getAccountByLocalAccountId( + ENVIRONMENT, + CLIENT_ID, + LOCAL_ACCOUNT_ID + ); + + assertNotNull(account); + } + + @Test + public void testGetAccountWithLocalAccountIdFociCache() throws ClientException { + configureMocksForFoci(); + + mBrokerOAuth2TokenCache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final AccountRecord account = mBrokerOAuth2TokenCache.getAccountByLocalAccountId( + ENVIRONMENT, + CLIENT_ID, + LOCAL_ACCOUNT_ID + ); + + assertNotNull(account); + } + + @Test + public void testRemoveAccountFromDevice() throws ClientException { + // Load up the 'other caches' which a bunch of test credentials, see if we can get them out... + int ii = 0; + for (final OAuth2TokenCache cache : mOtherAppTokenCaches) { + configureMocks(mOtherCacheTestBundles.get(ii)); + + final ICacheRecord cacheRecord = cache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final BrokerApplicationMetadata applicationMetadata = new BrokerApplicationMetadata(); + applicationMetadata.setClientId(cacheRecord.getIdToken().getClientId()); + applicationMetadata.setEnvironment(cacheRecord.getIdToken().getEnvironment()); + applicationMetadata.setFoci(cacheRecord.getRefreshToken().getFamilyId()); + applicationMetadata.setUid(testAppUids[ii++]); + + mApplicationMetadataCache.insert(applicationMetadata); + } + + final List clientIds = new ArrayList<>(); + + for (final MsalOAuth2TokenCacheTest.AccountCredentialTestBundle testBundle : mOtherCacheTestBundles) { + clientIds.add( + testBundle.mGeneratedRefreshToken.getClientId() + ); + } + + final List xAppAccounts = mBrokerOAuth2TokenCache.getAccounts(); + + // Deleting one of these AccountRecords should remove all of them... + final AccountDeletionRecord deletionRecord = mBrokerOAuth2TokenCache.removeAccountFromDevice( + xAppAccounts.get(0) + ); + + assertEquals(xAppAccounts.size(), deletionRecord.size()); + assertEquals(0, mBrokerOAuth2TokenCache.getAccounts().size()); + } + + @Test + public void testGetAccountsAdal() throws ClientException { + // Load up the 'other caches' which a bunch of test credentials, see if we can get them out... + int ii = 0; + for (final OAuth2TokenCache cache : mOtherAppTokenCaches) { + configureMocks(mOtherCacheTestBundles.get(ii)); + + final ICacheRecord cacheRecord = cache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + final BrokerApplicationMetadata applicationMetadata = new BrokerApplicationMetadata(); + applicationMetadata.setClientId(cacheRecord.getIdToken().getClientId()); + applicationMetadata.setEnvironment(cacheRecord.getIdToken().getEnvironment()); + applicationMetadata.setFoci(cacheRecord.getRefreshToken().getFamilyId()); + applicationMetadata.setUid(testAppUids[ii++]); + + mApplicationMetadataCache.insert(applicationMetadata); + } + + final List clientIds = new ArrayList<>(); + + for (final MsalOAuth2TokenCacheTest.AccountCredentialTestBundle testBundle : mOtherCacheTestBundles) { + clientIds.add( + testBundle.mGeneratedRefreshToken.getClientId() + ); + } + + final List xAppAccounts = new ArrayList<>(); + + for (final int testUid : testAppUids) { + // Create the cache to query... + mBrokerOAuth2TokenCache = new BrokerOAuth2TokenCache( + mPlatformComponents, + testUid, + mApplicationMetadataCache, + new BrokerOAuth2TokenCache.ProcessUidCacheFactory() { + @Override + public MsalOAuth2TokenCache getTokenCache(IPlatformComponents context, int bindingProcessUid) { + return initAppUidCache(context, bindingProcessUid); + } + }, + mFociCache + ); + + for (final String clientId : clientIds) { + final List accountsInCache = mBrokerOAuth2TokenCache.getAccounts( + ENVIRONMENT, + clientId + ); + + xAppAccounts.addAll(accountsInCache); + } + } + + assertEquals( + clientIds.size(), + xAppAccounts.size() + ); + + final List xAppAccountsNoParam = new ArrayList<>( + mBrokerOAuth2TokenCache.getAccounts() + ); + + assertEquals(xAppAccounts.size(), xAppAccountsNoParam.size()); + } + + @Test + public void testGetAccountsMsal() throws ClientException { + // Load up the 'other caches' which a bunch of test credentials, see if we can get them out... + int ii = 0; + for (final OAuth2TokenCache cache : mOtherAppTokenCaches) { + configureMocks(mOtherCacheTestBundles.get(ii)); + + final ICacheRecord cacheRecord = cache.save( + mockStrategy, + mockRequest, + mockResponse + ); + + + final BrokerApplicationMetadata applicationMetadata = new BrokerApplicationMetadata(); + applicationMetadata.setClientId(cacheRecord.getIdToken().getClientId()); + applicationMetadata.setEnvironment(cacheRecord.getIdToken().getEnvironment()); + applicationMetadata.setFoci(cacheRecord.getRefreshToken().getFamilyId()); + applicationMetadata.setUid(testAppUids[ii++]); + + mApplicationMetadataCache.insert(applicationMetadata); + } + + final List clientIds = new ArrayList<>(); + + for (final MsalOAuth2TokenCacheTest.AccountCredentialTestBundle testBundle : mOtherCacheTestBundles) { + clientIds.add( + testBundle.mGeneratedRefreshToken.getClientId() + ); + } + + final List xAppAccounts = new ArrayList<>(); + + for (final int testUid : testAppUids) { + // Create the cache to query... + mBrokerOAuth2TokenCache = new BrokerOAuth2TokenCache( + mPlatformComponents, + testUid, + mApplicationMetadataCache, + new BrokerOAuth2TokenCache.ProcessUidCacheFactory() { + @Override + public MsalOAuth2TokenCache getTokenCache(IPlatformComponents context, int bindingProcessUid) { + return initAppUidCache(context, bindingProcessUid); + } + }, + mFociCache + ); + + for (final String clientId : clientIds) { + final List accountsInCache = mBrokerOAuth2TokenCache.getAccounts( + ENVIRONMENT, + clientId + ); + + xAppAccounts.addAll(accountsInCache); + } + } + + assertEquals( + clientIds.size(), + xAppAccounts.size() + ); + + final List xAppAccountsNoParam = new ArrayList<>( + mBrokerOAuth2TokenCache.getAccounts() + ); + + assertEquals(xAppAccounts.size(), xAppAccountsNoParam.size()); + + final BrokerOAuth2TokenCache brokerOAuth2TokenCache = new BrokerOAuth2TokenCache( + mPlatformComponents, + TEST_APP_UID, + new NameValueStorageBrokerApplicationMetadataCache(mPlatformComponents) + ); + + assertEquals( + 0, + brokerOAuth2TokenCache.getAccounts(ENVIRONMENT, CLIENT_ID).size() + ); + + final BrokerOAuth2TokenCache brokerOAuth2TokenCache2 = new BrokerOAuth2TokenCache( + mPlatformComponents, + TEST_APP_UID, + new NameValueStorageBrokerApplicationMetadataCache(mPlatformComponents) + ); + + assertEquals( + xAppAccounts.size(), + brokerOAuth2TokenCache2.getAccounts().size() + ); + } + + @Test + public void testWPJSaveNonFoci_deprecated() throws ClientException { + final ICacheRecord saveResult = mBrokerOAuth2TokenCache.save( + mDefaultAppUidTestBundle.mGeneratedAccount, + mDefaultAppUidTestBundle.mGeneratedIdToken, + mDefaultAppUidTestBundle.mGeneratedAccessToken, + null + ); + + assertNotNull(saveResult); + assertNotNull(saveResult.getAccount()); + assertNotNull(saveResult.getIdToken()); + assertNotNull(saveResult.getAccessToken()); + assertNull(saveResult.getRefreshToken()); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccount, + saveResult.getAccount() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedIdToken, + saveResult.getIdToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccessToken, + saveResult.getAccessToken() + ); + + final ICacheRecord retrievedResult = mBrokerOAuth2TokenCache.load( + mDefaultAppUidTestBundle.mGeneratedIdToken.getClientId(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getApplicationIdentifier(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getMamEnrollmentIdentifier(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getTarget(), + mDefaultAppUidTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertNotNull(retrievedResult); + assertNotNull(retrievedResult.getAccount()); + assertNotNull(retrievedResult.getIdToken()); + assertNotNull(retrievedResult.getAccessToken()); + assertNull(retrievedResult.getRefreshToken()); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccount, + retrievedResult.getAccount() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedIdToken, + retrievedResult.getIdToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccessToken, + retrievedResult.getAccessToken() + ); + } + + @Test + public void testWPJSaveNonFoci() throws ClientException { + final ICacheRecord saveResult = mBrokerOAuth2TokenCache.save( + mDefaultAppUidTestBundle.mGeneratedAccount, + mDefaultAppUidTestBundle.mGeneratedIdToken, + mDefaultAppUidTestBundle.mGeneratedAccessToken, + mDefaultAppUidTestBundle.mGeneratedRefreshToken, + null + ); + + assertNotNull(saveResult); + assertNotNull(saveResult.getAccount()); + assertNotNull(saveResult.getIdToken()); + assertNotNull(saveResult.getAccessToken()); + assertNotNull(saveResult.getRefreshToken()); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccount, + saveResult.getAccount() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedIdToken, + saveResult.getIdToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccessToken, + saveResult.getAccessToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedRefreshToken, + saveResult.getRefreshToken() + ); + + final ICacheRecord retrievedResult = mBrokerOAuth2TokenCache.load( + mDefaultAppUidTestBundle.mGeneratedIdToken.getClientId(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getApplicationIdentifier(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getMamEnrollmentIdentifier(), + mDefaultAppUidTestBundle.mGeneratedAccessToken.getTarget(), + mDefaultAppUidTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertNotNull(retrievedResult); + assertNotNull(retrievedResult.getAccount()); + assertNotNull(retrievedResult.getIdToken()); + assertNotNull(retrievedResult.getAccessToken()); + assertNotNull(retrievedResult.getRefreshToken()); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccount, + retrievedResult.getAccount() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedIdToken, + retrievedResult.getIdToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedAccessToken, + retrievedResult.getAccessToken() + ); + + assertEquals( + mDefaultAppUidTestBundle.mGeneratedRefreshToken, + saveResult.getRefreshToken() + ); + } + + @Test + public void testWPJSaveFoci_deprecated() throws ClientException { + final ICacheRecord saveResult = mBrokerOAuth2TokenCache.save( + mDefaultFociTestBundle.mGeneratedAccount, + mDefaultFociTestBundle.mGeneratedIdToken, + mDefaultFociTestBundle.mGeneratedAccessToken, + "1" + ); + + assertNotNull(saveResult); + assertNotNull(saveResult.getAccount()); + assertNotNull(saveResult.getIdToken()); + assertNotNull(saveResult.getAccessToken()); + assertNull(saveResult.getRefreshToken()); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccount, + saveResult.getAccount() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedIdToken, + saveResult.getIdToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccessToken, + saveResult.getAccessToken() + ); + + + final ICacheRecord retrievedResult = mBrokerOAuth2TokenCache.load( + mDefaultFociTestBundle.mGeneratedIdToken.getClientId(), + mDefaultFociTestBundle.mGeneratedAccessToken.getApplicationIdentifier(), + mDefaultFociTestBundle.mGeneratedAccessToken.getMamEnrollmentIdentifier(), + mDefaultFociTestBundle.mGeneratedAccessToken.getTarget(), + mDefaultFociTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertNotNull(retrievedResult); + assertNotNull(retrievedResult.getAccount()); + assertNotNull(retrievedResult.getIdToken()); + assertNotNull(retrievedResult.getAccessToken()); + assertNull(retrievedResult.getRefreshToken()); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccount, + retrievedResult.getAccount() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedIdToken, + retrievedResult.getIdToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccessToken, + retrievedResult.getAccessToken() + ); + } + + @Test + public void testWPJSaveFoci() throws ClientException { + final ICacheRecord saveResult = mBrokerOAuth2TokenCache.save( + mDefaultFociTestBundle.mGeneratedAccount, + mDefaultFociTestBundle.mGeneratedIdToken, + mDefaultFociTestBundle.mGeneratedAccessToken, + mDefaultFociTestBundle.mGeneratedRefreshToken, + "1" + ); + + assertNotNull(saveResult); + assertNotNull(saveResult.getAccount()); + assertNotNull(saveResult.getIdToken()); + assertNotNull(saveResult.getAccessToken()); + assertNotNull(saveResult.getRefreshToken()); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccount, + saveResult.getAccount() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedIdToken, + saveResult.getIdToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccessToken, + saveResult.getAccessToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedRefreshToken, + saveResult.getRefreshToken() + ); + + final ICacheRecord retrievedResult = mBrokerOAuth2TokenCache.load( + mDefaultFociTestBundle.mGeneratedIdToken.getClientId(), + mDefaultFociTestBundle.mGeneratedAccessToken.getApplicationIdentifier(), + mDefaultFociTestBundle.mGeneratedAccessToken.getAccessTokenType(), + mDefaultFociTestBundle.mGeneratedAccessToken.getTarget(), + mDefaultFociTestBundle.mGeneratedAccount, + BEARER_AUTHENTICATION_SCHEME + ); + + assertNotNull(retrievedResult); + assertNotNull(retrievedResult.getAccount()); + assertNotNull(retrievedResult.getIdToken()); + assertNotNull(retrievedResult.getAccessToken()); + assertNotNull(retrievedResult.getRefreshToken()); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccount, + retrievedResult.getAccount() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedIdToken, + retrievedResult.getIdToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedAccessToken, + retrievedResult.getAccessToken() + ); + + assertEquals( + mDefaultFociTestBundle.mGeneratedRefreshToken, + retrievedResult.getRefreshToken() + ); + } + + @Test + public void testClearAll() throws ClientException { + int appIndex = 0; + for (final OAuth2TokenCache cache : mOtherAppTokenCaches) { + configureMocks(mOtherCacheTestBundles.get(appIndex)); + + final ICacheRecord cacheRecord = cache.save(mockStrategy, mockRequest, mockResponse); + + final BrokerApplicationMetadata applicationMetadata = new BrokerApplicationMetadata(); + applicationMetadata.setClientId(cacheRecord.getIdToken().getClientId()); + applicationMetadata.setEnvironment(cacheRecord.getIdToken().getEnvironment()); + applicationMetadata.setFoci(cacheRecord.getRefreshToken().getFamilyId()); + applicationMetadata.setUid(testAppUids[appIndex++]); + + mApplicationMetadataCache.insert(applicationMetadata); + } + + final List clientIds = new ArrayList<>(); + + for (final MsalOAuth2TokenCacheTest.AccountCredentialTestBundle testBundle : mOtherCacheTestBundles) { + clientIds.add( + testBundle.mGeneratedRefreshToken.getClientId() + ); + } + + configureMocksForFoci(); + final ICacheRecord fociCacheRecord = mBrokerOAuth2TokenCache.save(mockStrategy, mockRequest, mockResponse); + final BrokerApplicationMetadata applicationMetadata = new BrokerApplicationMetadata(); + applicationMetadata.setClientId(fociCacheRecord.getIdToken().getClientId()); + applicationMetadata.setEnvironment(fociCacheRecord.getIdToken().getEnvironment()); + applicationMetadata.setFoci(fociCacheRecord.getRefreshToken().getFamilyId()); + applicationMetadata.setUid(0); + + mApplicationMetadataCache.insert(applicationMetadata); + clientIds.add(fociCacheRecord.getIdToken().getClientId()); + + // Verify the broker cache is populated + assertEquals(true, mBrokerOAuth2TokenCache.getAccounts().size() > 0); + assertEquals(true, mBrokerOAuth2TokenCache.getFociCacheRecords().size() > 0); + assertEquals(true, mApplicationMetadataCache.getAll().size() > 0); + + for( final String clientId :clientIds) { + assertEquals(true, mBrokerOAuth2TokenCache.isClientIdKnownToCache(clientId)); + } + + // Clear Broker Cache + mBrokerOAuth2TokenCache.clearAll(); + + // Verify Broker cache is cleared + assertEquals(0, mBrokerOAuth2TokenCache.getAccounts().size()); + assertEquals(0, mBrokerOAuth2TokenCache.getFociCacheRecords().size()); + assertEquals(0, mApplicationMetadataCache.getAll().size()); + for( final String clientId :clientIds) { + assertEquals(false, mBrokerOAuth2TokenCache.isClientIdKnownToCache(clientId)); + } + } +} diff --git a/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java b/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java index a66081b041..a4c869a39c 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java +++ b/common4j/src/main/com/microsoft/identity/common/java/cache/BrokerOAuth2TokenCache.java @@ -347,7 +347,7 @@ public synchronized List saveAndLoadAggregatedAccountData( } @SuppressWarnings("unchecked") - public List loadAggregatedAccountData(final @NonNull AbstractAuthenticationScheme authScheme, + private List loadAggregatedAccountData(final @NonNull AbstractAuthenticationScheme authScheme, final @NonNull ICacheRecord cacheRecord) { final String methodName = ":loadAggregatedAccountData";