I have a similar scenario for an ASP.NET Core 2.0 application (use Windows Authentication throughout the app except a single controller) and Daboul's explanation was not enough.
I had to set up a custom middleware as indicated here since anonymous takes precedence.
The middleware
public class NtlmAndAnonymousSetupMiddleware
{
private readonly RequestDelegate next;
public NtlmAndAnonymousSetupMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated || context.Request.Path.ToString().StartsWith("/Anonymous"))
{
await next(context);
return;
}
await context.ChallengeAsync("Windows");
}
}
and its usage in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMiddleware<NtlmAndAnonymousSetupMiddleware>();
// other code here
}
So, the middleware accept anonymous requests for AnonymousController only and will provide a challenge if Windows Authentication info is not provided.
Anonymous controller
Since the middleware makes the differece between what is anonymous and requires authentication, this will look just like any ordinary controller:
[Route("Anonymous")]
public class AnonymousController : Controller
{
[HttpGet("Echo")]
public string Echo(string data)
{
return data;
}
}
Tests
(all done on a Windows machine)
Chrome + access non-anonymous controller action => works fine (both @User.Identity.Name
and @Context.User.Identity.Name
return the correct user
Chrome + anonymous action => works directly
Firefox (which does not directly transfer NTLM ticket from OS) + non-anonymous => a modal asks for user/pass => if provided correctly, it works fine
Firefox + anonymous action => works directly