19

How to correctly implement Windows Authentication with Identity Server 4? Are there any samples to do that?

I looked at the source code of IdentityServer 4, and in the Host project in the AccountController, I noticed that there is Windows Authentication checks and they are implemented as an External Provider, but I can't seem to work out the configuration.

Has anybody successfully implemented windows authentication with idsrv4 and how?

Pang
  • 9,564
  • 146
  • 81
  • 122
The Tech Geek
  • 575
  • 1
  • 5
  • 16

4 Answers4

10

For anyone coming across this in search results that is having trouble meshing the quickstart with the ASPNET Identity quickstart, here are the missing pieces.

For the most part you want to use the ASPNET Identity code, utilizing the SignInManager to do the heavy lifting. Once you get there and add the Window auth code from the quick start, you should get to the point where everything looks like it is working, but you get null at this line in the callback:

ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();

To get Windows treated as a real External provider, instead of adding "scheme" to the auth properties around line 163, you want to change the key to "LoginProvider":

properties.Items.Add("LoginProvider", AccountOptions.WindowsAuthenticationSchemeName);

I use a domain query to pull extra info on my users, looks something like this:

using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
using (UserPrincipal up = UserPrincipal.FindByIdentity(pc, wp.Identity.Name))
{
    if (up == null)
    {
        throw new NullReferenceException($"Unable to find user: {wp.Identity.Name}");
    }

    id.AddClaim(new Claim(ClaimTypes.NameIdentifier, up.Sid.Value));
    id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name));
    id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
    id.AddClaim(new Claim(JwtClaimTypes.Email, up.EmailAddress));
    id.AddClaim(new Claim(Constants.ClaimTypes.Upn, up.UserPrincipalName));
    id.AddClaim(new Claim(JwtClaimTypes.GivenName, up.GivenName));
    id.AddClaim(new Claim(JwtClaimTypes.FamilyName, up.Surname));
}

What claims you add is up to you, but you NEED one of type ClaimTypes.NameIdentifier for the SigninManager to find. SID seems like the best use to me. Last thing to change is the SignInAsync call to use the correct scheme around line 178-181:

await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), properties);

Unless you are overriding the default Schemes that IdentityServer4 is using in .net core 2, this is the correct default scheme. And now your call to GetExternalLoginInfoAsync in the callback will work and you can continue on!

Ondrej
  • 1,209
  • 1
  • 11
  • 21
Dan
  • 1,101
  • 1
  • 9
  • 30
  • I am unfortunately scratching my head. My issue is that windows auth works when in local IIS Express however when deployed to full IIS on my machine it does not. I keep getting challenged with a login popup. Windows Authentication is enabled on the site in iis. – DataNerd May 08 '18 at 20:41
  • Would need more info to help you troubleshoot, best guess would be IIS authentication methods are setup incorrectly or it doesn't have access to the domain you are trying to authenticate with. – Dan May 10 '18 at 12:26
  • My questions are in https://stackoverflow.com/questions/50255110/identity-server-4-windows-authentication – DataNerd May 10 '18 at 12:51
  • @Dan Could you please tell me what is the type of wp object to get Identity.Name?? – Simple Code Dec 27 '18 at 14:38
  • Wp is a WindowsPrincipal Simple Code – Dan Dec 28 '18 at 20:22
6

There will be more documentation soon here:

https://identityserver4.readthedocs.io

But in short - yes from IdentityServer's point of view Windows authentication is an external provider (as opposed to the IS native authentication cookie).

There is nothing that YOU need to do to implement Windows authentication - just use a host that supports it.

That's either

  • Kestrel with IIS integration
  • WebListener

In both cases you invoke the Windows machinery by challenging a scheme of either Negotiate or NTLM. This is not IS specific - but the way ASP.NET Core works.

Our quick start UI shows how to do that - check the AccountController.

https://github.com/IdentityServer/IdentityServer4.Quickstart.UI

leastprivilege
  • 18,196
  • 1
  • 34
  • 50
  • thank you for the answer and some clarification. When you said 'you invoke the Windows machinery by challenging a scheme' did you meant to use app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions{AuthenticationScheme = "NTLM", ...}); if you can provide me with some directions I can help you out. I'm currently in a stage of my project where I implement IS but it is blocking the development on other tasks, so this with IS must be resolved fast. Also since Windows Authentication is external provider does it means that I need to have one more web application that only implements NTLM? – The Tech Geek Dec 21 '16 at 14:23
  • You call Challenge on the ASP.NET Core AuthenticationManager. As I said - this is not IS specific. – leastprivilege Dec 21 '16 at 14:51
  • see here: https://github.com/IdentityServer/IdentityServer4/blob/dev/src/Host/Quickstart/Account/AccountController.cs#L160 – leastprivilege Dec 21 '16 at 15:03
  • Is it possible to create something like this: https://github.com/IdentityServer/WindowsAuthentication for the IdentityServer4? – Jenan Dec 28 '16 at 17:38
  • Further to @leastprivilege if you checkany of the later quick start examples which have the external authentication providers setup i.e. google etc, Windows should automatically display as an available Authentication scheme if Windows Authentication is enabled. Put a break point on the AccountService in the BuildLoginViewModelAsync method and you will see the Windows option being populated. When I logged in with the Windows button, as opposed to an account I had previously registered it worked first time for me. – sarin Jan 17 '17 at 17:39
1

Issue:

Like me, you have probably ended up here after having followed all the ASP.NET Identity / IdentityServer 4 quickstarts and tutorials you can find in the hopes of getting your Windows Authentication working but failing with an exception of:

Exception: External authentication error
    Host.Quickstart.Account.ExternalController.Callback() in ExternalController.cs, line 89

You then may have discovered that result?.Succeeded is false after the call to HttpContext.AuthenticateAsync(...) in the Callback function and the rest of the results properties are null...


Explanation:

The reason for this is due to the fact that the authentication scheme being validated during the callback is IdentityConstants.ExternalScheme...

However, during the ProcessWindowsLoginAsync function the call to HttpContext.SignInAsync is setup to use the authentication scheme of IdentityServerConstants.ExternalCookieAuthenticationScheme, which doesn't match what the callback is expecting and in turn causes your Windows Authentication attempt to fail.


Solution:

So all we need to do to resolve this problem is to change the call to HttpContext.SignInAsync to match the scheme expected by the callback:

await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), props);

Having done this your login using Windows Authentication will be successful and your "victory dance" can begin!!!


Big thanks to Dan for his answer!

Without his solution I'd probably still be tearing hair out over this.

Dan also mentions that you should change Properties.Items["scheme"] to "LoginProvider"...

This is however unnecessary and will cause the FindUserFromExternalProviderAsync function to fail, as it expects the login provider to be supplied in the "scheme" property.

The IdentityServer quickstart source seems to have been updated since Dan posted his answer, so I thought it best to post an update for those of you facing the same issue.

0

In AccountOptions.cs of your Identity Server make sure that public static bool WindowsAuthenticationEnabled = true; , I think the quickstart has this defaulted to false

Make sure your app pool for the identity server is using an account with proper credentials (I'm assuming an account that can Query AD). I couldn't use the built-in accounts AppPoolIdentity, LocalService, or Network. LocalSystem almost worked but through another error.

Login at least once to this web server with the account you created above for the app pool. This account does not have to be any sort of administrator. Set the advanced settings on the app pool to load the profile.

Use Anonymous and Windows Credentials set in IIS on the root of your Identity Server, you don't need digest or basic.

Post Impatica
  • 14,999
  • 9
  • 67
  • 78