diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs index bb9bbb97..1da6a0b9 100644 --- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticateResult.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Authentication /// public class AuthenticateResult { - private AuthenticateResult() { } + protected AuthenticateResult() { } /// /// If a ticket was produced, authenticate was successful. @@ -21,7 +21,7 @@ private AuthenticateResult() { } /// /// The authentication ticket. /// - public AuthenticationTicket Ticket { get; private set; } + public AuthenticationTicket Ticket { get; protected set; } /// /// Gets the claims-principal with authenticated user identities. @@ -36,18 +36,12 @@ private AuthenticateResult() { } /// /// Holds failure information from the authentication. /// - public Exception Failure { get; private set; } - - /// - /// Indicates that stage of authentication was directly handled by user intervention and no - /// further processing should be attempted. - /// - public bool Handled { get; private set; } + public Exception Failure { get; protected set; } /// /// Indicates that there was no information returned for this authentication scheme. /// - public bool Nothing { get; private set; } + public bool None { get; protected set; } /// /// Indicates that authentication was successful. @@ -63,23 +57,13 @@ public static AuthenticateResult Success(AuthenticationTicket ticket) return new AuthenticateResult() { Ticket = ticket }; } - /// - /// Indicates that stage of authentication was directly handled by user intervention and no - /// further processing should be attempted. - /// - /// The result. - public static AuthenticateResult Handle() - { - return new AuthenticateResult() { Handled = true }; - } - /// /// Indicates that there was no information returned for this authentication scheme. /// /// The result. - public static AuthenticateResult None() + public static AuthenticateResult NoResult() { - return new AuthenticateResult() { Nothing = true }; + return new AuthenticateResult() { None = true }; } /// diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs index cb8f1fb4..050e6c57 100644 --- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs @@ -76,7 +76,7 @@ public static Task ForbidAsync(this HttpContext context, string scheme) => context.ForbidAsync(scheme, properties: null); /// - /// Extension method for Forbid. + /// Extension method for Forbid using the scheme.. /// /// The context. /// The task. @@ -142,6 +142,13 @@ public static Task SignInAsync(this HttpContext context, ClaimsPrincipal princip public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) => context.RequestServices.GetRequiredService().SignInAsync(context, scheme, principal, properties); + /// + /// Extension method for SignOut using the . + /// + /// The context. + /// The task. + public static Task SignOutAsync(this HttpContext context) => context.SignOutAsync(scheme: null, properties: null); + /// /// Extension method for SignOut. /// diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs index af92cc76..aeb373e1 100644 --- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs @@ -1,7 +1,6 @@ // 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.Http; @@ -39,20 +38,5 @@ public interface IAuthenticationHandler /// The that contains the extra meta-data arriving with the authentication. /// A task. Task ForbidAsync(AuthenticationProperties properties); - - /// - /// Handle sign in. - /// - /// The user. - /// The that contains the extra meta-data arriving with the authentication. - /// A task. - Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); - - /// - /// Signout behavior. - /// - /// The that contains the extra meta-data arriving with the authentication. - /// A task. - Task SignOutAsync(AuthenticationProperties properties); } } diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs index fffe08f4..fb1b227a 100644 --- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationRequestHandler.cs @@ -10,7 +10,6 @@ namespace Microsoft.AspNetCore.Authentication /// public interface IAuthenticationRequestHandler : IAuthenticationHandler { - /// /// Returns true if request processing should stop. /// diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignInHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignInHandler.cs new file mode 100644 index 00000000..69b88032 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignInHandler.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to determine if a handler supports SignIn. + /// + public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler + { + /// + /// Handle sign in. + /// + /// The user. + /// The that contains the extra meta-data arriving with the authentication. + /// A task. + Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignOutHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignOutHandler.cs new file mode 100644 index 00000000..f76d116a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSignOutHandler.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.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Used to determine if a handler supports SignOut. + /// + public interface IAuthenticationSignOutHandler : IAuthenticationHandler + { + /// + /// Signout behavior. + /// + /// The that contains the extra meta-data arriving with the authentication. + /// A task. + Task SignOutAsync(AuthenticationProperties properties); + } + +} diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs index 3aed710a..83719015 100644 --- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs +++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IClaimsTransformation.cs @@ -18,4 +18,4 @@ public interface IClaimsTransformation /// The transformed principal. Task TransformAsync(ClaimsPrincipal principal); } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs index e56247e1..be02a5c3 100644 --- a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -36,6 +35,8 @@ public AuthenticationSchemeProvider(IOptions options) private IDictionary _map = new Dictionary(StringComparer.Ordinal); private List _requestHandlers = new List(); + private List _signOutHandlers = new List(); + private List _signInHandlers = new List(); /// /// Returns the scheme that will be used by default for . @@ -43,7 +44,7 @@ public AuthenticationSchemeProvider(IOptions options) /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. /// /// The scheme that will be used by default for . - public Task GetDefaultAuthenticateSchemeAsync() + public virtual Task GetDefaultAuthenticateSchemeAsync() { if (_options.DefaultAuthenticateScheme != null) { @@ -59,20 +60,16 @@ public Task GetDefaultAuthenticateSchemeAsync() /// /// Returns the scheme that will be used by default for . /// This is typically specified via . - /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// Otherwise, this will fallback to . /// /// The scheme that will be used by default for . - public Task GetDefaultChallengeSchemeAsync() + public virtual Task GetDefaultChallengeSchemeAsync() { if (_options.DefaultChallengeScheme != null) { return GetSchemeAsync(_options.DefaultChallengeScheme); } - if (_map.Count == 1) - { - return Task.FromResult(_map.Values.First()); - } - return Task.FromResult(null); + return GetDefaultAuthenticateSchemeAsync(); } /// @@ -81,7 +78,7 @@ public Task GetDefaultChallengeSchemeAsync() /// Otherwise, this will fallback to . /// /// The scheme that will be used by default for . - public Task GetDefaultForbidSchemeAsync() + public virtual Task GetDefaultForbidSchemeAsync() { if (_options.DefaultForbidScheme != null) { @@ -93,34 +90,40 @@ public Task GetDefaultForbidSchemeAsync() /// /// Returns the scheme that will be used by default for . /// This is typically specified via . - /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. + /// If only a single sign in handler scheme exists, that will be used, if more than one exists, + /// this will fallback to . /// /// The scheme that will be used by default for . - public Task GetDefaultSignInSchemeAsync() + public virtual Task GetDefaultSignInSchemeAsync() { if (_options.DefaultSignInScheme != null) { return GetSchemeAsync(_options.DefaultSignInScheme); } - if (_map.Count == 1) + if (_signInHandlers.Count == 1) { - return Task.FromResult(_map.Values.First()); + return Task.FromResult(_signInHandlers[0]); } - return Task.FromResult(null); + return GetDefaultAuthenticateSchemeAsync(); } /// /// Returns the scheme that will be used by default for . /// This is typically specified via . - /// Otherwise, this will fallback to . + /// If only a single sign out handler scheme exists, that will be used, if more than one exists, + /// this will fallback to if that supoorts sign out. /// /// The scheme that will be used by default for . - public Task GetDefaultSignOutSchemeAsync() + public virtual Task GetDefaultSignOutSchemeAsync() { if (_options.DefaultSignOutScheme != null) { return GetSchemeAsync(_options.DefaultSignOutScheme); } + if (_signOutHandlers.Count == 1) + { + return Task.FromResult(_signOutHandlers[0]); + } return GetDefaultSignInSchemeAsync(); } @@ -129,7 +132,7 @@ public Task GetDefaultSignOutSchemeAsync() /// /// The name of the authenticationScheme. /// The scheme or null if not found. - public Task GetSchemeAsync(string name) + public virtual Task GetSchemeAsync(string name) { if (_map.ContainsKey(name)) { @@ -142,7 +145,7 @@ public Task GetSchemeAsync(string name) /// Returns the schemes in priority order for request handling. /// /// The schemes in priority order for request handling - public Task> GetRequestHandlerSchemesAsync() + public virtual Task> GetRequestHandlerSchemesAsync() { return Task.FromResult>(_requestHandlers); } @@ -151,7 +154,7 @@ public Task> GetRequestHandlerSchemesAsync() /// Registers a scheme for use by . /// /// The scheme. - public void AddScheme(AuthenticationScheme scheme) + public virtual void AddScheme(AuthenticationScheme scheme) { if (_map.ContainsKey(scheme.Name)) { @@ -167,6 +170,14 @@ public void AddScheme(AuthenticationScheme scheme) { _requestHandlers.Add(scheme); } + if (typeof(IAuthenticationSignInHandler).IsAssignableFrom(scheme.HandlerType)) + { + _signInHandlers.Add(scheme); + } + if (typeof(IAuthenticationSignOutHandler).IsAssignableFrom(scheme.HandlerType)) + { + _signOutHandlers.Add(scheme); + } _map[scheme.Name] = scheme; } } @@ -175,7 +186,7 @@ public void AddScheme(AuthenticationScheme scheme) /// Removes a scheme, preventing it from being used by . /// /// The name of the authenticationScheme being removed. - public void RemoveScheme(string name) + public virtual void RemoveScheme(string name) { if (!_map.ContainsKey(name)) { @@ -186,15 +197,15 @@ public void RemoveScheme(string name) if (_map.ContainsKey(name)) { var scheme = _map[name]; - _requestHandlers.Remove(_requestHandlers.Where(s => s.Name == name).FirstOrDefault()); + _requestHandlers.Remove(scheme); + _signInHandlers.Remove(scheme); + _signOutHandlers.Remove(scheme); _map.Remove(name); } } } - public Task> GetAllSchemesAsync() - { - return Task.FromResult>(_map.Values); - } + public virtual Task> GetAllSchemesAsync() + => Task.FromResult>(_map.Values); } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs index 326b277f..e9fcfc0b 100644 --- a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs +++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs @@ -155,10 +155,10 @@ public virtual async Task SignInAsync(HttpContext context, string scheme, Claims } } - var handler = await Handlers.GetHandlerAsync(context, scheme); + var handler = await Handlers.GetHandlerAsync(context, scheme) as IAuthenticationSignInHandler; if (handler == null) { - throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); + throw new InvalidOperationException($"No IAuthenticationSignInHandler is configured to handle sign in for the scheme: {scheme}"); } await handler.SignInAsync(principal, properties); @@ -173,15 +173,20 @@ public virtual async Task SignInAsync(HttpContext context, string scheme, Claims /// A task. public virtual async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) { - if (string.IsNullOrEmpty(scheme)) + if (scheme == null) { - throw new ArgumentException(nameof(scheme)); + var defaultScheme = await Schemes.GetDefaultSignOutSchemeAsync(); + scheme = defaultScheme?.Name; + if (scheme == null) + { + throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignOutScheme found."); + } } - var handler = await Handlers.GetHandlerAsync(context, scheme); + var handler = await Handlers.GetHandlerAsync(context, scheme) as IAuthenticationSignOutHandler; if (handler == null) { - throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); + throw new InvalidOperationException($"No IAuthenticationSignOutHandler is configured to handle sign out for the scheme: {scheme}"); } await handler.SignOutAsync(properties); diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs index 3810f833..fc647138 100644 --- a/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs @@ -1,3 +1,4 @@ + // 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. @@ -17,7 +18,7 @@ public async Task DefaultSignOutFallsbackToSignIn() { var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => { - o.AddScheme("signin", "whatever"); + o.AddScheme("signin", "whatever"); o.AddScheme("foobly", "whatever"); o.DefaultSignInScheme = "signin"; }).BuildServiceProvider(); @@ -49,7 +50,7 @@ public async Task DefaultSchemesFallbackToOnlyScheme() { var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => { - o.AddScheme("single", "whatever"); + o.AddScheme("single", "whatever"); }).BuildServiceProvider(); var provider = services.GetRequiredService(); @@ -61,13 +62,31 @@ public async Task DefaultSchemesFallbackToOnlyScheme() } [Fact] - public async Task DefaultSchemesAreSet() + public async Task DefaultSchemesFallbackToAuthenticateScheme() { var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => { + o.DefaultAuthenticateScheme = "B"; o.AddScheme("A", "whatever"); - o.AddScheme("B", "whatever"); - o.AddScheme("C", "whatever"); + o.AddScheme("B", "whatever"); + }).BuildServiceProvider(); + + var provider = services.GetRequiredService(); + Assert.Equal("B", (await provider.GetDefaultForbidSchemeAsync()).Name); + Assert.Equal("B", (await provider.GetDefaultAuthenticateSchemeAsync()).Name); + Assert.Equal("B", (await provider.GetDefaultChallengeSchemeAsync()).Name); + Assert.Equal("B", (await provider.GetDefaultSignInSchemeAsync()).Name); + Assert.Equal("B", (await provider.GetDefaultSignOutSchemeAsync()).Name); + } + + [Fact] + public async Task DefaultSchemesAreSet() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("A", "whatever"); + o.AddScheme("B", "whatever"); + o.AddScheme("C", "whatever"); o.DefaultChallengeScheme = "A"; o.DefaultForbidScheme = "B"; o.DefaultSignInScheme = "C"; @@ -83,6 +102,38 @@ public async Task DefaultSchemesAreSet() Assert.Equal("A", (await provider.GetDefaultSignOutSchemeAsync()).Name); } + [Fact] + public async Task SignInSignOutDefaultsToOnlyOne() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("basic", "whatever"); + o.AddScheme("signout", "whatever"); + o.AddScheme("signin", "whatever"); + o.DefaultAuthenticateScheme = "basic"; + }).BuildServiceProvider(); + + var provider = services.GetRequiredService(); + Assert.Equal("basic", (await provider.GetDefaultForbidSchemeAsync()).Name); + Assert.Equal("basic", (await provider.GetDefaultAuthenticateSchemeAsync()).Name); + Assert.Equal("basic", (await provider.GetDefaultChallengeSchemeAsync()).Name); + Assert.Equal("signin", (await provider.GetDefaultSignInSchemeAsync()).Name); + Assert.Equal("signin", (await provider.GetDefaultSignOutSchemeAsync()).Name); // Defaults to single sign in scheme + } + + [Fact] + public async Task SignOutWillDefaultsToSignInThatDoesNotSignOut() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("signin", "whatever"); + o.DefaultSignInScheme = "signin"; + }).BuildServiceProvider(); + + var provider = services.GetRequiredService(); + Assert.NotNull(await provider.GetDefaultSignOutSchemeAsync()); + } + private class Handler : IAuthenticationHandler { public Task AuthenticateAsync() @@ -104,7 +155,10 @@ public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { throw new NotImplementedException(); } + } + private class SignInHandler : Handler, IAuthenticationSignInHandler + { public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { throw new NotImplementedException(); @@ -116,5 +170,12 @@ public Task SignOutAsync(AuthenticationProperties properties) } } + private class SignOutHandler : Handler, IAuthenticationSignOutHandler + { + public Task SignOutAsync(AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + } } } diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationServiceTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationServiceTests.cs new file mode 100644 index 00000000..471053e6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationServiceTests.cs @@ -0,0 +1,245 @@ +// 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.Http; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication +{ + public class AuthenticationServiceTests + { + [Fact] + public async Task CanOnlySignInIfSupported() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("uber", "whatever"); + o.AddScheme("base", "whatever"); + o.AddScheme("signin", "whatever"); + o.AddScheme("signout", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.SignInAsync("uber", new ClaimsPrincipal(), null); + await Assert.ThrowsAsync(() => context.SignInAsync("base", new ClaimsPrincipal(), null)); + await context.SignInAsync("signin", new ClaimsPrincipal(), null); + await Assert.ThrowsAsync(() => context.SignInAsync("signout", new ClaimsPrincipal(), null)); + } + + [Fact] + public async Task CanOnlySignOutIfSupported() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("uber", "whatever"); + o.AddScheme("base", "whatever"); + o.AddScheme("signin", "whatever"); + o.AddScheme("signout", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.SignOutAsync("uber"); + await Assert.ThrowsAsync(() => context.SignOutAsync("base")); + await context.SignOutAsync("signout"); + await context.SignOutAsync("signin"); + } + + [Fact] + public async Task ServicesWithDefaultIAuthenticationHandlerMethodsTest() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("base", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.AuthenticateAsync(); + await context.ChallengeAsync(); + await context.ForbidAsync(); + await Assert.ThrowsAsync(() => context.SignOutAsync()); + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + [Fact] + public async Task ServicesWithDefaultUberMethodsTest() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("base", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.AuthenticateAsync(); + await context.ChallengeAsync(); + await context.ForbidAsync(); + await context.SignOutAsync(); + await context.SignInAsync(new ClaimsPrincipal()); + } + + [Fact] + public async Task ServicesWithDefaultSignInMethodsTest() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("base", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.AuthenticateAsync(); + await context.ChallengeAsync(); + await context.ForbidAsync(); + await context.SignOutAsync(); + await context.SignInAsync(new ClaimsPrincipal()); + } + + [Fact] + public async Task ServicesWithDefaultSignOutMethodsTest() + { + var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o => + { + o.AddScheme("base", "whatever"); + }).BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = services; + + await context.AuthenticateAsync(); + await context.ChallengeAsync(); + await context.ForbidAsync(); + await context.SignOutAsync(); + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + + private class BaseHandler : IAuthenticationHandler + { + public Task AuthenticateAsync() + { + return Task.FromResult(AuthenticateResult.NoResult()); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + return Task.FromResult(0); + } + } + + private class SignInHandler : IAuthenticationSignInHandler + { + public Task AuthenticateAsync() + { + return Task.FromResult(AuthenticateResult.NoResult()); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + return Task.FromResult(0); + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + } + + public class SignOutHandler : IAuthenticationSignOutHandler + { + public Task AuthenticateAsync() + { + return Task.FromResult(AuthenticateResult.NoResult()); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + return Task.FromResult(0); + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + } + + private class UberHandler : IAuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler + { + public Task AuthenticateAsync() + { + return Task.FromResult(AuthenticateResult.NoResult()); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task HandleRequestAsync() + { + return Task.FromResult(false); + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + return Task.FromResult(0); + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + return Task.FromResult(0); + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + return Task.FromResult(0); + } + } + + } +}