private async Task <string> FetchTransferTokenAsync( WebAccountProvider accountProvider, WebAccount wamAcc, string clientId) { var transferTokenRequest = await _msaPlugin.CreateWebTokenRequestAsync( accountProvider, clientId, TransferTokenScopes) .ConfigureAwait(true); var transferResponse = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, transferTokenRequest, wamAcc).ConfigureAwait(false); if (!transferResponse.ResponseStatus.IsSuccessStatus()) { var errorResp = WamAdapters.CreateMsalResponseFromWamResponse(transferResponse, _msaPlugin, _logger, true); throw new MsalServiceException( errorResp.Error, "Error fetching the MSA-PT transfer token - " + errorResp.ErrorDescription); } var resp = _msaPlugin.ParseSuccessfullWamResponse(transferResponse.ResponseData[0], out var properties); properties.TryGetValue("code", out string code); _logger.Info("WAM MSA-PT: Transfer token obtained? " + !string.IsNullOrEmpty(code)); return(code); }
private async Task <WebAccount> TryFetchWebAccountFromMsaAsync( AuthenticationRequestParameters authenticationRequestParameters, WebAccountProvider accountProvider) { // This response has an v1 MSA Access Token, which MSAL should expose to the user var webTokenRequestMsa = await _msaPlugin.CreateWebTokenRequestAsync( accountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : true, isAccountInWam : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequestMsa); var webTokenResponseMsa = await _wamProxy.RequestTokenForWindowAsync(_parentHandle, webTokenRequestMsa) .ConfigureAwait(true); if (!webTokenResponseMsa.ResponseStatus.IsSuccessStatus()) { var errorResp = WamAdapters.CreateMsalResponseFromWamResponse(webTokenResponseMsa, _msaPlugin, _logger, true); _logger.Warning( "WAM MSA-PT: could not get a transfer token, ussually this is because the " + "1st party app is configured for MSA-PT but not configured to login MSA users (signinaudience =2). " + "Error was: " + errorResp.Error + " " + errorResp.ErrorDescription); return(null); } // Cannot use this WebAccount with the AAD provider WebAccount msaPtWebAccount = webTokenResponseMsa.ResponseData[0].WebAccount; return(msaPtWebAccount); }
public async Task <MsalTokenResponse> AcquireTokenSilentDefaultUserAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenSilentParameters acquireTokenSilentParameters) { using (_logger.LogMethodDuration()) { bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, null, _wamOptions.MsaPassthrough).ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider = await GetProviderAsync( authenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority, isMsa).ConfigureAwait(false); WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : false, isAccountInWam : false, isInteractive : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest); var wamResult = await _wamProxy.GetTokenSilentlyForDefaultAccountAsync(webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse(wamResult, wamPlugin, _logger, isInteractive: false)); } }
// only works for AAD plugin. MSA plugin does not allow for privacy reasons private async Task <MsalTokenResponse> AcquireInteractiveWithAadBrowserAsync( AuthenticationRequestParameters authenticationRequestParameters, Prompt msalPrompt) { var provider = await _webAccountProviderFactory.GetAccountProviderAsync( authenticationRequestParameters.Authority.TenantId).ConfigureAwait(true); WebTokenRequest webTokenRequest = await _aadPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : true, isInteractive : true, isAccountInWam : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger); AddPromptToRequest(msalPrompt, true, webTokenRequest); var wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, _aadPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); }
public async Task <string> TryFetchTransferTokenSilentAsync(AuthenticationRequestParameters authenticationRequestParameters, WebAccount account) { _logger.Verbose("WAM MSA-PT - fetching transfer token for silent flow"); var webTokenRequestMsa = await _msaPlugin.CreateWebTokenRequestAsync( account.WebAccountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : false, isAccountInWam : true, TransferTokenScopes) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequestMsa, _logger); var transferResponse = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequestMsa, account) .ConfigureAwait(true); return(ExtractTransferToken( authenticationRequestParameters.AppConfig.ClientId, transferResponse, isInteractive: false)); }
private async Task <WebAccount> FetchWebAccountFromMsaAsync( AuthenticationRequestParameters authenticationRequestParameters, WebAccountProvider accountProvider) { // This response has an v1 MSA Access Token, which MSAL should expose to the user var webTokenRequestMsa = await _msaPlugin.CreateWebTokenRequestAsync( accountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : true, isAccountInWam : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequestMsa); var webTokenResponseMsa = await _wamProxy.RequestTokenForWindowAsync(_parentHandle, webTokenRequestMsa) .ConfigureAwait(true); if (!webTokenResponseMsa.ResponseStatus.IsSuccessStatus()) { var errorResp = WamAdapters.CreateMsalResponseFromWamResponse(webTokenResponseMsa, _msaPlugin, _logger, true); throw new MsalServiceException( errorResp.Error, "Error fetching the MSA-PT initial token - " + errorResp.ErrorDescription); } // Cannot use this WebAccount with the AAD provider WebAccount msaPtWebAccount = webTokenResponseMsa.ResponseData[0].WebAccount; return(msaPtWebAccount); }
public async Task <MsalTokenResponse> AcquireTokenSilentAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenSilentParameters acquireTokenSilentParameters) { using (_logger.LogMethodDuration()) { // Important: MSAL will have already resolved the authority by now, // so we are not expecting "common" or "organizations" but a tenanted authority bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, null, _wamOptions.MsaPassthrough) .ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider = await GetProviderAsync( authenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority, isMsa).ConfigureAwait(false); WebAccount webAccount = await FindWamAccountForMsalAccountAsync( provider, wamPlugin, authenticationRequestParameters.Account, null, // ATS requires an account object, login_hint is not supported on its own authenticationRequestParameters.AppConfig.ClientId).ConfigureAwait(false); if (webAccount == null) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "Could not find a WAM account for the silent request."); } WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : false, isAccountInWam : true, isInteractive : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger); var wamResult = await _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: false)); } }
private async Task <IWebTokenRequestResultWrapper> AcquireInteractiveWithoutPickerAsync( AuthenticationRequestParameters authenticationRequestParameters, Prompt prompt, IWamPlugin wamPlugin, WebAccountProvider provider, WebAccount wamAccount) { bool isForceLoginPrompt = IsForceLoginPrompt(prompt); WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : isForceLoginPrompt, isInteractive : true, isAccountInWam : true) .ConfigureAwait(false); if (isForceLoginPrompt && ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 6)) { // this feature works correctly since windows RS4, aka 1803 with the AAD plugin only! webTokenRequest.Properties["prompt"] = prompt.PromptValue; } WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest); try { IWebTokenRequestResultWrapper wamResult; if (wamAccount != null) { wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest, wamAccount).ConfigureAwait(false); } else { // default user wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest).ConfigureAwait(false); } return(wamResult); } catch (Exception ex) { _logger.ErrorPii(ex); throw new MsalServiceException( MsalError.WamInteractiveError, "AcquireTokenInteractive without picker failed. See inner exception for details. ", ex); } }
private async Task <IWebTokenRequestResultWrapper> AcquireInteractiveWithWamAccountAsync( AuthenticationRequestParameters authenticationRequestParameters, Prompt msalPrompt, IWamPlugin wamPlugin, WebAccountProvider provider, WebAccount wamAccount) { WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : true, isAccountInWam : true) .ConfigureAwait(false); // because of https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/2476 string differentAuthority = null; if (string.Equals(wamAccount?.WebAccountProvider?.Authority, Constants.OrganizationsTenant) && string.Equals(authenticationRequestParameters.Authority.TenantId, Constants.OrganizationsTenant)) { differentAuthority = authenticationRequestParameters.Authority.GetTenantedAuthority("common"); } WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, differentAuthority); try { IWebTokenRequestResultWrapper wamResult; if (wamAccount != null) { wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest, wamAccount).ConfigureAwait(false); } else { // default user wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest).ConfigureAwait(false); } return(wamResult); } catch (Exception ex) { _logger.ErrorPii(ex); throw new MsalServiceException( MsalError.WamInteractiveError, "AcquireTokenInteractive without picker failed. See inner exception for details. ", ex); } }
public async Task <MsalTokenResponse> AcquireTokenSilentDefaultUserAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenSilentParameters acquireTokenSilentParameters) { using (_logger.LogMethodDuration()) { var defaultAccountProvider = await _webAccountProviderFactory.GetDefaultProviderAsync().ConfigureAwait(false); if (defaultAccountProvider == null) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "A default account was not found"); } // special case: passthrough + default MSA account. Need to use the transfer token protocol. if (_wamOptions.MsaPassthrough && _webAccountProviderFactory.IsConsumerProvider(defaultAccountProvider)) { return(await AcquireTokenSilentDefaultUserPassthroughAsync(authenticationRequestParameters, defaultAccountProvider).ConfigureAwait(false)); } bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, null, _wamOptions.MsaPassthrough).ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider = await GetProviderAsync( authenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority, isMsa).ConfigureAwait(false); WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : false, isAccountInWam : false, isInteractive : false) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger); var wamResult = await _wamProxy.GetTokenSilentlyForDefaultAccountAsync(webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: false)); } }
private async Task <IWebTokenRequestResultWrapper> AcquireInteractiveWithoutPickerAsync( AuthenticationRequestParameters authenticationRequestParameters, Prompt msalPrompt, IWamPlugin wamPlugin, WebAccountProvider provider, WebAccount wamAccount) { bool isForceLoginPrompt = IsForceLoginPrompt(msalPrompt); WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : isForceLoginPrompt, isInteractive : true, isAccountInWam : true) .ConfigureAwait(false); AddPromptToRequest(msalPrompt, isForceLoginPrompt, webTokenRequest); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest); try { IWebTokenRequestResultWrapper wamResult; if (wamAccount != null) { wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest, wamAccount).ConfigureAwait(false); } else { // default user wamResult = await _wamProxy.RequestTokenForWindowAsync( _parentHandle, webTokenRequest).ConfigureAwait(false); } return(wamResult); } catch (Exception ex) { _logger.ErrorPii(ex); throw new MsalServiceException( MsalError.WamInteractiveError, "AcquireTokenInteractive without picker failed. See inner exception for details. ", ex); } }
private string ExtractTransferToken( string clientId, IWebTokenRequestResultWrapper transferResponse, bool isInteractive) { if (!transferResponse.ResponseStatus.IsSuccessStatus()) { try { _ = WamAdapters.CreateMsalResponseFromWamResponse( transferResponse, _msaPlugin, clientId, _logger, isInteractive: isInteractive); } catch (MsalServiceException exception) { _logger.Warning( "WAM MSA-PT: could not get a transfer token, ussually this is because the " + "1st party app is configured for MSA-PT but not configured to login MSA users (signinaudience =2). " + "Error was: " + exception.ErrorCode + " " + exception.Message); } return(null); } _ = _msaPlugin.ParseSuccessfullWamResponse(transferResponse.ResponseData[0], out var properties); properties.TryGetValue("code", out string code); // Important: cannot use this WebAccount with the AAD provider WebAccount msaPtWebAccount = transferResponse.ResponseData[0].WebAccount; _logger.InfoPii($"Obtained a transfer token for {msaPtWebAccount.UserName} ? {code != null}", $"Obtained a transfer token? {code != null}"); return(code); }
private async Task <MsalTokenResponse> AcquireTokenSilentDefaultUserPassthroughAsync(AuthenticationRequestParameters authenticationRequestParameters, WebAccountProvider defaultAccountProvider) { var transferToken = await _msaPassthroughHandler.TryFetchTransferTokenSilentDefaultAccountAsync(authenticationRequestParameters, defaultAccountProvider).ConfigureAwait(false); if (string.IsNullOrEmpty(transferToken)) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "Cannot get a token silently (internal error: found an MSA account, but could not retrieve a transfer token for it when calling WAM)"); } var aadAccountProvider = await _webAccountProviderFactory.GetAccountProviderAsync("organizations").ConfigureAwait(false); var webTokenRequest = await _aadPlugin.CreateWebTokenRequestAsync( aadAccountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : false, isAccountInWam : true) .ConfigureAwait(false); _msaPassthroughHandler.AddTransferTokenToRequest(webTokenRequest, transferToken); string overrideAuthority = authenticationRequestParameters.Authority.GetTenantedAuthority(InfrastructureTenant, true); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger, overrideAuthority); var wamResult = await _wamProxy.RequestTokenForWindowAsync(_parentHandle, webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, _aadPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: false)); }
public async Task <string> TryFetchTransferTokenSilentDefaultAccountAsync(AuthenticationRequestParameters authenticationRequestParameters, WebAccountProvider accountProvider) { // First party apps can have MSA-PT enabled and can configured to allow MSA users _logger.Verbose("WAM MSA-PT - fetching transfer token for silent flow with default OS account"); var webTokenRequestMsa = await _msaPlugin.CreateWebTokenRequestAsync( accountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : false, isAccountInWam : true, TransferTokenScopes) .ConfigureAwait(false); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequestMsa, _logger); var transferResponse = await _wamProxy.GetTokenSilentlyForDefaultAccountAsync(webTokenRequestMsa) .ConfigureAwait(true); return(ExtractTransferToken( authenticationRequestParameters.AppConfig.ClientId, transferResponse, isInteractive: false)); }
private async Task <MsalTokenResponse> AcquireInteractiveWithPickerAsync( AuthenticationRequestParameters authenticationRequestParameters, Prompt msalPrompt) { bool isMsaPassthrough = _wamOptions.MsaPassthrough; var accountPicker = _accountPickerFactory.Create( _parentHandle, _logger, _synchronizationContext, authenticationRequestParameters.Authority, isMsaPassthrough); IWamPlugin wamPlugin; WebTokenRequest webTokenRequest; try { WebAccountProvider accountProvider = await accountPicker.DetermineAccountInteractivelyAsync().ConfigureAwait(false); if (accountProvider == null) { var errorMessage = "WAM Account Picker did not return an account."; #if !WINDOWS_APP if (WindowsNativeUtils.IsElevatedUser()) { errorMessage = MsalErrorMessage.AuthenticationFailedWamElevatedProcess; } #endif throw new MsalClientException(MsalError.AuthenticationCanceledError, errorMessage); } bool isConsumerTenant = _webAccountProviderFactory.IsConsumerProvider(accountProvider); // WAM returns the tenant here, not the full authority wamPlugin = (isConsumerTenant && !isMsaPassthrough) ? _msaPlugin : _aadPlugin; string transferToken = null; bool isForceLoginPrompt = false; if (isConsumerTenant && isMsaPassthrough) { // Get a transfer token to avoid prompting the user twice transferToken = await _msaPassthroughHandler.TryFetchTransferTokenAsync( authenticationRequestParameters, accountProvider).ConfigureAwait(false); // If a TT cannot be obtained, force the interactive experience again isForceLoginPrompt = string.IsNullOrEmpty(transferToken); // For MSA-PT, the MSA provider will issue v1 token, which cannot be used. // Only the AAD provider can issue a v2 token accountProvider = await _webAccountProviderFactory.GetAccountProviderAsync( authenticationRequestParameters.Authority.TenantId) .ConfigureAwait(false); } webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( accountProvider, authenticationRequestParameters, isForceLoginPrompt : isForceLoginPrompt, isInteractive : true, isAccountInWam : false) .ConfigureAwait(true); _msaPassthroughHandler.AddTransferTokenToRequest(webTokenRequest, transferToken); WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger); AddPromptToRequest(msalPrompt, isForceLoginPrompt, webTokenRequest); } catch (Exception ex) when(!(ex is MsalException)) { _logger.ErrorPii(ex); throw new MsalServiceException( MsalError.WamPickerError, "Could not get the account provider - account picker. See inner exception for details", ex); } IWebTokenRequestResultWrapper wamResult; try { wamResult = await _wamProxy.RequestTokenForWindowAsync(_parentHandle, webTokenRequest).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorPii(ex); throw new MsalServiceException( MsalError.WamPickerError, "Could not get the result - account picker. See inner exception for details", ex); } return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); }
/// <summary> /// In WAM, AcquireTokenInteractive is always associated to an account. WAM also allows for an "account picker" to be displayed, /// which is similar to the EVO browser experience, allowing the user to add an account or use an existing one. /// /// MSAL does not have a concept of account picker so MSAL.AccquireTokenInteractive will: /// /// 1. Call WAM.AccountPicker if an IAccount (or possibly login_hint) is not configured /// 2. Figure out the WAM.AccountID associated to the MSAL.Account /// 3. Call WAM.AcquireTokenInteractive with the WAM.AccountID /// /// To make matters more complicated, WAM has 2 plugins - AAD and MSA. With AAD plugin, /// it is possible to list all WAM accounts and find the one associated to the MSAL account. /// However, MSA plugin does NOT allow listing of accounts, and the only way to figure out the /// WAM account ID is to use the account picker. This makes AcquireTokenSilent impossible for MSA, /// because we would not be able to map an Msal.Account to a WAM.Account. To overcome this, /// we save the WAM.AccountID in MSAL's cache. /// </summary> public async Task <MsalTokenResponse> AcquireTokenInteractiveAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenInteractiveParameters acquireTokenInteractiveParameters) { #if WINDOWS_APP if (_synchronizationContext == null) { throw new MsalClientException( MsalError.WamUiThread, "AcquireTokenInteractive with broker must be called from the UI thread when using WAM." + ErrorMessageSuffix); } #endif if (authenticationRequestParameters.Account != null || !string.IsNullOrEmpty(authenticationRequestParameters.LoginHint)) { bool isMsaPassthrough = _wamOptions.MsaPassthrough; bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, authenticationRequestParameters?.Account?.HomeAccountId?.TenantId, // TODO: we could further optimize here by searching for an account based on UPN isMsaPassthrough).ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider = await GetProviderAsync( authenticationRequestParameters.Authority.TenantId, isMsa) .ConfigureAwait(false); if (PublicClientApplication.IsOperatingSystemAccount(authenticationRequestParameters.Account)) { var wamResult = await AcquireInteractiveWithWamAccountAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt, wamPlugin, provider, null) .ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); } var wamAccount = await FindWamAccountForMsalAccountAsync( provider, wamPlugin, authenticationRequestParameters.Account, authenticationRequestParameters.LoginHint, authenticationRequestParameters.AppConfig.ClientId).ConfigureAwait(false); if (wamAccount != null) { var wamResult = await AcquireInteractiveWithWamAccountAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt, wamPlugin, provider, wamAccount) .ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); } else { if (IsAadOnlyAuthority(authenticationRequestParameters.Authority)) { return(await AcquireInteractiveWithAadBrowserAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt).ConfigureAwait(false)); } } } return(await AcquireInteractiveWithPickerAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt) .ConfigureAwait(false)); }
/// <summary> /// In WAM, AcquireTokenInteractive is always associated to an account. WAM also allows for an "account picker" to be displayed, /// which is similar to the EVO browser experience, allowing the user to add an account or use an existing one. /// /// MSAL does not have a concept of account picker so MSAL.AccquireTokenInteractive will: /// /// 1. Call WAM.AccountPicker if an IAccount (or possibly login_hint) is not configured /// 2. Figure out the WAM.AccountID associated to the MSAL.Account /// 3. Call WAM.AcquireTokenInteractive with the WAM.AccountID /// /// To make matters more complicated, WAM has 2 plugins - AAD and MSA. With AAD plugin, /// it is possible to list all WAM accounts and find the one associated to the MSAL account. /// However, MSA plugin does NOT allow listing of accounts, and the only way to figure out the /// WAM account ID is to use the account picker. This makes AcquireTokenSilent impossible for MSA, /// because we would not be able to map an Msal.Account to a WAM.Account. To overcome this, /// we save the WAM.AccountID in MSAL's cache. /// </summary> public async Task <MsalTokenResponse> AcquireTokenInteractiveAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenInteractiveParameters acquireTokenInteractiveParameters) { #if WINDOWS_APP if (_synchronizationContext == null) { throw new MsalClientException( MsalError.WamUiThread, "AcquireTokenInteractive with broker must be called from the UI thread when using WAM." + ErrorMessageSuffix); } #endif if (authenticationRequestParameters.Account != null || !string.IsNullOrEmpty(authenticationRequestParameters.LoginHint)) { _logger.Verbose("[WamBroker] AcquireTokenIntractive - account information provided. Trying to find a Windows account that matches."); bool isMsaPassthrough = _wamOptions.MsaPassthrough; bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, authenticationRequestParameters?.Account?.HomeAccountId?.TenantId, isMsaPassthrough).ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider = await GetProviderAsync( authenticationRequestParameters.Authority.TenantId, isMsa) .ConfigureAwait(false); if (PublicClientApplication.IsOperatingSystemAccount(authenticationRequestParameters.Account)) { var wamResult = await AcquireInteractiveWithWamAccountAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt, wamPlugin, provider, null) .ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); } var wamAccount = await FindWamAccountForMsalAccountAsync( provider, wamPlugin, authenticationRequestParameters.Account, authenticationRequestParameters.LoginHint, authenticationRequestParameters.AppConfig.ClientId).ConfigureAwait(false); if (wamAccount != null) { var wamResult = await AcquireInteractiveWithWamAccountAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt, wamPlugin, provider, wamAccount) .ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: true)); } _logger.Verbose("[WamBroker] AcquireTokenIntractive - account information provided but no matching account was found "); } // no account information available, need an account picker if (CanSkipAccountPicker(authenticationRequestParameters.Authority)) { _logger.Verbose("[WamBroker] Using AAD plugin account picker"); return(await AcquireInteractiveWithAadBrowserAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt).ConfigureAwait(false)); } _logger.Verbose("[WamBroker] Using Windows account picker (AccountsSettingsPane)"); return(await AcquireInteractiveWithPickerAsync( authenticationRequestParameters, acquireTokenInteractiveParameters.Prompt) .ConfigureAwait(false)); }
public async Task <MsalTokenResponse> AcquireTokenSilentAsync( AuthenticationRequestParameters authenticationRequestParameters, AcquireTokenSilentParameters acquireTokenSilentParameters) { using (_logger.LogMethodDuration()) { // Important: MSAL will have already resolved the authority by now, // so we are not expecting "common" or "organizations" but a tenanted authority bool isMsa = await IsMsaRequestAsync( authenticationRequestParameters.Authority, null, _wamOptions.MsaPassthrough) .ConfigureAwait(false); IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin; WebAccountProvider provider; if (_wamOptions.MsaPassthrough) { provider = await GetProviderAsync( "organizations", false).ConfigureAwait(false); } else { provider = await GetProviderAsync( authenticationRequestParameters.AuthorityInfo.CanonicalAuthority, isMsa).ConfigureAwait(false); } WebAccount webAccount = await FindWamAccountForMsalAccountAsync( provider, wamPlugin, authenticationRequestParameters.Account, null, // ATS requires an account object, login_hint is not supported on its own authenticationRequestParameters.AppConfig.ClientId).ConfigureAwait(false); if (webAccount == null && _wamOptions.MsaPassthrough) { return(await AcquireMsaTokenSilentForPassthroughAsync( authenticationRequestParameters, provider).ConfigureAwait(false)); } if (webAccount == null) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "Could not find a WAM account for the silent request."); } WebTokenRequest webTokenRequest = await wamPlugin.CreateWebTokenRequestAsync( provider, authenticationRequestParameters, isForceLoginPrompt : false, isAccountInWam : true, isInteractive : false) .ConfigureAwait(false); // For MSA-PT scenario, MSAL's authority is wrong. MSAL will use Account.HomeTenantId // which will essentialyl be /consumers. This is wrong, we are not trying to obtain // an MSA token, we are trying to obtain an ADD *guest* token. string differentAuthority = null; if (_wamOptions.MsaPassthrough && authenticationRequestParameters.Authority is AadAuthority aadAuthority && aadAuthority.IsConsumers()) { differentAuthority = authenticationRequestParameters.Authority.GetTenantedAuthority("organizations", forceTenantless: true); } WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger, differentAuthority); var wamResult = await _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, wamPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: false)); } }
private async Task <MsalTokenResponse> AcquireMsaTokenSilentForPassthroughAsync( AuthenticationRequestParameters authenticationRequestParameters, WebAccountProvider aadAccountProvider) { // Try to find an MSA account which matches the MSAL account var msaProvider = await GetProviderAsync("consumers", true).ConfigureAwait(false); var msaWebAccount = await FindWamAccountForMsalAccountAsync( msaProvider, _msaPlugin, authenticationRequestParameters.Account, null, // ATS requires an account object, login_hint is not supported on its own authenticationRequestParameters.AppConfig.ClientId).ConfigureAwait(false); if (msaWebAccount == null) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "Could not find a WAM MSA account for the silent request."); } // We can't use the account as is to get a token, because this account is from MSA but the provider is AAD // so we have to perform the transfer token flow string transferToken = await _msaPassthroughHandler.TryFetchTransferTokenSilentAsync( authenticationRequestParameters, msaWebAccount).ConfigureAwait(false); if (string.IsNullOrEmpty(transferToken)) { throw new MsalUiRequiredException( MsalError.InteractionRequired, "Found an MSA account, but could not retrieve a transfer token for it."); } // Now make a request to AAD plugin, including the login hint and transfer token var webTokenRequest = await _aadPlugin.CreateWebTokenRequestAsync( aadAccountProvider, authenticationRequestParameters, isForceLoginPrompt : false, isInteractive : false, isAccountInWam : true) .ConfigureAwait(true); _msaPassthroughHandler.AddTransferTokenToRequest(webTokenRequest, transferToken); // We can't make this request on the /consumers authority, this is a known MSA-PT issue with the browser as well // but we can make the request over /organizations or over /MicrosoftInfrastructureTenant string overrideAuthority = null; if (authenticationRequestParameters.Authority is AadAuthority aadAuthority && aadAuthority.IsConsumers()) { overrideAuthority = authenticationRequestParameters.Authority.GetTenantedAuthority("organizations", true); } WamAdapters.AddMsalParamsToRequest(authenticationRequestParameters, webTokenRequest, _logger, overrideAuthority); var wamResult = await _wamProxy.RequestTokenForWindowAsync(_parentHandle, webTokenRequest).ConfigureAwait(false); return(WamAdapters.CreateMsalResponseFromWamResponse( wamResult, _aadPlugin, authenticationRequestParameters.AppConfig.ClientId, _logger, isInteractive: false)); }