public void Init()
        {
            _synchronizationContext = new DedicatedThreadSynchronisationContext();

            _coreUIParent = new CoreUIParent()
            {
                SynchronizationContext = _synchronizationContext
            };

            _logger    = Substitute.For <ICoreLogger>();
            _aadPlugin = Substitute.For <IWamPlugin>();
            _msaPlugin = Substitute.For <IWamPlugin>();
            _wamProxy  = Substitute.For <IWamProxy>();
            _webAccountProviderFactory = Substitute.For <IWebAccountProviderFactory>();
            _accountPickerFactory      = Substitute.For <IAccountPickerFactory>();

            _wamBroker = new WamBroker(
                _coreUIParent,
                _logger,
                _aadPlugin,
                _msaPlugin,
                _wamProxy,
                _webAccountProviderFactory,
                _accountPickerFactory);
        }
Beispiel #2
0
        public async Task <MsalTokenResponse> AcquireTokenSilentDefaultUserAsync(
            AuthenticationRequestParameters authenticationRequestParameters,
            AcquireTokenSilentParameters acquireTokenSilentParameters)
        {
            using (_logger.LogMethodDuration())
            {
                bool isMsa = await IsMsaRequestAsync(
                    authenticationRequestParameters.Authority,
                    null,
                    IsMsaPassthrough(authenticationRequestParameters)).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);

                AddCommonParamsToRequest(authenticationRequestParameters, webTokenRequest);

                var wamResult =
                    await _wamProxy.GetTokenSilentlyForDefaultAccountAsync(webTokenRequest).ConfigureAwait(false);

                return(CreateMsalTokenResponse(wamResult, wamPlugin, isInteractive: false));
            }
        }
Beispiel #3
0
        private static WebAccount MatchWamAccountToMsalAccount(
            IWamPlugin wamPlugin,
            IAccount account,
            string loginHint,
            IEnumerable <WebAccount> wamAccounts)
        {
            WebAccount matchedAccountByLoginHint = null;

            foreach (var wamAccount in wamAccounts)
            {
                string homeAccountId = wamPlugin.GetHomeAccountIdOrNull(wamAccount);
                if (!string.IsNullOrEmpty(homeAccountId) &&
                    string.Equals(homeAccountId, account?.HomeAccountId?.Identifier, StringComparison.OrdinalIgnoreCase))
                {
                    return(wamAccount);
                }

                if (!string.IsNullOrEmpty(loginHint) &&
                    string.Equals(loginHint, wamAccount.UserName, StringComparison.OrdinalIgnoreCase))
                {
                    matchedAccountByLoginHint = wamAccount;
                }
            }

            return(matchedAccountByLoginHint);
        }
Beispiel #4
0
        /// <summary>
        /// Ctor. Only call if on Win10, otherwise a TypeLoadException occurs. See DesktopOsHelper.IsWin10
        /// </summary>
        public WamBroker(
            CoreUIParent uiParent,
            ApplicationConfiguration appConfig,
            ICoreLogger logger,
            IWamPlugin testAadPlugin = null,
            IWamPlugin testmsaPlugin = null,
            IWamProxy wamProxy       = null,
            IWebAccountProviderFactory webAccountProviderFactory = null,
            IAccountPickerFactory accountPickerFactory           = null,
            IMsaPassthroughHandler msaPassthroughHandler         = null)
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _synchronizationContext = uiParent?.SynchronizationContext;

            _wamProxy     = wamProxy ?? new WamProxy(_logger, _synchronizationContext);
            _parentHandle = GetParentWindow(uiParent);

            _webAccountProviderFactory = webAccountProviderFactory ?? new WebAccountProviderFactory();
            _accountPickerFactory      = accountPickerFactory ?? new AccountPickerFactory();
            _aadPlugin = testAadPlugin ?? new AadPlugin(_wamProxy, _webAccountProviderFactory, _logger);
            _msaPlugin = testmsaPlugin ?? new MsaPlugin(_wamProxy, _webAccountProviderFactory, _logger);

            _msaPassthroughHandler = msaPassthroughHandler ??
                                     new MsaPassthroughHandler(_logger, _msaPlugin, _wamProxy, _parentHandle);

            _wamOptions = appConfig.WindowsBrokerOptions ??
                          WindowsBrokerOptions.CreateDefault();
        }
