Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,13 @@ http_get_authentication_information(
)
{
*pstrAuthType = SysAllocString(pInProcessHandler->QueryHttpContext()->GetUser()->GetAuthenticationType());
// prefer GetPrimaryToken over GetImpersonationToken as that's what we've been using since before .NET 10
// we'll fallback to GetImpersonationToken if GetPrimaryToken is not available
*pvToken = pInProcessHandler->QueryHttpContext()->GetUser()->GetPrimaryToken();
if (*pvToken == nullptr)
{
*pvToken = pInProcessHandler->QueryHttpContext()->GetUser()->GetImpersonationToken();
}

return S_OK;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,11 +819,20 @@ FORWARDING_HANDLER::GetHeaders(
(_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"negotiate") == 0 ||
_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"ntlm") == 0))
{
if (m_pW3Context->GetUser()->GetPrimaryToken() != nullptr &&
m_pW3Context->GetUser()->GetPrimaryToken() != INVALID_HANDLE_VALUE)
// prefer GetPrimaryToken over GetImpersonationToken as that's what we've been using since before .NET 10
// we'll fallback to GetImpersonationToken if GetPrimaryToken is not available
HANDLE authToken = m_pW3Context->GetUser()->GetPrimaryToken();
if (authToken == nullptr ||
authToken == INVALID_HANDLE_VALUE)
{
authToken = m_pW3Context->GetUser()->GetImpersonationToken();
}

if (authToken != nullptr &&
authToken != INVALID_HANDLE_VALUE)
{
HANDLE hTargetTokenHandle = nullptr;
RETURN_IF_FAILED(pServerProcess->SetWindowsAuthToken(m_pW3Context->GetUser()->GetPrimaryToken(),
RETURN_IF_FAILED(pServerProcess->SetWindowsAuthToken(authToken,
&hTargetTokenHandle));

//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public RequiresIISAttribute(IISCapability capabilities)
IsMet &= available;
if (!available)
{
SkipReason += $"The machine does have {module.Capability} available.";
SkipReason += $"The machine does not have {module.Capability} available.";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
Expand Down Expand Up @@ -58,4 +60,51 @@ public async Task WindowsAuthTest(TestVariant variant)
Assert.StartsWith("Windows:", responseText);
Assert.Contains(Environment.UserName, responseText);
}

[ConditionalTheory]
[RequiresIIS(IISCapability.WindowsAuthentication)]
[MemberData(nameof(TestVariants))]
public async Task WindowsAuthWithImpersonationLevelTest(TestVariant variant)
{
var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);
deploymentParameters.SetAnonymousAuth(enabled: false);
deploymentParameters.SetWindowsAuth();

// The default in hosting sets windows auth to true.
var deploymentResult = await DeployAsync(deploymentParameters);

var impersonationLevels = new TokenImpersonationLevel[]
{
TokenImpersonationLevel.None,
TokenImpersonationLevel.Identification,
TokenImpersonationLevel.Impersonation,
TokenImpersonationLevel.Delegation,
TokenImpersonationLevel.Anonymous
};

foreach (var impersonationLevel in impersonationLevels)
{
// TokenImpersonationLevel is not supported by HttpClient so we need to use HttpWebRequest to test it.
#pragma warning disable SYSLIB0014 // Type or member is obsolete
var request = HttpWebRequest.CreateHttp($"{deploymentResult.HttpClient.BaseAddress}Auth");
#pragma warning restore SYSLIB0014 // Type or member is obsolete
request.ImpersonationLevel = impersonationLevel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say ImpersonationLevel isn't supported, but went to check the source code in case anything has changed and was surprised to see that they use reflection to enable it for HttpWebRequest, even though HttpClientHandler/SocketsHttpHandler don't expose the api. Time to open an issue as this is an important missing feature that WCF needs.

request.Method = "GET";
request.UseDefaultCredentials = true;

using var response = request.GetResponse();
using var reader = new StreamReader(response.GetResponseStream());
var responseText = await reader.ReadToEndAsync();

try
{
Assert.StartsWith("Windows:", responseText);
Assert.Contains(Environment.UserName, responseText);
}
catch (Exception ex)
{
Assert.Fail($"'TokenImpersonationLevel.{impersonationLevel}' failed with: {ex.Message}");
}
}
}
}
Loading