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?
ASP.NET and ASP.NET Core differ quite vastly (and .NET Framework and .NET (Core) too). Current implementation uses
WindowsIdentity.RunImpersonatedInternalwhich has the following:And
ExecutionContext.SuppressFlow();will set current context to null, which results in theInvalidOperation_NullContext(i.e. theCannot call Set on a null contextmessage).It seems that you will need to dabble with conditional compilation, i.e. something like (not tested, maybe multitargeting will be required):
Read also:
Configure Windows Authentication in ASP.NET Core: Impersonation:
Support multiple .NET Framework versions in your project file
ASP.NET Core SynchronizationContext: There Isn’t One - one of the biggest changes in ASP.NET Core - removal of the synchronization context, not sure but maybe this is related.