Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 6d031a0

Browse files
authored
Check available credentials when choosing authentication scheme (#28105)
When choosing what authentication scheme to use, WinHttpHandler picks the most secure scheme supported by the server. When the client does not support this scheme it closes the connection. This fix updates the authentication logic to instead pick the most secure scheme offered by the server that is also supported by the client. This change also adds a test of the relevant behavior. Fixes: #27672
1 parent 4e7b366 commit 6d031a0

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

src/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public void CheckResponseForAuthentication(
102102
// But we can validate with assert.
103103
Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER);
104104

105-
serverAuthScheme = ChooseAuthScheme(supportedSchemes);
105+
serverAuthScheme = ChooseAuthScheme(supportedSchemes, state.RequestMessage.RequestUri, state.ServerCredentials);
106106
if (serverAuthScheme != 0)
107107
{
108108
if (SetWinHttpCredential(
@@ -155,7 +155,7 @@ public void CheckResponseForAuthentication(
155155
// But we can validate with assert.
156156
Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY);
157157

158-
proxyAuthScheme = ChooseAuthScheme(supportedSchemes);
158+
proxyAuthScheme = ChooseAuthScheme(supportedSchemes, state.Proxy.GetProxy(state.RequestMessage.RequestUri), proxyCreds);
159159
state.RetryRequest = true;
160160
break;
161161

@@ -371,11 +371,16 @@ private bool SetWinHttpCredential(
371371
return true;
372372
}
373373

374-
private static uint ChooseAuthScheme(uint supportedSchemes)
374+
private static uint ChooseAuthScheme(uint supportedSchemes, Uri uri, ICredentials credentials)
375375
{
376+
if (credentials == null)
377+
{
378+
return 0;
379+
}
380+
376381
foreach (uint authScheme in s_authSchemePriorityOrder)
377382
{
378-
if ((supportedSchemes & authScheme) != 0)
383+
if ((supportedSchemes & authScheme) != 0 && credentials.GetCredential(uri, s_authSchemeStringMapping[authScheme]) != null)
379384
{
380385
return authScheme;
381386
}

src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Authentication.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public abstract class HttpClientHandler_Authentication_Test : HttpClientTestBase
1919

2020
private static readonly NetworkCredential s_credentials = new NetworkCredential(Username, Password, Domain);
2121

22-
private static readonly Func<HttpClientHandler, Uri, HttpStatusCode, NetworkCredential, Task> s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
22+
private static readonly Func<HttpClientHandler, Uri, HttpStatusCode, ICredentials, Task> s_createAndValidateRequest = async (handler, url, expectedStatusCode, credentials) =>
2323
{
2424
handler.Credentials = credentials;
2525

@@ -90,6 +90,32 @@ await LoopbackServer.CreateServerAsync(async (server, url) =>
9090
}, options);
9191
}
9292

93+
[Theory]
94+
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: NTLM\r\n", "Basic", "Negotiate")]
95+
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\nWWW-Authenticate: Digest realm=\"hello\", nonce=\"hello\", algorithm=MD5\r\nWWW-Authenticate: NTLM\r\n", "Digest", "Negotiate")]
96+
public async Task HttpClientHandler_MultipleAuthenticateHeaders_PicksSupported(string authenticateHeader, string supportedAuth, string unsupportedAuth)
97+
{
98+
if (PlatformDetection.IsWindowsNanoServer || (IsCurlHandler && authenticateHeader.Contains("Digest")))
99+
{
100+
// TODO: #28065: Fix failing authentication test cases on different httpclienthandlers.
101+
return;
102+
}
103+
104+
var options = new LoopbackServer.Options { Domain = Domain, Username = Username, Password = Password };
105+
await LoopbackServer.CreateServerAsync(async (server, url) =>
106+
{
107+
HttpClientHandler handler = CreateHttpClientHandler();
108+
handler.UseDefaultCredentials = false;
109+
110+
var credentials = new CredentialCache();
111+
credentials.Add(url, supportedAuth, new NetworkCredential(Username, Password, Domain));
112+
credentials.Add(url, unsupportedAuth, new NetworkCredential(Username, Password, Domain));
113+
114+
Task serverTask = server.AcceptConnectionPerformAuthenticationAndCloseAsync(authenticateHeader);
115+
await TestHelper.WhenAllCompletedOrAnyFailed(s_createAndValidateRequest(handler, url, HttpStatusCode.OK, credentials), serverTask);
116+
}, options);
117+
}
118+
93119
[Theory]
94120
[InlineData("WWW-Authenticate: Basic realm=\"hello\"\r\n")]
95121
[InlineData("WWW-Authenticate: Digest realm=\"hello\", nonce=\"testnonce\"\r\n")]

0 commit comments

Comments
 (0)