public void WriteAdalRefreshToken_ErrorLog() { // Arrange _legacyCachePersistence.ThrowOnWrite = true; var rtItem = new MsalRefreshTokenCacheItem( CoreTestConstants.ProductionPrefNetworkEnvironment, CoreTestConstants.ClientId, "someRT", MockHelpers.CreateClientInfo("u1", "ut1")); var idTokenCacheItem = new MsalIdTokenCacheItem( CoreTestConstants.ProductionPrefCacheEnvironment, // different env CoreTestConstants.ClientId, MockHelpers.CreateIdToken("u1", "username"), MockHelpers.CreateClientInfo("u1", "ut1"), "ut1"); // Act CacheFallbackOperations.WriteAdalRefreshToken( _legacyCachePersistence, rtItem, idTokenCacheItem, "https://some_env.com/common", // yet another env "uid", "scope1"); // Assert MsalLogger.Default.Received().Error(Arg.Is <string>(CacheFallbackOperations.DifferentAuthorityError)); MsalLogger.Default.Received().Error(Arg.Is <string>(CacheFallbackOperations.DifferentEnvError)); }
public void UnifiedCache_ProcessAdalDictionaryForDuplicateKeyTest() { using (var harness = CreateTestHarness()) { var app = PublicClientApplicationBuilder .Create(MsalTestConstants.ClientId) .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true) .WithUserTokenLegacyCachePersistenceForTest(new TestLegacyCachePersistance()) .WithTelemetry(new TraceTelemetryConfig()) .BuildConcrete(); CreateAdalCache(harness.ServiceBundle.DefaultLogger, app.UserTokenCacheInternal.LegacyPersistence, MsalTestConstants.Scope.ToString()); var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( harness.ServiceBundle.DefaultLogger, app.UserTokenCacheInternal.LegacyPersistence, MsalTestConstants.ClientId); CreateAdalCache(harness.ServiceBundle.DefaultLogger, app.UserTokenCacheInternal.LegacyPersistence, "user.read"); var adalUsers2 = CacheFallbackOperations.GetAllAdalUsersForMsal( harness.ServiceBundle.DefaultLogger, app.UserTokenCacheInternal.LegacyPersistence, MsalTestConstants.ClientId); Assert.AreEqual( adalUsers.GetUsersWithClientInfo(null).Single().Key, adalUsers2.GetUsersWithClientInfo(null).Single().Key); app.UserTokenCacheInternal.Accessor.ClearAccessTokens(); app.UserTokenCacheInternal.Accessor.ClearRefreshTokens(); } }
public void DoNotWriteFRTs() { // Arrange _legacyCachePersistence.ThrowOnWrite = true; string clientInfo = MockHelpers.CreateClientInfo("u1", "ut1"); string homeAccountId = ClientInfo.CreateFromJson(clientInfo).ToAccountIdentifier(); var rtItem = new MsalRefreshTokenCacheItem( TestConstants.ProductionPrefNetworkEnvironment, TestConstants.ClientId, "someRT", clientInfo, "familyId", homeAccountId); var idTokenCacheItem = new MsalIdTokenCacheItem( TestConstants.ProductionPrefNetworkEnvironment, // different env TestConstants.ClientId, MockHelpers.CreateIdToken("u1", "username"), clientInfo, tenantId: "ut1", homeAccountId: homeAccountId); // Act CacheFallbackOperations.WriteAdalRefreshToken( _logger, _legacyCachePersistence, rtItem, idTokenCacheItem, "https://some_env.com/common", // yet another env "uid", "scope1"); AssertCacheEntryCount(0); }
public void GetAllAdalEntriesForMsal_FilterBy_Upn() { // Arrange PopulateLegacyCache(_legacyCachePersistence); // Act - query Adal Entries For Msal with valid Upn as a filter var rt = CacheFallbackOperations.GetAdalEntryForMsal( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, TestConstants.ClientId, "User1", null); Assert.AreEqual("uid1.tenantId1", rt.HomeAccountId); // Act - query Adal Entries For Msal with invalid Upn as a filter rt = CacheFallbackOperations.GetAdalEntryForMsal( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, TestConstants.ClientId, "UserX", null); Assert.IsNull(rt, "Expected to find no items"); }
public void GetAllAdalEntriesForMsal_MultipleRTsPerEnv() { // Arrange PopulateLegacyWithRtAndId( _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, "uid", "tenantId", "user1"); PopulateLegacyWithRtAndId( _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, "uid", "tenantId", "user2"); // Act var rt = CacheFallbackOperations.GetAdalEntryForMsal( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment }, TestConstants.ClientId, null, "uid"); // Assert Assert.IsNull(rt, "Null should be returned."); }
public void GetAllAdalEntriesForMsal_MultipleRTsPerEnv() { // Arrange PopulateLegacyWithRtAndId( _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, "uid", "tenantId1", "user1"); PopulateLegacyWithRtAndId( _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, "uid", "tenantId2", "user2"); // Act var rt = CacheFallbackOperations.GetRefreshToken( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment }, TestConstants.ClientId, new Account("uid.b", null, null)); // Assert Assert.IsNotNull(rt, "One of the tokens should be returned"); }
public void GetAllAdalEntriesForMsal_FilterBy_UniqueId() { // Arrange LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence); // Act - query Adal Entries For Msal with valid UniqueId as a filter var rt = CacheFallbackOperations.GetRefreshToken( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, TestConstants.ClientId, new Account("uid2.tenant", null, null)); Assert.AreEqual("uid2.tenantId2", rt.HomeAccountId); // Act - query Adal Entries For Msal with invalid UniqueId as a filter rt = CacheFallbackOperations.GetRefreshToken( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, TestConstants.ClientId, new Account("udiX.tenant", null, null)); Assert.IsNull(rt, "Expected to find no items"); }
async Task <MsalRefreshTokenCacheItem> ITokenCacheInternal.FindRefreshTokenAsync( AuthenticationRequestParameters requestParams, string familyId) { if (requestParams.Authority == null) { return(null); } IEnumerable <MsalRefreshTokenCacheItem> allRts = _accessor.GetAllRefreshTokens(); var metadata = await ServiceBundle.InstanceDiscoveryManager.GetMetadataEntryTryAvoidNetworkAsync( requestParams.AuthorityInfo.CanonicalAuthority, allRts.Select(rt => rt.Environment), // if all environments are known, a network call can be avoided requestParams.RequestContext) .ConfigureAwait(false); var aliases = metadata.Aliases; IEnumerable <MsalRefreshTokenCacheKey> candidateRtKeys = aliases.Select( al => new MsalRefreshTokenCacheKey( al, requestParams.ClientId, requestParams.Account?.HomeAccountId?.Identifier, familyId)); MsalRefreshTokenCacheItem candidateRt = allRts.FirstOrDefault( rt => candidateRtKeys.Any( candidateKey => object.Equals(rt.GetKey(), candidateKey))); requestParams.RequestContext.Logger.Info("Refresh token found in the cache? - " + (candidateRt != null)); if (candidateRt != null) { return(candidateRt); } requestParams.RequestContext.Logger.Info("Checking ADAL cache for matching RT"); string upn = string.IsNullOrWhiteSpace(requestParams.LoginHint) ? requestParams.Account?.Username : requestParams.LoginHint; // ADAL legacy cache does not store FRTs if (requestParams.Account != null && string.IsNullOrEmpty(familyId)) { return(CacheFallbackOperations.GetAdalEntryForMsal( Logger, LegacyCachePersistence, metadata.PreferredCache, aliases, requestParams.ClientId, upn, requestParams.Account.HomeAccountId?.Identifier)); } return(null); }
public void GetAllAdalUsersForMsal_ScopedBy_ClientIdAndEnv() { // Arrange PopulateLegacyCache(_legacyCachePersistence); // Act - query users by env and clientId var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _logger, _legacyCachePersistence, TestConstants.ClientId); AssertByUsername( adalUsers, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, new[] { "user1", "user2", "sovereign_user5" }, new[] { "no_client_info_user3", "no_client_info_user4" }); AssertByUsername( adalUsers, new[] { TestConstants.SovereignNetworkEnvironment }, new[] { "sovereign_user5" }, Enumerable.Empty <string>()); // Act - query users for different clientId and env adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _logger, _legacyCachePersistence, "other_client_id"); // Assert AssertByUsername( adalUsers, null, new[] { "user6" }, Enumerable.Empty <string>()); }
private void RemoveAdalUser(IAccount account) { CacheFallbackOperations.RemoveAdalUser( Logger, LegacyCachePersistence, ClientId, account.Username, account.HomeAccountId?.Identifier); }
/// <remarks> /// Get accounts should not make a network call, if possible. This can be achieved if /// all the environments in the token cache are known to MSAL, as MSAL keeps a list of /// known environments in <see cref="KnownMetadataProvider"/> /// </remarks> async Task <IEnumerable <IAccount> > ITokenCacheInternal.GetAccountsAsync(string authority, RequestContext requestContext) { var environment = Authority.GetEnviroment(authority); bool filterByClientId = !_featureFlags.IsFociEnabled; IEnumerable <MsalRefreshTokenCacheItem> rtCacheItems = GetAllRefreshTokensWithNoLocks(filterByClientId); IEnumerable <MsalAccountCacheItem> accountCacheItems = _accessor.GetAllAccounts(); AdalUsersForMsal adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal( Logger, LegacyCachePersistence, ClientId); // Multi-cloud support - must filter by env. ISet <string> allEnvironmentsInCache = new HashSet <string>( accountCacheItems.Select(aci => aci.Environment), StringComparer.OrdinalIgnoreCase); allEnvironmentsInCache.UnionWith(rtCacheItems.Select(rt => rt.Environment)); allEnvironmentsInCache.UnionWith(adalUsersResult.GetAdalUserEnviroments()); var instanceMetadata = await ServiceBundle.InstanceDiscoveryManager.GetMetadataEntryTryAvoidNetworkAsync( authority, allEnvironmentsInCache, requestContext).ConfigureAwait(false); rtCacheItems = rtCacheItems.Where(rt => instanceMetadata.Aliases.ContainsOrdinalIgnoreCase(rt.Environment)); accountCacheItems = accountCacheItems.Where(acc => instanceMetadata.Aliases.ContainsOrdinalIgnoreCase(acc.Environment)); IDictionary <string, Account> clientInfoToAccountMap = new Dictionary <string, Account>(); foreach (MsalRefreshTokenCacheItem rtItem in rtCacheItems) { foreach (MsalAccountCacheItem account in accountCacheItems) { if (RtMatchesAccount(rtItem, account)) { clientInfoToAccountMap[rtItem.HomeAccountId] = new Account( account.HomeAccountId, account.PreferredUsername, environment); // Preserve the env passed in by the user break; } } } IEnumerable <IAccount> accounts = UpdateWithAdalAccounts( environment, instanceMetadata.Aliases, adalUsersResult, clientInfoToAccountMap); return(accounts); }
public void RemoveAdalUser_RemovesUserWithSameId() { // Arrange LegacyTokenCacheHelper.PopulateLegacyCache(_logger, _legacyCachePersistence); LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( // different clientId -> should not be deleted _logger, _legacyCachePersistence, "other_client_id", TestConstants.ProductionPrefNetworkEnvironment, "uid1", "tenantId1", "user1_other_client_id"); LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( // different env -> should be deleted _logger, _legacyCachePersistence, TestConstants.ClientId, "other_env", "uid1", "tenantId1", "user1_other_env"); // Act - delete with id and displayname CacheFallbackOperations.RemoveAdalUser( _logger, _legacyCachePersistence, TestConstants.ClientId, "username_does_not_matter", "uid1.tenantId1"); // Assert var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _logger, _legacyCachePersistence, TestConstants.ClientId); AssertByUsername( adalUsers, new[] { TestConstants.ProductionPrefNetworkEnvironment }, new[] { "user2", }, new[] { "no_client_info_user3", "no_client_info_user4" }); }
public void RemoveAdalUser_RemovesUserWithSameId() { // Arrange PopulateLegacyCache(_legacyCachePersistence); PopulateLegacyWithRtAndId( // different clientId -> should not be deleted _legacyCachePersistence, "other_client_id", CoreTestConstants.ProductionPrefNetworkEnvironment, "uid1", "tenantId1", "user1_other_client_id"); PopulateLegacyWithRtAndId( // different env -> should be deleted _legacyCachePersistence, CoreTestConstants.ClientId, "other_env", "uid1", "tenantId1", "user1_other_env"); // Act - delete with id and displayname CacheFallbackOperations.RemoveAdalUser( _legacyCachePersistence, CoreTestConstants.ClientId, "username_does_not_matter", "uid1.tenantId1"); // Assert Tuple <Dictionary <string, AdalUserInfo>, List <AdalUserInfo> > userTuple = CacheFallbackOperations.GetAllAdalUsersForMsal( _legacyCachePersistence, CoreTestConstants.ClientId); AssertByUsername( userTuple, new[] { "user2", "sovereign_user5" // this user has different environment but same client id }, new[] { "no_client_info_user3", "no_client_info_user4" }); }
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); }
public void GetAllAdalEntriesForMsal_NoFilter() { // Arrange PopulateLegacyCache(_legacyCachePersistence); // Act - query Adal Entries For Msal with valid Upn and UniqueId as a filter var rt = CacheFallbackOperations.GetRefreshToken( _logger, _legacyCachePersistence, new[] { TestConstants.ProductionPrefNetworkEnvironment, TestConstants.SovereignNetworkEnvironment }, TestConstants.ClientId, new Account(null, null, null)); // too litle info here, do not return RT Assert.IsNull(rt, "Expected to find no items"); }
[TestCategory(TestCategories.Regression)] // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/1815 public async Task UnifiedCache_ProcessAdalDictionaryForDuplicateKey_Async() { using (var harness = CreateTestHarness()) { var app = PublicClientApplicationBuilder .Create(TestConstants.ClientId) .WithAuthority(new Uri(ClientApplicationBase.DefaultAuthority), true) .WithUserTokenLegacyCachePersistenceForTest(new TestLegacyCachePersistance()) .WithHttpManager(harness.HttpManager) .BuildConcrete(); CreateAdalCache(harness.ServiceBundle.ApplicationLogger, app.UserTokenCacheInternal.LegacyPersistence, TestConstants.s_scope.ToString()); var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( harness.ServiceBundle.ApplicationLogger, app.UserTokenCacheInternal.LegacyPersistence, TestConstants.ClientId); CreateAdalCache(harness.ServiceBundle.ApplicationLogger, app.UserTokenCacheInternal.LegacyPersistence, "user.read"); AdalUsersForMsal adalUsers2 = CacheFallbackOperations.GetAllAdalUsersForMsal( harness.ServiceBundle.ApplicationLogger, app.UserTokenCacheInternal.LegacyPersistence, TestConstants.ClientId); Assert.AreEqual( adalUsers.GetUsersWithClientInfo(null).Single().Key, adalUsers2.GetUsersWithClientInfo(null).Single().Key); var accounts = await app.GetAccountsAsync().ConfigureAwait(false); Assert.AreEqual(1, accounts.Count(), "The 2 RTs belong to the same user"); // The ADAL cache contains access tokens, but these are NOT usable by MSAL / v2 endpoint. // MSAL will however use the RT from ADAL to fetch new access tokens... harness.HttpManager.AddAllMocks(TokenResponseType.Valid); var res = await app.AcquireTokenSilent(TestConstants.s_scope, accounts.First()).ExecuteAsync().ConfigureAwait(false); Assert.IsNotNull(res); } }
public void RemoveAdalUser_RemovesUserNoClientInfo_And_NoDisplayName() { // Arrange PopulateLegacyCache(_legacyCachePersistence); IDictionary <AdalTokenCacheKey, AdalResultWrapper> adalCacheBeforeDelete = AdalCacheOperations.Deserialize(_legacyCachePersistence.LoadCache()); Assert.AreEqual(6, adalCacheBeforeDelete.Count); // Act - nothing happens and a message is logged CacheFallbackOperations.RemoveAdalUser( _legacyCachePersistence, CoreTestConstants.ClientId, "", ""); // Assert AssertCacheEntryCount(6); MsalLogger.Default.Received().Error(Arg.Is <string>(CoreErrorMessages.InternalErrorCacheEmptyUsername)); }
public void GetAllAdalUsersForMsal_ScopedBy_ClientIdAndEnv() { // Arrange PopulateLegacyCache(_legacyCachePersistence); // Act - query users by env and clientId var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _legacyCachePersistence, CoreTestConstants.ClientId); AssertByUsername( adalUsers, new[] { "user1", "user2", "sovereign_user5" // this user has different environment but same client id }, new[] { "no_client_info_user3", "no_client_info_user4" }); // Act - query users for different clientId and env adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _legacyCachePersistence, "other_client_id"); // Assert AssertByUsername( adalUsers, new[] { "user6" }, Enumerable.Empty <string>()); }
public void RemoveAdalUser_RemovesAdalEntitiesWithClientInfoAndWithout() { // in case of adalv3 -> adalv4 -> msal2 migration // adal cache can have different cache entities for the // same user/account with client info and wihout // CacheFallbackOperations.RemoveAdalUser should remove both LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( _logger, _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, TestConstants.Uid, TestConstants.Utid, TestConstants.DisplayableId, TestConstants.ScopeStr); AssertCacheEntryCount(1); LegacyTokenCacheHelper.PopulateLegacyWithRtAndId( _logger, _legacyCachePersistence, TestConstants.ClientId, TestConstants.ProductionPrefNetworkEnvironment, null, null, TestConstants.DisplayableId, TestConstants.ScopeForAnotherResourceStr); AssertCacheEntryCount(2); CacheFallbackOperations.RemoveAdalUser( _logger, _legacyCachePersistence, TestConstants.ClientId, TestConstants.DisplayableId, TestConstants.Uid + "." + TestConstants.Utid); AssertCacheEntryCount(0); }
public void WriteAdalRefreshToken_ErrorLog() { // Arrange _legacyCachePersistence.ThrowOnWrite = true; string clientInfo = MockHelpers.CreateClientInfo("u1", "ut1"); string homeAccountId = ClientInfo.CreateFromJson(clientInfo).ToAccountIdentifier(); var rtItem = new MsalRefreshTokenCacheItem( TestConstants.ProductionPrefNetworkEnvironment, TestConstants.ClientId, "someRT", clientInfo, null, homeAccountId); var idTokenCacheItem = new MsalIdTokenCacheItem( TestConstants.ProductionPrefCacheEnvironment, // different env TestConstants.ClientId, MockHelpers.CreateIdToken("u1", "username"), clientInfo, tenantId: "ut1", homeAccountId: homeAccountId); // Act CacheFallbackOperations.WriteAdalRefreshToken( _logger, _legacyCachePersistence, rtItem, idTokenCacheItem, "https://some_env.com/common", // yet another env "uid", "scope1"); // Assert _logger.Received().Error(Arg.Is <string>(CacheFallbackOperations.DifferentAuthorityError)); _logger.Received().Error(Arg.Is <string>(CacheFallbackOperations.DifferentEnvError)); }
private void PopulateLegacyWithRtAndId( ILegacyCachePersistence legacyCachePersistence, string clientId, string env, string uid, string uniqueTenantId, string username, string scope) { string clientInfoString; if (string.IsNullOrEmpty(uid) || string.IsNullOrEmpty(uniqueTenantId)) { clientInfoString = null; } else { clientInfoString = MockHelpers.CreateClientInfo(uid, uniqueTenantId); } var rtItem = new MsalRefreshTokenCacheItem(env, clientId, "someRT", clientInfoString); var idTokenCacheItem = new MsalIdTokenCacheItem( env, clientId, MockHelpers.CreateIdToken(uid, username), clientInfoString, uniqueTenantId); CacheFallbackOperations.WriteAdalRefreshToken( _logger, legacyCachePersistence, rtItem, idTokenCacheItem, "https://" + env + "/common", "uid", scope); }
public void UnifiedCache_ProcessAdalDictionaryForDuplicateKeyTest() { using (var httpManager = new MockHttpManager()) { var serviceBundle = ServiceBundle.CreateWithCustomHttpManager(httpManager); var app = new PublicClientApplication( serviceBundle, MsalTestConstants.ClientId, ClientApplicationBase.DefaultAuthority) { UserTokenCache = { LegacyCachePersistence = new TestLegacyCachePersistance() } }; CreateAdalCache(app.UserTokenCache.LegacyCachePersistence, MsalTestConstants.Scope.ToString()); var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( app.UserTokenCache.LegacyCachePersistence, MsalTestConstants.ClientId); CreateAdalCache(app.UserTokenCache.LegacyCachePersistence, "user.read"); var adalUsers2 = CacheFallbackOperations.GetAllAdalUsersForMsal( app.UserTokenCache.LegacyCachePersistence, MsalTestConstants.ClientId); Assert.AreEqual(adalUsers.ClientInfoUsers.Keys.First(), adalUsers2.ClientInfoUsers.Keys.First()); app.UserTokenCache.TokenCacheAccessor.ClearAccessTokens(); app.UserTokenCache.TokenCacheAccessor.ClearRefreshTokens(); } }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { var tenantId = Authority .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority) .GetTenantId(); bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; IdToken idToken = IdToken.Parse(response.IdToken); string subject = idToken?.Subject; if (idToken != null && string.IsNullOrEmpty(subject)) { requestParams.RequestContext.Logger.Warning("Subject not present in Id token"); } string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken); // Do a full instance discovery when saving tokens (if not cached), // so that the PreferredNetwork environment is up to date. var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager .GetMetadataEntryAsync( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext) .ConfigureAwait(false); var msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, subject, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, subject) { IsAdfs = isAdfsAuthority }; } await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { try { Account account = null; string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; if (msalAccessTokenCacheItem.HomeAccountId != null) { account = new Account( msalAccessTokenCacheItem.HomeAccountId, username, instanceDiscoveryMetadata.PreferredCache); } var args = new TokenCacheNotificationArgs(this, ClientId, account, true, (this as ITokenCacheInternal).IsApplicationCache); #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false); try { await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false); DeleteAccessTokensWithIntersectingScopes( requestParams, instanceDiscoveryMetadata.Aliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId, msalAccessTokenCacheItem.TokenType); _accessor.SaveAccessToken(msalAccessTokenCacheItem); if (idToken != null) { _accessor.SaveIdToken(msalIdTokenCacheItem); var msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response, preferredUsername, tenantId); //The ADFS direct scenario does not return client info so the home account id is acquired from the subject if (isAdfsAuthority && String.IsNullOrEmpty(msalAccountCacheItem.HomeAccountId)) { msalAccountCacheItem.HomeAccountId = idToken.Subject; } _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (response.RefreshToken != null) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, subject); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId); // save RT in ADAL cache for public clients // do not save RT in ADAL cache for MSAL B2C scenarios if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C)) { var authorityWithPrefferedCache = Authority.CreateAuthorityWithEnvironment( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPrefferedCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { await(this as ITokenCacheInternal).OnAfterAccessAsync(args).ConfigureAwait(false); } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem)); } finally { #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } } finally { _semaphoreSlim.Release(); } }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { var tenantId = Authority .CreateAuthority(ServiceBundle, requestParams.TenantUpdatedCanonicalAuthority) .GetTenantId(); bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; IdToken idToken = IdToken.Parse(response.IdToken); string subject = idToken?.Subject; if (idToken != null && string.IsNullOrEmpty(subject)) { requestParams.RequestContext.Logger.Warning("Subject not present in Id token"); } string preferredUsername; // The preferred_username value cannot be null or empty in order to comply with the ADAL/MSAL Unified cache schema. // It will be set to "preferred_username not in idtoken" if (idToken == null) { preferredUsername = NullPreferredUsernameDisplayLabel; } else if (string.IsNullOrWhiteSpace(idToken.PreferredUsername)) { if (isAdfsAuthority) { //The direct to adfs scenario does not return preferred_username in the id token so it needs to be set to the upn preferredUsername = !string.IsNullOrEmpty(idToken.Upn) ? idToken.Upn : NullPreferredUsernameDisplayLabel; } else { preferredUsername = NullPreferredUsernameDisplayLabel; } } else { preferredUsername = idToken.PreferredUsername; } var instanceDiscoveryMetadataEntry = GetCachedAuthorityMetaData(requestParams.TenantUpdatedCanonicalAuthority); var environmentAliases = GetEnvironmentAliases( requestParams.TenantUpdatedCanonicalAuthority, instanceDiscoveryMetadataEntry); var preferredEnvironmentHost = GetPreferredEnvironmentHost( requestParams.AuthorityInfo.Host, instanceDiscoveryMetadataEntry); var msalAccessTokenCacheItem = new MsalAccessTokenCacheItem(preferredEnvironmentHost, requestParams.ClientId, response, tenantId, subject) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( preferredEnvironmentHost, requestParams.ClientId, response, tenantId, subject) { IsAdfs = isAdfsAuthority }; } await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { try { Account account = null; string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; if (msalAccessTokenCacheItem.HomeAccountId != null) { account = new Account( msalAccessTokenCacheItem.HomeAccountId, username, preferredEnvironmentHost); } var args = new TokenCacheNotificationArgs(this, ClientId, account, true); #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete await OnBeforeAccessAsync(args).ConfigureAwait(false); try { await OnBeforeWriteAsync(args).ConfigureAwait(false); DeleteAccessTokensWithIntersectingScopes( requestParams, environmentAliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId); _accessor.SaveAccessToken(msalAccessTokenCacheItem); if (idToken != null) { _accessor.SaveIdToken(msalIdTokenCacheItem); var msalAccountCacheItem = new MsalAccountCacheItem( preferredEnvironmentHost, response, preferredUsername, tenantId); //The ADFS direct scenrio does not return client info so the home account id is acquired from the subject if (isAdfsAuthority && String.IsNullOrEmpty(msalAccountCacheItem.HomeAccountId)) { msalAccountCacheItem.HomeAccountId = idToken.Subject; } _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (response.RefreshToken != null) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( preferredEnvironmentHost, requestParams.ClientId, response, subject); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.ClientId, preferredEnvironmentHost, response.FamilyId); // save RT in ADAL cache for public clients // do not save RT in ADAL cache for MSAL B2C scenarios if (!requestParams.IsClientCredentialRequest && !requestParams.AuthorityInfo.AuthorityType.Equals(AuthorityType.B2C)) { CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, Authority.CreateAuthorityUriWithHost( requestParams.TenantUpdatedCanonicalAuthority, preferredEnvironmentHost), msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem)); } finally { #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } } finally { _semaphoreSlim.Release(); } }
public void RemoveAdalUser_RemovesUserNoClientInfo() { // Arrange PopulateLegacyCache(_legacyCachePersistence); PopulateLegacyWithRtAndId( _legacyCachePersistence, "other_client_id", CoreTestConstants.ProductionPrefNetworkEnvironment, null, null, "no_client_info_user3"); // no client info, different client id -> won't be deleted PopulateLegacyWithRtAndId( _legacyCachePersistence, CoreTestConstants.ClientId, "other_env", null, null, "no_client_info_user3"); // no client info, different env -> won't be deleted AssertCacheEntryCount(8); var adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _legacyCachePersistence, CoreTestConstants.ClientId); AssertByUsername( adalUsers, new[] { "user2", "user1", "sovereign_user5" // this user has different environment but same client id }, new[] { "no_client_info_user3", "no_client_info_user3", "no_client_info_user4" }); // Act - delete with no client info -> displayable id is used CacheFallbackOperations.RemoveAdalUser( _legacyCachePersistence, CoreTestConstants.ClientId, "no_client_info_user3", ""); AssertCacheEntryCount(6); // Assert adalUsers = CacheFallbackOperations.GetAllAdalUsersForMsal( _legacyCachePersistence, CoreTestConstants.ClientId); AssertByUsername( adalUsers, new[] { "user2", "user1", "sovereign_user5" // this user has different environment but same client id }, new[] { "no_client_info_user4" }); }
/// <remarks> /// Get accounts should not make a network call, if possible. This can be achieved if /// all the environments in the token cache are known to MSAL, as MSAL keeps a list of /// known environments in <see cref="KnownMetadataProvider"/> /// </remarks> async Task <IEnumerable <IAccount> > ITokenCacheInternal.GetAccountsAsync(AuthenticationRequestParameters requestParameters) { var logger = requestParameters.RequestContext.Logger; var environment = requestParameters.AuthorityInfo.Host; bool filterByClientId = !_featureFlags.IsFociEnabled; IEnumerable <MsalRefreshTokenCacheItem> rtCacheItems = GetAllRefreshTokensWithNoLocks(filterByClientId); IEnumerable <MsalAccountCacheItem> accountCacheItems = _accessor.GetAllAccounts(); if (logger.IsLoggingEnabled(LogLevel.Verbose)) { logger.Verbose($"GetAccounts found {rtCacheItems.Count()} RTs and {accountCacheItems.Count()} accounts in MSAL cache. "); } // Multi-cloud support - must filter by env. ISet <string> allEnvironmentsInCache = new HashSet <string>( accountCacheItems.Select(aci => aci.Environment), StringComparer.OrdinalIgnoreCase); allEnvironmentsInCache.UnionWith(rtCacheItems.Select(rt => rt.Environment)); 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, allEnvironmentsInCache, requestParameters.RequestContext).ConfigureAwait(false); rtCacheItems = rtCacheItems.Where(rt => instanceMetadata.Aliases.ContainsOrdinalIgnoreCase(rt.Environment)); accountCacheItems = accountCacheItems.Where(acc => instanceMetadata.Aliases.ContainsOrdinalIgnoreCase(acc.Environment)); if (logger.IsLoggingEnabled(LogLevel.Verbose)) { logger.Verbose($"GetAccounts found {rtCacheItems.Count()} RTs and {accountCacheItems.Count()} accounts in MSAL cache after environment filtering. "); } IDictionary <string, Account> clientInfoToAccountMap = new Dictionary <string, Account>(); foreach (MsalRefreshTokenCacheItem rtItem in rtCacheItems) { foreach (MsalAccountCacheItem account in accountCacheItems) { if (RtMatchesAccount(rtItem, account)) { clientInfoToAccountMap[rtItem.HomeAccountId] = new Account( account.HomeAccountId, account.PreferredUsername, environment, // Preserve the env passed in by the user account.WamAccountIds); break; } } } 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.AppConfig.IsBrokerEnabled && ServiceBundle.PlatformProxy.BrokerSupportsWamAccounts) { foreach (MsalAccountCacheItem wamAccountCache in accountCacheItems.Where( acc => acc.WamAccountIds != null && acc.WamAccountIds.ContainsKey(requestParameters.AppConfig.ClientId))) { var wamAccount = new Account( wamAccountCache.HomeAccountId, wamAccountCache.PreferredUsername, environment, wamAccountCache.WamAccountIds); clientInfoToAccountMap[wamAccountCache.HomeAccountId] = wamAccount; } } IEnumerable <IAccount> accounts = UpdateWithAdalAccountsWithoutClientInfo(environment, instanceMetadata.Aliases, adalUsersResult, clientInfoToAccountMap); if (!string.IsNullOrEmpty(requestParameters.HomeAccountId)) { accounts = accounts.Where(acc => acc.HomeAccountId.Identifier.Equals( requestParameters.HomeAccountId, StringComparison.OrdinalIgnoreCase)); if (logger.IsLoggingEnabled(LogLevel.Verbose)) { logger.Verbose($"Filtered by home account id. Remaining accounts {accounts.Count()} "); } } return(accounts); }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem, Account> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { response.Log(requestParams.RequestContext.Logger, LogLevel.Verbose); MsalAccessTokenCacheItem msalAccessTokenCacheItem = null; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; MsalAccountCacheItem msalAccountCacheItem = null; IdToken idToken = IdToken.Parse(response.IdToken); if (idToken == null) { requestParams.RequestContext.Logger.Info("ID Token not present in response. "); } var tenantId = GetTenantId(idToken, requestParams); bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken); string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; string homeAccountId = GetHomeAccountId(requestParams, response, idToken); string suggestedWebCacheKey = SuggestedWebCacheKeyFactory.GetKeyFromResponse(requestParams, homeAccountId); // Do a full instance discovery when saving tokens (if not cached), // so that the PreferredNetwork environment is up to date. var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager .GetMetadataEntryAsync( requestParams.Authority.AuthorityInfo, requestParams.RequestContext) .ConfigureAwait(false); #region Create Cache Objects if (!string.IsNullOrEmpty(response.AccessToken)) { msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, tenantId, homeAccountId, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; } if (!string.IsNullOrEmpty(response.RefreshToken)) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, homeAccountId); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } } Dictionary <string, string> wamAccountIds = GetWamAccountIds(requestParams, response); Account account; if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.AppConfig.ClientId, response, tenantId, homeAccountId) { IsAdfs = isAdfsAuthority }; msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response.ClientInfo, homeAccountId, idToken, preferredUsername, tenantId, wamAccountIds); } #endregion account = new Account( homeAccountId, username, instanceDiscoveryMetadata.PreferredNetwork, wamAccountIds); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entering token cache semaphore. "); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Entered token cache semaphore. "); try { #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete try { ITokenCacheInternal tokenCacheInternal = this; if (tokenCacheInternal.IsTokenCacheSerialized()) { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, tokenCacheInternal.IsApplicationCache, hasTokens: tokenCacheInternal.HasTokensNoLocks(), requestParams.RequestContext.UserCancellationToken, suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = Stopwatch.StartNew(); await tokenCacheInternal.OnBeforeAccessAsync(args).ConfigureAwait(false); await tokenCacheInternal.OnBeforeWriteAsync(args).ConfigureAwait(false); requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds; } if (msalAccessTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving AT in cache and removing overlapping ATs..."); DeleteAccessTokensWithIntersectingScopes( requestParams, instanceDiscoveryMetadata.Aliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId, msalAccessTokenCacheItem.TokenType); _accessor.SaveAccessToken(msalAccessTokenCacheItem); } if (idToken != null) { requestParams.RequestContext.Logger.Info("Saving Id Token and Account in cache ..."); _accessor.SaveIdToken(msalIdTokenCacheItem); MergeWamAccountIds(msalAccountCacheItem); _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (msalRefreshTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.AppConfig.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId); // 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 tenatedAuthority = Authority.CreateAuthorityWithTenant(requestParams.AuthorityInfo, tenantId); var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment( tenatedAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { ITokenCacheInternal tokenCacheInternal = this; if (tokenCacheInternal.IsTokenCacheSerialized()) { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, tokenCacheInternal.IsApplicationCache, tokenCacheInternal.HasTokensNoLocks(), requestParams.RequestContext.UserCancellationToken, suggestedCacheKey: suggestedWebCacheKey); Stopwatch sw = Stopwatch.StartNew(); await tokenCacheInternal.OnAfterAccessAsync(args).ConfigureAwait(false); requestParams.RequestContext.ApiEvent.DurationInCacheInMs += sw.ElapsedMilliseconds; } #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem, account)); } finally { _semaphoreSlim.Release(); requestParams.RequestContext.Logger.Verbose("[SaveTokenResponseAsync] Released token cache semaphore. "); } }
async Task <Tuple <MsalAccessTokenCacheItem, MsalIdTokenCacheItem> > ITokenCacheInternal.SaveTokenResponseAsync( AuthenticationRequestParameters requestParams, MsalTokenResponse response) { MsalAccessTokenCacheItem msalAccessTokenCacheItem = null; MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = null; MsalIdTokenCacheItem msalIdTokenCacheItem = null; MsalAccountCacheItem msalAccountCacheItem = null; IdToken idToken = IdToken.Parse(response.IdToken); var tenantId = Authority .CreateAuthority(requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority) .TenantId; bool isAdfsAuthority = requestParams.AuthorityInfo.AuthorityType == AuthorityType.Adfs; string preferredUsername = GetPreferredUsernameFromIdToken(isAdfsAuthority, idToken); string username = isAdfsAuthority ? idToken?.Upn : preferredUsername; string homeAccountId = GetHomeAccountId(requestParams, response, idToken); // Do a full instance discovery when saving tokens (if not cached), // so that the PreferredNetwork environment is up to date. var instanceDiscoveryMetadata = await ServiceBundle.InstanceDiscoveryManager .GetMetadataEntryAsync( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext) .ConfigureAwait(false); #region Create Cache Objects if (!string.IsNullOrEmpty(response.AccessToken)) { msalAccessTokenCacheItem = new MsalAccessTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, homeAccountId, requestParams.AuthenticationScheme.KeyId) { UserAssertionHash = requestParams.UserAssertion?.AssertionHash, IsAdfs = isAdfsAuthority }; } if (!string.IsNullOrEmpty(response.RefreshToken)) { msalRefreshTokenCacheItem = new MsalRefreshTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, homeAccountId); if (!_featureFlags.IsFociEnabled) { msalRefreshTokenCacheItem.FamilyId = null; } } if (idToken != null) { msalIdTokenCacheItem = new MsalIdTokenCacheItem( instanceDiscoveryMetadata.PreferredCache, requestParams.ClientId, response, tenantId, homeAccountId) { IsAdfs = isAdfsAuthority }; msalAccountCacheItem = new MsalAccountCacheItem( instanceDiscoveryMetadata.PreferredCache, response.ClientInfo, homeAccountId, idToken, preferredUsername, tenantId); } #endregion Account account = new Account( homeAccountId, username, instanceDiscoveryMetadata.PreferredCache); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { var args = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, (this as ITokenCacheInternal).IsApplicationCache, hasTokens: (this as ITokenCacheInternal).HasTokensNoLocks(), requestParams.SuggestedWebAppCacheKey); #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = true; #pragma warning restore CS0618 // Type or member is obsolete try { await(this as ITokenCacheInternal).OnBeforeAccessAsync(args).ConfigureAwait(false); await(this as ITokenCacheInternal).OnBeforeWriteAsync(args).ConfigureAwait(false); if (msalAccessTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving AT in cache and removing overlapping ATs..."); DeleteAccessTokensWithIntersectingScopes( requestParams, instanceDiscoveryMetadata.Aliases, tenantId, msalAccessTokenCacheItem.ScopeSet, msalAccessTokenCacheItem.HomeAccountId, msalAccessTokenCacheItem.TokenType); _accessor.SaveAccessToken(msalAccessTokenCacheItem); } if (idToken != null) { requestParams.RequestContext.Logger.Info("Saving Id Token and Account in cache ..."); _accessor.SaveIdToken(msalIdTokenCacheItem); _accessor.SaveAccount(msalAccountCacheItem); } // if server returns the refresh token back, save it in the cache. if (msalRefreshTokenCacheItem != null) { requestParams.RequestContext.Logger.Info("Saving RT in cache..."); _accessor.SaveRefreshToken(msalRefreshTokenCacheItem); } UpdateAppMetadata(requestParams.ClientId, instanceDiscoveryMetadata.PreferredCache, response.FamilyId); // Do not save RT in ADAL cache for confidential client or B2C if (!requestParams.IsClientCredentialRequest && requestParams.AuthorityInfo.AuthorityType != AuthorityType.B2C) { var authorityWithPreferredCache = Authority.CreateAuthorityWithEnvironment( requestParams.TenantUpdatedCanonicalAuthority.AuthorityInfo, instanceDiscoveryMetadata.PreferredCache); CacheFallbackOperations.WriteAdalRefreshToken( Logger, LegacyCachePersistence, msalRefreshTokenCacheItem, msalIdTokenCacheItem, authorityWithPreferredCache.AuthorityInfo.CanonicalAuthority, msalIdTokenCacheItem.IdToken.ObjectId, response.Scope); } } finally { var args2 = new TokenCacheNotificationArgs( this, ClientId, account, hasStateChanged: true, (this as ITokenCacheInternal).IsApplicationCache, (this as ITokenCacheInternal).HasTokensNoLocks(), requestParams.SuggestedWebAppCacheKey); await(this as ITokenCacheInternal).OnAfterAccessAsync(args2).ConfigureAwait(false); #pragma warning disable CS0618 // Type or member is obsolete HasStateChanged = false; #pragma warning restore CS0618 // Type or member is obsolete } return(Tuple.Create(msalAccessTokenCacheItem, msalIdTokenCacheItem)); } finally { _semaphoreSlim.Release(); } }
/// <remarks> /// Get accounts should not make a network call, if possible. /// </remarks> async Task <IEnumerable <IAccount> > ITokenCacheInternal.GetAccountsAsync(string authority, RequestContext requestContext) { var environment = Authority.GetEnviroment(authority); // FetchAllAccountItemsFromCacheAsync... IEnumerable <MsalRefreshTokenCacheItem> rtCacheItems; IEnumerable <MsalAccountCacheItem> accountCacheItems; AdalUsersForMsal adalUsersResult; bool filterByClientId = !_featureFlags.IsFociEnabled; await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { var args = new TokenCacheNotificationArgs(this, ClientId, null, false); await OnBeforeAccessAsync(args).ConfigureAwait(false); try { rtCacheItems = GetAllRefreshTokensWithNoLocks(filterByClientId); accountCacheItems = _accessor.GetAllAccounts(); adalUsersResult = CacheFallbackOperations.GetAllAdalUsersForMsal( Logger, LegacyCachePersistence, ClientId); } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } } finally { _semaphoreSlim.Release(); } // Multi-cloud support - must filter by env. // Use all env aliases to filter, in case PreferredCacheEnv changes in the future ISet <string> existingEnvs = new HashSet <string>( accountCacheItems.Select(aci => aci.Environment), StringComparer.OrdinalIgnoreCase); var aliases = await GetEnvAliasesTryAvoidNetworkCallAsync( authority, adalUsersResult.GetAdalUserEnviroments(), existingEnvs, requestContext) .ConfigureAwait(false); rtCacheItems = rtCacheItems.Where(rt => aliases.ContainsOrdinalIgnoreCase(rt.Environment)); accountCacheItems = accountCacheItems.Where(acc => aliases.ContainsOrdinalIgnoreCase(acc.Environment)); IDictionary <string, Account> clientInfoToAccountMap = new Dictionary <string, Account>(); foreach (MsalRefreshTokenCacheItem rtItem in rtCacheItems) { foreach (MsalAccountCacheItem account in accountCacheItems) { if (RtMatchesAccount(rtItem, account)) { clientInfoToAccountMap[rtItem.HomeAccountId] = new Account( account.HomeAccountId, account.PreferredUsername, environment); // Preserve the env passed in by the user break; } } } List <IAccount> accounts = UpdateWithAdalAccounts( environment, aliases, adalUsersResult, clientInfoToAccountMap); return(accounts); }
async Task <MsalRefreshTokenCacheItem> ITokenCacheInternal.FindRefreshTokenAsync( AuthenticationRequestParameters requestParams, string familyId) { var cacheEvent = new CacheEvent( CacheEvent.TokenCacheLookup, requestParams.RequestContext.TelemetryCorrelationId) { TokenType = CacheEvent.TokenTypes.RT }; using (ServiceBundle.TelemetryManager.CreateTelemetryHelper(cacheEvent)) { if (requestParams.Authority == null) { return(null); } var instanceDiscoveryMetadataEntry = await GetCachedOrDiscoverAuthorityMetaDataAsync( requestParams.AuthorityInfo.CanonicalAuthority, requestParams.RequestContext).ConfigureAwait(false); var environmentAliases = GetEnvironmentAliases( requestParams.AuthorityInfo.CanonicalAuthority, instanceDiscoveryMetadataEntry); var preferredEnvironmentHost = GetPreferredEnvironmentHost( requestParams.AuthorityInfo.Host, instanceDiscoveryMetadataEntry); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { requestParams.RequestContext.Logger.Info("Looking up refresh token in the cache.."); TokenCacheNotificationArgs args = new TokenCacheNotificationArgs(this, ClientId, requestParams.Account, false); // make sure to check preferredEnvironmentHost first var allEnvAliases = new List <string>() { preferredEnvironmentHost }; allEnvAliases.AddRange(environmentAliases); var keysAcrossEnvs = allEnvAliases.Select(ea => new MsalRefreshTokenCacheKey( ea, requestParams.ClientId, requestParams.Account?.HomeAccountId?.Identifier, familyId)); await OnBeforeAccessAsync(args).ConfigureAwait(false); try { // Try to load from all env aliases, but stop at the first valid one MsalRefreshTokenCacheItem msalRefreshTokenCacheItem = keysAcrossEnvs .Select(key => _accessor.GetRefreshToken(key)) .FirstOrDefault(item => item != null); requestParams.RequestContext.Logger.Info("Refresh token found in the cache? - " + (msalRefreshTokenCacheItem != null)); if (msalRefreshTokenCacheItem != null) { return(msalRefreshTokenCacheItem); } requestParams.RequestContext.Logger.Info("Checking ADAL cache for matching RT"); string upn = string.IsNullOrWhiteSpace(requestParams.LoginHint) ? requestParams.Account?.Username : requestParams.LoginHint; // ADAL legacy cache does not store FRTs if (requestParams.Account != null && string.IsNullOrEmpty(familyId)) { return(CacheFallbackOperations.GetAdalEntryForMsal( Logger, LegacyCachePersistence, preferredEnvironmentHost, environmentAliases, requestParams.ClientId, upn, requestParams.Account.HomeAccountId?.Identifier)); } return(null); } finally { await OnAfterAccessAsync(args).ConfigureAwait(false); } } finally { _semaphoreSlim.Release(); } } }