Windows Authentication
There are several ways how you can enable Windows authentication in ASP.NET Core (and thus in your IdentityServer).
- On Windows using IIS hosting (both in- and out-of process)
- On Windows using HTTP.SYS hosting
- On any platform using the Negotiate authentication handler (added in ASP.NET Core 3.0)
See the Microsoft documentation for additional information.
On Windows Using IIS hosting
Section titled “On Windows Using IIS hosting”The typical ASP.NET Core CreateDefaultBuilder
host setup enables support for IIS-based Windows authentication when hosting in IIS.
Make sure that Windows authentication is enabled in launchSettings.json
or your IIS configuration.
The IIS integration layer will configure a Windows authentication handler into the ASP.NET Core service provider that can be invoked via the authentication service. Typically, in your IdentityServer it is advisable to disable the automatic behavior.
This is done in ConfigureServices
(details vary depending on in-proc vs out-of-proc hosting)::
// configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882)builder.Services.Configure<IISOptions>(iis =>{ iis.AuthenticationDisplayName = "Windows"; iis.AutomaticAuthentication = false;});
// ...or configures IIS in-proc settingsbuilder.Services.Configure<IISServerOptions>(iis =>{ iis.AuthenticationDisplayName = "Windows"; iis.AutomaticAuthentication = false;});
You trigger Windows authentication by calling ChallengeAsync
using the Windows
scheme (or if you want to use a constant: Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme
).
This will send the Www-Authenticate
header back to the browser which will then re-load the current URL including the Windows identity.
You can tell that Windows authentication was successful, when you call AuthenticateAsync
on the Windows
scheme and the principal returned
is of type WindowsPrincipal
.
The principal will have information like user and group SID and the Windows account name. The following snippet shows how to
trigger authentication, and if successful convert the information into a standard ClaimsPrincipal
for the temp-Cookie approach::
private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl){ // see if windows auth has already been requested and succeeded var result = await HttpContext.AuthenticateAsync("Windows"); if (result?.Principal is WindowsPrincipal wp) { // we will issue the external cookie and then redirect the // user back to the external callback, in essence, treating windows // auth the same as any other external authentication mechanism var props = new AuthenticationProperties() { RedirectUri = Url.Action("Callback"), Items = { { "returnUrl", returnUrl }, { "scheme", "Windows" }, } };
var id = new ClaimsIdentity("Windows");
// the sid is a good sub value id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));
// the account name is the closest we have to a display name id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
// add the groups as claims -- be careful if the number of groups is too large var wi = wp.Identity as WindowsIdentity;
// translate group SIDs to display names var groups = wi.Groups.Translate(typeof(NTAccount)); var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); id.AddClaims(roles);
await HttpContext.SignInAsync( IdentityServerConstants.ExternalCookieAuthenticationScheme, new ClaimsPrincipal(id), props); return Redirect(props.RedirectUri); } else { // trigger windows auth // since windows auth don't support the redirect uri, // this URL is re-triggered when we call challenge return Challenge("Windows"); }}
A sample is provided here.