Esempio n. 1
0
        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));
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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,
                    clientId,
                    _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);
        }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
        /// <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));
        }
Esempio n. 6
0
        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));
        }
Esempio n. 7
0
        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));
            }
        }
Esempio n. 8
0
        /// <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));
        }