public override async Task <ICredential> GenerateCredentialAsync(InputArguments input)
        {
            ThrowIfDisposed();

            Uri uri = input.GetRemoteUri();

            // Determine the if the host supports Windows Integration Authentication (WIA)
            if (IsWindowsAuthAllowed)
            {
                if (PlatformUtils.IsWindows())
                {
                    Context.Trace.WriteLine($"Checking host '{uri.AbsoluteUri}' for Windows Integrated Authentication...");
                    bool isWiaSupported = await _winAuth.GetIsSupportedAsync(uri);

                    if (!isWiaSupported)
                    {
                        Context.Trace.WriteLine("Host does not support WIA.");
                    }
                    else
                    {
                        Context.Trace.WriteLine("Host supports WIA - generating empty credential...");

                        // WIA is signaled to Git using an empty username/password
                        return(new GitCredential(string.Empty, string.Empty));
                    }
                }
                else
                {
                    string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType;
                    Context.Trace.WriteLine($"Skipping check for Windows Integrated Authentication on {osType}.");
                }
            }
            else
            {
                Context.Trace.WriteLine("Windows Integrated Authentication detection has been disabled.");
            }

            Context.Trace.WriteLine("Prompting for basic credentials...");
            return(_basicAuth.GetCredentials(uri.AbsoluteUri, input.UserName));
        }
 /// <summary>
 /// Return a string that uniquely identifies the service that a credential should be stored against.
 /// </summary>
 /// <remarks>
 /// <para>
 /// This key forms part of the identifier used to retrieve and store credentials from the OS secure
 /// credential storage system. It is important the returned value is stable over time to avoid any
 /// potential re-authentication requests.
 /// </para>
 /// <para>
 /// The default implementation returns the absolute URI formed by from the <see cref="InputArguments"/>
 /// without any userinfo component. Any trailing slashes are trimmed.
 /// </para>
 /// </remarks>
 /// <param name="input">Input arguments of a Git credential query.</param>
 /// <returns>Credential service name.</returns>
 public virtual string GetServiceName(InputArguments input)
 {
     // By default we assume the service name will be the absolute URI based on the
     // input arguments from Git, without any userinfo part.
     return(input.GetRemoteUri(includeUser: false).AbsoluteUri.TrimEnd('/'));
 }
        public async Task <IHostProvider> GetProviderAsync(InputArguments input)
        {
            IHostProvider provider;

            //
            // Try and locate a specified provider
            //
            if (_context.Settings.ProviderOverride is string providerId)
            {
                _context.Trace.WriteLine($"Host provider override was set id='{providerId}'");

                if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.ProviderIdAuto, providerId))
                {
                    provider = _hostProviders
                               .SelectMany(x => x.Value)
                               .FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Id, providerId));

                    if (provider is null)
                    {
                        _context.Trace.WriteLine($"No host provider was found with ID '{providerId}'.. falling back to auto-detection.");
                        _context.Streams.Error.WriteLine($"warning: a host provider override was set but no such provider '{providerId}' was found. Falling back to auto-detection.");
                    }
                    else
                    {
                        return(provider);
                    }
                }
            }
            //
            // Try and locate a provider by supported authorities
            //
            else if (_context.Settings.LegacyAuthorityOverride is string authority)
            {
                _context.Trace.WriteLine($"Host provider authority override was set authority='{authority}'");
                _context.Streams.Error.WriteLine("warning: the `credential.authority` and `GCM_AUTHORITY` settings are deprecated.");
                _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAuthorityDeprecated} for more information.");

                if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.AuthorityIdAuto, authority))
                {
                    provider = _hostProviders
                               .SelectMany(x => x.Value)
                               .FirstOrDefault(x => x.SupportedAuthorityIds.Contains(authority, StringComparer.OrdinalIgnoreCase));

                    if (provider is null)
                    {
                        _context.Trace.WriteLine($"No host provider was found with authority '{authority}'.. falling back to auto-detection.");
                        _context.Streams.Error.WriteLine($"warning: a supported authority override was set but no such provider supporting authority '{authority}' was found. Falling back to auto-detection.");
                    }
                    else
                    {
                        return(provider);
                    }
                }
            }

            //
            // Auto-detection
            //
            _context.Trace.WriteLine("Performing auto-detection of host provider.");

            var uri           = input.GetRemoteUri();
            var queryResponse = new Lazy <Task <HttpResponseMessage> >(() =>
            {
                _context.Trace.WriteLine("Querying remote URL for host provider auto-detection.");
                return(HttpClient.HeadAsync(uri));
            });

            async Task <IHostProvider> MatchProviderAsync(HostProviderPriority priority)
            {
                if (_hostProviders.TryGetValue(priority, out ICollection <IHostProvider> providers))
                {
                    _context.Trace.WriteLine($"Checking against {providers.Count} host providers registered with priority '{priority}'.");

                    // Try matching using the static Git input arguments first (cheap)
                    if (providers.TryGetFirst(x => x.IsSupported(input), out IHostProvider match))
                    {
                        return(match);
                    }

                    HttpResponseMessage response = await queryResponse.Value;

                    // Try matching using the HTTP response from a query to the remote URL (expensive)
                    if (providers.TryGetFirst(x => x.IsSupported(response), out match))
                    {
                        return(match);
                    }
                }

                return(null);
            }

            // Match providers starting with the highest priority
            return(await MatchProviderAsync(HostProviderPriority.High) ??
                   await MatchProviderAsync(HostProviderPriority.Normal) ??
                   await MatchProviderAsync(HostProviderPriority.Low) ??
                   throw new Exception("No host provider available to service this request."));
        }
        public async Task <IHostProvider> GetProviderAsync(InputArguments input)
        {
            IHostProvider provider;

            //
            // Try and locate a specified provider
            //
            if (_context.Settings.ProviderOverride is string providerId)
            {
                _context.Trace.WriteLine($"Host provider override was set id='{providerId}'");

                if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.ProviderIdAuto, providerId))
                {
                    provider = _hostProviders
                               .SelectMany(x => x.Value)
                               .FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Id, providerId));

                    if (provider is null)
                    {
                        _context.Trace.WriteLine($"No host provider was found with ID '{providerId}'.. falling back to auto-detection.");
                        _context.Streams.Error.WriteLine($"warning: a host provider override was set but no such provider '{providerId}' was found. Falling back to auto-detection.");
                    }
                    else
                    {
                        return(provider);
                    }
                }
            }
            //
            // Try and locate a provider by supported authorities
            //
            else if (_context.Settings.LegacyAuthorityOverride is string authority)
            {
                _context.Trace.WriteLine($"Host provider authority override was set authority='{authority}'");
                _context.Streams.Error.WriteLine("warning: the `credential.authority` and `GCM_AUTHORITY` settings are deprecated.");
                _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAuthorityDeprecated} for more information.");

                if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.AuthorityIdAuto, authority))
                {
                    provider = _hostProviders
                               .SelectMany(x => x.Value)
                               .FirstOrDefault(x => x.SupportedAuthorityIds.Contains(authority, StringComparer.OrdinalIgnoreCase));

                    if (provider is null)
                    {
                        _context.Trace.WriteLine($"No host provider was found with authority '{authority}'.. falling back to auto-detection.");
                        _context.Streams.Error.WriteLine($"warning: a supported authority override was set but no such provider supporting authority '{authority}' was found. Falling back to auto-detection.");
                    }
                    else
                    {
                        return(provider);
                    }
                }
            }

            //
            // Auto-detection
            //
            _context.Trace.WriteLine("Performing auto-detection of host provider.");

            var uri = input.GetRemoteUri();

            var probeTimeout = TimeSpan.FromMilliseconds(_context.Settings.AutoDetectProviderTimeout);

            _context.Trace.WriteLine($"Auto-detect probe timeout is {probeTimeout.TotalSeconds} ms.");

            HttpResponseMessage probeResponse = null;

            async Task <IHostProvider> MatchProviderAsync(HostProviderPriority priority)
            {
                if (_hostProviders.TryGetValue(priority, out ICollection <IHostProvider> providers))
                {
                    _context.Trace.WriteLine($"Checking against {providers.Count} host providers registered with priority '{priority}'.");

                    // Try matching using the static Git input arguments first (cheap)
                    if (providers.TryGetFirst(x => x.IsSupported(input), out IHostProvider match))
                    {
                        return(match);
                    }

                    // Try matching using the HTTP response from a query to the remote URL (expensive).
                    // The user may have disabled this feature with a zero or negative timeout for performance reasons.
                    // We only probe the remote once and reuse the same response for all providers.
                    if (probeTimeout.TotalMilliseconds > 0)
                    {
                        if (probeResponse is null)
                        {
                            _context.Trace.WriteLine("Querying remote URL for host provider auto-detection.");

                            using (HttpClient client = _context.HttpClientFactory.CreateClient())
                            {
                                client.Timeout = probeTimeout;

                                try
                                {
                                    probeResponse = await client.HeadAsync(uri);
                                }
                                catch (TaskCanceledException)
                                {
                                    _context.Streams.Error.WriteLine($"warning: auto-detection of host provider took too long (>{probeTimeout.TotalMilliseconds}ms)");
                                    _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAutoDetect} for more information.");
                                }
                                catch (Exception ex)
                                {
                                    // The auto detect probing failed for some other reason.
                                    // We don't particular care why, but we should not crash!
                                    _context.Streams.Error.WriteLine($"warning: failed to probe '{uri}' to detect provider");
                                    _context.Streams.Error.WriteLine($"warning: {ex.Message}");
                                    _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAutoDetect} for more information.");
                                }
                            }
                        }

                        if (providers.TryGetFirst(x => x.IsSupported(probeResponse), out match))
                        {
                            return(match);
                        }
                    }
                }

                return(null);
            }

            // Match providers starting with the highest priority
            return(await MatchProviderAsync(HostProviderPriority.High) ??
                   await MatchProviderAsync(HostProviderPriority.Normal) ??
                   await MatchProviderAsync(HostProviderPriority.Low) ??
                   throw new Exception("No host provider available to service this request."));
        }