7

I am trying to configure Windows authentication on a subroute only in my ASP.NET Core MVC app.

My problem is that when I add

services.AddAuthentication().AddNegotiate()

I get an error

The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication.

which lead me to adding web.config as the docs explained:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <location path="." inheritInChildApplications="false">
        <system.webServer>
            <security>
                <authentication>
                    <anonymousAuthentication enabled="false" />
                    <windowsAuthentication enabled="true" />
                </authentication>
            </security>
        </system.webServer>
    </location>
</configuration>

and the error goes away. However, now the Windows authentication is popping up on each request.

I tried changing the location path to .testendpoint but that then throws the original error at the base path.

So, is it possible and how do I make such only /testendpoint will ask for Windows authentication and the remaining of the application will work with whatever other auth I configured in my ASP.NET Core app?

3
  • Judging from my very humble knowledge gathered through a similar research a few weeks ago (I was facing a similar scenario in which the application should fall back to cookie authentication if Negotiation/NTLM fails) I don't think it is possible at all as Windows Authentication happens on server rather than application level. But i would more than happy to be proven incorrect. Commented Aug 25, 2021 at 5:56
  • I am making progress, annonymousauthentication=true also allows the server to pass to and use cookie if it fails at the server level, and form aspnet core i can challenge the negotiate auth schema and trigger the windows authentication which comes back with a claims principal for ntlm. So i am basically making a login route that challenge and signs in the application cookie based on the info i get form ntlm Commented Aug 25, 2021 at 16:45
  • I came back to tampering with it myself and @pfx's is definitely the right answer here. Works like a charm on IISExpress as long as ´Anonymous´ AND ´Windows´ authentication are enabled (tested on IISExpress with customized Cookie- and Negotiate authentication). The manual way to invoke the scheme's challenge from inside a middleware or similar is shown below as well. Commented Aug 26, 2021 at 13:35

3 Answers 3

10

Just thought I'd share this tidbit of information:

First off, just because you installed Windows Authentication with Server Manager, doesn't mean it's enabled in IIS. It's NOT enabled, by default.
You have to open IIS Manager, click on your server (NOT the website - the name of the server machine hosting IIS). Then click on Authentication - you will see "Windows Authentication" is disabled. Enable it. Now it will work.

Check this is correctly set first, before making other config changes. The default project for dotNet5 and dotNet6 will work w/o any modifications if IIS is correctly configured for Windows Authentication.

Sign up to request clarification or add additional context in comments.

Comments

6

Another way using endpoint routing:

We have an application schema for the application that will be used all over the app called eavfw.

Using a custom endpoint here called login/ntlm with metadata new AuthorizeAttribute(NegotiateDefaults.AuthenticationScheme) its only allowed to be visited by a valid windows authenticated user.

Here we then create the user in our DB using its AD username.

endpoints.MapGet("/.auth/login/ntlm", async httpcontext =>
{
    var loggger = httpcontext.RequestServices.GetRequiredService<ILogger<Startup>>();
    var windowsAuth = await httpcontext.AuthenticateAsync(NegotiateDefaults.AuthenticationScheme);
    
    if (!windowsAuth.Succeeded)
    {
        loggger.LogWarning("Not authenticated: Challening"); 
    }
    if (windowsAuth.Succeeded)
    {
        loggger.LogWarning("Authenticated");
       
        var name = string.Join("\\", windowsAuth.Principal.Claims.FirstOrDefault(c => c.Type.EndsWith("name")).Value.Split("\\").Skip(1));

        var context = httpcontext.RequestServices.GetRequiredService<DynamicContext>();
        var users = context.Set<SystemUser>();
        var user = await context.Set<SystemUser>().Where(c => c.PrincipalName == name).FirstOrDefaultAsync();
        if (user == null)
        {
            user = new SystemUser
            {
                PrincipalName = name,
                Name = name,
                // Email = email,
            };
            await users.AddAsync(user);
            await context.SaveChangesAsync();
        }
         
        var principal = new ClaimsPrincipal(new ClaimsIdentity(new Claim[] {                                  
                   new Claim(Claims.Subject,user.Id.ToString())
                }, "ntlm"))
        {

        };

        await httpcontext.SignInAsync("ntlm",
             principal, new AuthenticationProperties(
                        new Dictionary<string, string>
                        {
                            ["schema"] = "ntlm"
                        }));

        httpcontext.Response.Redirect("/account/login/callback");
    }
}).WithMetadata(new AuthorizeAttribute(NegotiateDefaults.AuthenticationScheme));

using a auxility authentication cookie, we can now make it such that specific areas of our app that requires windows authentication, it can simply rely on Authorize("ntlm") as it automatically forward the authenticate call to check if already signin, and it as part of the signin call in the endpoint above actually sign in eavfw.external before it redirects to the general account callback page that will do some final validation before signing in eavfw from the eavfw.external cookie

services.AddAuthentication().AddCookie("ntlm", o => {
    o.LoginPath = "/.auth/login/ntlm";
    o.ForwardSignIn = "eavfw.external";
    o.ForwardAuthenticate = "eavfw";
});

So there are a few ways to extend and use the authentication system in auth core depending on how MVC framework heavy your application is.

3 Comments

Super helpful and fully functional with some project alterations. This helped me a ton. I was using websockets, so the whole MVC thing doesn't even exist. Being able to take care of this using Middleware is precisely what I needed, and isn't documented anywhere it seems. Thank you!
@Barry can you share the working statup file please
@MuhammadNasir Apologies on delay, didn't see this 'til now. Certainly, here's a repo for sample purposes (not complete project code; but useful to this topic, relevant code is there): github.com/ensemblebd/sample-netcore5-ntlm-nomvc
2

In order to have a certain page/action method secured via Windows authentation, specify the corresponding authentication scheme in the action methods Authorize attribute.

[Authorize(AuthenticationSchemes = IISServerDefaults.AuthenticationScheme)]
public IActionResult UsingWindowsAuthentication()

Make sure to have Windows authentication enabled on your website.
In order to use other authentication schemes, e.g. "Individual Accounts", anonymous authentication is also enabled.

The controllers and/or action methods that must not use Windows Authentication have the default scheme specified.
For example, for an ASP.NET Core MVC project that uses the out of the box "Individual Accounts" authentication type as default authentication method, that is Identity.Application.

[Authorize(AuthenticationSchemes = "Identity.Application")]
public IActionResult Index()

See the documentation about how to set up and configure multiple authentication schemes.

4 Comments

Is it possible to form a disjunction between these schemes? For instance attempting Windows-Authentication based on certain conditions, and if that fails to complete whithout prompt, initiating the challenge of the the second scheme?
@Beltway That linked article shows that an AuthorizeAttribute can have more than 1 scheme, and all get a chance to handle the request. I assume these run in that order. Any other rules might be set up via a custom AuthorizAttribute or maybe policy requirements.
policy.AuthenticationSchemes.Add({schemeDefaults}.AuthenticationScheme); was exactly was I was looking for. I omitted this earlier as it did not appear to execute the correct scheme, which was merely due to Edge not deleting cookies properly and showing false claims. Thanks for pointing me back there, that helped a lot!
where you able to configure windows authentication with .net 6.0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.