5

My WebApi client project is loosing the WindowsIdentity of the current user when executing SendAsync Method from HttpClient.

HttpContext.User returns the application pool identity. Not the impersonated WindowsIdentity.

The client project is .NET Standard, so it can be used in .NET Framework and .NET Core.

When using RestSharp all works without problems and ExecutionContext.SuppressFlow() is not needed. (I can´t use RestSharp because the clients are generated by NSwag.)

.NET Framework:

When using ExecutionContext.SuppressFlow() the Identity will be correctly delegating to the server.

.NET Core:

When using .NET 7 following exception will be thrown:

Cannot call Set on a null context

Exception:

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Security.Principal.WindowsIdentity.RunImpersonatedInternal(SafeAccessTokenHandle token, Action action)
at System.Security.Principal.WindowsIdentity.GetName()
at System.Net.Http.CurrentUserIdentityProvider.GetIdentity()
at System.Net.Http.HttpConnectionPoolManager.GetConnectionKey(HttpRequestMessage request, Uri proxyUri, Boolean isProxyConnect)
at System.Net.Http.HttpConnectionPoolManager.SendAsyncCore(HttpRequestMessage request, Uri proxyUri, Boolean async, Boolean doRequestAuth, Boolean isProxyConnect, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPoolManager.SendAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.HttpAuthenticatedConnectionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpMessageHandlerStage.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.<SendAsync>d__4.MoveNext()
at System.Net.Http.HttpClient.<<SendAsync>g__Core|83_0>d.MoveNext()

Minimal code snippet:

UriBuilder uriBuilder = new()
{
    Scheme = Uri.UriSchemeHttps,
    Host = "localhost",
    Port = 7042
};

var urlBuilder = new StringBuilder();
urlBuilder.Append(uriBuilder.ToString() + "/WeatherForecast");

HttpClientHandler handler = new()
{
    UseDefaultCredentials = true,
    PreAuthenticate = true
};
         
HttpClient client = new(handler);
using var request = new HttpRequestMessage();
request.Method = new HttpMethod("GET");
var url = urlBuilder.ToString();
request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute);

ExecutionContext.SuppressFlow();
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);

Does the ExcecutionContext need to be used differently in .NET Core?

1
  • You're using a completely different runtime built to run on Linux and MacOS, not just Windows. Besides, your existing Web API code wouldn't impersonate the caller either without explicit configuration. Commented Aug 29, 2023 at 10:35

1 Answer 1

1

ASP.NET and ASP.NET Core differ quite vastly (and .NET Framework and .NET (Core) too). Current implementation uses WindowsIdentity.RunImpersonatedInternal which has the following:

ExecutionContext? currentContext = ExecutionContext.Capture();

// Run everything else inside of ExecutionContext.Run, so that any EC changes will be undone
// on the way out.
ExecutionContext.Run(currentContext, ...)

And ExecutionContext.SuppressFlow(); will set current context to null, which results in the InvalidOperation_NullContext (i.e. the Cannot call Set on a null context message).

It seems that you will need to dabble with conditional compilation, i.e. something like (not tested, maybe multitargeting will be required):

#if NETFRAMEWORK
    ExecutionContext.SuppressFlow();
#endif

Read also:

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. Conditional compilation seems to be working.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.