private static List <IAccount> UpdateWithAdalAccounts( string envFromRequest, IEnumerable <string> envAliases, AdalUsersForMsal adalUsers, IDictionary <string, Account> clientInfoToAccountMap) { var accounts = new List <IAccount>(); foreach (KeyValuePair <string, AdalUserInfo> pair in adalUsers.GetUsersWithClientInfo(envAliases)) { var clientInfo = ClientInfo.CreateFromJson(pair.Key); string accountIdentifier = clientInfo.ToAccountIdentifier(); if (!clientInfoToAccountMap.ContainsKey(accountIdentifier)) { clientInfoToAccountMap[accountIdentifier] = new Account( accountIdentifier, pair.Value.DisplayableId, envFromRequest); } } accounts.AddRange(clientInfoToAccountMap.Values); var uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList(); foreach (AdalUserInfo user in adalUsers.GetUsersWithoutClientInfo(envAliases)) { if (!string.IsNullOrEmpty(user.DisplayableId) && !uniqueUserNames.Contains(user.DisplayableId)) { accounts.Add(new Account(null, user.DisplayableId, envFromRequest)); uniqueUserNames.Add(user.DisplayableId); } } return(accounts); }
private List <IAccount> UpdateWithAdalAccountsWithoutClientInfo( string envFromRequest, IEnumerable <string> envAliases, AdalUsersForMsal adalUsers, IDictionary <string, Account> clientInfoToAccountMap) { var accounts = new List <IAccount>(); accounts.AddRange(clientInfoToAccountMap.Values); if (ServiceBundle.Config.LegacyCacheCompatibilityEnabled) { var uniqueUserNames = clientInfoToAccountMap.Values.Select(o => o.Username).Distinct().ToList(); foreach (AdalUserInfo user in adalUsers.GetUsersWithoutClientInfo(envAliases)) { if (!string.IsNullOrEmpty(user.DisplayableId) && !uniqueUserNames.Contains(user.DisplayableId)) { accounts.Add(new Account(null, user.DisplayableId, envFromRequest)); uniqueUserNames.Add(user.DisplayableId); } } } return(accounts); }
/// <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); }
private static void AssertByUsername( AdalUsersForMsal adalUsers, IEnumerable <string> enviroments, IEnumerable <string> expectedUsersWithClientInfo, IEnumerable <string> expectedUsersWithoutClientInfo) { // Assert var usersWithClientInfo = adalUsers.GetUsersWithClientInfo(enviroments).Select(x => x.Value); IEnumerable <AdalUserInfo> usersWithoutClientInfo = adalUsers.GetUsersWithoutClientInfo(enviroments); AssertUsersByDisplayName( expectedUsersWithClientInfo, usersWithClientInfo, "Expecting only user1 and user2 because the other users either have no ClientInfo or have a different env or clientid"); AssertUsersByDisplayName(expectedUsersWithoutClientInfo, usersWithoutClientInfo); }
private static void UpdateMapWithAdalAccountsWithClientInfo( string envFromRequest, IEnumerable <string> envAliases, AdalUsersForMsal adalUsers, IDictionary <string, Account> clientInfoToAccountMap) { foreach (KeyValuePair <string, AdalUserInfo> pair in adalUsers.GetUsersWithClientInfo(envAliases)) { var clientInfo = ClientInfo.CreateFromJson(pair.Key); string accountIdentifier = clientInfo.ToAccountIdentifier(); if (!clientInfoToAccountMap.ContainsKey(accountIdentifier)) { clientInfoToAccountMap[accountIdentifier] = new Account( accountIdentifier, pair.Value.DisplayableId, envFromRequest); } } }
[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); } }
/// <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); }