diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs index 017e7911c..5a4ffc547 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -59,39 +59,6 @@ protected override Task InitializeHandlerAsync() /// A new instance of the events instance. protected override Task CreateEventsAsync() => Task.FromResult(new CookieAuthenticationEvents()); - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (String.IsNullOrEmpty(Options.CookieName)) - { - Options.CookieName = CookieAuthenticationDefaults.CookiePrefix + Scheme.Name; - } - if (Options.TicketDataFormat == null) - { - var provider = Options.DataProtectionProvider ?? Context.RequestServices.GetRequiredService(); - // Note: the purpose for the data protector must remain fixed for interop to work. - var dataProtector = provider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", Scheme.Name, "v2"); - Options.TicketDataFormat = new TicketDataFormat(dataProtector); - } - if (Options.CookieManager == null) - { - Options.CookieManager = new ChunkingCookieManager(); - } - if (!Options.LoginPath.HasValue) - { - Options.LoginPath = CookieAuthenticationDefaults.LoginPath; - } - if (!Options.LogoutPath.HasValue) - { - Options.LogoutPath = CookieAuthenticationDefaults.LogoutPath; - } - if (!Options.AccessDeniedPath.HasValue) - { - Options.AccessDeniedPath = CookieAuthenticationDefaults.AccessDeniedPath; - } - } - private Task EnsureCookieTicket() { // We only need to read the ticket once diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationInitializer.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationInitializer.cs new file mode 100644 index 000000000..af4a85b19 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationInitializer.cs @@ -0,0 +1,60 @@ +// 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.DataProtection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Cookies +{ + /// + /// Used to setup defaults for all . + /// + public class CookieAuthenticationInitializer : IInitializeOptions + { + private readonly IDataProtectionProvider _dp; + + public CookieAuthenticationInitializer(IDataProtectionProvider dataProtection) + { + _dp = dataProtection; + } + + /// + /// Invoked to initialize a TOptions instance. + /// + /// The name of the options instance being initialized. + /// The options instance to initialize. + public void Initialize(string name, CookieAuthenticationOptions options) + { + options.DataProtectionProvider = options.DataProtectionProvider ?? _dp; + + if (String.IsNullOrEmpty(options.CookieName)) + { + options.CookieName = CookieAuthenticationDefaults.CookiePrefix + name; + } + if (options.TicketDataFormat == null) + { + // Note: the purpose for the data protector must remain fixed for interop to work. + var dataProtector = options.DataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", name, "v2"); + options.TicketDataFormat = new TicketDataFormat(dataProtector); + } + if (options.CookieManager == null) + { + options.CookieManager = new ChunkingCookieManager(); + } + if (!options.LoginPath.HasValue) + { + options.LoginPath = CookieAuthenticationDefaults.LoginPath; + } + if (!options.LogoutPath.HasValue) + { + options.LogoutPath = CookieAuthenticationDefaults.LogoutPath; + } + if (!options.AccessDeniedPath.HasValue) + { + options.AccessDeniedPath = CookieAuthenticationDefaults.AccessDeniedPath; + } + } + + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs index 56d6ca238..9b6b51d8e 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs @@ -4,6 +4,7 @@ using System; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Authentication.Cookies { @@ -71,7 +72,7 @@ public string CookieName public CookieSecurePolicy CookieSecure { get; set; } /// - /// If set this will be used by the CookieAuthenticationMiddleware for data protection. + /// If set this will be used by the CookieAuthenticationHandler for data protection. /// public IDataProtectionProvider DataProtectionProvider { get; set; } @@ -129,9 +130,7 @@ public string CookieName /// /// The TicketDataFormat is used to protect and unprotect the identity and other properties which are stored in the - /// cookie value. If it is not provided a default data handler is created using the data protection service contained - /// in the IApplicationBuilder.Properties. The default data protection service is based on machine key when running on ASP.NET, - /// and on DPAPI when running in a different process. + /// cookie value. If not provided one will be created using . /// public ISecureDataFormat TicketDataFormat { get; set; } diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieExtensions.cs index e8a21d01b..b528dec9c 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieExtensions.cs @@ -3,6 +3,10 @@ using System; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.AspNetCore.Authentication; namespace Microsoft.Extensions.DependencyInjection { @@ -15,7 +19,10 @@ public static class CookieExtensions public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, Action configureOptions) => services.AddCookieAuthentication(CookieAuthenticationDefaults.AuthenticationScheme, configureOptions); - public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) => - services.AddScheme(authenticationScheme, configureOptions); + public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) + { + services.TryAddEnumerable(ServiceDescriptor.Singleton, CookieAuthenticationInitializer>()); + return services.AddScheme(authenticationScheme, configureOptions); + } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs index 79d9ac66c..032be8235 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddFacebookAuthentication(this IServiceCollecti public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { - return services.AddScheme(authenticationScheme, authenticationScheme, configureOptions); + return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs index 521684d14..c94048f9b 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs @@ -9,7 +9,6 @@ using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -19,8 +18,8 @@ namespace Microsoft.AspNetCore.Authentication.Facebook { internal class FacebookHandler : OAuthHandler { - public FacebookHandler(IOptions sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, logger, encoder, dataProtection, clock) + public FacebookHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { } protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs index 420d14030..d71e8b461 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddGoogleAuthentication(this IServiceCollection public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { - return services.AddScheme(authenticationScheme, authenticationScheme, configureOptions); + return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs index c699f5cc9..3a93c5cbf 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs @@ -9,7 +9,6 @@ using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -19,8 +18,8 @@ namespace Microsoft.AspNetCore.Authentication.Google { internal class GoogleHandler : OAuthHandler { - public GoogleHandler(IOptions sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, logger, encoder, dataProtection, clock) + public GoogleHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { } protected override async Task CreateTicketAsync( diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs index 77ffd76ff..4f6453bd9 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection @@ -26,6 +27,7 @@ public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollect public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { + services.TryAddEnumerable(ServiceDescriptor.Singleton, JwtBearerInitializer>()); return services.AddScheme(authenticationScheme, configureOptions); } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index 2ea03a51f..ec48e3e20 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -40,49 +40,6 @@ public JwtBearerHandler(IOptionsSnapshot options, ILoggerFacto protected override Task CreateEventsAsync() => Task.FromResult(new JwtBearerEvents()); - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (string.IsNullOrEmpty(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(Options.Audience)) - { - Options.TokenValidationParameters.ValidAudience = Options.Audience; - } - - if (Options.ConfigurationManager == null) - { - if (Options.Configuration != null) - { - Options.ConfigurationManager = new StaticConfigurationManager(Options.Configuration); - } - else if (!(string.IsNullOrEmpty(Options.MetadataAddress) && string.IsNullOrEmpty(Options.Authority))) - { - if (string.IsNullOrEmpty(Options.MetadataAddress) && !string.IsNullOrEmpty(Options.Authority)) - { - Options.MetadataAddress = Options.Authority; - if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal)) - { - Options.MetadataAddress += "/"; - } - - Options.MetadataAddress += ".well-known/openid-configuration"; - } - - if (Options.RequireHttpsMetadata && !Options.MetadataAddress.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false."); - } - - var httpClient = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler()); - httpClient.Timeout = Options.BackchannelTimeout; - httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB - - Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever(httpClient) { RequireHttps = Options.RequireHttpsMetadata }); - } - } - } - /// /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using set in the options. /// diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerInitializer.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerInitializer.cs new file mode 100644 index 000000000..0aeed1183 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerInitializer.cs @@ -0,0 +1,63 @@ +// 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.Net.Http; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; + +namespace Microsoft.AspNetCore.Authentication.JwtBearer +{ + /// + /// Used to setup defaults for all . + /// + public class JwtBearerInitializer : IInitializeOptions + { + /// + /// Invoked to initialize a JwtBearerOptions instance. + /// + /// The name of the options instance being initialized. + /// The options instance to initialize. + public void Initialize(string name, JwtBearerOptions options) + { + if (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(options.Audience)) + { + options.TokenValidationParameters.ValidAudience = options.Audience; + } + + if (options.ConfigurationManager == null) + { + if (options.Configuration != null) + { + options.ConfigurationManager = new StaticConfigurationManager(options.Configuration); + } + else if (!(string.IsNullOrEmpty(options.MetadataAddress) && string.IsNullOrEmpty(options.Authority))) + { + if (string.IsNullOrEmpty(options.MetadataAddress) && !string.IsNullOrEmpty(options.Authority)) + { + options.MetadataAddress = options.Authority; + if (!options.MetadataAddress.EndsWith("/", StringComparison.Ordinal)) + { + options.MetadataAddress += "/"; + } + + options.MetadataAddress += ".well-known/openid-configuration"; + } + + if (options.RequireHttpsMetadata && !options.MetadataAddress.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false."); + } + + var httpClient = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler()); + httpClient.Timeout = options.BackchannelTimeout; + httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + + options.ConfigurationManager = new ConfigurationManager(options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), + new HttpDocumentRetriever(httpClient) { RequireHttps = options.RequireHttpsMetadata }); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs index 509016ff2..6ccb39234 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddMicrosoftAccountAuthentication(this IService public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { - return services.AddScheme(authenticationScheme, authenticationScheme, configureOptions); + return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs index b2b787b97..815b94bf4 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs @@ -7,7 +7,6 @@ using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; @@ -16,8 +15,8 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount { internal class MicrosoftAccountHandler : OAuthHandler { - public MicrosoftAccountHandler(IOptions sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, logger, encoder, dataProtection, clock) + public MicrosoftAccountHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { } protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Events/OAuthCreatingTicketContext.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Events/OAuthCreatingTicketContext.cs index f50dff3f5..6e3105639 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Events/OAuthCreatingTicketContext.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Events/OAuthCreatingTicketContext.cs @@ -170,7 +170,7 @@ public void RunClaimActions(JObject userData) foreach (var action in Options.ClaimActions) { - action.Run(userData, Identity, Options.ClaimsIssuer); + action.Run(userData, Identity, Options.ClaimsIssuer ?? Scheme.Name); } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthExtensions.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthExtensions.cs index 6fd0f57f4..408789b03 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthExtensions.cs @@ -3,13 +3,24 @@ using System; using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.Extensions.DependencyInjection { public static class OAuthExtensions { - public static IServiceCollection AddOAuthAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) => - services.AddScheme>(authenticationScheme, authenticationScheme, configureOptions); + public static IServiceCollection AddOAuthAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) + { + return services.AddScheme>(authenticationScheme, authenticationScheme, configureOptions); + } + + public static IServiceCollection AddOAuthAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) + where TOptions : OAuthOptions, new() + where THandler : OAuthHandler + { + services.TryAddEnumerable(ServiceDescriptor.Singleton, OAuthInitializer>()); + return services.AddRemoteScheme(authenticationScheme, authenticationScheme, configureOptions); + } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs index cafc4f0bc..63bcdbdf2 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs @@ -10,7 +10,6 @@ using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -33,30 +32,10 @@ namespace Microsoft.AspNetCore.Authentication.OAuth set { base.Events = value; } } - public OAuthHandler(IOptions sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, dataProtection, logger, encoder, clock) + public OAuthHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { } - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (Options.Backchannel == null) - { - Options.Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler()); - Options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OAuth handler"); - Options.Backchannel.Timeout = Options.BackchannelTimeout; - Options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB - } - - if (Options.StateDataFormat == null) - { - var dataProtector = DataProtection.CreateProtector( - GetType().FullName, Scheme.Name, "v1"); - Options.StateDataFormat = new PropertiesDataFormat(dataProtector); - } - } - /// /// Creates a new instance of the events instance. /// @@ -119,7 +98,7 @@ protected override async Task HandleRemoteAuthenticateAsync( return AuthenticateResult.Fail("Failed to retrieve access token."); } - var identity = new ClaimsIdentity(Options.ClaimsIssuer); + var identity = new ClaimsIdentity(ClaimsIssuer); if (Options.SaveTokens) { diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthInitializer.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthInitializer.cs new file mode 100644 index 000000000..99f65253a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthInitializer.cs @@ -0,0 +1,45 @@ +// 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.Net.Http; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Used to setup defaults for the OAuthOptions. + /// + public class OAuthInitializer : IInitializeOptions + where TOptions : OAuthOptions, new() + where THandler : OAuthHandler + { + private readonly IDataProtectionProvider _dp; + + public OAuthInitializer(IDataProtectionProvider dataProtection) + { + _dp = dataProtection; + } + + public void Initialize(string name, TOptions options) + { + options.DataProtectionProvider = options.DataProtectionProvider ?? _dp; + if (options.Backchannel == null) + { + options.Backchannel = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler()); + options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OAuth handler"); + options.Backchannel.Timeout = options.BackchannelTimeout; + options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + } + + if (options.StateDataFormat == null) + { + var dataProtector = options.DataProtectionProvider.CreateProtector( + typeof(THandler).FullName, name, "v1"); + options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs index 64737a9ad..c79dc7212 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection @@ -26,7 +27,8 @@ public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCol public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { - return services.AddScheme(authenticationScheme, authenticationScheme, configureOptions); + services.TryAddEnumerable(ServiceDescriptor.Singleton, OpenIdConnectInitializer>()); + return services.AddRemoteScheme(authenticationScheme, authenticationScheme, configureOptions); } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs index 69acbf9a0..d51c324dc 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -13,12 +13,10 @@ using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; -using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; using Microsoft.Net.Http.Headers; @@ -57,8 +55,8 @@ public class OpenIdConnectHandler : RemoteAuthenticationHandler sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, dataProtection, logger, encoder, clock) + public OpenIdConnectHandler(IOptionsSnapshot options, ILoggerFactory logger, HtmlEncoder htmlEncoder, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { HtmlEncoder = htmlEncoder; } @@ -75,76 +73,6 @@ public OpenIdConnectHandler(IOptions sharedOptions, IOpti protected override Task CreateEventsAsync() => Task.FromResult(new OpenIdConnectEvents()); - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (string.IsNullOrEmpty(Options.SignOutScheme)) - { - Options.SignOutScheme = SignInScheme; - } - - if (Options.StateDataFormat == null) - { - var dataProtector = DataProtection.CreateProtector( - GetType().FullName, Scheme.Name, "v1"); - Options.StateDataFormat = new PropertiesDataFormat(dataProtector); - } - - if (Options.StringDataFormat == null) - { - var dataProtector = DataProtection.CreateProtector( - GetType().FullName, - typeof(string).FullName, - Scheme.Name, - "v1"); - - Options.StringDataFormat = new SecureDataFormat(new StringSerializer(), dataProtector); - } - - if (string.IsNullOrEmpty(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(Options.ClientId)) - { - Options.TokenValidationParameters.ValidAudience = Options.ClientId; - } - - if (Options.Backchannel == null) - { - Options.Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler()); - Options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OpenIdConnect handler"); - Options.Backchannel.Timeout = Options.BackchannelTimeout; - Options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB - } - - if (Options.ConfigurationManager == null) - { - if (Options.Configuration != null) - { - Options.ConfigurationManager = new StaticConfigurationManager(Options.Configuration); - } - else if (!(string.IsNullOrEmpty(Options.MetadataAddress) && string.IsNullOrEmpty(Options.Authority))) - { - if (string.IsNullOrEmpty(Options.MetadataAddress) && !string.IsNullOrEmpty(Options.Authority)) - { - Options.MetadataAddress = Options.Authority; - if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal)) - { - Options.MetadataAddress += "/"; - } - - Options.MetadataAddress += ".well-known/openid-configuration"; - } - - if (Options.RequireHttpsMetadata && !Options.MetadataAddress.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false."); - } - - Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever(Backchannel) { RequireHttps = Options.RequireHttpsMetadata }); - } - } - } - public override Task HandleRequestAsync() { if (Options.RemoteSignOutPath.HasValue && Options.RemoteSignOutPath == Request.Path) @@ -749,7 +677,7 @@ protected override async Task HandleRemoteAuthenticateAsync( var identity = (ClaimsIdentity)ticket.Principal.Identity; foreach (var action in Options.ClaimActions) { - action.Run(null, identity, Options.ClaimsIssuer); + action.Run(null, identity, ClaimsIssuer); } } @@ -902,7 +830,7 @@ protected virtual async Task GetUserInformationAsync(OpenIdC foreach (var action in Options.ClaimActions) { - action.Run(user, identity, Options.ClaimsIssuer); + action.Run(user, identity, ClaimsIssuer); } return AuthenticateResult.Success(ticket); @@ -1301,18 +1229,5 @@ private OpenIdConnectProtocolException CreateOpenIdConnectProtocolException(Open description, errorUri)); } - - private class StringSerializer : IDataSerializer - { - public string Deserialize(byte[] data) - { - return Encoding.UTF8.GetString(data); - } - - public byte[] Serialize(string model) - { - return Encoding.UTF8.GetBytes(model); - } - } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectInitializer.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectInitializer.cs new file mode 100644 index 000000000..7421af9c0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectInitializer.cs @@ -0,0 +1,114 @@ +// 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.Net.Http; +using System.Text; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; + +namespace Microsoft.AspNetCore.Authentication.OpenIdConnect +{ + /// + /// Used to setup defaults for all . + /// + public class OpenIdConnectInitializer : IInitializeOptions + { + private readonly IDataProtectionProvider _dp; + + public OpenIdConnectInitializer(IDataProtectionProvider dataProtection) + { + _dp = dataProtection; + } + + /// + /// Invoked to initialize a TOptions instance. + /// + /// The name of the options instance being initialized. + /// The options instance to initialize. + public void Initialize(string name, OpenIdConnectOptions options) + { + options.DataProtectionProvider = options.DataProtectionProvider ?? _dp; + + if (string.IsNullOrEmpty(options.SignOutScheme)) + { + options.SignOutScheme = options.SignInScheme; + } + + if (options.StateDataFormat == null) + { + var dataProtector = options.DataProtectionProvider.CreateProtector( + typeof(OpenIdConnectHandler).FullName, name, "v1"); + options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + + if (options.StringDataFormat == null) + { + var dataProtector = options.DataProtectionProvider.CreateProtector( + typeof(OpenIdConnectHandler).FullName, + typeof(string).FullName, + name, + "v1"); + + options.StringDataFormat = new SecureDataFormat(new StringSerializer(), dataProtector); + } + + if (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(options.ClientId)) + { + options.TokenValidationParameters.ValidAudience = options.ClientId; + } + + if (options.Backchannel == null) + { + options.Backchannel = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler()); + options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OpenIdConnect handler"); + options.Backchannel.Timeout = options.BackchannelTimeout; + options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + } + + if (options.ConfigurationManager == null) + { + if (options.Configuration != null) + { + options.ConfigurationManager = new StaticConfigurationManager(options.Configuration); + } + else if (!(string.IsNullOrEmpty(options.MetadataAddress) && string.IsNullOrEmpty(options.Authority))) + { + if (string.IsNullOrEmpty(options.MetadataAddress) && !string.IsNullOrEmpty(options.Authority)) + { + options.MetadataAddress = options.Authority; + if (!options.MetadataAddress.EndsWith("/", StringComparison.Ordinal)) + { + options.MetadataAddress += "/"; + } + + options.MetadataAddress += ".well-known/openid-configuration"; + } + + if (options.RequireHttpsMetadata && !options.MetadataAddress.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false."); + } + + options.ConfigurationManager = new ConfigurationManager(options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), + new HttpDocumentRetriever(options.Backchannel) { RequireHttps = options.RequireHttpsMetadata }); + } + } + } + + private class StringSerializer : IDataSerializer + { + public string Deserialize(byte[] data) + { + return Encoding.UTF8.GetString(data); + } + + public byte[] Serialize(string model) + { + return Encoding.UTF8.GetBytes(model); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs index 5ca270dde..b2d69d524 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs @@ -39,7 +39,6 @@ public class OpenIdConnectOptions : RemoteAuthenticationOptions /// public OpenIdConnectOptions() { - DisplayName = OpenIdConnectDefaults.Caption; CallbackPath = new PathString("/signin-oidc"); SignedOutCallbackPath = new PathString("/signout-callback-oidc"); RemoteSignOutPath = new PathString("/signout-oidc"); diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs index 1e126d4c4..3bcc80c3d 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNetCore.Authentication.Twitter; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection @@ -26,7 +27,8 @@ public static IServiceCollection AddTwitterAuthentication(this IServiceCollectio public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { - return services.AddScheme(authenticationScheme, authenticationScheme, configureOptions); + services.TryAddEnumerable(ServiceDescriptor.Singleton, TwitterInitializer>()); + return services.AddRemoteScheme(authenticationScheme, authenticationScheme, configureOptions); } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs index c166b175a..7ae9f973c 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -40,36 +40,12 @@ internal class TwitterHandler : RemoteAuthenticationHandler set { base.Events = value; } } - public TwitterHandler(IOptions sharedOptions, IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) - : base(sharedOptions, options, dataProtection, logger, encoder, clock) + public TwitterHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) { } protected override Task CreateEventsAsync() => Task.FromResult(new TwitterEvents()); - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (Options.StateDataFormat == null) - { - var dataProtector = DataProtection.CreateProtector( - GetType().FullName, Scheme.Name, "v1"); - Options.StateDataFormat = new SecureDataFormat( - new RequestTokenSerializer(), - dataProtector); - } - - if (Options.Backchannel == null) - { - Options.Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler()); - Options.Backchannel.Timeout = Options.BackchannelTimeout; - Options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB - Options.Backchannel.DefaultRequestHeaders.Accept.ParseAdd("*/*"); - Options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core Twitter handler"); - Options.Backchannel.DefaultRequestHeaders.ExpectContinue = false; - } - } - protected override async Task HandleRemoteAuthenticateAsync() { AuthenticationProperties properties = null; @@ -116,12 +92,12 @@ protected override async Task HandleRemoteAuthenticateAsync( var identity = new ClaimsIdentity(new[] { - new Claim(ClaimTypes.NameIdentifier, accessToken.UserId, ClaimValueTypes.String, Options.ClaimsIssuer), - new Claim(ClaimTypes.Name, accessToken.ScreenName, ClaimValueTypes.String, Options.ClaimsIssuer), - new Claim("urn:twitter:userid", accessToken.UserId, ClaimValueTypes.String, Options.ClaimsIssuer), - new Claim("urn:twitter:screenname", accessToken.ScreenName, ClaimValueTypes.String, Options.ClaimsIssuer) + new Claim(ClaimTypes.NameIdentifier, accessToken.UserId, ClaimValueTypes.String, ClaimsIssuer), + new Claim(ClaimTypes.Name, accessToken.ScreenName, ClaimValueTypes.String, ClaimsIssuer), + new Claim("urn:twitter:userid", accessToken.UserId, ClaimValueTypes.String, ClaimsIssuer), + new Claim("urn:twitter:screenname", accessToken.ScreenName, ClaimValueTypes.String, ClaimsIssuer) }, - Options.ClaimsIssuer); + ClaimsIssuer); JObject user = null; if (Options.RetrieveUserDetails) @@ -145,7 +121,7 @@ protected virtual async Task CreateTicketAsync( { foreach (var action in Options.ClaimActions) { - action.Run(user, identity, Options.ClaimsIssuer); + action.Run(user, identity, ClaimsIssuer); } var context = new TwitterCreatingTicketContext(Context, Scheme, Options, properties, token.UserId, token.ScreenName, token.Token, token.TokenSecret, user) diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterInitializer.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterInitializer.cs new file mode 100644 index 000000000..08e9c5b83 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterInitializer.cs @@ -0,0 +1,51 @@ +// 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.Net.Http; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Twitter +{ + /// + /// Used to setup defaults for all . + /// + public class TwitterInitializer : IInitializeOptions + { + private readonly IDataProtectionProvider _dp; + + public TwitterInitializer(IDataProtectionProvider dataProtection) + { + _dp = dataProtection; + } + + /// + /// Invoked to initialize a TOptions instance. + /// + /// The name of the options instance being initialized. + /// The options instance to initialize. + public void Initialize(string name, TwitterOptions options) + { + options.DataProtectionProvider = options.DataProtectionProvider ?? _dp; + + if (options.StateDataFormat == null) + { + var dataProtector = options.DataProtectionProvider.CreateProtector( + typeof(TwitterHandler).FullName, name, "v1"); + options.StateDataFormat = new SecureDataFormat( + new RequestTokenSerializer(), + dataProtector); + } + + if (options.Backchannel == null) + { + options.Backchannel = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler()); + options.Backchannel.Timeout = options.BackchannelTimeout; + options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + options.Backchannel.DefaultRequestHeaders.Accept.ParseAdd("*/*"); + options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core Twitter handler"); + options.Backchannel.DefaultRequestHeaders.ExpectContinue = false; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs index 083884a02..ba1c919fe 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs @@ -48,6 +48,8 @@ protected HttpResponse Response /// protected virtual object Events { get; set; } + protected virtual string ClaimsIssuer => Options.ClaimsIssuer ?? Scheme.Name; + protected string CurrentUri { get @@ -85,18 +87,6 @@ public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext conte Context = context; Options = OptionsSnapshot.Get(Scheme.Name) ?? new TOptions(); - if (!Options.Initialized) - { - lock (Options.InitializeLock) - { - if (!Options.Initialized) - { - InitializeOptions(); - Options.Initialized = true; - } - } - } - Options.Validate(); await InitializeEventsAsync(); @@ -122,16 +112,6 @@ protected virtual async Task InitializeEventsAsync() /// A new instance of the events instance. protected virtual Task CreateEventsAsync() => Task.FromResult(new object()); - /// - /// Initializes the options, will be called only once by . - /// - protected virtual void InitializeOptions() - { - // REVIEW: is there a better place for this default? - Options.DisplayName = Options.DisplayName ?? Scheme.Name; - Options.ClaimsIssuer = Options.ClaimsIssuer ?? Scheme.Name; - } - /// /// Called after options/events have been initialized for the handler to finish initializing itself. /// diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs index 09e7abbd4..55bc09d8a 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs @@ -2,9 +2,18 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Authentication { + public class InitializeAuthenticationSchemeOptions : InitializeOptions + where TOptions : AuthenticationSchemeOptions + { + public InitializeAuthenticationSchemeOptions(string name) + : base(name, options => options.ClaimsIssuer = options.ClaimsIssuer ?? name) + { } + } + /// /// Contains the options used by the . /// @@ -17,11 +26,6 @@ public virtual void Validate() { } - /// - /// Gets or sets the display name for the authentication provider. - /// - public string DisplayName { get; set; } - /// /// Gets or sets the issuer that should be used for any claims that are created /// @@ -36,15 +40,5 @@ public virtual void Validate() /// If set, will be used as the service type to get the Events instance instead of the property. /// public Type EventsType { get; set; } - - /// - /// Used to ensure that the options are only initialized once. - /// - public bool Initialized { get; set; } - - /// - /// Used to prevent concurrent access during intialization. - /// - public object InitializeLock { get; } = new object(); } } diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs index 0315562ff..ff367f555 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs @@ -74,5 +74,30 @@ public static IServiceCollection AddScheme(this IServiceColl where TOptions : AuthenticationSchemeOptions, new() where THandler : AuthenticationHandler => services.AddScheme(authenticationScheme, displayName, configureScheme: null, configureOptions: configureOptions); + + public static IServiceCollection AddRemoteScheme(this IServiceCollection services, string authenticationScheme, string displayName, Action configureOptions) + where TOptions : RemoteAuthenticationOptions, new() + where THandler : RemoteAuthenticationHandler + { + services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureSignInScheme>()); + return services.AddScheme(authenticationScheme, displayName, configureScheme: null, configureOptions: configureOptions); + } + + // Used to ensure that there's always a default data protection provider + private class EnsureSignInScheme : IInitializeOptions where TOptions : RemoteAuthenticationOptions + { + private readonly AuthenticationOptions _authOptions; + + public EnsureSignInScheme(IOptions authOptions) + { + _authOptions = authOptions.Value; + } + + public void Initialize(string name, TOptions options) + { + options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme; + } + } + } } diff --git a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj index 54b560702..062f7879e 100644 --- a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj +++ b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs index fc663317e..bf875a7a0 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs @@ -5,9 +5,7 @@ using System.Security.Cryptography; using System.Text.Encodings.Web; using System.Threading.Tasks; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -25,10 +23,6 @@ public abstract class RemoteAuthenticationHandler : AuthenticationHand protected string SignInScheme => Options.SignInScheme; - protected IDataProtectionProvider DataProtection { get; set; } - - private readonly AuthenticationOptions _authOptions; - /// /// The handler calls methods on the events which give the application control at certain points where processing is occurring. /// If it is not provided a default instance is supplied which does nothing when the methods are called. @@ -39,17 +33,9 @@ public abstract class RemoteAuthenticationHandler : AuthenticationHand set { base.Events = value; } } - protected RemoteAuthenticationHandler(IOptions sharedOptions, IOptionsSnapshot options, IDataProtectionProvider dataProtection, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + protected RemoteAuthenticationHandler(IOptionsSnapshot options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { - _authOptions = sharedOptions.Value; - DataProtection = dataProtection; - } - - protected override Task InitializeHandlerAsync() - { - DataProtection = Options.DataProtectionProvider ?? DataProtection; - return TaskCache.CompletedTask; } protected override Task CreateEventsAsync() @@ -57,16 +43,6 @@ protected override Task CreateEventsAsync() return Task.FromResult(new RemoteAuthenticationEvents()); } - protected override void InitializeOptions() - { - base.InitializeOptions(); - - if (Options.SignInScheme == null) - { - Options.SignInScheme = _authOptions.DefaultSignInScheme; - } - } - public virtual Task ShouldHandleRequestAsync() { return Task.FromResult(Options.CallbackPath == Request.Path); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs index edd9eb578..060eb649f 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs @@ -50,7 +50,6 @@ public void AddCanBindAgainstDefaultConfig() {"Facebook:BackchannelTimeout", "0.0:0:30"}, //{"Facebook:CallbackPath", "/callbackpath"}, // PathString doesn't convert {"Facebook:ClaimsIssuer", ""}, - {"Facebook:DisplayName", ""}, {"Facebook:RemoteAuthenticationTimeout", "0.0:0:30"}, {"Facebook:SaveTokens", "true"}, {"Facebook:SendAppSecretProof", "true"}, @@ -73,7 +72,6 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.ClaimsIssuer); Assert.Equal("", options.ClientId); Assert.Equal("", options.ClientSecret); - Assert.Equal("", options.DisplayName); Assert.Equal(new TimeSpan(0, 0, 0, 30), options.RemoteAuthenticationTimeout); Assert.True(options.SaveTokens); Assert.True(options.SendAppSecretProof); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs index 0ab3e4493..0a0fbbd19 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs @@ -49,7 +49,6 @@ public void AddCanBindAgainstDefaultConfig() {"Google:BackchannelTimeout", "0.0:0:30"}, //{"Google:CallbackPath", "/callbackpath"}, // PathString doesn't convert {"Google:ClaimsIssuer", ""}, - {"Google:DisplayName", ""}, {"Google:RemoteAuthenticationTimeout", "0.0:0:30"}, {"Google:SaveTokens", "true"}, {"Google:SendAppSecretProof", "true"}, @@ -70,7 +69,6 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.ClaimsIssuer); Assert.Equal("", options.ClientId); Assert.Equal("", options.ClientSecret); - Assert.Equal("", options.DisplayName); Assert.Equal(new TimeSpan(0, 0, 0, 30), options.RemoteAuthenticationTimeout); Assert.True(options.SaveTokens); Assert.Equal("", options.SignInScheme); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs index 42efe00dd..59f1e880c 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs @@ -48,11 +48,10 @@ public void AddCanBindAgainstDefaultConfig() {"Bearer:BackchannelTimeout", "0.0:0:30"}, {"Bearer:Challenge", ""}, {"Bearer:ClaimsIssuer", ""}, - {"Bearer:DisplayName", ""}, {"Bearer:IncludeErrorDetails", "true"}, {"Bearer:MetadataAddress", ""}, {"Bearer:RefreshOnIssuerKeyNotFound", "true"}, - {"Bearer:RequireHttpsMetadata", "true"}, + {"Bearer:RequireHttpsMetadata", "false"}, {"Bearer:SaveToken", "true"}, }; var configurationBuilder = new ConfigurationBuilder(); @@ -67,11 +66,10 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.Authority); Assert.Equal("", options.Challenge); Assert.Equal("", options.ClaimsIssuer); - Assert.Equal("", options.DisplayName); Assert.True(options.IncludeErrorDetails); Assert.Equal("", options.MetadataAddress); Assert.True(options.RefreshOnIssuerKeyNotFound); - Assert.True(options.RequireHttpsMetadata); + Assert.False(options.RequireHttpsMetadata); Assert.True(options.SaveToken); } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs index 1f0f394f3..062215ff4 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs @@ -50,7 +50,6 @@ public void AddCanBindAgainstDefaultConfig() {"Microsoft:BackchannelTimeout", "0.0:0:30"}, //{"Microsoft:CallbackPath", "/callbackpath"}, // PathString doesn't convert {"Microsoft:ClaimsIssuer", ""}, - {"Microsoft:DisplayName", ""}, {"Microsoft:RemoteAuthenticationTimeout", "0.0:0:30"}, {"Microsoft:SaveTokens", "true"}, {"Microsoft:SendAppSecretProof", "true"}, @@ -71,7 +70,6 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.ClaimsIssuer); Assert.Equal("", options.ClientId); Assert.Equal("", options.ClientSecret); - Assert.Equal("", options.DisplayName); Assert.Equal(new TimeSpan(0, 0, 0, 30), options.RemoteAuthenticationTimeout); Assert.True(options.SaveTokens); Assert.Equal("", options.SignInScheme); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs index a3d7f5130..b2459cf81 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs @@ -36,6 +36,7 @@ public void AddCanBindAgainstDefaultConfig() { {"OpenIdConnect:ClientId", ""}, {"OpenIdConnect:ClientSecret", ""}, + {"OpenIdConnect:RequireHttpsMetadata", "false"}, {"OpenIdConnect:Authority", ""} }; var configurationBuilder = new ConfigurationBuilder(); @@ -48,6 +49,7 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.ClientId); Assert.Equal("", options.ClientSecret); Assert.Equal("", options.Authority); + Assert.False(options.RequireHttpsMetadata); } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs index 9993559f6..1acddc5af 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs @@ -42,7 +42,6 @@ public void AddCanBindAgainstDefaultConfig() {"Twitter:BackchannelTimeout", "0.0:0:30"}, //{"Twitter:CallbackPath", "/callbackpath"}, // PathString doesn't convert {"Twitter:ClaimsIssuer", ""}, - {"Twitter:DisplayName", ""}, {"Twitter:RemoteAuthenticationTimeout", "0.0:0:30"}, {"Twitter:SaveTokens", "true"}, {"Twitter:SendAppSecretProof", "true"}, @@ -60,7 +59,6 @@ public void AddCanBindAgainstDefaultConfig() Assert.Equal("", options.ClaimsIssuer); Assert.Equal("", options.ConsumerKey); Assert.Equal("", options.ConsumerSecret); - Assert.Equal("", options.DisplayName); Assert.Equal(new TimeSpan(0, 0, 0, 30), options.RemoteAuthenticationTimeout); Assert.True(options.SaveTokens); Assert.Equal("", options.SignInScheme);