diff --git a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs index 0635ed2ec2eb..d985c46f3eac 100644 --- a/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs +++ b/src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs @@ -102,7 +102,7 @@ public void CheckResponseForAuthentication( // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER); - serverAuthScheme = ChooseAuthScheme(supportedSchemes); + serverAuthScheme = ChooseAuthScheme(supportedSchemes, state.RequestMessage.RequestUri, state.ServerCredentials); if (serverAuthScheme != 0) { if (SetWinHttpCredential( @@ -155,7 +155,7 @@ public void CheckResponseForAuthentication( // But we can validate with assert. Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY); - proxyAuthScheme = ChooseAuthScheme(supportedSchemes); + proxyAuthScheme = ChooseAuthScheme(supportedSchemes, state.Proxy.GetProxy(state.RequestMessage.RequestUri), proxyCreds); state.RetryRequest = true; break; @@ -371,11 +371,16 @@ private bool SetWinHttpCredential( return true; } - private static uint ChooseAuthScheme(uint supportedSchemes) + private static uint ChooseAuthScheme(uint supportedSchemes, Uri uri, ICredentials credentials) { + if (credentials == null) + { + return 0; + } + foreach (uint authScheme in s_authSchemePriorityOrder) { - if ((supportedSchemes & authScheme) != 0) + if ((supportedSchemes & authScheme) != 0 && credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]) != null) { return authScheme; } diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs index 0eada261f433..70f7b95a4754 100644 --- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs +++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs @@ -19,7 +19,7 @@ public abstract class HttpClientHandler_Authentication_Test : HttpClientTestBase private static readonly NetworkCredential s_credentials = new NetworkCredential(Username, Password, Domain); - private static readonly Func s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) => + private static readonly Func s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) => { handler.Credentials = credentials; @@ -90,6 +90,32 @@ await LoopbackServer.CreateServerAsync(async (server, url) => }, options); } + [Theory] + [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: NTLM\r\n", "Basic", "Negotiate")] + [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: NTLM\r\n", "Digest", "Negotiate")] + public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth) + { + if (PlatformDetection.IsWindowsNanoServer || (IsCurlHandler && authenticateHeader.Contains("Digest"))) + { + // TODO: #28065: Fix failing authentication test cases on different httpclienthandlers. + return; + } + + var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password }; + await LoopbackServer.CreateServerAsync(async (server, url) => + { + HttpClientHandler handler = CreateHttpClientHandler(); + handler.UseDefaultCredentials = false; + + var credentials = new CredentialCache(); + credentials.Add(url, supportedAuth, new NetworkCredential(Username, Password, Domain)); + credentials.Add(url, unsupportedAuth, new NetworkCredential(Username, Password, Domain)); + + Task serverTask = server.AcceptConnectionPerformAuthenticationAndCloseAsync(authenticateHeader); + await TestHelper.WhenAllCompletedOrAnyFailed(s_createAndValidateRequest(handler, url, HttpStatusCode.OK, credentials), serverTask); + }, options); + } + [Theory] [InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\n")] [InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\n")]