Beispiel #5
0
        public async Task RemoveAccountAsync(ApplicationConfiguration appConfig, IAccount account)
        {
            string homeTenantId = account?.HomeAccountId?.TenantId;

            if (!string.IsNullOrEmpty(homeTenantId))
            {
                bool               isMsaAccount = IsConsumerTenantId(homeTenantId);
                IWamPlugin         wamPlugin    = isMsaAccount ? _msaPlugin : _aadPlugin;
                WebAccountProvider provider;
                if (isMsaAccount)
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false);
                }
                else
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("organizations")
                               .ConfigureAwait(false);
                }

                var webAccount = await FindWamAccountForMsalAccountAsync(provider, wamPlugin, account, null, appConfig.ClientId)
                                 .ConfigureAwait(false);

                _logger.Info("Found a webAccount? " + (webAccount != null));

                if (webAccount != null)
                {
                    await webAccount.SignOutAsync();
                }
            }
        }
Beispiel #6
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 (_synchronizationContext == null)
            {
                throw new MsalClientException(
                          MsalError.WamUiThread,
                          "AcquireTokenInteractive with broker must be called from the UI thread when using WAM. " +
                          "Note that console applications are not currently supported in conjuction with WAM." + ErrorMessageSuffix);
            }


            if (authenticationRequestParameters.Account != null ||
                !string.IsNullOrEmpty(authenticationRequestParameters.LoginHint))
            {
                bool isMsa = IsMsaRequest(
                    authenticationRequestParameters.Authority,
                    authenticationRequestParameters?.Account?.HomeAccountId?.TenantId, // TODO: we could furher optimize here by searching for an account based on UPN
                    IsMsaPassthrough(authenticationRequestParameters));

                IWamPlugin         wamPlugin = isMsa ? _msaPlugin : _aadPlugin;
                WebAccountProvider provider;
                if (isMsa)
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false);
                }
                else
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync(authenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority)
                               .ConfigureAwait(false);
                }

                var wamAccount = await FindWamAccountForMsalAccountAsync(
                    provider,
                    wamPlugin,
                    authenticationRequestParameters.Account,
                    authenticationRequestParameters.LoginHint,
                    authenticationRequestParameters.ClientId).ConfigureAwait(false);

                if (wamAccount != null)
                {
                    var wamResult = await AcquireInteractiveWithoutPickerAsync(
                        authenticationRequestParameters,
                        acquireTokenInteractiveParameters.Prompt,
                        wamPlugin,
                        provider,
                        wamAccount)
                                    .ConfigureAwait(false);

                    return(CreateMsalTokenResponse(wamResult, wamPlugin, isInteractive: true));
                }
            }

            return(await AcquireInteractiveWithPickerAsync(
                       authenticationRequestParameters)
                   .ConfigureAwait(false));
        }
Beispiel #7
0
        private MsalTokenResponse CreateMsalTokenResponse(
            IWebTokenRequestResultWrapper wamResponse,
            IWamPlugin wamPlugin,
            bool isInteractive)
        {
            string internalErrorCode = null;
            string errorMessage;
            string errorCode;

            switch (wamResponse.ResponseStatus)
            {
            case WebTokenRequestStatus.Success:
                _logger.Info("WAM response status success");
                return(wamPlugin.ParseSuccesfullWamResponse(wamResponse.ResponseData[0]));

            // Account Switch occurs when a login hint is passed to WAM but the user chooses a different account for login.
            // MSAL treats this as a success scenario
            case WebTokenRequestStatus.AccountSwitch:
                _logger.Info("WAM response status account switch. Treating as success");
                return(wamPlugin.ParseSuccesfullWamResponse(wamResponse.ResponseData[0]));

            case WebTokenRequestStatus.UserInteractionRequired:
                errorCode =
                    wamPlugin.MapTokenRequestError(wamResponse.ResponseStatus, wamResponse.ResponseError?.ErrorCode ?? 0, isInteractive);
                internalErrorCode = (wamResponse.ResponseError?.ErrorCode ?? 0).ToString(CultureInfo.InvariantCulture);
                errorMessage      = WamErrorPrefix +
                                    $"Wam plugin {wamPlugin.GetType()}" +
                                    $" error code: {internalErrorCode}" +
                                    $" error: " + wamResponse.ResponseError?.ErrorMessage;
                break;

            case WebTokenRequestStatus.UserCancel:
                errorCode    = MsalError.AuthenticationCanceledError;
                errorMessage = MsalErrorMessage.AuthenticationCanceled;
                break;

            case WebTokenRequestStatus.ProviderError:
                errorCode =
                    wamPlugin.MapTokenRequestError(wamResponse.ResponseStatus, wamResponse.ResponseError?.ErrorCode ?? 0, isInteractive);
                errorMessage      = WamErrorPrefix + wamPlugin.GetType() + wamResponse.ResponseError?.ErrorMessage;
                internalErrorCode = (wamResponse.ResponseError?.ErrorCode ?? 0).ToString(CultureInfo.InvariantCulture);
                break;

            default:
                errorCode         = MsalError.UnknownBrokerError;
                internalErrorCode = wamResponse.ResponseError.ErrorCode.ToString(CultureInfo.InvariantCulture);
                errorMessage      = $"Unknown WebTokenRequestStatus {wamResponse.ResponseStatus} (internal error code {internalErrorCode})";
                break;
            }

            return(new MsalTokenResponse()
            {
                Error = errorCode,
                ErrorCodes = internalErrorCode != null ? new[] { internalErrorCode } : null,
                ErrorDescription = errorMessage
            });
        }
