diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
index 08df75a2ad..9e1df2b455 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
@@ -47,6 +47,19 @@ namespace Microsoft.AspNetCore.Authentication
SchemeMap[name] = builder;
}
+ ///
+ /// Adds an .
+ ///
+ /// The responsible for the scheme.
+ /// The name of the scheme being added.
+ /// The display name for the scheme.
+ public void AddScheme(string name, string displayName) where THandler : IAuthenticationHandler
+ => AddScheme(name, b =>
+ {
+ b.DisplayName = displayName;
+ b.HandlerType = typeof(THandler);
+ });
+
///
/// Used by as the default scheme by .
///
@@ -57,9 +70,19 @@ namespace Microsoft.AspNetCore.Authentication
///
public string DefaultSignInScheme { get; set; }
+ ///
+ /// Used by as the default scheme by .
+ ///
+ public string DefaultSignOutScheme { get; set; }
+
///
/// Used by as the default scheme by .
///
public string DefaultChallengeScheme { get; set; }
+
+ ///
+ /// Used by as the default scheme by .
+ ///
+ public string DefaultForbidScheme { get; set; }
}
}
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
index 5fa08c977a..675cf52cf9 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
@@ -41,6 +41,14 @@ namespace Microsoft.AspNetCore.Authentication
/// The scheme that will be used by default for .
Task GetDefaultChallengeSchemeAsync();
+ ///
+ /// Returns the scheme that will be used by default for .
+ /// This is typically specified via .
+ /// Otherwise, this will fallback to .
+ ///
+ /// The scheme that will be used by default for .
+ Task GetDefaultForbidSchemeAsync();
+
///
/// Returns the scheme that will be used by default for .
/// This is typically specified via .
@@ -49,6 +57,14 @@ namespace Microsoft.AspNetCore.Authentication
/// The scheme that will be used by default for .
Task GetDefaultSignInSchemeAsync();
+ ///
+ /// Returns the scheme that will be used by default for .
+ /// This is typically specified via .
+ /// Otherwise, this will fallback to .
+ ///
+ /// The scheme that will be used by default for .
+ Task GetDefaultSignOutSchemeAsync();
+
///
/// Registers a scheme for use by .
///
diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
index 2ce3801b5b..e56247e18c 100644
--- a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
@@ -75,6 +75,21 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(null);
}
+ ///
+ /// Returns the scheme that will be used by default for .
+ /// This is typically specified via .
+ /// Otherwise, this will fallback to .
+ ///
+ /// The scheme that will be used by default for .
+ public Task GetDefaultForbidSchemeAsync()
+ {
+ if (_options.DefaultForbidScheme != null)
+ {
+ return GetSchemeAsync(_options.DefaultForbidScheme);
+ }
+ return GetDefaultChallengeSchemeAsync();
+ }
+
///
/// Returns the scheme that will be used by default for .
/// This is typically specified via .
@@ -94,6 +109,21 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(null);
}
+ ///
+ /// Returns the scheme that will be used by default for .
+ /// This is typically specified via .
+ /// Otherwise, this will fallback to .
+ ///
+ /// The scheme that will be used by default for .
+ public Task GetDefaultSignOutSchemeAsync()
+ {
+ if (_options.DefaultSignOutScheme != null)
+ {
+ return GetSchemeAsync(_options.DefaultSignOutScheme);
+ }
+ return GetDefaultSignInSchemeAsync();
+ }
+
///
/// Returns the matching the name, or null.
///
diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs
new file mode 100644
index 0000000000..3810f8335f
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/AuthenticationSchemeProviderTests.cs
@@ -0,0 +1,120 @@
+// 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 AuthenticationSchemeProviderTests
+ {
+ [Fact]
+ public async Task DefaultSignOutFallsbackToSignIn()
+ {
+ var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
+ {
+ o.AddScheme("signin", "whatever");
+ o.AddScheme("foobly", "whatever");
+ o.DefaultSignInScheme = "signin";
+ }).BuildServiceProvider();
+
+ var provider = services.GetRequiredService();
+ var scheme = await provider.GetDefaultSignOutSchemeAsync();
+ Assert.NotNull(scheme);
+ Assert.Equal("signin", scheme.Name);
+ }
+
+ [Fact]
+ public async Task DefaultForbidFallsbackToChallenge()
+ {
+ var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
+ {
+ o.AddScheme("challenge", "whatever");
+ o.AddScheme("foobly", "whatever");
+ o.DefaultChallengeScheme = "challenge";
+ }).BuildServiceProvider();
+
+ var provider = services.GetRequiredService();
+ var scheme = await provider.GetDefaultForbidSchemeAsync();
+ Assert.NotNull(scheme);
+ Assert.Equal("challenge", scheme.Name);
+ }
+
+ [Fact]
+ public async Task DefaultSchemesFallbackToOnlyScheme()
+ {
+ var services = new ServiceCollection().AddOptions().AddAuthenticationCore(o =>
+ {
+ o.AddScheme("single", "whatever");
+ }).BuildServiceProvider();
+
+ var provider = services.GetRequiredService();
+ Assert.Equal("single", (await provider.GetDefaultForbidSchemeAsync()).Name);
+ Assert.Equal("single", (await provider.GetDefaultAuthenticateSchemeAsync()).Name);
+ Assert.Equal("single", (await provider.GetDefaultChallengeSchemeAsync()).Name);
+ Assert.Equal("single", (await provider.GetDefaultSignInSchemeAsync()).Name);
+ Assert.Equal("single", (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";
+ o.DefaultSignOutScheme = "A";
+ o.DefaultAuthenticateScheme = "C";
+ }).BuildServiceProvider();
+
+ var provider = services.GetRequiredService();
+ Assert.Equal("B", (await provider.GetDefaultForbidSchemeAsync()).Name);
+ Assert.Equal("C", (await provider.GetDefaultAuthenticateSchemeAsync()).Name);
+ Assert.Equal("A", (await provider.GetDefaultChallengeSchemeAsync()).Name);
+ Assert.Equal("C", (await provider.GetDefaultSignInSchemeAsync()).Name);
+ Assert.Equal("A", (await provider.GetDefaultSignOutSchemeAsync()).Name);
+ }
+
+ private class Handler : IAuthenticationHandler
+ {
+ public Task AuthenticateAsync()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ChallengeAsync(AuthenticationProperties properties)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task ForbidAsync(AuthenticationProperties properties)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task SignOutAsync(AuthenticationProperties properties)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ }
+}