Search code examples
c#asp.net-coreauthentication.net-6.0windows-authentication

Handshake error in ASP.NET Core 6 application using Negotiate authentication


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?


Solution

  • 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.