Beispiel #8
0
        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;
            }

            AddCommonParamsToRequest(authenticationRequestParameters, webTokenRequest);

            try
            {
#if WINDOWS_APP
                // UWP requires being on the UI thread
                await _synchronizationContext;
#endif
                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);
            }
        }
Beispiel #9
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 = IsMsaRequest(
                    authenticationRequestParameters.Authority,
                    null,
                    IsMsaPassthrough(authenticationRequestParameters));

                IWamPlugin wamPlugin = isMsa ? _msaPlugin : _aadPlugin;

                WebAccountProvider provider;
                if (isMsa)
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false);
                }
                else
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync(authenticationRequestParameters.Authority.AuthorityInfo.CanonicalAuthority)
                               .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.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);

                AddCommonParamsToRequest(authenticationRequestParameters, webTokenRequest);

                var wamResult =
                    await _wamProxy.GetTokenSilentlyAsync(webAccount, webTokenRequest).ConfigureAwait(false);

                return(CreateMsalTokenResponse(wamResult, wamPlugin, isInteractive: false));
            }
        }
Beispiel #10
0
 public MsaPassthroughHandler(
     ICoreLogger logger,
     IWamPlugin msaPlugin,
     IWamProxy wamProxy,
     IntPtr parentHandle)
 {
     _logger       = logger;
     _msaPlugin    = msaPlugin;
     _wamProxy     = wamProxy;
     _parentHandle = parentHandle;
 }
Beispiel #11
0
        public void Init()
        {
            _logger    = Substitute.For <ICoreLogger>();
            _msaPlugin = Substitute.For <IWamPlugin>();
            _wamProxy  = Substitute.For <IWamProxy>();

            _msaPassthroughHandler = new MsaPassthroughHandler(
                _logger,
                _msaPlugin,
                _wamProxy,
                IntPtr.Zero);
        }
Beispiel #12
0
        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);
            }
        }
Beispiel #13
0
        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));
            }
        }
Beispiel #14
0
        public void Init()
        {
            _logger   = Substitute.For <ICoreLogger>();
            _wamProxy = Substitute.For <IWamProxy>();
            _webAccountProviderFactory = Substitute.For <IWebAccountProviderFactory>();
            _accountPickerFactory      = Substitute.For <IAccountPickerFactory>();

            _webAccountProviderFactory.ClearReceivedCalls();
            _cacheSessionManager = Substitute.For <ICacheSessionManager>();

            _instanceDiscoveryManager = Substitute.For <IInstanceDiscoveryManager>();

            _aadPlugin = new AadPlugin(_wamProxy, _webAccountProviderFactory, _logger);
        }
        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);
            }
        }
Beispiel #16
0
        public WamBroker(
            CoreUIParent uiParent,
            ICoreLogger logger,
            IWamPlugin testAadPlugin = null,
            IWamPlugin testmsaPlugin = null,
            IWamProxy wamProxy       = null,
            IWebAccountProviderFactory webAccountProviderFactory = null,
            IAccountPickerFactory accountPickerFactory           = null)
        {
            _logger                 = logger ?? throw new ArgumentNullException(nameof(logger));
            _wamProxy               = wamProxy ?? new WamProxy(_logger);
            _parentHandle           = GetParentWindow(uiParent);
            _synchronizationContext = uiParent?.SynchronizationContext;

            _webAccountProviderFactory = webAccountProviderFactory ?? new WebAccountProviderFactory();
            _accountPickerFactory      = accountPickerFactory ?? new AccountPickerFactory();
            _aadPlugin = testAadPlugin ?? new AadPlugin(_wamProxy, _webAccountProviderFactory, _logger);
            _msaPlugin = testmsaPlugin ?? new MsaPlugin(_wamProxy, _webAccountProviderFactory, _logger);
        }
