private bool TryFromCredentialCache(Uri uri, CredentialRequestType type, bool isRetry, ICredentialProvider provider,
                                            out CredentialResponse credentials)
        {
            credentials = null;

            var key = CredentialsKeyHelper.GetCacheKey(uri, type, provider);

            if (isRetry)
            {
                CredentialResponse removed;
                _providerCredentialCache.TryRemove(key, out removed);
                return(false);
            }

            return(_providerCredentialCache.TryGetValue(key, out credentials));
        }
        /// <summary>
        /// Provides credentials for http requests.
        /// </summary>
        /// <param name="uri">
        /// The URI of a web resource for which credentials are needed.
        /// </param>
        /// <param name="proxy">
        /// The currently configured proxy. It may be necessary for CredentialProviders
        /// to use this proxy in order to acquire credentials from their authentication source.
        /// </param>
        /// <param name="type">
        /// The type of credential request that is being made.
        /// </param>
        /// <param name="message">
        /// A default, user-readable message explaining why they are being prompted for credentials.
        /// The credential provider can choose to ignore this value and write their own message.
        /// </param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A credential object, or null if no credentials could be acquired.</returns>
        public async Task <ICredentials> GetCredentialsAsync(
            Uri uri,
            IWebProxy proxy,
            CredentialRequestType type,
            string message,
            CancellationToken cancellationToken)
        {
            if (uri is null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

            ICredentials creds = null;

            foreach (var provider in await _providers)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var retryKey = CredentialsKeyHelper.GetUriKey(uri, type, provider);
                var isRetry  = _retryCache.ContainsKey(retryKey);

                try
                {
                    //original implementation contains semaphore
                    //in fact unecessary, because service called
                    //only when no one provider cached

                    _providerSemaphore.WaitOne();

                    Log.Debug($"Requesting credentials, _retryCache count = {_retryCache.Count}");

                    if (!TryFromCredentialCache(uri, type, isRetry, provider, out var response))
                    {
                        response = await provider.GetAsync(
                            uri,
                            proxy,
                            type,
                            message,
                            isRetry,
                            _nonInteractive,
                            cancellationToken);

                        // Check that the provider gave us a valid response.
                        if (!IsValidResponse(response))
                        {
                            throw new ProviderException("Credential provider gaves malformed response.");
                        }

                        if (response.Status == CredentialStatus.UserCanceled)
                        {
                            //create cancellation
                            cancellationToken.ThrowIfCancellationRequested();
                        }
                        else
                        {
                            AddToCredentialCache(uri, type, provider, response);
                        }
                    }

                    if (response.Status == CredentialStatus.Success)
                    {
                        _retryCache[retryKey] = true;
                        Log.Debug($"_retryCache count now is {_retryCache.Count}");
                        creds = response.Credentials;
                        break;
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    _providerSemaphore.Release();
                }
            }

            return(creds);
        }
 private void AddToCredentialCache(Uri uri, CredentialRequestType type, ICredentialProvider provider,
                                   CredentialResponse credentials)
 {
     _providerCredentialCache[CredentialsKeyHelper.GetCacheKey(uri, type, provider)] = credentials;
 }