I have an ASP.NET Core 6 application that uses the Negotiate
authentication scheme to authenticate using the Windows authentication (NTLM/Kerberos).
I'm configuring the authentication service like this:
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(NegotiateDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
}
This works correctly on my local dev machine, however when I try to deploy it to a production server, something breaks.
In particular, once deployed if I try to call an endpoint marked with [Authorize]
the browser correctly shows me the login prompt, but after entering my credentials the backend responds with a generic 500 error.
One important thing I noticed is that if I try to access the app from within the server itself by accessing via https://localhost
the authentication works, but if I use the machine full machine URL, like: https://myserver.domain.local
, it doesn't work and I get the 500 error.
So this might be something related to a network proxy or something like that? How can I debug this properly? I tried enabling all logging down to the "debug" level, and this is what I see when I get the error:
Connection id "0HN6OHVHNEJKS", Request id "0HN6OHVHNEJKS:00000007": An unhandled exception was thrown by the application.
System.InvalidOperationException: An anonymous request was received in between authentication handshake requests.
at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
The stack trace seems to suggest I'm performing other connections during the handshake, but I checked in the browser Network panel and I don't have any extra pending connections.
Any ideas?
Well, I figured it out. Apparently if you are in a domain and you self-host the app as a Windows service you need to run this command, otherwise the service user cannot authenticate calls:
setspn -s HTTP/mymachine.mydomain.local mydomain\serviceuser
after this, it works correctly.
Interestingly, the official Microsoft Documentation on the subject says that this is only necessary in macOS/Linux environments, but I was on Windows Server and I still needed to do this.