Beispiel #17
0
        public async Task RemoveAccountAsync(ApplicationConfiguration appConfig, IAccount account)
        {
            string homeTenantId = account?.HomeAccountId?.TenantId;

            if (!string.IsNullOrEmpty(homeTenantId))
            {
                // If it's an AAD account, only the AAD plugin should remove it
                // If it's an MSA account - MSA plugin should remove it, but in MSA-PT scenarios it's still the AAD plugin
                bool isMsaRequest = await IsMsaRequestAsync(
                    appConfig.Authority,
                    homeTenantId,
                    _wamOptions.MsaPassthrough).ConfigureAwait(false);

                IWamPlugin         wamPlugin = isMsaRequest ? _msaPlugin : _aadPlugin;
                WebAccountProvider provider;
                if (isMsaRequest)
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("consumers").ConfigureAwait(false);
                }
                else
                {
                    provider = await _webAccountProviderFactory.GetAccountProviderAsync("organizations")
                               .ConfigureAwait(false);
                }

                var webAccount = await FindWamAccountForMsalAccountAsync(provider, wamPlugin, account, null, appConfig.ClientId)
                                 .ConfigureAwait(false);

                _logger.Info("Found a webAccount? " + (webAccount != null));

                if (webAccount != null)
                {
                    await webAccount.SignOutAsync();
                }
            }
        }
Beispiel #18
0
        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));
        }
Beispiel #19
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));
        }
Beispiel #20
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));
        }
Beispiel #21
0
        internal static MsalTokenResponse CreateMsalResponseFromWamResponse(
            IWebTokenRequestResultWrapper wamResponse,
            IWamPlugin wamPlugin,
            string clientId,
            ICoreLogger logger,
            bool isInteractive)
        {
            string internalErrorCode = null;
            string errorMessage;
            string errorCode;

            switch (wamResponse.ResponseStatus)
            {
            case WebTokenRequestStatus.Success:
                logger.Info("WAM response status success");
                return(wamPlugin.ParseSuccessfullWamResponse(wamResponse.ResponseData[0], out _));

            // Account Switch occurs when a login hint is passed to WAM but the user chooses a different account for login.
            // MSAL treats this as a success scenario
            case WebTokenRequestStatus.AccountSwitch:
                logger.Info("WAM response status account switch. Treating as success");
                return(wamPlugin.ParseSuccessfullWamResponse(wamResponse.ResponseData[0], out _));

            case WebTokenRequestStatus.UserInteractionRequired:
                errorCode =
                    wamPlugin.MapTokenRequestError(wamResponse.ResponseStatus, wamResponse.ResponseError?.ErrorCode ?? 0, isInteractive);
                internalErrorCode = (wamResponse.ResponseError?.ErrorCode ?? 0).ToString(CultureInfo.InvariantCulture);
                errorMessage      = WamErrorPrefix +
                                    $"Wam plugin {wamPlugin.GetType()}" +
                                    $" Error code: {internalErrorCode}" +
                                    $" Error Message: " + wamResponse.ResponseError?.ErrorMessage;
                break;

            case WebTokenRequestStatus.UserCancel:
                errorCode    = MsalError.AuthenticationCanceledError;
                errorMessage = MsalErrorMessage.AuthenticationCanceled;
                break;

            case WebTokenRequestStatus.ProviderError:
                errorCode =
                    wamPlugin.MapTokenRequestError(wamResponse.ResponseStatus, wamResponse.ResponseError?.ErrorCode ?? 0, isInteractive);
                errorMessage =
                    $"{WamErrorPrefix} {wamPlugin.GetType()} \n" +
                    $" Error Code: {errorCode} \n" +
                    $" Error Message: {wamResponse.ResponseError?.ErrorMessage} \n" +
                    $" Possible causes: \n " +
                    $"- Invalid redirect uri - ensure you have configured the following url in the AAD portal App Registration: {GetExpectedRedirectUri(clientId)} \n" +
                    $"- No Internet connection \n" +
                    $"Please see https://aka.ms/msal-net-wam for details about Windows Broker integration";

                internalErrorCode = (wamResponse.ResponseError?.ErrorCode ?? 0).ToString(CultureInfo.InvariantCulture);
                break;

            default:
                errorCode         = MsalError.UnknownBrokerError;
                internalErrorCode = wamResponse.ResponseError.ErrorCode.ToString(CultureInfo.InvariantCulture);
                errorMessage      = $"Unknown WebTokenRequestStatus {wamResponse.ResponseStatus} (internal error code {internalErrorCode})";
                break;
            }

            return(new MsalTokenResponse()
            {
                Error = errorCode,
                ErrorCodes = internalErrorCode != null ? new[] { internalErrorCode } : null,
                ErrorDescription = errorMessage
            });
        }
Beispiel #22
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));
            }
        }