20

I am running an ASP.NET Core application hosted in a Windows service as described here:

https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-2.1

I need this application to support Windows Authentication. What are my options? I tried using Application Request Routing / URL Rewrite module in IIS as a reverse proxy with Windows Authentication but could not figure out how to make that work. Any guidance would be appreciated.

Sandy
  • 1,284
  • 2
  • 14
  • 32

3 Answers3

23

There are different web servers that can be used with a .NET Core Web Application:

  • IIS Express (when pressing F5 in Visual Studio)
    Supports NTLM, Negotiate (Kerberos)
    Windows only
  • IIS (when deploying to an IIS Folder)
    Supports NTLM, Negotiate
    Windows only
  • Kestrel (when using "dotnet run" or executing from the command line)
    Supports Negotiate (with a nuget package, see Yush0s reply)
    Windows / Linux
  • http.sys (Like kestrel but configured in the Startup.cs)
    Supports NTLM, Negotiate
    Windows only

Windows authentication in IIS / IIS Express works without problems.

Kestrel can only use Negotiate (Kerberos). This means you need to setup a trusted connection with a service principle name (SPN). This can be done with the setspn command line tool. Unfortunally I have no experience in it because on a development machine you tend to skip that.

http.sys can use NTLM but is not compatible with IIS / IIS Express. This means you can't use Visual Studio debugging when using this. As a workarround you can add an environment variable that decides to use http.sys or not. For example add the following line to the "Project" profile in the launchSettings.json:

  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "USE_HTTP_SYS": "true"
  }

Now it's possible to conditional use http.sys or not:

 public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
        {
            if (bool.Parse(Environment.GetEnvironmentVariable("USE_HTTP_SYS") ?? "false"))
            {
                webBuilder.UseHttpSys(options =>
                {
                    options.Authentication.Schemes = AuthenticationSchemes.NTLM;
                    options.Authentication.AllowAnonymous = false;
                });
            }

            webBuilder.UseStartup<Startup>();
        });

As a sidenote, since .NET Core 3.0 there is a new interface "IHostBuilder" instead of "IWebHostBuilder" (only exists for backward compatibility).


Sadly, when you use "dotnet run" and goto the website you might see an error message:

  • Internet Explorer: Can't connect securely to this page
  • Chrome: This site can't be reached
  • Firefox: Unable to connect

Kestrel brings it's own certificate management. It runs in user mode and looks for the certificates at "CurrentUser\My". In constrast http.sys is kernel mode, which means the current user is not known. http.sys looks for the certificate at "LocalMachine\My".

Because http.sys doesn't know which certificate is used on the port, you also need to assign the certificate to the https port of the .net application. This needs to be done via PowerShell as a local administrator:

$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where { $_.Subject -eq "CN=localhost" } | Select -First 1
netsh http add sslcert ipport=0.0.0.0:5001 appid='{12345678-1234-43f8-a778-f05f3c6b9709}' certhash=$($cert.Thumbprint)

Note that "CN=localhost" is the uri, "0.0.0.0:5001" is the port of the dotnet app and the appid is a random identifier (if your app has an guid you can use that too, but it's not required).

If you don't have a certificate (e.g. for development) you might create a self signed certificate for the machine (Win10 and admin right required):

$rootextension = [System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]::new($true, $true, 0, $true)
$cert = New-SelfSignedCertificate -Subject "CN=localhost" -FriendlyName "Development localhost Certificate" -Extension $rootextension -NotAfter ([DateTime]::Now).AddYears(10) -KeyUsage DigitalSignature,KeyEncipherment,DataEncipherment -CertStoreLocation "Cert:\LocalMachine\My" -KeyExportPolicy Exportable
GreyMana
  • 241
  • 2
  • 4
18

With .Net Core 3.0 you can use Windows Authentication with Kestrel. There is a Nuget Package for it: Microsoft.AspNetCore.Authentication.Negotiate

You then can add it in Startup.ConfigureServices:

services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
   .AddNegotiate();

For more Information also see:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio#kestrel

Community
  • 1
  • 1
Yush0
  • 1,547
  • 18
  • 22
  • I have followed the instructions on your link, but for some reason it will not allow NTLM. Only Negotiate. Do you have any ideas why? – Torben Nielsen Oct 08 '19 at 06:44
  • are you running you app under windows or linux? with windows try running the service with a domain user. – Yush0 Oct 10 '19 at 12:42
  • Windows 10. I am running it with a domain user. – Torben Nielsen Oct 10 '19 at 16:10
  • you also have app.UseAuthentication();? I will test it further as soon as we can upgrade our prod app to .net core 3 – Yush0 Oct 17 '19 at 08:23
  • I want to add a route called `/login` that will get the username and password from a frontend client... let's say from [FromBody]. Which method should I call to authenticate the user using the received credentials? – Foolish and Hungry Feb 12 '20 at 22:46
  • Hi @Yush0 .. have you managed it to work or you just copied what they say in the documentation? I research this issue in here: https://stackoverflow.com/questions/60296237/windows-authentication-in-linux-docker-container could you please help me to set it up? – Scholtz Mar 04 '20 at 11:30
  • @Scholtz: I didn't try it out with docker images, but ran into problems with them in the past regarding joining the domain and proxy settings ultimately breaking the windows authentication. But seeing your question in the provided link I guess you are already much deeper down this rabbit hole ;) – Yush0 Jul 03 '20 at 05:04
3

Microsoft has a whole article about Windows Authentication in ASP.NET Core, including a section describing how to do it without IIS. Kestrel doesn't support Windows Authentication (Update: it does now), so you have to host with HTTP.sys. It looks easy at first (in your Program.cs):

.UseHttpSys(options =>
{
    options.Authentication.Schemes = 
        AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate;
    options.Authentication.AllowAnonymous = false;
})

Until you realize that there's a whole other article about hosting in HTTP.sys, so you may find some other reasons it might break something else.

It might be easier to host it in IIS (instead of a Windows Service) and let IIS handle the Windows Authentication.

Is there a reason you decided to host in a Windows Service in the first place?

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thank you Gabriel. Hosting in IIS is fine for stateless request/response applications. But if you run dedicated threads say via BackgroundService to do stuff not related to requests, IIS is not a good host for that. You can configure IIS to prevent idle shutdown and auto-start after a reboot but Windows services are specifically designed for this while getting IIS to do this is kind of a hack. You might then ask why not use .NET Framework? Well, all the cool features like native dependency injection, etc. are in .NET Core. – Sandy Dec 19 '18 at 18:27
  • That makes sense :) – Gabriel Luci Dec 19 '18 at 18:48
  • 2
    If this becomes too problematic, you could split the application: use a Windows Service for the background processes, and use a separate web application hosted in IIS for the front-end. – Gabriel Luci Dec 19 '18 at 18:53