async Task <ICredentials> PromptForCredentialsAsync(
            CredentialType type,
            AmbientAuthenticationState authState,
            CancellationToken token)
        {
            ICredentials promptCredentials;

            try {
                // Only one prompt may display at a time.
                await credentialPromptLock.WaitAsync();

                // Get the proxy for this URI so we can pass it to the credentialService methods
                // this lets them use the proxy if they have to hit the network.
                var proxyCache = WebRequestHelper.ProxyCache;
                var proxy      = proxyCache?.GetProxy(source);

                bool isRetry = authState.AuthenticationRetriesCount > 0;
                promptCredentials = await credentialService.GetCredentialsAsync(source, proxy, type, isRetry, token);

                if (promptCredentials == null)
                {
                    // If this is the case, this means none of the credential providers were able to
                    // handle the credential request or no credentials were available for the
                    // endpoint.
                    authState.Block();
                }
                else
                {
                    authState.Increment();
                }
            } catch (OperationCanceledException) {
                // This indicates a non-human cancellation.
                throw;
            } catch (Exception) {
                // If this is the case, this means there was a fatal exception when interacting
                // with the credential service (or its underlying credential providers). Either way,
                // block asking for credentials for the live of this operation.
                promptCredentials = null;
                authState.Block();
            } finally {
                credentialPromptLock.Release();
            }

            return(promptCredentials);
        }
        protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response          = null;
            ICredentials        promptCredentials = null;
            var authState = new AmbientAuthenticationState();

            // Authorizing may take multiple attempts
            while (true)
            {
                // Clean up any previous responses
                if (response != null)
                {
                    response.Dispose();
                }

                // store the auth state before sending the request
                var beforeLockVersion = credentials.Version;

                response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

                if (credentialService == null)
                {
                    return(response);
                }

                if (response.StatusCode == HttpStatusCode.Unauthorized ||
                    response.StatusCode == HttpStatusCode.Forbidden)
                {
                    promptCredentials = await AcquireCredentialsAsync(
                        authState,
                        beforeLockVersion,
                        cancellationToken);

                    if (promptCredentials == null)
                    {
                        return(response);
                    }

                    continue;
                }

                return(response);
            }
        }
        async Task <ICredentials> AcquireCredentialsAsync(AmbientAuthenticationState authState, Guid credentialsVersion, CancellationToken cancellationToken)
        {
            try {
                // Only one request may prompt and attempt to auth at a time
                await httpClientLock.WaitAsync();

                cancellationToken.ThrowIfCancellationRequested();

                // Auth may have happened on another thread, if so just continue
                if (credentialsVersion != credentials.Version)
                {
                    return(credentials.Credentials);
                }

                if (authState.IsBlocked)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    return(null);
                }

                var promptCredentials = await PromptForCredentialsAsync(
                    CredentialType.RequestCredentials,
                    authState,
                    cancellationToken);

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

                credentials.Credentials = promptCredentials;

                return(promptCredentials);
            } finally {
                httpClientLock.Release();
            }
        }