public async Task ATS_AccountMatchingInWAM_MatchingHomeAccId_Async() { string homeAccId = $"{TestConstants.Uid}.{TestConstants.Utid}"; // Arrange using (var harness = CreateTestHarness()) { var wamAccountProvider = new WebAccountProvider("id", "*****@*****.**", null); var requestParams = harness.CreateAuthenticationRequestParameters(TestConstants.AuthorityConsumerTidTenant); // MSA var webAccount = new WebAccount(wamAccountProvider, "*****@*****.**", WebAccountState.Connected); IReadOnlyList <WebAccount> webAccounts = new List <WebAccount>() { webAccount }; var webTokenRequest = new WebTokenRequest(wamAccountProvider); var webTokenResponseWrapper = Substitute.For <IWebTokenRequestResultWrapper>(); webTokenResponseWrapper.ResponseStatus.Returns(WebTokenRequestStatus.Success); var webTokenResponse = new WebTokenResponse(); webTokenResponseWrapper.ResponseData.Returns(new List <WebTokenResponse>() { webTokenResponse }); _wamProxy.FindAllWebAccountsAsync(wamAccountProvider, TestConstants.ClientId).Returns(Task.FromResult(webAccounts)); // WAM can give MSAL the home account ID of a Wam account, which MSAL matches to a WAM account _msaPlugin.GetHomeAccountIdOrNull(webAccount).Returns(homeAccId); _msaPlugin.CreateWebTokenRequestAsync( wamAccountProvider, requestParams, isForceLoginPrompt: false, isAccountInWam: true, isInteractive: false) .Returns(Task.FromResult(webTokenRequest)); requestParams.Account = new Account( homeAccId, // matching in on home acc id "*****@*****.**", // matching is not on UPN null); // account does not have wam_id, might be coming directly from WAM var atsParams = new AcquireTokenSilentParameters(); _webAccountProviderFactory.GetAccountProviderAsync(null).ReturnsForAnyArgs(Task.FromResult(wamAccountProvider)); _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest). Returns(Task.FromResult(webTokenResponseWrapper)); _msaPlugin.ParseSuccesfullWamResponse(webTokenResponse).Returns(_msalTokenResponse); // Act var result = await _wamBroker.AcquireTokenSilentAsync(requestParams, atsParams).ConfigureAwait(false); // Assert Assert.AreSame(_msalTokenResponse, result); } }
/// <summary> /// The algorithm here is much more complex in order to workaround a limitation in the AAD plugin's /// handling of guest accounts: /// /// 1. Read the accounts from WAM.AADPlugin /// 2. For each account, we need to find its home_account_id as the one from WAM may not be correct /// 3. If we can find a cached account with the same LocalAccountId or UPN, use it /// 4. If not, make a simple silent token request and use the client info provided /// </summary> public async Task <IReadOnlyList <IAccount> > GetAccountsAsync( string clientId, AuthorityInfo authorityInfo, Cache.ICacheSessionManager cacheSessionManager, Instance.Discovery.IInstanceDiscoveryManager instanceDiscoveryManager) { var webAccountProvider = await _webAccountProviderFactory.GetAccountProviderAsync("organizations").ConfigureAwait(false); var wamAccounts = await _wamProxy.FindAllWebAccountsAsync(webAccountProvider, clientId).ConfigureAwait(false); if (wamAccounts.Count > 0) { var webAccountEnvs = wamAccounts .Select(w => { _wamProxy.TryGetAccountProperty(w, "Authority", out string accountAuthority); if (accountAuthority != null) { return((new Uri(accountAuthority)).Host); } else { _logger.WarningPii( $"[WAM AAD Provider] Could not convert the WAM account {w.UserName} (id: {w.Id}) to an MSAL account because the Authority could not be found", $"[WAM AAD Provider] Could not convert the WAM account {w.Id} to an MSAL account because the Authority could not be found"); return(null); } }) .Where(a => a != null); var instanceMetadata = await instanceDiscoveryManager.GetMetadataEntryTryAvoidNetworkAsync(authorityInfo, webAccountEnvs, cacheSessionManager.RequestContext) .ConfigureAwait(false); var accountsFromCache = await cacheSessionManager.GetAccountsAsync().ConfigureAwait(false); var msalAccountTasks = wamAccounts .Select( async webAcc => await ConvertToMsalAccountOrNullAsync( clientId, webAcc, instanceMetadata, cacheSessionManager, accountsFromCache).ConfigureAwait(false)); var msalAccounts = (await Task.WhenAll(msalAccountTasks).ConfigureAwait(false)).Where(a => a != null).ToList(); _logger.Info($"[WAM AAD Provider] GetAccountsAsync converted {msalAccounts.Count} accounts from {wamAccounts.Count} WAM accounts"); return(msalAccounts); } _logger.Info("[WAM AAD provider] No accounts found."); return(Array.Empty <IAccount>()); }
/// <summary> /// Generally the MSA plugin will NOT return the accounts back to the app. This is due /// to privacy concerns. However, some test apps are allowed to do this, hence the code. /// Normal 1st and 3rd party apps must use AcquireTokenInteractive to login first, and then MSAL will /// save the account for later use. /// </summary> public async Task <IEnumerable <IAccount> > GetAccountsAsync(string clientID) { var webAccounProvider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false); var webAccounts = await _wamProxy.FindAllWebAccountsAsync(webAccounProvider, clientID).ConfigureAwait(false); var msalAccounts = webAccounts .Select(webAcc => ConvertToMsalAccountOrNull(webAcc)) .Where(a => a != null) .ToList(); _logger.Info($"[WAM MSA Plugin] GetAccountsAsync converted {webAccounts.Count()} MSAL accounts"); return(msalAccounts); }
/// <summary> /// Generally the MSA plugin will NOT return the accounts back to the app. This is due /// to privacy concerns. However, some test apps are allowed to do this, hence the code. /// Normal 1st and 3rd party apps must use AcquireTokenInteractive to login first, and then MSAL will /// save the account for later use. /// </summary> public async Task <IReadOnlyList <IAccount> > GetAccountsAsync( string clientID, string authority, ICacheSessionManager cacheSessionManager, IInstanceDiscoveryManager instanceDiscoveryManager) { var webAccounProvider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false); var webAccounts = await _wamProxy.FindAllWebAccountsAsync(webAccounProvider, clientID).ConfigureAwait(false); var msalAccounts = webAccounts .Select(webAcc => ConvertToMsalAccountOrNull(webAcc)) .Where(a => a != null) .ToList(); _logger.Info($"[WAM MSA Plugin] GetAccountsAsync converted {webAccounts.Count} MSAL accounts"); return(msalAccounts); }
private async Task <WebAccount> FindWamAccountForMsalAccountAsync( WebAccountProvider provider, IWamPlugin wamPlugin, IAccount msalAccount, string loginHint, string clientId) { if (msalAccount == null && string.IsNullOrEmpty(loginHint)) { return(null); } Account accountInternal = (msalAccount as Account); if (accountInternal?.WamAccountIds != null && accountInternal.WamAccountIds.TryGetValue(clientId, out string wamAccountId)) { _logger.Info("WAM will try to find an account based on the WAM account id from the cache"); WebAccount result = await _wamProxy.FindAccountAsync(provider, wamAccountId).ConfigureAwait(false); if (result != null) { return(result); } _logger.Warning("WAM account was not found for given WAM account id."); } var wamAccounts = await _wamProxy.FindAllWebAccountsAsync(provider, clientId).ConfigureAwait(false); return(MatchWamAccountToMsalAccount( wamPlugin, msalAccount, loginHint, wamAccounts)); }
public async Task GetAccounts_WamAccounts_NoAuthority_Async() { // Arrange using (MockHttpAndServiceBundle harness = CreateTestHarness()) { var wamAccountProvider = new WebAccountProvider("id", "*****@*****.**", null); _webAccountProviderFactory.GetAccountProviderAsync("organizations").Returns(wamAccountProvider); var wamAccount = new WebAccount(wamAccountProvider, "*****@*****.**", WebAccountState.Connected); // no authority ... skip these accounts _wamProxy.FindAllWebAccountsAsync(wamAccountProvider, TestConstants.ClientId).Returns(new[] { wamAccount }); // Act var accounts = await _aadPlugin.GetAccountsAsync( TestConstants.ClientId, AuthorityInfo.FromAuthorityUri(TestConstants.AuthorityCommonTenant, true), _cacheSessionManager, _instanceDiscoveryManager).ConfigureAwait(false); // Assert Assert.AreEqual(0, accounts.Count()); } }