diff --git a/Security.sln b/Security.sln index b9e6ac5672..61ea7b0c31 100644 --- a/Security.sln +++ b/Security.sln @@ -62,6 +62,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution build\repo.props = build\repo.props EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization.Policy", "src\Microsoft.AspNetCore.Authorization.Policy\Microsoft.AspNetCore.Authorization.Policy.csproj", "{58194599-F07D-47A3-9DF2-E21A22C5EF9E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Abstractions", "..\HttpAbstractions\src\Microsoft.AspNetCore.Authentication.Abstractions\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "{97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Core", "..\HttpAbstractions\src\Microsoft.AspNetCore.Authentication.Core\Microsoft.AspNetCore.Authentication.Core.csproj", "{85545633-7E70-47EA-8CD2-30654C80112C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -436,6 +442,54 @@ Global {51563775-C659-4907-9BAF-9995BAB87D01}.Release|x64.Build.0 = Release|Any CPU {51563775-C659-4907-9BAF-9995BAB87D01}.Release|x86.ActiveCfg = Release|Any CPU {51563775-C659-4907-9BAF-9995BAB87D01}.Release|x86.Build.0 = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|x64.ActiveCfg = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|x64.Build.0 = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|x86.ActiveCfg = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Debug|x86.Build.0 = Debug|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|Any CPU.Build.0 = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x64.ActiveCfg = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x64.Build.0 = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x86.ActiveCfg = Release|Any CPU + {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x86.Build.0 = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|x64.ActiveCfg = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|x64.Build.0 = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|x86.ActiveCfg = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Debug|x86.Build.0 = Debug|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|Any CPU.Build.0 = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|x64.ActiveCfg = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|x64.Build.0 = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|x86.ActiveCfg = Release|Any CPU + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D}.Release|x86.Build.0 = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|x64.ActiveCfg = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|x64.Build.0 = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|x86.ActiveCfg = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Debug|x86.Build.0 = Debug|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|Any CPU.Build.0 = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|x64.ActiveCfg = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|x64.Build.0 = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|x86.ActiveCfg = Release|Any CPU + {85545633-7E70-47EA-8CD2-30654C80112C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -464,5 +518,8 @@ Global {A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} {3A7AD414-EBDE-4F92-B307-4E8F19B6117E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} {51563775-C659-4907-9BAF-9995BAB87D01} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} + {58194599-F07D-47A3-9DF2-E21A22C5EF9E} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} + {97D7B36D-1C0D-4F6D-A6A2-808BB7A4574D} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} + {85545633-7E70-47EA-8CD2-30654C80112C} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs index 13a20e55b9..bb2c10fee5 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -247,7 +247,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies } } - protected override async Task HandleSignInAsync(SignInContext signin) + protected override async Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { _signInCalled = true; @@ -259,8 +259,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies Context, Scheme, Options, - signin.Principal, - signin.Properties, + user, + properties, cookieOptions); DateTimeOffset issuedUtc; @@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies await ApplyHeaders(shouldRedirect, signedInContext.Properties); } - protected override async Task HandleSignOutAsync(SignOutContext signOutContext) + protected override async Task HandleSignOutAsync(AuthenticationProperties properties) { _signOutCalled = true; @@ -341,7 +341,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies Context, Scheme, Options, - signOutContext.Properties, + properties, cookieOptions); await Events.SigningOut(context); @@ -401,9 +401,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies return path[0] == '/' && path[1] != '/' && path[1] != '\\'; } - protected override async Task HandleForbiddenAsync(ChallengeContext context) + protected override async Task HandleForbiddenAsync(AuthenticationProperties properties) { - var properties = context.Properties; var returnUrl = properties.RedirectUri; if (string.IsNullOrEmpty(returnUrl)) { @@ -414,14 +413,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies await Events.RedirectToAccessDenied(redirectContext); } - protected override async Task HandleUnauthorizedAsync(ChallengeContext context) + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - var properties = context.Properties; var redirectUri = properties.RedirectUri; if (string.IsNullOrEmpty(redirectUri)) { diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookConfigureOptions.cs index 9305623dad..887789ebf1 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookConfigureOptions.cs @@ -2,15 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.Facebook { - internal class FacebookConfigureOptions : ConfigureNamedOptions + internal class FacebookConfigureOptions : ConfigureDefaultOptions { public FacebookConfigureOptions(IConfiguration config) : base(FacebookDefaults.AuthenticationScheme, - options => config.GetSection(FacebookDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+FacebookDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs index 032be82356..1bb065414d 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookExtensions.cs @@ -3,29 +3,21 @@ using System; using Microsoft.AspNetCore.Authentication.Facebook; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class FacebookAuthenticationOptionsExtensions { - /// - /// Adds facebook authentication with options bound against the "Facebook" section - /// from the IConfiguration in the service container. - /// - /// - /// - public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services) - { - services.AddSingleton, FacebookConfigureOptions>(); - return services.AddFacebookAuthentication(FacebookDefaults.AuthenticationScheme, _ => { }); - } + public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services) + => services.AddFacebookAuthentication(FacebookDefaults.AuthenticationScheme, _ => { }); public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services, Action configureOptions) => services.AddFacebookAuthentication(FacebookDefaults.AuthenticationScheme, configureOptions); public static IServiceCollection AddFacebookAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { + services.AddSingleton, FacebookConfigureOptions>(); return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleConfigureOptions.cs index e19c1fdb1d..1041f1eec5 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleConfigureOptions.cs @@ -2,15 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.Google { - internal class GoogleConfigureOptions : ConfigureNamedOptions + internal class GoogleConfigureOptions : ConfigureDefaultOptions { public GoogleConfigureOptions(IConfiguration config) : base(GoogleDefaults.AuthenticationScheme, - options => config.GetSection(GoogleDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+GoogleDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs index d71e8b461c..c11b155d9c 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleExtensions.cs @@ -3,29 +3,21 @@ using System; using Microsoft.AspNetCore.Authentication.Google; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class GoogleExtensions { - /// - /// Adds google authentication with options bound against the "Google" section - /// from the IConfiguration in the service container. - /// - /// - /// - public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services) - { - services.AddSingleton, GoogleConfigureOptions>(); - return services.AddGoogleAuthentication(GoogleDefaults.AuthenticationScheme, _ => { }); - } + public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services) + => services.AddGoogleAuthentication(GoogleDefaults.AuthenticationScheme, _ => { }); public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services, Action configureOptions) => services.AddGoogleAuthentication(GoogleDefaults.AuthenticationScheme, configureOptions); public static IServiceCollection AddGoogleAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { + services.AddSingleton, GoogleConfigureOptions>(); return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerConfigureOptions.cs index f3571a49c4..d26bf66711 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerConfigureOptions.cs @@ -2,16 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.JwtBearer { - internal class JwtBearerConfigureOptions : ConfigureNamedOptions + internal class JwtBearerConfigureOptions : ConfigureDefaultOptions { // Bind to "Bearer" section by default public JwtBearerConfigureOptions(IConfiguration config) : base(JwtBearerDefaults.AuthenticationScheme, - options => config.GetSection(JwtBearerDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+JwtBearerDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs index 4f6453bd96..0bd3a9400c 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerExtensions.cs @@ -5,22 +5,14 @@ using System; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class JwtBearerExtensions { - /// - /// Adds JwtBearer authentication with options bound against the "Bearer" section - /// from the IConfiguration in the service container. - /// - /// - /// - public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services) - { - services.AddSingleton, JwtBearerConfigureOptions>(); - return services.AddJwtBearerAuthentication(JwtBearerDefaults.AuthenticationScheme, _ => { }); - } + public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services) + => services.AddJwtBearerAuthentication(JwtBearerDefaults.AuthenticationScheme, _ => { }); public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services, Action configureOptions) => services.AddJwtBearerAuthentication(JwtBearerDefaults.AuthenticationScheme, configureOptions); @@ -28,6 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddJwtBearerAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { services.TryAddEnumerable(ServiceDescriptor.Singleton, JwtBearerInitializer>()); + services.AddSingleton, JwtBearerConfigureOptions>(); return services.AddScheme(authenticationScheme, configureOptions); } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index ec48e3e20b..0087d38d0f 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -201,10 +201,10 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer } } - protected override async Task HandleUnauthorizedAsync(ChallengeContext context) + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { var authResult = await HandleAuthenticateOnceSafeAsync(); - var eventContext = new JwtBearerChallengeContext(Context, Scheme, Options, context.Properties) + var eventContext = new JwtBearerChallengeContext(Context, Scheme, Options, properties) { AuthenticateFailure = authResult?.Failure }; @@ -330,12 +330,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer return string.Join("; ", messages); } - protected override Task HandleSignOutAsync(SignOutContext context) + protected override Task HandleSignOutAsync(AuthenticationProperties properties) { throw new NotSupportedException(); } - protected override Task HandleSignInAsync(SignInContext context) + protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotSupportedException(); } diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountConfigureOptions.cs index 520c3758d5..b0be3de977 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountConfigureOptions.cs @@ -2,16 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount { - internal class MicrosoftAccountConfigureOptions : ConfigureNamedOptions + internal class MicrosoftAccountConfigureOptions : ConfigureDefaultOptions { // Bind to "Microsoft" section by default public MicrosoftAccountConfigureOptions(IConfiguration config) : base(MicrosoftAccountDefaults.AuthenticationScheme, - options => config.GetSection(MicrosoftAccountDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+MicrosoftAccountDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs index 6ccb392344..1a7fee370f 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountExtensions.cs @@ -3,29 +3,21 @@ using System; using Microsoft.AspNetCore.Authentication.MicrosoftAccount; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class MicrosoftAccountExtensions { - /// - /// Adds MicrosoftAccount authentication with options bound against the "Microsoft" section - /// from the IConfiguration in the service container. - /// - /// - /// - public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services) - { - services.AddSingleton, MicrosoftAccountConfigureOptions>(); - return services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, o => { }); - } + public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services) + => services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, _ => { }); - public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services, Action configureOptions) => - services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, configureOptions); + public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services, Action configureOptions) + => services.AddMicrosoftAccountAuthentication(MicrosoftAccountDefaults.AuthenticationScheme, configureOptions); public static IServiceCollection AddMicrosoftAccountAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { + services.AddSingleton, MicrosoftAccountConfigureOptions>(); return services.AddOAuthAuthentication(authenticationScheme, configureOptions); } } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs index 63bcdbdf2b..bbe4f4038b 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs @@ -191,14 +191,8 @@ namespace Microsoft.AspNetCore.Authentication.OAuth return context.Ticket; } - protected override async Task HandleUnauthorizedAsync(ChallengeContext context) + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - var properties = context.Properties; if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = CurrentUri; diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectConfigureOptions.cs index 9afae436dd..1a6450b7b2 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectConfigureOptions.cs @@ -2,16 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.OpenIdConnect { - internal class OpenIdConnectConfigureOptions : ConfigureNamedOptions + internal class OpenIdConnectConfigureOptions : ConfigureDefaultOptions { // Bind to "OpenIdConnect" section by default public OpenIdConnectConfigureOptions(IConfiguration config) : base(OpenIdConnectDefaults.AuthenticationScheme, - options => config.GetSection(OpenIdConnectDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+OpenIdConnectDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs index c79dc7212d..b4ac4b9fb7 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectExtensions.cs @@ -5,22 +5,14 @@ using System; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class OpenIdConnectExtensions { - /// - /// Adds OpenIdConnect authentication with options bound against the "OpenIdConnect" section - /// from the IConfiguration in the service container. - /// - /// - /// public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services) - { - services.AddSingleton, OpenIdConnectConfigureOptions>(); - return services.AddOpenIdConnectAuthentication(OpenIdConnectDefaults.AuthenticationScheme, _ => { }); - } + => services.AddOpenIdConnectAuthentication(OpenIdConnectDefaults.AuthenticationScheme, _ => { }); public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services, Action configureOptions) => services.AddOpenIdConnectAuthentication(OpenIdConnectDefaults.AuthenticationScheme, configureOptions); @@ -28,6 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddOpenIdConnectAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { services.TryAddEnumerable(ServiceDescriptor.Singleton, OpenIdConnectInitializer>()); + services.AddSingleton, OpenIdConnectConfigureOptions>(); 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 04359142a6..9c8f4ecc6f 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// Redirect user to the identity provider for sign out /// /// A task executing the sign out procedure - protected override async Task HandleSignOutAsync(SignOutContext signout) + protected override async Task HandleSignOutAsync(AuthenticationProperties properties) { Logger.EnteringOpenIdAuthenticationHandlerHandleSignOutAsync(GetType().FullName); @@ -180,7 +180,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect }; // Get the post redirect URI. - var properties = signout.Properties; if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = BuildRedirectUriIfRelative(Options.PostLogoutRedirectUri); @@ -292,19 +291,13 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect /// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity. /// /// - protected override async Task HandleUnauthorizedAsync(ChallengeContext context) + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - Logger.EnteringOpenIdAuthenticationHandlerHandleUnauthorizedAsync(GetType().FullName); // order for local RedirectUri // 1. challenge.Properties.RedirectUri // 2. CurrentUri if RedirectUri is not set) - var properties = context.Properties; if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = CurrentUri; diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterConfigureOptions.cs index b10435f189..03d7ae8092 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterConfigureOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterConfigureOptions.cs @@ -2,16 +2,16 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.AspNetCore.Authentication.Twitter { - internal class TwitterConfigureOptions : ConfigureNamedOptions + internal class TwitterConfigureOptions : ConfigureDefaultOptions { // Bind to "Twitter" section by default public TwitterConfigureOptions(IConfiguration config) : base(TwitterDefaults.AuthenticationScheme, - options => config.GetSection(TwitterDefaults.AuthenticationScheme).Bind(options)) + options => config.GetSection("Microsoft:AspNetCore:Authentication:Schemes:"+TwitterDefaults.AuthenticationScheme).Bind(options)) { } } } diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs index 3bcc80c3d7..11586f9844 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterExtensions.cs @@ -5,22 +5,14 @@ using System; using Microsoft.AspNetCore.Authentication.Twitter; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; namespace Microsoft.Extensions.DependencyInjection { public static class TwitterExtensions { - /// - /// Adds Twitter authentication with options bound against the "Twitter" section - /// from the IConfiguration in the service container. - /// - /// - /// public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services) - { - services.AddSingleton, TwitterConfigureOptions>(); - return services.AddTwitterAuthentication(TwitterDefaults.AuthenticationScheme, _ => { }); - } + => services.AddTwitterAuthentication(TwitterDefaults.AuthenticationScheme, _ => { }); public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services, Action configureOptions) => services.AddTwitterAuthentication(TwitterDefaults.AuthenticationScheme, configureOptions); @@ -28,6 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddTwitterAuthentication(this IServiceCollection services, string authenticationScheme, Action configureOptions) { services.TryAddEnumerable(ServiceDescriptor.Singleton, TwitterInitializer>()); + services.AddSingleton, TwitterConfigureOptions>(); 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 ff054d3c6f..63a18e83bc 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -140,15 +140,8 @@ namespace Microsoft.AspNetCore.Authentication.Twitter return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); } - protected override async Task HandleUnauthorizedAsync(ChallengeContext context) + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - var properties = context.Properties; - if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = CurrentUri; diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs index ba1c919fe5..b094709196 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -178,44 +179,41 @@ namespace Microsoft.AspNetCore.Authentication protected abstract Task HandleAuthenticateAsync(); - public async Task SignInAsync(SignInContext context) + public async Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { - if (context == null) + if (user == null) { - throw new ArgumentNullException(nameof(context)); + throw new ArgumentNullException(nameof(user)); } - await HandleSignInAsync(context); + properties = properties ?? new AuthenticationProperties(); + await HandleSignInAsync(user, properties); Logger.AuthenticationSchemeSignedIn(Scheme.Name); } - protected virtual Task HandleSignInAsync(SignInContext context) + protected virtual Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { return TaskCache.CompletedTask; } - public async Task SignOutAsync(SignOutContext context) + public async Task SignOutAsync(AuthenticationProperties properties) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - await HandleSignOutAsync(context); + properties = properties ?? new AuthenticationProperties(); + await HandleSignOutAsync(properties); Logger.AuthenticationSchemeSignedOut(Scheme.Name); } - protected virtual Task HandleSignOutAsync(SignOutContext context) + protected virtual Task HandleSignOutAsync(AuthenticationProperties properties) { return TaskCache.CompletedTask; } /// - /// Override this method to deal with a challenge that is forbidden. + /// Override this method to handle Forbid. /// - /// + /// /// A Task. - protected virtual Task HandleForbiddenAsync(ChallengeContext context) + protected virtual Task HandleForbiddenAsync(AuthenticationProperties properties) { Response.StatusCode = 403; return TaskCache.CompletedTask; @@ -226,35 +224,26 @@ namespace Microsoft.AspNetCore.Authentication /// deals an authentication interaction as part of it's request flow. (like adding a response header, or /// changing the 401 result to 302 of a login page or external sign-in location.) /// - /// + /// /// A Task. - protected virtual Task HandleUnauthorizedAsync(ChallengeContext context) + protected virtual Task HandleChallengeAsync(AuthenticationProperties properties) { Response.StatusCode = 401; return TaskCache.CompletedTask; } - public async Task ChallengeAsync(ChallengeContext context) + public async Task ChallengeAsync(AuthenticationProperties properties) { - switch (context.Behavior) - { - case ChallengeBehavior.Automatic: - // If there is a principal already, invoke the forbidden code path - var result = await HandleAuthenticateOnceSafeAsync(); - if (result?.Principal != null) - { - goto case ChallengeBehavior.Forbidden; - } - goto case ChallengeBehavior.Unauthorized; - case ChallengeBehavior.Unauthorized: - await HandleUnauthorizedAsync(context); - Logger.AuthenticationSchemeChallenged(Scheme.Name); - break; - case ChallengeBehavior.Forbidden: - await HandleForbiddenAsync(context); - Logger.AuthenticationSchemeForbidden(Scheme.Name); - break; - } + properties = properties ?? new AuthenticationProperties(); + await HandleChallengeAsync(properties); + Logger.AuthenticationSchemeChallenged(Scheme.Name); + } + + public async Task ForbidAsync(AuthenticationProperties properties) + { + properties = properties ?? new AuthenticationProperties(); + await HandleForbiddenAsync(properties); + Logger.AuthenticationSchemeForbidden(Scheme.Name); } } } diff --git a/src/Microsoft.AspNetCore.Authentication/Events/BaseAuthenticationContext.cs b/src/Microsoft.AspNetCore.Authentication/Events/BaseAuthenticationContext.cs new file mode 100644 index 0000000000..cfe5809c5a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/Events/BaseAuthenticationContext.cs @@ -0,0 +1,41 @@ +// 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.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Base context for authentication. + /// + public abstract class BaseAuthenticationContext : BaseContext + { + /// + /// Constructor. + /// + /// The context. + /// The name of the scheme. + /// The properties. + protected BaseAuthenticationContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties) : base(context) + { + if (string.IsNullOrEmpty(authenticationScheme)) + { + throw new ArgumentException(nameof(authenticationScheme)); + } + + AuthenticationScheme = authenticationScheme; + Properties = properties ?? new AuthenticationProperties(); + } + + /// + /// The name of the scheme. + /// + public string AuthenticationScheme { get; } + + /// + /// Contains the extra meta-data arriving with the authentication. May be altered. + /// + public AuthenticationProperties Properties { get; protected set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication/Events/BaseContext.cs b/src/Microsoft.AspNetCore.Authentication/Events/BaseContext.cs new file mode 100644 index 0000000000..3d65f0dd75 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/Events/BaseContext.cs @@ -0,0 +1,49 @@ +// 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.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Base class used by other context classes. + /// + public abstract class BaseContext + { + /// + /// Constructor. + /// + /// The request context. + protected BaseContext(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + HttpContext = context; + } + + /// + /// The context. + /// + public HttpContext HttpContext { get; } + + /// + /// The request. + /// + public HttpRequest Request + { + get { return HttpContext.Request; } + } + + /// + /// The response. + /// + public HttpResponse Response + { + get { return HttpContext.Response; } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs index a7de95dfed..9394b75532 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Security.Claims; using System.Security.Cryptography; using System.Text.Encodings.Web; using System.Threading.Tasks; @@ -173,18 +174,17 @@ namespace Microsoft.AspNetCore.Authentication return AuthenticateResult.Fail("Remote authentication does not directly support AuthenticateAsync"); } - protected override Task HandleSignOutAsync(SignOutContext context) + protected override Task HandleSignOutAsync(AuthenticationProperties properties) { throw new NotSupportedException(); } - protected override Task HandleSignInAsync(SignInContext context) + protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotSupportedException(); } - // REVIEW: This behaviour needs a test (forwarding of forbidden to sign in scheme) - protected override Task HandleForbiddenAsync(ChallengeContext context) + protected override Task HandleForbiddenAsync(AuthenticationProperties properties) { return Context.ForbidAsync(SignInScheme); } diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/IPolicyEvaluator.cs b/src/Microsoft.AspNetCore.Authorization.Policy/IPolicyEvaluator.cs new file mode 100644 index 0000000000..1717a0ae0a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/IPolicyEvaluator.cs @@ -0,0 +1,36 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authorization.Policy +{ + /// + /// Base class for authorization handlers that need to be called for a specific requirement type. + /// + public interface IPolicyEvaluator + { + /// + /// Does authentication for and sets the resulting + /// to . If no schemes are set, this is a no-op. + /// + /// The . + /// The . + /// unless all schemes specified by fail to authenticate. + Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context); + + /// + /// Attempts authorization for a policy using . + /// + /// The . + /// The result of a call to . + /// The . + /// Returns if authorization succeeds. + /// Otherwise returns if , otherwise + /// returns + Task AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj b/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj new file mode 100644 index 0000000000..5123b70699 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj @@ -0,0 +1,27 @@ + + + + + + ASP.NET Core authorization policy helper classes. + netstandard2.0 + $(NoWarn);CS1591 + true + aspnetcore;authorization + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/PolicyAuthorizationResult.cs b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyAuthorizationResult.cs new file mode 100644 index 0000000000..d7d481dcd6 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyAuthorizationResult.cs @@ -0,0 +1,35 @@ +// 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.Authorization.Policy +{ + public class PolicyAuthorizationResult + { + private PolicyAuthorizationResult() { } + + /// + /// If true, means the callee should challenge and try again. + /// + public bool Challenged { get; private set; } + + /// + /// Authorization was forbidden. + /// + public bool Forbidden { get; private set; } + + /// + /// Authorization was successful. + /// + public bool Succeeded { get; private set; } + + public static PolicyAuthorizationResult Challenge() + => new PolicyAuthorizationResult { Challenged = true }; + + public static PolicyAuthorizationResult Forbid() + => new PolicyAuthorizationResult { Forbidden = true }; + + public static PolicyAuthorizationResult Success() + => new PolicyAuthorizationResult { Succeeded = true }; + + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/PolicyEvaluator.cs b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyEvaluator.cs new file mode 100644 index 0000000000..60f291f671 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyEvaluator.cs @@ -0,0 +1,92 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Internal; + +namespace Microsoft.AspNetCore.Authorization.Policy +{ + public class PolicyEvaluator : IPolicyEvaluator + { + private readonly IAuthorizationService _authorization; + + /// + /// Constructor + /// + /// The authorization service. + public PolicyEvaluator(IAuthorizationService authorization) + { + _authorization = authorization; + } + + /// + /// Does authentication for and sets the resulting + /// to . If no schemes are set, this is a no-op. + /// + /// The . + /// The . + /// unless all schemes specified by failed to authenticate. + public virtual async Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context) + { + if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0) + { + ClaimsPrincipal newPrincipal = null; + foreach (var scheme in policy.AuthenticationSchemes) + { + var result = await context.AuthenticateAsync(scheme); + if (result != null && result.Succeeded) + { + newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, result.Principal); + } + } + + if (newPrincipal != null) + { + context.User = newPrincipal; + return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes))); + } + else + { + context.User = new ClaimsPrincipal(new ClaimsIdentity()); + return AuthenticateResult.None(); + } + } + + return (context.User?.Identity?.IsAuthenticated ?? false) + ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) + : AuthenticateResult.None(); + } + + /// + /// Attempts authorization for a policy using . + /// + /// The . + /// The result of a call to . + /// The . + /// Returns if authorization succeeds. + /// Otherwise returns if , otherwise + /// returns + public virtual async Task AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context) + { + if (policy == null) + { + throw new ArgumentNullException(nameof(policy)); + } + + var result = await _authorization.AuthorizeAsync(context.User, context, policy); + if (result.Succeeded) + { + return PolicyAuthorizationResult.Success(); + } + + // If authentication was successful, return forbidden, otherwise challenge + return (authenticationResult.Succeeded) + ? PolicyAuthorizationResult.Forbid() + : PolicyAuthorizationResult.Challenge(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/PolicyServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyServiceCollectionExtensions.cs new file mode 100644 index 0000000000..9b72a5cab4 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/PolicyServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +// 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.Authorization.Policy; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extension methods for setting up authorization services in an . + /// + public static class PolicyServiceCollectionExtensions + { + /// + /// Adds authorization policy services to the specified . + /// + /// The to add services to. + /// The so that additional calls can be chained. + public static IServiceCollection AddAuthorizationPolicyEvaluator(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.TryAdd(ServiceDescriptor.Transient()); + return services; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationFailure.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationFailure.cs new file mode 100644 index 0000000000..89956c9aa0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationFailure.cs @@ -0,0 +1,46 @@ +// 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.Collections.Generic; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Authorization +{ + /// + /// Encapsulates a failure result of . + /// + public class AuthorizationFailure + { + private AuthorizationFailure() { } + + /// + /// Failure was due to being called. + /// + public bool FailCalled { get; private set; } + + /// + /// Failure was due to these requirements not being met via . + /// + public IEnumerable FailedRequirements { get; private set; } + + /// + /// Return a failure due to being called. + /// + /// The failure. + public static AuthorizationFailure ExplicitFail() + => new AuthorizationFailure + { + FailCalled = true, + FailedRequirements = new IAuthorizationRequirement[0] + }; + + /// + /// Return a failure due to some requirements not being met via . + /// + /// The requirements that were not met. + /// The failure. + public static AuthorizationFailure Failed(IEnumerable failed) + => new AuthorizationFailure { FailedRequirements = failed }; + + } +} diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs index 5cba0fd9e3..36e0ca7c38 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs @@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Authorization policyBuilder.RequireRole(trimmedRolesSplit); useDefaultPolicy = false; } - var authTypesSplit = authorizeDatum.ActiveAuthenticationSchemes?.Split(','); + var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(','); if (authTypesSplit != null && authTypesSplit.Any()) { foreach (var authType in authTypesSplit) diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationResult.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationResult.cs new file mode 100644 index 0000000000..46dca35fb5 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationResult.cs @@ -0,0 +1,37 @@ +// 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.Collections.Generic; +using System.Security.Claims; + +namespace Microsoft.AspNetCore.Authorization +{ + /// + /// Encapsulates the result of . + /// + public class AuthorizationResult + { + private AuthorizationResult() { } + + /// + /// True if authorization was successful. + /// + public bool Succeeded { get; private set; } + + /// + /// Contains information about why authorization failed. + /// + public AuthorizationFailure Failure { get; private set; } + + /// + /// Returns a successful result. + /// + /// A successful result. + public static AuthorizationResult Success() => new AuthorizationResult { Succeeded = true }; + + public static AuthorizationResult Failed(AuthorizationFailure failure) => new AuthorizationResult { Failure = failure }; + + public static AuthorizationResult Failed() => new AuthorizationResult { Failure = AuthorizationFailure.ExplicitFail() }; + + } +} diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs index a9961b69ef..c089fba285 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs @@ -27,6 +27,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); + services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); services.TryAdd(ServiceDescriptor.Transient()); services.TryAddEnumerable(ServiceDescriptor.Transient()); diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceExtensions.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceExtensions.cs index 3b78c952eb..c128152326 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceExtensions.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceExtensions.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether requirement evaluation has succeeded or failed. /// This value is true when the user fulfills the policy, otherwise false. /// - public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, IAuthorizationRequirement requirement) + public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, IAuthorizationRequirement requirement) { if (service == null) { @@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether policy evaluation has succeeded or failed. /// This value is true when the user fulfills the policy, otherwise false. /// - public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy) + public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy) { if (service == null) { @@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether policy evaluation has succeeded or failed. /// This value is true when the user fulfills the policy, otherwise false. /// - public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, AuthorizationPolicy policy) + public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, AuthorizationPolicy policy) { if (service == null) { @@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether policy evaluation has succeeded or failed. /// This value is true when the user fulfills the policy, otherwise false. /// - public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName) + public static Task AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName) { if (service == null) { diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs index c911dc56bc..63bfa30d45 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs @@ -25,13 +25,29 @@ namespace Microsoft.AspNetCore.Authorization Policy = policy; } - /// + /// + /// Gets or sets the policy name that determines access to the resource. + /// public string Policy { get; set; } - /// + /// + /// Gets or sets a comma delimited list of roles that are allowed to access the resource. + /// public string Roles { get; set; } - /// - public string ActiveAuthenticationSchemes { get; set; } + /// + /// Gets or sets a comma delimited list of schemes from which user information is constructed. + /// + public string AuthenticationSchemes { get; set; } + + /// + /// Gets or sets a comma delimited list of schemes from which user information is constructed. + /// + [Obsolete("Use AuthenticationSchemes instead.", error: false)] + public string ActiveAuthenticationSchemes + { + get => AuthenticationSchemes; + set => AuthenticationSchemes = value; + } } } diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationEvaluator.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationEvaluator.cs index 64cc695b88..4bbc283be0 100644 --- a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationEvaluator.cs +++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationEvaluator.cs @@ -9,23 +9,15 @@ namespace Microsoft.AspNetCore.Authorization public class DefaultAuthorizationEvaluator : IAuthorizationEvaluator { /// - /// Returns true, if authorization has failed. + /// Determines whether the authorization result was successful or not. /// /// The authorization information. - /// True if authorization has failed. - public virtual bool HasFailed(AuthorizationHandlerContext context) - { - return context.HasFailed; - } - - /// - /// Returns true, if authorization has succeeded. - /// - /// The authorization information. - /// True if authorization has succeeded. - public virtual bool HasSucceeded(AuthorizationHandlerContext context) - { - return context.HasSucceeded; - } + /// The . + public AuthorizationResult Evaluate(AuthorizationHandlerContext context) + => context.HasSucceeded + ? AuthorizationResult.Success() + : AuthorizationResult.Failed(context.HasFailed + ? AuthorizationFailure.ExplicitFail() + : AuthorizationFailure.Failed(context.PendingRequirements)); } } diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerProvider.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerProvider.cs new file mode 100644 index 0000000000..d297d4cdc6 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerProvider.cs @@ -0,0 +1,35 @@ +// 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.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authorization +{ + /// + /// The default implementation of a handler provider, + /// which provides the s for an authorization request. + /// + public class DefaultAuthorizationHandlerProvider : IAuthorizationHandlerProvider + { + private readonly IEnumerable _handlers; + + /// + /// Creates a new instance of . + /// + /// The s. + public DefaultAuthorizationHandlerProvider(IEnumerable handlers) + { + if (handlers == null) + { + throw new ArgumentNullException(nameof(handlers)); + } + + _handlers = handlers; + } + + public Task> GetHandlersAsync(AuthorizationHandlerContext context) + => Task.FromResult(_handlers); + } +} diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs index 8980d51ac6..3380f64b14 100644 --- a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs +++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; @@ -19,19 +18,11 @@ namespace Microsoft.AspNetCore.Authorization { private readonly AuthorizationOptions _options; private readonly IAuthorizationHandlerContextFactory _contextFactory; + private readonly IAuthorizationHandlerProvider _handlers; private readonly IAuthorizationEvaluator _evaluator; private readonly IAuthorizationPolicyProvider _policyProvider; - private readonly IList _handlers; private readonly ILogger _logger; - /// - /// Creates a new instance of . - /// - /// The used to provide policies. - /// The handlers used to fulfill s. - /// The logger used to log messages, warnings and errors. - public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger) : this(policyProvider, handlers, logger, new DefaultAuthorizationHandlerContextFactory(), new DefaultAuthorizationEvaluator(), Options.Create(new AuthorizationOptions())) { } - /// /// Creates a new instance of . /// @@ -41,7 +32,7 @@ namespace Microsoft.AspNetCore.Authorization /// The used to create the context to handle the authorization. /// The used to determine if authorzation was successful. /// The used. - public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator, IOptions options) + public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IAuthorizationHandlerProvider handlers, ILogger logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator, IOptions options) { if (options == null) { @@ -69,7 +60,7 @@ namespace Microsoft.AspNetCore.Authorization } _options = options.Value; - _handlers = handlers.ToArray(); + _handlers = handlers; _policyProvider = policyProvider; _logger = logger; _evaluator = evaluator; @@ -86,7 +77,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether authorization has succeded. /// This value is true when the user fulfills the policy otherwise false. /// - public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) + public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) { if (requirements == null) { @@ -94,7 +85,8 @@ namespace Microsoft.AspNetCore.Authorization } var authContext = _contextFactory.CreateContext(requirements, user, resource); - foreach (var handler in _handlers) + var handlers = await _handlers.GetHandlersAsync(authContext); + foreach (var handler in handlers) { await handler.HandleAsync(authContext); if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed) @@ -103,16 +95,16 @@ namespace Microsoft.AspNetCore.Authorization } } - if (_evaluator.HasSucceeded(authContext)) + var result = _evaluator.Evaluate(authContext); + if (result.Succeeded) { _logger.UserAuthorizationSucceeded(GetUserNameForLogging(user)); - return true; } else { _logger.UserAuthorizationFailed(GetUserNameForLogging(user)); - return false; } + return result; } private string GetUserNameForLogging(ClaimsPrincipal user) @@ -147,7 +139,7 @@ namespace Microsoft.AspNetCore.Authorization /// A flag indicating whether authorization has succeded. /// This value is true when the user fulfills the policy otherwise false. /// - public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) + public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) { if (policyName == null) { diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationEvaluator.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationEvaluator.cs index 7b2c5d1bc5..baa6f828cd 100644 --- a/src/Microsoft.AspNetCore.Authorization/IAuthorizationEvaluator.cs +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationEvaluator.cs @@ -9,17 +9,10 @@ namespace Microsoft.AspNetCore.Authorization public interface IAuthorizationEvaluator { /// - /// Returns true, if authorization has failed. + /// Determines whether the authorization result was successful or not. /// /// The authorization information. - /// True if authorization has failed. - bool HasFailed(AuthorizationHandlerContext context); - - /// - /// Returns true, if authorization has succeeded. - /// - /// The authorization information. - /// True if authorization has succeeded. - bool HasSucceeded(AuthorizationHandlerContext context); + /// The . + AuthorizationResult Evaluate(AuthorizationHandlerContext context); } } diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerProvider.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerProvider.cs new file mode 100644 index 0000000000..7f0d9f5d31 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerProvider.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. + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authorization +{ + /// + /// A type which can provide the s for an authorization request. + /// + public interface IAuthorizationHandlerProvider + { + /// + /// Return the handlers that will be called for the authorization request. + /// + /// The . + /// The list of handlers. + Task> GetHandlersAsync(AuthorizationHandlerContext context); + } +} diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs index a130c84b0d..32af746072 100644 --- a/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Authorization /// Resource is an optional parameter and may be null. Please ensure that you check it is not /// null before acting upon it. /// - Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements); + Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements); /// /// Checks if a user meets a specific authorization policy @@ -49,6 +49,6 @@ namespace Microsoft.AspNetCore.Authorization /// Resource is an optional parameter and may be null. Please ensure that you check it is not /// null before acting upon it. /// - Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName); + Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName); } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizeData.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizeData.cs index b48449b5cb..1196db82d4 100644 --- a/src/Microsoft.AspNetCore.Authorization/IAuthorizeData.cs +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizeData.cs @@ -21,6 +21,6 @@ namespace Microsoft.AspNetCore.Authorization /// /// Gets or sets a comma delimited list of schemes from which user information is constructed. /// - string ActiveAuthenticationSchemes { get; set; } + string AuthenticationSchemes { get; set; } } } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/AuthenticationMiddlewareTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/AuthenticationMiddlewareTests.cs index c4720eb30c..b09f13cab9 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/AuthenticationMiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/AuthenticationMiddlewareTests.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. using System; -using System.Net; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -77,7 +77,12 @@ namespace Microsoft.AspNetCore.Authentication throw new NotImplementedException(); } - public Task ChallengeAsync(ChallengeContext context) + public Task ChallengeAsync(AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + + public Task ForbidAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } @@ -94,12 +99,12 @@ namespace Microsoft.AspNetCore.Authentication return Task.FromResult(0); } - public Task SignInAsync(SignInContext context) + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotImplementedException(); } - public Task SignOutAsync(SignOutContext context) + public Task SignOutAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } @@ -114,7 +119,12 @@ namespace Microsoft.AspNetCore.Authentication throw new NotImplementedException(); } - public Task ChallengeAsync(ChallengeContext context) + public Task ChallengeAsync(AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + + public Task ForbidAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } @@ -130,12 +140,12 @@ namespace Microsoft.AspNetCore.Authentication return Task.FromResult(0); } - public Task SignInAsync(SignInContext context) + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotImplementedException(); } - public Task SignOutAsync(SignOutContext context) + public Task SignOutAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } @@ -150,7 +160,12 @@ namespace Microsoft.AspNetCore.Authentication throw new NotImplementedException(); } - public Task ChallengeAsync(ChallengeContext context) + public Task ChallengeAsync(AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + + public Task ForbidAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } @@ -166,12 +181,12 @@ namespace Microsoft.AspNetCore.Authentication return Task.FromResult(0); } - public Task SignInAsync(SignInContext context) + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotImplementedException(); } - public Task SignOutAsync(SignOutContext context) + public Task SignOutAsync(AuthenticationProperties properties) { throw new NotImplementedException(); } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs index e38dc15870..677120e4be 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs @@ -702,22 +702,6 @@ namespace Microsoft.AspNetCore.Authentication.Cookies Assert.True(transaction1.SetCookie.Contains("path=/base")); } - [Fact] - public async Task CookieTurnsChallengeIntoForbidWithCookie() - { - var server = CreateServer(o => { }, SignInAsAlice); - - var transaction1 = await SendAsync(server, "http://example.com/testpath"); - - var url = "http://example.com/challenge"; - var transaction2 = await SendAsync(server, url, transaction1.CookieNameValue); - - Assert.Equal(HttpStatusCode.Redirect, transaction2.Response.StatusCode); - var location = transaction2.Response.Headers.Location; - Assert.Equal("/Account/AccessDenied", location.LocalPath); - Assert.Equal("?ReturnUrl=%2Fchallenge", location.Query); - } - [Fact] public async Task CookieChallengeRedirectsToLoginWithoutCookie() { @@ -744,25 +728,6 @@ namespace Microsoft.AspNetCore.Authentication.Cookies Assert.Equal("/Account/AccessDenied", location.LocalPath); } - [Fact] - public async Task CookieTurns401ToAccessDeniedWhenSetWithCookie() - { - var server = CreateServer(o => - { - o.AccessDeniedPath = new PathString("/accessdenied"); - }, - SignInAsAlice); - - var transaction1 = await SendAsync(server, "http://example.com/testpath"); - - var transaction2 = await SendAsync(server, "http://example.com/challenge", transaction1.CookieNameValue); - - Assert.Equal(HttpStatusCode.Redirect, transaction2.Response.StatusCode); - - var location = transaction2.Response.Headers.Location; - Assert.Equal("/accessdenied", location.LocalPath); - } - [Fact] public async Task CookieChallengeRedirectsWithLoginPath() { @@ -799,7 +764,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies public async Task MapWillAffectChallengeOnlyWithUseAuth(bool useAuth) { var builder = new WebHostBuilder() - .Configure(app => { + .Configure(app => + { if (useAuth) { app.UseAuthentication(); @@ -1296,7 +1262,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies } else if (req.Path == new PathString("/unauthorized")) { - await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties(), ChallengeBehavior.Unauthorized); + await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties()); } else if (req.Path == new PathString("/protected/CustomRedirect")) { diff --git a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs index 060eb649fc..657c957bfb 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Newtonsoft.Json; using Xunit; @@ -44,23 +45,26 @@ namespace Microsoft.AspNetCore.Authentication.Facebook { var dic = new Dictionary { - {"Facebook:AppId", ""}, - {"Facebook:AppSecret", ""}, - {"Facebook:AuthorizationEndpoint", ""}, - {"Facebook:BackchannelTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:AppId", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:AppSecret", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:AuthorizationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:BackchannelTimeout", "0.0:0:30"}, //{"Facebook:CallbackPath", "/callbackpath"}, // PathString doesn't convert - {"Facebook:ClaimsIssuer", ""}, - {"Facebook:RemoteAuthenticationTimeout", "0.0:0:30"}, - {"Facebook:SaveTokens", "true"}, - {"Facebook:SendAppSecretProof", "true"}, - {"Facebook:SignInScheme", ""}, - {"Facebook:TokenEndpoint", ""}, - {"Facebook:UserInformationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:ClaimsIssuer", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:RemoteAuthenticationTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:SaveTokens", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:SendAppSecretProof", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:SignInScheme", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:TokenEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Facebook:UserInformationEndpoint", ""}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddFacebookAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddFacebookAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(FacebookDefaults.AuthenticationScheme); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs index 2c86bc7781..b316e62751 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs @@ -19,6 +19,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Newtonsoft.Json; using Xunit; @@ -43,23 +44,26 @@ namespace Microsoft.AspNetCore.Authentication.Google { var dic = new Dictionary { - {"Google:ClientId", ""}, - {"Google:ClientSecret", ""}, - {"Google:AuthorizationEndpoint", ""}, - {"Google:BackchannelTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:ClientId", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:ClientSecret", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:AuthorizationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:BackchannelTimeout", "0.0:0:30"}, //{"Google:CallbackPath", "/callbackpath"}, // PathString doesn't convert - {"Google:ClaimsIssuer", ""}, - {"Google:RemoteAuthenticationTimeout", "0.0:0:30"}, - {"Google:SaveTokens", "true"}, - {"Google:SendAppSecretProof", "true"}, - {"Google:SignInScheme", ""}, - {"Google:TokenEndpoint", ""}, - {"Google:UserInformationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:ClaimsIssuer", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:RemoteAuthenticationTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:SaveTokens", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:SendAppSecretProof", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:SignInScheme", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:TokenEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Google:UserInformationEndpoint", ""}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddGoogleAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddGoogleAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(GoogleDefaults.AuthenticationScheme); @@ -883,42 +887,6 @@ namespace Microsoft.AspNetCore.Authentication.Google Assert.Equal("yup", transaction.FindClaimValue("xform")); } - [Fact] - public async Task ChallengeGoogleWhenAlreadySignedInReturnsForbidden() - { - var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("GoogleTest")); - var server = CreateServer(o => - { - o.ClientId = "Test Id"; - o.ClientSecret = "Test Secret"; - o.StateDataFormat = stateFormat; - o.SaveTokens = true; - o.BackchannelHttpHandler = CreateBackchannel(); - }); - - // Skip the challenge step, go directly to the callback path - - var properties = new AuthenticationProperties(); - var correlationKey = ".xsrf"; - var correlationValue = "TestCorrelationId"; - properties.Items.Add(correlationKey, correlationValue); - properties.RedirectUri = "/me"; - var state = stateFormat.Protect(properties); - var transaction = await server.SendAsync( - "https://example.com/signin-google?code=TestCode&state=" + UrlEncoder.Default.Encode(state), - $".AspNetCore.Correlation.Google.{correlationValue}=N"); - Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); - Assert.Equal("/me", transaction.Response.Headers.GetValues("Location").First()); - Assert.Equal(2, transaction.SetCookie.Count); - Assert.Contains($".AspNetCore.Correlation.Google.{correlationValue}", transaction.SetCookie[0]); // Delete - Assert.Contains(".AspNetCore." + TestExtensions.CookieAuthenticationScheme, transaction.SetCookie[1]); - - var authCookie = transaction.AuthenticationCookieValue; - transaction = await server.SendAsync("https://example.com/challenge", authCookie); - Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); - Assert.StartsWith("https://example.com/Account/AccessDenied?", transaction.Response.Headers.Location.OriginalString); - } - [Fact] public async Task AuthenticateFacebookWhenAlreadySignedWithGoogleReturnsNull() { diff --git a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs index 59f1e880c7..6746f5c3f1 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs @@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Microsoft.IdentityModel.Tokens; using Xunit; @@ -43,21 +44,24 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer { var dic = new Dictionary { - {"Bearer:Audience", ""}, - {"Bearer:Authority", ""}, - {"Bearer:BackchannelTimeout", "0.0:0:30"}, - {"Bearer:Challenge", ""}, - {"Bearer:ClaimsIssuer", ""}, - {"Bearer:IncludeErrorDetails", "true"}, - {"Bearer:MetadataAddress", ""}, - {"Bearer:RefreshOnIssuerKeyNotFound", "true"}, - {"Bearer:RequireHttpsMetadata", "false"}, - {"Bearer:SaveToken", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:Audience", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:Authority", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:BackchannelTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:Challenge", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:ClaimsIssuer", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:IncludeErrorDetails", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:MetadataAddress", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:RefreshOnIssuerKeyNotFound", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:RequireHttpsMetadata", "false"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:SaveToken", "true"}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddJwtBearerAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddJwtBearerAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(JwtBearerDefaults.AuthenticationScheme); @@ -74,21 +78,26 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer } [Fact] - public void AddWithDelegateIgnoresConfig() + public void AddWithDelegateOverridesConfig() { var dic = new Dictionary { - {"Bearer:Audience", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:Audience", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:Authority", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Bearer:RequireHttpsMetadata", "false"} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddJwtBearerAuthentication(o => o.IncludeErrorDetails = true).AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddJwtBearerAuthentication(o => o.Authority = "authority") + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(JwtBearerDefaults.AuthenticationScheme); - Assert.Null(options.Audience); - Assert.True(options.IncludeErrorDetails); + Assert.Equal("", options.Audience); + Assert.Equal("authority", options.Authority); } [ConditionalFact(Skip = "Need to remove dependency on AAD since the generated tokens will expire")] @@ -440,27 +449,6 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer Assert.Equal("Bob le Tout Puissant", response.ResponseText); } - [Fact] - public async Task BearerTurns401To403IfAuthenticated() - { - var server = CreateServer(options => - { - options.SecurityTokenValidators.Clear(); - options.SecurityTokenValidators.Add(new BlobTokenValidator("JWT")); - }); - - var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token"); - Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode); - } - - [Fact] - public async Task BearerDoesNothingTo401IfNotAuthenticated() - { - var server = CreateServer(); - var response = await SendAsync(server, "http://example.com/unauthorized"); - Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode); - } - [Fact] public async Task EventOnMessageReceivedSkip_NoMoreEventsExecuted() { diff --git a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs index 062215ff46..0d49052f5b 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Newtonsoft.Json; using Xunit; @@ -44,23 +45,26 @@ namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount { var dic = new Dictionary { - {"Microsoft:ClientId", ""}, - {"Microsoft:ClientSecret", ""}, - {"Microsoft:AuthorizationEndpoint", ""}, - {"Microsoft:BackchannelTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:ClientId", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:ClientSecret", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:AuthorizationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:BackchannelTimeout", "0.0:0:30"}, //{"Microsoft:CallbackPath", "/callbackpath"}, // PathString doesn't convert - {"Microsoft:ClaimsIssuer", ""}, - {"Microsoft:RemoteAuthenticationTimeout", "0.0:0:30"}, - {"Microsoft:SaveTokens", "true"}, - {"Microsoft:SendAppSecretProof", "true"}, - {"Microsoft:SignInScheme", ""}, - {"Microsoft:TokenEndpoint", ""}, - {"Microsoft:UserInformationEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:ClaimsIssuer", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:RemoteAuthenticationTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:SaveTokens", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:SendAppSecretProof", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:SignInScheme", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:TokenEndpoint", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Microsoft:UserInformationEndpoint", ""}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddMicrosoftAccountAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddMicrosoftAccountAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(MicrosoftAccountDefaults.AuthenticationScheme); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs index b2459cf819..9dc597b856 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Xunit; @@ -34,15 +35,18 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect { var dic = new Dictionary { - {"OpenIdConnect:ClientId", ""}, - {"OpenIdConnect:ClientSecret", ""}, - {"OpenIdConnect:RequireHttpsMetadata", "false"}, - {"OpenIdConnect:Authority", ""} + {"Microsoft:AspNetCore:Authentication:Schemes:OpenIdConnect:ClientId", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:OpenIdConnect:ClientSecret", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:OpenIdConnect:RequireHttpsMetadata", "false"}, + {"Microsoft:AspNetCore:Authentication:Schemes:OpenIdConnect:Authority", ""} }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddOpenIdConnectAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddOpenIdConnectAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(OpenIdConnectDefaults.AuthenticationScheme); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs index 41429cd377..b37ae53d5b 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs @@ -5,7 +5,7 @@ using System; using System.IO; using System.Linq; using System.Security.Claims; -using Microsoft.AspNetCore.Http.Authentication; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Testing.xunit; using Xunit; diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs index 1acddc5afd..b8af66cdfb 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options.Infrastructure; using Xunit; namespace Microsoft.AspNetCore.Authentication.Twitter @@ -37,20 +38,23 @@ namespace Microsoft.AspNetCore.Authentication.Twitter { var dic = new Dictionary { - {"Twitter:ConsumerKey", ""}, - {"Twitter:ConsumerSecret", ""}, - {"Twitter:BackchannelTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:ConsumerKey", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:ConsumerSecret", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:BackchannelTimeout", "0.0:0:30"}, //{"Twitter:CallbackPath", "/callbackpath"}, // PathString doesn't convert - {"Twitter:ClaimsIssuer", ""}, - {"Twitter:RemoteAuthenticationTimeout", "0.0:0:30"}, - {"Twitter:SaveTokens", "true"}, - {"Twitter:SendAppSecretProof", "true"}, - {"Twitter:SignInScheme", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:ClaimsIssuer", ""}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:RemoteAuthenticationTimeout", "0.0:0:30"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:SaveTokens", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:SendAppSecretProof", "true"}, + {"Microsoft:AspNetCore:Authentication:Schemes:Twitter:SignInScheme", ""}, }; var configurationBuilder = new ConfigurationBuilder(); configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); - var services = new ServiceCollection().AddTwitterAuthentication().AddSingleton(config); + var services = new ServiceCollection() + .AddSingleton, ConfigureDefaults>() + .AddTwitterAuthentication() + .AddSingleton(config); var sp = services.BuildServiceProvider(); var options = sp.GetRequiredService>().Get(TwitterDefaults.AuthenticationScheme); diff --git a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs index 6825dd868b..714b26c7e1 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs +++ b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs @@ -25,9 +25,9 @@ namespace Microsoft.AspNetCore.Authroization.Test // Arrange var attributes = new AuthorizeAttribute[] { new AuthorizeAttribute(), - new AuthorizeAttribute("1") { ActiveAuthenticationSchemes = "dupe" }, - new AuthorizeAttribute("2") { ActiveAuthenticationSchemes = "dupe" }, - new AuthorizeAttribute { Roles = "r1,r2", ActiveAuthenticationSchemes = "roles" }, + new AuthorizeAttribute("1") { AuthenticationSchemes = "dupe" }, + new AuthorizeAttribute("2") { AuthenticationSchemes = "dupe" }, + new AuthorizeAttribute { Roles = "r1,r2", AuthenticationSchemes = "roles" }, }; var options = new AuthorizationOptions(); options.AddPolicy("1", policy => policy.RequireClaim("1")); @@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Authroization.Test // Arrange var attributes = new AuthorizeAttribute[] { new AuthorizeAttribute(), - new AuthorizeAttribute("2") { ActiveAuthenticationSchemes = "dupe" } + new AuthorizeAttribute("2") { AuthenticationSchemes = "dupe" } }; var options = new AuthorizationOptions(); options.DefaultPolicy = new AuthorizationPolicyBuilder("default").RequireClaim("default").Build(); @@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Authroization.Test { // Arrange var attributes = new AuthorizeAttribute[] { - new AuthorizeAttribute() { ActiveAuthenticationSchemes = "a1 , a2" } + new AuthorizeAttribute() { AuthenticationSchemes = "a1 , a2" } }; var options = new AuthorizationOptions(); @@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Authroization.Test { // Arrange var attributes = new AuthorizeAttribute[] { - new AuthorizeAttribute() { ActiveAuthenticationSchemes = "a1 , , ,,, a2" } + new AuthorizeAttribute() { AuthenticationSchemes = "a1 , , ,,, a2" } }; var options = new AuthorizationOptions(); diff --git a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs index 246eed9a50..ef17b94620 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs +++ b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs @@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -126,7 +126,8 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(new ClaimsPrincipal(), "Custom"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); + Assert.True(allowed.Failure.FailCalled); Assert.True(handler1.Invoked); Assert.True(handler2.Invoked); } @@ -154,7 +155,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(new ClaimsPrincipal(), "Custom"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); Assert.True(handler1.Invoked); Assert.Equal(invokeAllHandlers, handler2.Invoked); } @@ -171,8 +172,6 @@ namespace Microsoft.AspNetCore.Authorization.Test } } - - [Fact] public async Task Authorize_ShouldFailWhenAllRequirementsNotHandled() { @@ -196,7 +195,8 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); + Assert.IsType(allowed.Failure.FailedRequirements.First()); } [Fact] @@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -248,7 +248,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -291,7 +291,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(null, null, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -311,7 +311,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -372,7 +372,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, policy.Build()); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -393,7 +393,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, policy.Build()); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, policy.Build()); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -427,7 +427,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, policy.Build()); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -443,7 +443,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, policy.Build()); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -464,7 +464,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, policy.Build()); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -489,7 +489,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -527,7 +527,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Hao"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -553,7 +553,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Hao"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -575,7 +575,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Hao"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -597,7 +597,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Hao"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -622,7 +622,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Any"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -642,7 +642,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Any"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } public class CustomRequirement : IAuthorizationRequirement { } @@ -675,7 +675,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Custom"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -696,7 +696,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Custom"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } public class PassThroughRequirement : AuthorizationHandler, IAuthorizationRequirement @@ -736,7 +736,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Passthrough"); // Assert - Assert.Equal(shouldSucceed, allowed); + Assert.Equal(shouldSucceed, allowed.Succeeded); } [Fact] @@ -764,7 +764,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Combined"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -791,7 +791,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Combined"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } [Fact] @@ -818,7 +818,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, null, "Combined"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } public class ExpenseReport { } @@ -880,9 +880,9 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.True(await authorizationService.AuthorizeAsync(user, null, Operations.Edit)); - Assert.True(await authorizationService.AuthorizeAsync(user, null, Operations.Delete)); - Assert.True(await authorizationService.AuthorizeAsync(user, null, Operations.Create)); + Assert.True((await authorizationService.AuthorizeAsync(user, null, Operations.Edit)).Succeeded); + Assert.True((await authorizationService.AuthorizeAsync(user, null, Operations.Delete)).Succeeded); + Assert.True((await authorizationService.AuthorizeAsync(user, null, Operations.Create)).Succeeded); } public class NotCalledHandler : AuthorizationHandler @@ -922,8 +922,8 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.False(await authorizationService.AuthorizeAsync(user, 1, Operations.Edit)); - Assert.True(await authorizationService.AuthorizeAsync(user, 2, Operations.Edit)); + Assert.False((await authorizationService.AuthorizeAsync(user, 1, Operations.Edit)).Succeeded); + Assert.True((await authorizationService.AuthorizeAsync(user, 2, Operations.Edit)).Succeeded); } @@ -945,7 +945,7 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.False(await authorizationService.AuthorizeAsync(user, 1, Operations.Edit)); + Assert.False((await authorizationService.AuthorizeAsync(user, 1, Operations.Edit)).Succeeded); } [Fact] @@ -960,9 +960,9 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.True(await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Edit)); - Assert.False(await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Delete)); - Assert.False(await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Create)); + Assert.True((await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Edit)).Succeeded); + Assert.False((await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Delete)).Succeeded); + Assert.False((await authorizationService.AuthorizeAsync(user, new ExpenseReport(), Operations.Create)).Succeeded); } [Fact] @@ -977,7 +977,7 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.False(await authorizationService.AuthorizeAsync(user, null, Operations.Edit)); + Assert.False((await authorizationService.AuthorizeAsync(user, null, Operations.Edit)).Succeeded); } [Fact] @@ -996,7 +996,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } [Fact] @@ -1015,7 +1015,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.True(allowed); + Assert.True(allowed.Succeeded); } public class StaticPolicyProvider : IAuthorizationPolicyProvider @@ -1049,7 +1049,7 @@ namespace Microsoft.AspNetCore.Authorization.Test var allowed = await authorizationService.AuthorizeAsync(user, "Basic"); // Assert - Assert.False(allowed); + Assert.False(allowed.Succeeded); } public class DynamicPolicyProvider : IAuthorizationPolicyProvider @@ -1081,23 +1081,15 @@ namespace Microsoft.AspNetCore.Authorization.Test // Act // Assert - Assert.False(await authorizationService.AuthorizeAsync(user, "0")); - Assert.True(await authorizationService.AuthorizeAsync(user, "1")); - Assert.True(await authorizationService.AuthorizeAsync(user, "2")); - Assert.False(await authorizationService.AuthorizeAsync(user, "3")); + Assert.False((await authorizationService.AuthorizeAsync(user, "0")).Succeeded); + Assert.True((await authorizationService.AuthorizeAsync(user, "1")).Succeeded); + Assert.True((await authorizationService.AuthorizeAsync(user, "2")).Succeeded); + Assert.False((await authorizationService.AuthorizeAsync(user, "3")).Succeeded); } public class SuccessEvaluator : IAuthorizationEvaluator { - public bool HasFailed(AuthorizationHandlerContext context) - { - return false; - } - - public bool HasSucceeded(AuthorizationHandlerContext context) - { - return true; - } + public AuthorizationResult Evaluate(AuthorizationHandlerContext context) => AuthorizationResult.Success(); } [Fact] @@ -1108,7 +1100,8 @@ namespace Microsoft.AspNetCore.Authorization.Test services.AddSingleton(); services.AddAuthorization(options => options.AddPolicy("Fail", p => p.RequireAssertion(c => false))); }); - Assert.True(await authorizationService.AuthorizeAsync(null, "Fail")); + var result = await authorizationService.AuthorizeAsync(null, "Fail"); + Assert.True(result.Succeeded); } @@ -1149,7 +1142,26 @@ namespace Microsoft.AspNetCore.Authorization.Test services.AddSingleton(); services.AddAuthorization(options => options.AddPolicy("Success", p => p.RequireAssertion(c => true))); }); - Assert.False(await authorizationService.AuthorizeAsync(null, "Success")); + Assert.False((await authorizationService.AuthorizeAsync(null, "Success")).Succeeded); + } + + public class SadHandlerProvider : IAuthorizationHandlerProvider + { + public Task> GetHandlersAsync(AuthorizationHandlerContext context) + { + return Task.FromResult>(new IAuthorizationHandler[1] { new FailHandler() }); + } + } + + [Fact] + public async Task CanUseCustomHandlerProvider() + { + var authorizationService = BuildAuthorizationService(services => + { + services.AddSingleton(); + services.AddAuthorization(options => options.AddPolicy("Success", p => p.RequireAssertion(c => true))); + }); + Assert.False((await authorizationService.AuthorizeAsync(null, "Success")).Succeeded); } } diff --git a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj index 48b134740a..6eb74aeebf 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj +++ b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj @@ -9,9 +9,11 @@ + + diff --git a/test/Microsoft.AspNetCore.Authorization.Test/PolicyEvaluatorTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/PolicyEvaluatorTests.cs new file mode 100644 index 0000000000..216fc1440e --- /dev/null +++ b/test/Microsoft.AspNetCore.Authorization.Test/PolicyEvaluatorTests.cs @@ -0,0 +1,179 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Authorization.Policy.Test +{ + public class PolicyEvaluatorTests + { + [Fact] + public async Task AuthenticateFailsIfNoPrincipalReturned() + { + // Arrange + var evaluator = new PolicyEvaluator(new HappyAuthorization()); + var context = new DefaultHttpContext(); + var services = new ServiceCollection().AddSingleton(); + context.RequestServices = services.BuildServiceProvider(); + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + + // Act + var result = await evaluator.AuthenticateAsync(policy, context); + + // Assert + Assert.False(result.Succeeded); + } + + [Fact] + public async Task AuthenticateMergeSchemes() + { + // Arrange + var evaluator = new PolicyEvaluator(new HappyAuthorization()); + var context = new DefaultHttpContext(); + var services = new ServiceCollection().AddSingleton(); + context.RequestServices = services.BuildServiceProvider(); + var policy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes("A","B","C").RequireAssertion(_ => true).Build(); + + // Act + var result = await evaluator.AuthenticateAsync(policy, context); + + // Assert + Assert.True(result.Succeeded); + Assert.Equal(3, result.Principal.Identities.Count()); + } + + + [Fact] + public async Task AuthorizeSucceedsEvenIfAuthenticationFails() + { + // Arrange + var evaluator = new PolicyEvaluator(new HappyAuthorization()); + var context = new DefaultHttpContext(); + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + + // Act + var result = await evaluator.AuthorizeAsync(policy, AuthenticateResult.Fail("Nooo"), context); + + // Assert + Assert.True(result.Succeeded); + Assert.False(result.Challenged); + Assert.False(result.Forbidden); + } + + [Fact] + public async Task AuthorizeChallengesIfAuthenticationFails() + { + // Arrange + var evaluator = new PolicyEvaluator(new SadAuthorization()); + var context = new DefaultHttpContext(); + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + + // Act + var result = await evaluator.AuthorizeAsync(policy, AuthenticateResult.Fail("Nooo"), context); + + // Assert + Assert.False(result.Succeeded); + Assert.True(result.Challenged); + Assert.False(result.Forbidden); + } + + [Fact] + public async Task AuthorizeForbidsIfAuthenticationSuceeds() + { + // Arrange + var evaluator = new PolicyEvaluator(new SadAuthorization()); + var context = new DefaultHttpContext(); + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + + // Act + var result = await evaluator.AuthorizeAsync(policy, AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(), "scheme")), context); + + // Assert + Assert.False(result.Succeeded); + Assert.False(result.Challenged); + Assert.True(result.Forbidden); + } + + public class HappyAuthorization : IAuthorizationService + { + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) + => Task.FromResult(AuthorizationResult.Success()); + + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) + => Task.FromResult(AuthorizationResult.Success()); + } + + public class SadAuthorization : IAuthorizationService + { + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) + => Task.FromResult(AuthorizationResult.Failed()); + + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) + => Task.FromResult(AuthorizationResult.Failed()); + } + + public class SadAuthentication : IAuthenticationService + { + public Task AuthenticateAsync(HttpContext context, string scheme) + { + return Task.FromResult(AuthenticateResult.Fail("Sad.")); + } + + public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + } + + public class EchoAuthentication : IAuthenticationService + { + public Task AuthenticateAsync(HttpContext context, string scheme) + { + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(scheme)), scheme))); + } + + public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + } + + } +} \ No newline at end of file