From 3ce6c1dc5b022116ec9b13f9158e092323ac1158 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Sat, 22 Jun 2019 10:39:10 -0700 Subject: [PATCH] Consolidate Windows Auth config #10530 (#11421) --- ...sting.Server.Abstractions.netcoreapp3.0.cs | 11 +++ .../src/IServerIntegratedAuth.cs | 21 ++++++ .../src/ServerIntegratedAuth.cs | 21 ++++++ ...AspNetCore.Authentication.Negotiate.csproj | 1 + ....Authentication.Negotiate.netcoreapp3.0.cs | 5 ++ .../NegotiateAuthSample.csproj | 3 +- .../samples/NegotiateAuthSample/Program.cs | 10 +++ .../Properties/launchSettings.json | 15 +++- .../Internal/NegotiateLoggingExtensions.cs | 8 ++ ...AspNetCore.Authentication.Negotiate.csproj | 1 + .../Negotiate/src/NegotiateExtensions.cs | 10 +-- .../Negotiate/src/NegotiateHandler.cs | 3 +- .../Negotiate/src/NegotiateOptions.cs | 6 ++ .../src/PostConfigureNegotiateOptions.cs | 64 ++++++++++++++++ .../Negotiate.Test/ServerDeferralTests.cs | 75 +++++++++++++++++++ src/Security/Security.sln | 7 ++ .../src/WebHostBuilderHttpSysExtensions.cs | 12 ++- .../IIS/src/WebHostBuilderIISExtensions.cs | 5 ++ ...ft.AspNetCore.Server.IISIntegration.csproj | 1 + ...ft.AspNetCore.Server.IISIntegration.csproj | 1 + .../src/WebHostBuilderIISExtensions.cs | 10 ++- .../ServerComparison.TestSites/Program.cs | 55 ++++++-------- .../StartupNtlmAuthentication.cs | 26 ++----- 23 files changed, 303 insertions(+), 68 deletions(-) create mode 100644 src/Hosting/Server.Abstractions/src/IServerIntegratedAuth.cs create mode 100644 src/Hosting/Server.Abstractions/src/ServerIntegratedAuth.cs create mode 100644 src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs create mode 100644 src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs diff --git a/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs b/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs index 5797d1f08c..a775c4229e 100644 --- a/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs +++ b/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs @@ -15,6 +15,17 @@ namespace Microsoft.AspNetCore.Hosting.Server System.Threading.Tasks.Task StartAsync(Microsoft.AspNetCore.Hosting.Server.IHttpApplication application, System.Threading.CancellationToken cancellationToken); System.Threading.Tasks.Task StopAsync(System.Threading.CancellationToken cancellationToken); } + public partial interface IServerIntegratedAuth + { + string AuthenticationScheme { get; } + bool IsEnabled { get; } + } + public partial class ServerIntegratedAuth : Microsoft.AspNetCore.Hosting.Server.IServerIntegratedAuth + { + public ServerIntegratedAuth() { } + public string AuthenticationScheme { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool IsEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } } namespace Microsoft.AspNetCore.Hosting.Server.Features { diff --git a/src/Hosting/Server.Abstractions/src/IServerIntegratedAuth.cs b/src/Hosting/Server.Abstractions/src/IServerIntegratedAuth.cs new file mode 100644 index 0000000000..d099656ac4 --- /dev/null +++ b/src/Hosting/Server.Abstractions/src/IServerIntegratedAuth.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Hosting.Server +{ + /// + /// Used by servers to advertise if they support integrated Windows authentication, if it's enabled, and it's scheme. + /// + public interface IServerIntegratedAuth + { + /// + /// Indicates if integrated Windows authentication is enabled for the current application instance. + /// + bool IsEnabled { get; } + + /// + /// The name of the authentication scheme for the server authentication handler. + /// + string AuthenticationScheme { get; } + } +} diff --git a/src/Hosting/Server.Abstractions/src/ServerIntegratedAuth.cs b/src/Hosting/Server.Abstractions/src/ServerIntegratedAuth.cs new file mode 100644 index 0000000000..93d9029913 --- /dev/null +++ b/src/Hosting/Server.Abstractions/src/ServerIntegratedAuth.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Hosting.Server +{ + /// + /// Used by servers to advertise if they support integrated Windows authentication, if it's enabled, and it's scheme. + /// + public class ServerIntegratedAuth : IServerIntegratedAuth + { + /// + /// Indicates if integrated Windows authentication is enabled for the current application instance. + /// + public bool IsEnabled { get; set;} + + /// + /// The name of the authentication scheme for the server authentication handler. + /// + public string AuthenticationScheme { get; set; } + } +} diff --git a/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.csproj b/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.csproj index ea9c20cd85..9aae0adc89 100644 --- a/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.csproj +++ b/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.csproj @@ -7,5 +7,6 @@ + diff --git a/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.netcoreapp3.0.cs b/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.netcoreapp3.0.cs index 4f9f3a900e..5821e59a77 100644 --- a/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.netcoreapp3.0.cs +++ b/src/Security/Authentication/Negotiate/ref/Microsoft.AspNetCore.Authentication.Negotiate.netcoreapp3.0.cs @@ -51,6 +51,11 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate public bool PersistKerberosCredentials { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool PersistNtlmCredentials { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } + public partial class PostConfigureNegotiateOptions : Microsoft.Extensions.Options.IPostConfigureOptions + { + public PostConfigureNegotiateOptions(System.Collections.Generic.IEnumerable serverAuthServices, Microsoft.Extensions.Logging.ILogger logger) { } + public void PostConfigure(string name, Microsoft.AspNetCore.Authentication.Negotiate.NegotiateOptions options) { } + } } namespace Microsoft.Extensions.DependencyInjection { diff --git a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/NegotiateAuthSample.csproj b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/NegotiateAuthSample.csproj index 225b146856..9df9e8a9ec 100644 --- a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/NegotiateAuthSample.csproj +++ b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/NegotiateAuthSample.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -9,6 +9,7 @@ + diff --git a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Program.cs b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Program.cs index 05f8132cb2..085cdc6282 100644 --- a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Program.cs +++ b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Program.cs @@ -1,7 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.HttpSys; using Microsoft.Extensions.Hosting; namespace NegotiateAuthSample @@ -17,6 +19,14 @@ namespace NegotiateAuthSample Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { + if (string.Equals("HttpSys", webBuilder.GetSetting("server"), StringComparison.OrdinalIgnoreCase)) + { + webBuilder.UseHttpSys(options => + { + options.Authentication.AllowAnonymous = true; + options.Authentication.Schemes = AuthenticationSchemes.Negotiate; + }); + } webBuilder.UseStartup(); }); } diff --git a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Properties/launchSettings.json b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Properties/launchSettings.json index 128e6417fa..d117d261b3 100644 --- a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Properties/launchSettings.json +++ b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "iisSettings": { - "windowsAuthentication": false, + "windowsAuthentication": true, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:6449", @@ -12,16 +12,27 @@ "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { + "ASPNETCORE_SERVER": "IISOutOfProc", "ASPNETCORE_ENVIRONMENT": "Development" } }, + "HttpSys": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_SERVER": "HttpSys", + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:5000" + }, "NegotiateAuthSample": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { + "ASPNETCORE_SERVER": "Kestrel", "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001;http://localhost:5000" } } -} +} \ No newline at end of file diff --git a/src/Security/Authentication/Negotiate/src/Internal/NegotiateLoggingExtensions.cs b/src/Security/Authentication/Negotiate/src/Internal/NegotiateLoggingExtensions.cs index 399b6fbe52..11141bb123 100644 --- a/src/Security/Authentication/Negotiate/src/Internal/NegotiateLoggingExtensions.cs +++ b/src/Security/Authentication/Negotiate/src/Internal/NegotiateLoggingExtensions.cs @@ -14,6 +14,7 @@ namespace Microsoft.Extensions.Logging private static Action _exceptionProcessingAuth; private static Action _challengeNegotiate; private static Action _reauthenticating; + private static Action _deferring; static NegotiateLoggingExtensions() { @@ -45,6 +46,10 @@ namespace Microsoft.Extensions.Logging eventId: new EventId(7, "Reauthenticating"), logLevel: LogLevel.Debug, formatString: "Negotiate data received for an already authenticated connection, Re-authenticating."); + _deferring = LoggerMessage.Define( + eventId: new EventId(8, "Deferring"), + logLevel: LogLevel.Information, + formatString: "Deferring to the server's implementation of Windows Authentication."); } public static void IncompleteNegotiateChallenge(this ILogger logger) @@ -67,5 +72,8 @@ namespace Microsoft.Extensions.Logging public static void Reauthenticating(this ILogger logger) => _reauthenticating(logger, null); + + public static void Deferring(this ILogger logger) + => _deferring(logger, null); } } diff --git a/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj b/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj index f71af581a9..d337c88445 100644 --- a/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj +++ b/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs index ea01f040a8..f5bbf8cbc8 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs @@ -4,6 +4,8 @@ using System; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Negotiate; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection { @@ -49,13 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection /// The original builder. public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) { - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable($"ASPNETCORE_TOKEN"))) - { - throw new NotSupportedException( - "The Negotiate authentication handler must not be used with IIS out-of-process mode or similar reverse proxies that share connections between users." - + " Use the Windows Authentication features available within IIS or IIS Express."); - } - + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureNegotiateOptions>()); return builder.AddScheme(authenticationScheme, displayName, configureOptions); } } diff --git a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs index 27844382fb..e49cd5cd97 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs @@ -67,11 +67,12 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate { try { - if (_requestProcessed) + if (_requestProcessed || Options.DeferToServer) { // This request was already processed but something is re-executing it like an exception handler. // Don't re-run because we could corrupt the connection state, e.g. if this was a stage2 NTLM request // that we've already completed the handshake for. + // Or we're in deferral mode where we let the server handle the authentication. return false; } diff --git a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs index 78df485897..3f5d36b39f 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs @@ -33,6 +33,12 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate /// public bool PersistNtlmCredentials { get; set; } = true; + /// + /// Indicates if integrated server Windows Auth is being used instead of this handler. + /// See . + /// + internal bool DeferToServer { get; set; } + // For testing internal INegotiateStateFactory StateFactory { get; set; } = new ReflectedNegotiateStateFactory(); } diff --git a/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs b/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs new file mode 100644 index 0000000000..91384c3293 --- /dev/null +++ b/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Negotiate +{ + /// + /// Reconfigures the NegotiateOptions to defer to the integrated server authentication if present. + /// + public class PostConfigureNegotiateOptions : IPostConfigureOptions + { + private readonly IServerIntegratedAuth _serverAuth; + private readonly ILogger _logger; + + /// + /// Creates a new + /// + /// + /// + public PostConfigureNegotiateOptions(IEnumerable serverAuthServices, ILogger logger) + { + _serverAuth = serverAuthServices.LastOrDefault(); + _logger = logger; + } + + /// + /// Invoked to post configure a TOptions instance. + /// + /// The name of the options instance being configured. + /// The options instance to configure. + public void PostConfigure(string name, NegotiateOptions options) + { + // If the server supports integrated authentication... + if (_serverAuth != null) + { + // And it's on... + if (_serverAuth.IsEnabled) + { + // Forward to the server if something else wasn't already configured. + if (options.ForwardDefault == null) + { + Debug.Assert(_serverAuth.AuthenticationScheme != null); + options.ForwardDefault = _serverAuth.AuthenticationScheme; + options.DeferToServer = true; + _logger.Deferring(); + } + } + // Otherwise fail, you shouldn't be using this auth handler on a server that supports integrated auth. + else + { + throw new InvalidOperationException("The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication." + + " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it."); + } + } + } + } +} diff --git a/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs b/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs new file mode 100644 index 0000000000..eba5c12fea --- /dev/null +++ b/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs @@ -0,0 +1,75 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication.Negotiate +{ + public class ServerDeferralTests + { + [Fact] + public async Task ServerDoesNotSupportAuth_NoError() + { + using var host = await CreateHostAsync(supportsAuth: false); + var options = host.Services.GetRequiredService>().Value; + Assert.False(options.DeferToServer); + Assert.Null(options.ForwardDefault); + } + + [Fact] + public async Task ServerSupportsAuthButDisabled_Error() + { + using var host = await CreateHostAsync(supportsAuth: true, isEnabled: false); + var ex = Assert.Throws(() => host.Services.GetRequiredService>().Value); + Assert.Equal("The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication." + + " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it.", ex.Message); + } + + [Fact] + public async Task ServerSupportsAuthAndEnabled_Deferred() + { + using var host = await CreateHostAsync(supportsAuth: true, isEnabled: true, authScheme: "DeferralScheme"); + var options = host.Services.GetRequiredService>().Value; + Assert.True(options.DeferToServer); + Assert.Equal("DeferralScheme", options.ForwardDefault); + } + + private static async Task CreateHostAsync(bool supportsAuth = false, bool isEnabled = false, string authScheme = null) + { + var builder = new HostBuilder() + .ConfigureServices(services => + { + services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) + .AddNegotiate(); + + if (supportsAuth) + { + services.AddSingleton(new ServerIntegratedAuth() + { + IsEnabled = isEnabled, + AuthenticationScheme = authScheme, + }); + } + }) + .ConfigureWebHost(webHostBuilder => + { + webHostBuilder.UseTestServer(); + webHostBuilder.Configure(app => + { + app.UseAuthentication(); + }); + }); + + return await builder.StartAsync(); + } + } +} diff --git a/src/Security/Security.sln b/src/Security/Security.sln index 55cad7f545..32566c7143 100644 --- a/src/Security/Security.sln +++ b/src/Security/Security.sln @@ -162,6 +162,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HostFi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IIS", "..\Servers\IIS\IIS\src\Microsoft.AspNetCore.Server.IIS.csproj", "{666AFB4D-68A5-4621-BB55-2CD82F0FB1F8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.HttpSys", "..\Servers\HttpSys\src\Microsoft.AspNetCore.Server.HttpSys.csproj", "{D6C3C4A9-197B-47B5-8B72-35047CBC4F22}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -420,6 +422,10 @@ Global {666AFB4D-68A5-4621-BB55-2CD82F0FB1F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {666AFB4D-68A5-4621-BB55-2CD82F0FB1F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {666AFB4D-68A5-4621-BB55-2CD82F0FB1F8}.Release|Any CPU.Build.0 = Release|Any CPU + {D6C3C4A9-197B-47B5-8B72-35047CBC4F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6C3C4A9-197B-47B5-8B72-35047CBC4F22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6C3C4A9-197B-47B5-8B72-35047CBC4F22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6C3C4A9-197B-47B5-8B72-35047CBC4F22}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -500,6 +506,7 @@ Global {16C26D50-6849-4BA4-AEB6-E5C9AA2369A2} = {A3766414-EB5C-40F7-B031-121804ED5D0A} {A665A1F8-D1A4-42AC-B8E9-71B6F57481D8} = {A3766414-EB5C-40F7-B031-121804ED5D0A} {666AFB4D-68A5-4621-BB55-2CD82F0FB1F8} = {A3766414-EB5C-40F7-B031-121804ED5D0A} + {D6C3C4A9-197B-47B5-8B72-35047CBC4F22} = {A3766414-EB5C-40F7-B031-121804ED5D0A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ABF8089E-43D0-4010-84A7-7A9DCFE49357} diff --git a/src/Servers/HttpSys/src/WebHostBuilderHttpSysExtensions.cs b/src/Servers/HttpSys/src/WebHostBuilderHttpSysExtensions.cs index d7042643b9..7b4caaef62 100644 --- a/src/Servers/HttpSys/src/WebHostBuilderHttpSysExtensions.cs +++ b/src/Servers/HttpSys/src/WebHostBuilderHttpSysExtensions.cs @@ -1,10 +1,11 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Server.HttpSys; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Hosting { @@ -23,6 +24,15 @@ namespace Microsoft.AspNetCore.Hosting { return hostBuilder.ConfigureServices(services => { services.AddSingleton(); + services.AddSingleton(services => + { + var options = services.GetRequiredService>().Value; + return new ServerIntegratedAuth() + { + IsEnabled = options.Authentication.Schemes != AuthenticationSchemes.None, + AuthenticationScheme = HttpSysDefaults.AuthenticationScheme, + }; + }); services.AddAuthenticationCore(); }); } diff --git a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs index 1f1480b1d3..c726a75b22 100644 --- a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs +++ b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs @@ -40,6 +40,11 @@ namespace Microsoft.AspNetCore.Hosting services.AddSingleton(); services.AddSingleton(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath)); services.AddAuthenticationCore(); + services.AddSingleton(_ => new ServerIntegratedAuth() + { + IsEnabled = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled, + AuthenticationScheme = IISServerDefaults.AuthenticationScheme + }); services.Configure( options => { options.ServerAddresses = iisConfigData.pwzBindings.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Servers/IIS/IISIntegration/ref/Microsoft.AspNetCore.Server.IISIntegration.csproj b/src/Servers/IIS/IISIntegration/ref/Microsoft.AspNetCore.Server.IISIntegration.csproj index 7067e9a85d..e5054cf688 100644 --- a/src/Servers/IIS/IISIntegration/ref/Microsoft.AspNetCore.Server.IISIntegration.csproj +++ b/src/Servers/IIS/IISIntegration/ref/Microsoft.AspNetCore.Server.IISIntegration.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Servers/IIS/IISIntegration/src/Microsoft.AspNetCore.Server.IISIntegration.csproj b/src/Servers/IIS/IISIntegration/src/Microsoft.AspNetCore.Server.IISIntegration.csproj index 80220bc29a..441768e93c 100644 --- a/src/Servers/IIS/IISIntegration/src/Microsoft.AspNetCore.Server.IISIntegration.csproj +++ b/src/Servers/IIS/IISIntegration/src/Microsoft.AspNetCore.Server.IISIntegration.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Servers/IIS/IISIntegration/src/WebHostBuilderIISExtensions.cs b/src/Servers/IIS/IISIntegration/src/WebHostBuilderIISExtensions.cs index b57ad38b00..a9234ee4b8 100644 --- a/src/Servers/IIS/IISIntegration/src/WebHostBuilderIISExtensions.cs +++ b/src/Servers/IIS/IISIntegration/src/WebHostBuilderIISExtensions.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Runtime.InteropServices; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Server.IISIntegration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting.Server; namespace Microsoft.AspNetCore.Hosting { @@ -82,9 +81,14 @@ namespace Microsoft.AspNetCore.Hosting hostBuilder.ConfigureServices(services => { - // Delay register the url so users don't accidently overwrite it. + // Delay register the url so users don't accidentally overwrite it. hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, address); hostBuilder.PreferHostingUrls(true); + services.AddSingleton(_ => new ServerIntegratedAuth() + { + IsEnabled = enableAuth, + AuthenticationScheme = IISDefaults.AuthenticationScheme + }); services.AddSingleton(new IISSetupFilter(pairingToken, new PathString(path), isWebSocketsSupported)); services.Configure(options => { diff --git a/src/Servers/testassets/ServerComparison.TestSites/Program.cs b/src/Servers/testassets/ServerComparison.TestSites/Program.cs index df87d7d52c..dbd4907d3b 100644 --- a/src/Servers/testassets/ServerComparison.TestSites/Program.cs +++ b/src/Servers/testassets/ServerComparison.TestSites/Program.cs @@ -13,12 +13,10 @@ namespace ServerComparison.TestSites { public static void Main(string[] args) { - var config = new ConfigurationBuilder() - .AddCommandLine(args) - .Build(); - var builder = new WebHostBuilder() - .UseConfiguration(config) + .UseConfiguration(new ConfigurationBuilder() + .AddCommandLine(args) + .Build()) .ConfigureLogging((_, factory) => { factory.AddConsole(); @@ -26,37 +24,28 @@ namespace ServerComparison.TestSites }) .UseStartup("ServerComparison.TestSites"); - // Switch between Kestrel, IIS, and HttpSys for different tests. Default to Kestrel for normal app execution. - if (string.Equals(builder.GetSetting("server"), "Microsoft.AspNetCore.Server.HttpSys", StringComparison.Ordinal)) - { - if (string.Equals(builder.GetSetting("environment") ?? - Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), - "NtlmAuthentication", System.StringComparison.Ordinal)) - { - // Set up NTLM authentication for HttpSys as follows. - // For IIS and IISExpress use inetmgr to setup NTLM authentication on the application or - // modify the applicationHost.config to enable NTLM. - builder.UseHttpSys(options => - { - options.Authentication.AllowAnonymous = true; - options.Authentication.Schemes = - AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM; - }); - } - else - { - builder.UseHttpSys(); - } - } - else - { - // Check that we are not using IIS inproc before we add Kestrel. - builder.UseKestrel(); - } - + builder.UseKestrel(); builder.UseIISIntegration(); builder.UseIIS(); + // Switch between Kestrel, IIS, and HttpSys for different tests. Default to Kestrel for normal app execution. + if (string.Equals(builder.GetSetting("server"), "Microsoft.AspNetCore.Server.HttpSys", StringComparison.OrdinalIgnoreCase)) + { + builder.UseHttpSys(options => + { + if (string.Equals(builder.GetSetting("environment") ?? + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), + "NtlmAuthentication", StringComparison.OrdinalIgnoreCase)) + { + // Set up NTLM authentication for HttpSys as follows. + // For IIS and IISExpress use inetmgr to setup NTLM authentication on the application or + // modify the applicationHost.config to enable NTLM. + options.Authentication.AllowAnonymous = true; + options.Authentication.Schemes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM; + } + }); + } + var host = builder.Build(); host.Run(); diff --git a/src/Servers/testassets/ServerComparison.TestSites/StartupNtlmAuthentication.cs b/src/Servers/testassets/ServerComparison.TestSites/StartupNtlmAuthentication.cs index 5528d19578..c2adffb4db 100644 --- a/src/Servers/testassets/ServerComparison.TestSites/StartupNtlmAuthentication.cs +++ b/src/Servers/testassets/ServerComparison.TestSites/StartupNtlmAuthentication.cs @@ -6,8 +6,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Negotiate; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.IISIntegration; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -15,27 +13,15 @@ namespace ServerComparison.TestSites { public class StartupNtlmAuthentication { - public IConfiguration Configuration { get; } - public bool IsKestrel => string.Equals(Configuration["server"], "Microsoft.AspNetCore.Server.Kestrel"); - - public StartupNtlmAuthentication(IConfiguration configuration) - { - Configuration = configuration; - } - public void ConfigureServices(IServiceCollection services) { services.AddHttpContextAccessor(); - services.AddSingleton(); - if (IsKestrel) - { - services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) - .AddNegotiate(); - } - else - { - services.AddAuthentication(IISDefaults.AuthenticationScheme); - } + // https://github.com/aspnet/AspNetCore/issues/11462 + // services.AddSingleton(); + + // This will deffer to the server implementations when available. + services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) + .AddNegotiate(); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)