Exception when using ExecutionContext.SuppressFlow() in .NET 7

101 Views Asked by At

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

There are 1 best solutions below

1
Guru Stron On BEST ANSWER

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: