We would like to use Kestrel to host our web-api. We must support both NTLM and Negotiate authentication.
That should be possible with Core 3.0 https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio
However when Kestrel responds to a challange only Negotiate scheme is returned. Has anyone managed to implement NTLM authentication with Kestrel?
The application runs on a Windows 10 machine
Basically we have followed the recommendations. First added Authentication to services:
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
and then added authentication to the pipeline
app.UseAuthentication();
Also in the pipeline we have our own middleware to ensure user has been validated
app.UseMiddleware<ValidateAuthentication>();
Implementation looks like this
internal class ValidateAuthentication : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.User.Identity.IsAuthenticated)
await next(context);
else
await context.ChallengeAsync();
}
}
Problem is that the challange response only has Negotiate
WWW-Authenticate Negotiate
I would have expected both NTLM and Negotiate
WWW-Authenticate NTLM, Negotiate
You can override the HandleChallengeAsync method and then replace the handler:
public sealed class NtlmNegotiateHandler : NegotiateHandler
{
public NtlmNegotiateHandler(
IOptionsMonitor<NegotiateOptions> options,
ILoggerFactory logger, UrlEncoder encoder,
ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
await base.HandleChallengeAsync(properties);
if (Response.StatusCode == StatusCodes.Status401Unauthorized)
{
Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, "NTLM");
}
}
}
public sealed class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
// replace the handler
var serviceDescriptor = new ServiceDescriptor(typeof(NegotiateHandler),
typeof(NtlmNegotiateHandler),
ServiceLifetime.Transient);
services.Replace(serviceDescriptor);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With