/// <summary>
        /// The algorithm here is much more complex in order to workaround a limitation in the AAD plugin's
        /// handling of guest accounts:
        ///
        /// 1. Read the accounts from WAM.AADPlugin
        /// 2. For each account, we need to find its home_account_id as the one from WAM may not be correct
        /// 3. If we can find a cached account with the same LocalAccountId or UPN, use it
        /// 4. If not, make a simple silent token request and use the client info provided
        /// </summary>
        public async Task <IReadOnlyList <IAccount> > GetAccountsAsync(
            string clientId,
            AuthorityInfo authorityInfo,
            Cache.ICacheSessionManager cacheSessionManager,
            Instance.Discovery.IInstanceDiscoveryManager instanceDiscoveryManager)
        {
            var webAccountProvider = await _webAccountProviderFactory.GetAccountProviderAsync("organizations").ConfigureAwait(false);

            var wamAccounts = await _wamProxy.FindAllWebAccountsAsync(webAccountProvider, clientId).ConfigureAwait(false);

            if (wamAccounts.Count > 0)
            {
                var webAccountEnvs = wamAccounts
                                     .Select(w =>
                {
                    _wamProxy.TryGetAccountProperty(w, "Authority", out string accountAuthority);
                    if (accountAuthority != null)
                    {
                        return((new Uri(accountAuthority)).Host);
                    }
                    else
                    {
                        _logger.WarningPii(
                            $"[WAM AAD Provider] Could not convert the WAM account {w.UserName} (id: {w.Id}) to an MSAL account because the Authority could not be found",
                            $"[WAM AAD Provider] Could not convert the WAM account {w.Id} to an MSAL account because the Authority could not be found");

                        return(null);
                    }
                })
                                     .Where(a => a != null);

                var instanceMetadata = await instanceDiscoveryManager.GetMetadataEntryTryAvoidNetworkAsync(authorityInfo, webAccountEnvs, cacheSessionManager.RequestContext)
                                       .ConfigureAwait(false);

                var accountsFromCache = await cacheSessionManager.GetAccountsAsync().ConfigureAwait(false);

                var msalAccountTasks = wamAccounts
                                       .Select(
                    async webAcc =>
                    await ConvertToMsalAccountOrNullAsync(
                        clientId,
                        webAcc,
                        instanceMetadata,
                        cacheSessionManager,
                        accountsFromCache).ConfigureAwait(false));

                var msalAccounts = (await Task.WhenAll(msalAccountTasks).ConfigureAwait(false)).Where(a => a != null).ToList();

                _logger.Info($"[WAM AAD Provider] GetAccountsAsync converted {msalAccounts.Count} accounts from {wamAccounts.Count} WAM accounts");
                return(msalAccounts);
            }

            _logger.Info("[WAM AAD provider] No accounts found.");
            return(Array.Empty <IAccount>());
        }
        public async Task <MsalTokenResponse> AcquireTokenSilentAsync(
            AuthenticationRequestParameters authenticationRequestParameters,
            AcquireTokenSilentParameters acquireTokenSilentParameters)
        {
            var cancellationToken = authenticationRequestParameters.RequestContext.UserCancellationToken;
            MsalTokenResponse msalTokenResponse = null;

            _logger.Verbose("[WamBroker] Acquiring token silently.");

            using (var core = new NativeInterop.Core())
                using (var authParams = WamAdapters.GetCommonAuthParameters(authenticationRequestParameters, _wamOptions.MsaPassthrough))
                {
                    using (var account = await core.ReadAccountByIdAsync(
                               acquireTokenSilentParameters.Account.HomeAccountId.ObjectId,
                               authenticationRequestParameters.CorrelationId.ToString("D"),
                               cancellationToken).ConfigureAwait(false))
                    {
                        if (account == null)
                        {
                            _logger.WarningPii(
                                $"Could not find a WAM account for the selected user {acquireTokenSilentParameters.Account.Username}",
                                "Could not find a WAM account for the selected user");

                            throw new MsalUiRequiredException(
                                      "wam_no_account_for_id",
                                      $"Could not find a WAM account for the selected user {acquireTokenSilentParameters.Account.Username}");
                        }

                        using (NativeInterop.AuthResult result = await core.AcquireTokenSilentlyAsync(
                                   authParams,
                                   authenticationRequestParameters.CorrelationId.ToString("D"),
                                   account,
                                   cancellationToken).ConfigureAwait(false))
                        {
                            if (result.IsSuccess)
                            {
                                msalTokenResponse = WamAdapters.ParseRuntimeResponse(result, authenticationRequestParameters, _logger);
                            }
                            else
                            {
                                WamAdapters.ThrowExceptionFromWamError(result, authenticationRequestParameters, _logger);
                            }
                        }
                    }
                }

            return(msalTokenResponse);
        }
Beispiel #3
0
        void ILegacyCachePersistence.WriteCache(byte[] serializedCache)
        {
            try
            {
                var s = new SecRecord(SecKind.GenericPassword)
                {
                    Generic     = NSData.FromString(LocalSettingsContainerName),
                    Accessible  = SecAccessible.Always,
                    Service     = NAME + " Service",
                    Account     = NAME + " cache",
                    Label       = NAME + " Label",
                    Comment     = NAME + " Cache",
                    Description = "Storage for cache"
                };

                if (_keychainGroup != null)
                {
                    s.AccessGroup = _keychainGroup;
                }

                var err = SecKeyChain.Remove(s);
                if (err != SecStatusCode.Success)
                {
                    string msg = "Failed to remove adal cache record: ";
                    _logger.WarningPii(msg + err, msg);
                }

                if (serializedCache != null && serializedCache.Length > 0)
                {
                    s.ValueData = NSData.FromArray(serializedCache);
                    err         = SecKeyChain.Add(s);
                    if (err != SecStatusCode.Success)
                    {
                        string msg = "Failed to save adal cache record: ";
                        _logger.WarningPii(msg + err, msg);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.WarningPiiWithPrefix(ex, "Failed to save adal cache: ");
            }
        }
Beispiel #4
0
        public static void TryThrowServiceException(string thumbprint, ThrottlingCache cache, ICoreLogger logger, string providerName)
        {
            if (cache.TryGetOrRemoveExpired(thumbprint, logger, out var ex))
            {
                logger.WarningPii(
                    $"[Throttling] Exception thrown because of throttling rule {providerName} - thumbprint: {thumbprint}",
                    $"[Throttling] Exception thrown because of throttling rule {providerName}");

                // mark the exception for logging purposes
                throw new MsalThrottledServiceException(ex);
            }
        }
        private Account ConvertToMsalAccountOrNull(WebAccount webAccount)
        {
            if (!webAccount.Properties.TryGetValue("Authority", out string authority))
            {
                _logger.WarningPii(
                    $"[WAM AAD Provider] Could not convert the WAM account {webAccount.UserName} (id: {webAccount.Id}) to an MSAL account because the Authority could not be found",
                    $"[WAM AAD Provider] Could not convert the WAM account {webAccount.Id} to an MSAL account because the Authority could not be found");

                return(null);
            }

            string environment   = (new Uri(authority)).Host;
            string homeAccountId = GetHomeAccountIdOrNull(webAccount);

            if (homeAccountId != null)
            {
                var msalAccount = new Account(homeAccountId, webAccount?.UserName, environment);
                return(msalAccount);
            }

            return(null);
        }
        private void TryThrowException(string thumbprint, ICoreLogger logger)
        {
            if (ThrottlingCache.TryGetOrRemoveExpired(thumbprint, logger, out MsalServiceException ex) &&
                ex is MsalUiRequiredException uiException)
            {
                logger.WarningPii(
                    $"[Throttling] Exception thrown because of throttling rule UiRequired - thumbprint: {thumbprint}",
                    $"[Throttling] Exception thrown because of throttling rule UiRequired ");

                // mark the exception for logging purposes
                throw new MsalThrottledUiRequiredException(uiException);
            }
        }
Beispiel #7
0
        public MsalIdTokenCacheItem GetIdToken(MsalAccessTokenCacheItem accessTokenCacheItem)
        {
            string partitionKey = CacheKeyFactory.GetIdTokenKeyFromCachedItem(accessTokenCacheItem);

            IdTokenCacheDictionary.TryGetValue(partitionKey, out var partition);
            if (partition != null && partition.TryGetValue(accessTokenCacheItem.GetIdTokenItemKey().ToString(), out var idToken))
            {
                return(idToken);
            }

            _logger.WarningPii(
                $"Could not find an id token for the access token with key {accessTokenCacheItem.GetKey()}",
                $"Could not find an id token for the access token for realm {accessTokenCacheItem.TenantId} ");
            return(null);
        }
Beispiel #8
0
        public string GetHomeAccountIdOrNull(WebAccount webAccount)
        {
            if (!webAccount.Properties.TryGetValue("SafeCustomerId", out string cid))
            {
                _logger.Warning("[WAM MSA Plugin] MSAL account cannot be created without MSA CID");
                return(null);
            }

            if (!TryConvertCidToGuid(cid, out string localAccountId))
            {
                _logger.WarningPii($"[WAM MSA Plugin] Invalid CID: {cid}", $"[WAM MSA Provider] Invalid CID, length {cid.Length}");
                return(null);
            }

            if (localAccountId == null)
            {
                return(null);
            }

            string homeAccountId = localAccountId + "." + Constants.MsaTenantId;

            return(homeAccountId);
        }