Rename VirtualScheme => PolicyScheme (#1665)
* VirtualScheme => PolicyScheme * Use SignInHandler base for cookies * PolicySchemeHandlers throw NotImplemented by default * Remove redundant interface
This commit is contained in:
parent
4103d47f09
commit
bee77ebf91
|
|
@ -14,10 +14,7 @@ using Microsoft.Net.Http.Headers;
|
|||
|
||||
namespace Microsoft.AspNetCore.Authentication.Cookies
|
||||
{
|
||||
public class CookieAuthenticationHandler :
|
||||
AuthenticationHandler<CookieAuthenticationOptions>,
|
||||
IAuthenticationSignInHandler,
|
||||
IAuthenticationSignOutHandler
|
||||
public class CookieAuthenticationHandler : SignInAuthenticationHandler<CookieAuthenticationOptions>
|
||||
{
|
||||
private const string HeaderValueNoCache = "no-cache";
|
||||
private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
|
|
@ -252,20 +249,13 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
|
|||
}
|
||||
}
|
||||
|
||||
public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
protected async override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
var target = ResolveTarget(Options.ForwardSignIn);
|
||||
if (target != null)
|
||||
{
|
||||
await Context.SignInAsync(target, user, properties);
|
||||
return;
|
||||
}
|
||||
|
||||
properties = properties ?? new AuthenticationProperties();
|
||||
|
||||
_signInCalled = true;
|
||||
|
|
@ -346,15 +336,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
|
|||
Logger.SignedIn(Scheme.Name);
|
||||
}
|
||||
|
||||
public async virtual Task SignOutAsync(AuthenticationProperties properties)
|
||||
protected async override Task HandleSignOutAsync(AuthenticationProperties properties)
|
||||
{
|
||||
var target = ResolveTarget(Options.ForwardSignOut);
|
||||
if (target != null)
|
||||
{
|
||||
await Context.SignOutAsync(target, properties);
|
||||
return;
|
||||
}
|
||||
|
||||
properties = properties ?? new AuthenticationProperties();
|
||||
|
||||
_signOutCalled = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler : Microsoft.AspNetCore.Authentication.AuthenticationHandler<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>, Microsoft.AspNetCore.Authentication.IAuthenticationSignInHandler",
|
||||
"Kind": "Removal"
|
||||
}
|
||||
]
|
||||
|
|
@ -91,15 +91,15 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="VirtualAuthenticationHandler"/> based authentication handler which can be used to
|
||||
/// Adds a <see cref="PolicySchemeHandler"/> based authentication handler which can be used to
|
||||
/// redirect to other authentication schemes.
|
||||
/// </summary>
|
||||
/// <param name="authenticationScheme">The name of this scheme.</param>
|
||||
/// <param name="displayName">The display name of this scheme.</param>
|
||||
/// <param name="configureOptions">Used to configure the scheme options.</param>
|
||||
/// <returns>The builder.</returns>
|
||||
public virtual AuthenticationBuilder AddVirtualScheme(string authenticationScheme, string displayName, Action<VirtualSchemeOptions> configureOptions)
|
||||
=> AddSchemeHelper<VirtualSchemeOptions, VirtualAuthenticationHandler>(authenticationScheme, displayName, configureOptions);
|
||||
public virtual AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string displayName, Action<PolicySchemeOptions> configureOptions)
|
||||
=> AddSchemeHelper<PolicySchemeOptions, PolicySchemeHandler>(authenticationScheme, displayName, configureOptions);
|
||||
|
||||
// Used to ensure that there's always a default sign in scheme that's not itself
|
||||
private class EnsureSignInScheme<TOptions> : IPostConfigureOptions<TOptions> where TOptions : RemoteAuthenticationOptions
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// PolicySchemes are used to redirect authentication methods to another scheme.
|
||||
/// </summary>
|
||||
public class PolicySchemeHandler : SignInAuthenticationHandler<PolicySchemeOptions>
|
||||
{
|
||||
public PolicySchemeHandler(IOptionsMonitor<PolicySchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{ }
|
||||
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
protected override Task HandleSignOutAsync(AuthenticationProperties properties)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the options used by the <see cref="PolicySchemeHandler"/>.
|
||||
/// </summary>
|
||||
public class PolicySchemeOptions : AuthenticationSchemeOptions
|
||||
{ }
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds support for SignInAsync
|
||||
/// </summary>
|
||||
public abstract class SignInAuthenticationHandler<TOptions> : SignOutAuthenticationHandler<TOptions>, IAuthenticationSignInHandler
|
||||
where TOptions : AuthenticationSchemeOptions, new()
|
||||
{
|
||||
public SignInAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{ }
|
||||
|
||||
public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
{
|
||||
var target = ResolveTarget(Options.ForwardSignIn);
|
||||
return (target != null)
|
||||
? Context.SignInAsync(target, user, properties)
|
||||
: HandleSignInAsync(user, properties ?? new AuthenticationProperties());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to handle SignIn.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns>A Task.</returns>
|
||||
protected abstract Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds support for SignOutAsync
|
||||
/// </summary>
|
||||
public abstract class SignOutAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationSignOutHandler
|
||||
where TOptions : AuthenticationSchemeOptions, new()
|
||||
{
|
||||
public SignOutAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{ }
|
||||
|
||||
public virtual Task SignOutAsync(AuthenticationProperties properties)
|
||||
{
|
||||
var target = ResolveTarget(Options.ForwardSignOut);
|
||||
return (target != null)
|
||||
? Context.SignOutAsync(target, properties)
|
||||
: HandleSignOutAsync(properties ?? new AuthenticationProperties());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to handle SignOut.
|
||||
/// </summary>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns>A Task.</returns>
|
||||
protected abstract Task HandleSignOutAsync(AuthenticationProperties properties);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
// 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.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Forwards calls to another authentication scheme.
|
||||
/// </summary>
|
||||
public class VirtualAuthenticationHandler : IAuthenticationHandler, IAuthenticationSignInHandler
|
||||
{
|
||||
protected IOptionsMonitor<VirtualSchemeOptions> OptionsMonitor { get; }
|
||||
public AuthenticationScheme Scheme { get; private set; }
|
||||
public VirtualSchemeOptions Options { get; private set; }
|
||||
protected HttpContext Context { get; private set; }
|
||||
|
||||
public VirtualAuthenticationHandler(IOptionsMonitor<VirtualSchemeOptions> options)
|
||||
{
|
||||
OptionsMonitor = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the handler, resolve the options and validate them.
|
||||
/// </summary>
|
||||
/// <param name="scheme"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>A Task.</returns>
|
||||
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
|
||||
{
|
||||
if (scheme == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(scheme));
|
||||
}
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
Scheme = scheme;
|
||||
Context = context;
|
||||
|
||||
Options = OptionsMonitor.Get(Scheme.Name) ?? new VirtualSchemeOptions();
|
||||
Options.Validate();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual string ResolveTarget(string scheme)
|
||||
=> scheme ?? Options.DefaultSelector?.Invoke(Context) ?? Options.Default;
|
||||
|
||||
public virtual Task<AuthenticateResult> AuthenticateAsync()
|
||||
=> Context.AuthenticateAsync(ResolveTarget(Options.Authenticate));
|
||||
|
||||
public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
|
||||
=> Context.SignInAsync(ResolveTarget(Options.SignIn), user, properties);
|
||||
|
||||
public virtual Task SignOutAsync(AuthenticationProperties properties)
|
||||
=> Context.SignOutAsync(ResolveTarget(Options.SignOut), properties);
|
||||
|
||||
public virtual Task ChallengeAsync(AuthenticationProperties properties)
|
||||
=> Context.ChallengeAsync(ResolveTarget(Options.Challenge), properties);
|
||||
|
||||
public virtual Task ForbidAsync(AuthenticationProperties properties)
|
||||
=> Context.ForbidAsync(ResolveTarget(Options.Forbid), properties);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to redirect authentication methods to another scheme
|
||||
/// </summary>
|
||||
public class VirtualSchemeOptions
|
||||
{
|
||||
public string Default { get; set; }
|
||||
|
||||
public string Authenticate { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
public string Forbid { get; set; }
|
||||
public string SignIn { get; set; }
|
||||
public string SignOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to select a default scheme to target based on the request.
|
||||
/// </summary>
|
||||
public Func<HttpContext, string> DefaultSelector { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check that the options are valid. Should throw an exception if things are not ok.
|
||||
/// </summary>
|
||||
public virtual void Validate() { }
|
||||
}
|
||||
}
|
||||
|
|
@ -12,26 +12,26 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Authentication
|
||||
{
|
||||
public class VirtualHandlerTests
|
||||
public class PolicyTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CanDispatch()
|
||||
{
|
||||
var server = CreateServer(services =>
|
||||
{
|
||||
services.AddAuthentication(o =>
|
||||
services.AddLogging().AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler>("auth2", "auth2");
|
||||
o.AddScheme<TestHandler>("auth3", "auth3");
|
||||
})
|
||||
.AddVirtualScheme("policy1", "policy1", p =>
|
||||
.AddPolicyScheme("policy1", "policy1", p =>
|
||||
{
|
||||
p.Default = "auth1";
|
||||
p.ForwardDefault = "auth1";
|
||||
})
|
||||
.AddVirtualScheme("policy2", "policy2", p =>
|
||||
.AddPolicyScheme("policy2", "policy2", p =>
|
||||
{
|
||||
p.Authenticate = "auth2";
|
||||
p.ForwardAuthenticate = "auth2";
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -54,16 +54,15 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
[Fact]
|
||||
public async Task DefaultTargetSelectorWinsOverDefaultTarget()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
||||
var services = new ServiceCollection().AddOptions().AddLogging();
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler2>("auth2", "auth2");
|
||||
})
|
||||
.AddVirtualScheme("forward", "forward", p => {
|
||||
p.Default = "auth2";
|
||||
p.DefaultSelector = ctx => "auth1";
|
||||
.AddPolicyScheme("forward", "forward", p => {
|
||||
p.ForwardDefault= "auth2";
|
||||
p.ForwardDefaultSelector = ctx => "auth1";
|
||||
});
|
||||
|
||||
var handler1 = new TestHandler();
|
||||
|
|
@ -110,16 +109,15 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
[Fact]
|
||||
public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
||||
var services = new ServiceCollection().AddOptions().AddLogging();
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler2>("auth2", "auth2");
|
||||
})
|
||||
.AddVirtualScheme("forward", "forward", p => {
|
||||
p.Default = "auth1";
|
||||
p.DefaultSelector = ctx => null;
|
||||
.AddPolicyScheme("forward", "forward", p => {
|
||||
p.ForwardDefault= "auth1";
|
||||
p.ForwardDefaultSelector = ctx => null;
|
||||
});
|
||||
|
||||
var handler1 = new TestHandler();
|
||||
|
|
@ -166,21 +164,20 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
[Fact]
|
||||
public async Task SpecificTargetAlwaysWinsOverDefaultTarget()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
||||
var services = new ServiceCollection().AddOptions().AddLogging();
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler2>("auth2", "auth2");
|
||||
})
|
||||
.AddVirtualScheme("forward", "forward", p => {
|
||||
p.Default = "auth2";
|
||||
p.DefaultSelector = ctx => "auth2";
|
||||
p.Authenticate = "auth1";
|
||||
p.SignIn = "auth1";
|
||||
p.SignOut = "auth1";
|
||||
p.Forbid = "auth1";
|
||||
p.Challenge = "auth1";
|
||||
.AddPolicyScheme("forward", "forward", p => {
|
||||
p.ForwardDefault= "auth2";
|
||||
p.ForwardDefaultSelector = ctx => "auth2";
|
||||
p.ForwardAuthenticate = "auth1";
|
||||
p.ForwardSignIn = "auth1";
|
||||
p.ForwardSignOut = "auth1";
|
||||
p.ForwardForbid = "auth1";
|
||||
p.ForwardChallenge = "auth1";
|
||||
});
|
||||
|
||||
var handler1 = new TestHandler();
|
||||
|
|
@ -227,14 +224,13 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
[Fact]
|
||||
public async Task VirtualSchemeTargetsForwardWithDefaultTarget()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
||||
var services = new ServiceCollection().AddOptions().AddLogging();
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler2>("auth2", "auth2");
|
||||
})
|
||||
.AddVirtualScheme("forward", "forward", p => p.Default = "auth1");
|
||||
.AddPolicyScheme("forward", "forward", p => p.ForwardDefault= "auth1");
|
||||
|
||||
var handler1 = new TestHandler();
|
||||
services.AddSingleton(handler1);
|
||||
|
|
@ -280,18 +276,17 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
[Fact]
|
||||
public async Task VirtualSchemeTargetsOverrideDefaultTarget()
|
||||
{
|
||||
var services = new ServiceCollection().AddOptions();
|
||||
|
||||
var services = new ServiceCollection().AddOptions().AddLogging();
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("auth1", "auth1");
|
||||
o.AddScheme<TestHandler2>("auth2", "auth2");
|
||||
})
|
||||
.AddVirtualScheme("forward", "forward", p =>
|
||||
.AddPolicyScheme("forward", "forward", p =>
|
||||
{
|
||||
p.Default = "auth1";
|
||||
p.Challenge = "auth2";
|
||||
p.SignIn = "auth2";
|
||||
p.ForwardDefault= "auth1";
|
||||
p.ForwardChallenge = "auth2";
|
||||
p.ForwardSignIn = "auth2";
|
||||
});
|
||||
|
||||
var handler1 = new TestHandler();
|
||||
|
|
@ -346,9 +341,9 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
o.AddScheme<TestHandler>("auth2", "auth2");
|
||||
o.AddScheme<TestHandler>("auth3", "auth3");
|
||||
})
|
||||
.AddVirtualScheme("dynamic", "dynamic", p =>
|
||||
.AddPolicyScheme("dynamic", "dynamic", p =>
|
||||
{
|
||||
p.DefaultSelector = c => c.Request.QueryString.Value.Substring(1);
|
||||
p.ForwardDefaultSelector = c => c.Request.QueryString.Value.Substring(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -360,39 +355,6 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
Assert.Equal("auth3", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth3"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TargetsDefaultSchemeByDefault()
|
||||
{
|
||||
var server = CreateServer(services =>
|
||||
{
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.DefaultScheme = "default";
|
||||
o.AddScheme<TestHandler>("default", "default");
|
||||
})
|
||||
.AddVirtualScheme("virtual", "virtual", p => { });
|
||||
});
|
||||
|
||||
var transaction = await server.SendAsync("http://example.com/auth/virtual");
|
||||
Assert.Equal("default", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "default"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TargetsDefaultSchemeThrowsWithNoDefault()
|
||||
{
|
||||
var server = CreateServer(services =>
|
||||
{
|
||||
services.AddAuthentication(o =>
|
||||
{
|
||||
o.AddScheme<TestHandler>("default", "default");
|
||||
})
|
||||
.AddVirtualScheme("virtual", "virtual", p => { });
|
||||
});
|
||||
|
||||
var error = await Assert.ThrowsAsync<InvalidOperationException>(() => server.SendAsync("http://example.com/auth/virtual"));
|
||||
Assert.Contains("No authenticationScheme was specified", error.Message);
|
||||
}
|
||||
|
||||
private class TestHandler : IAuthenticationSignInHandler
|
||||
{
|
||||
public AuthenticationScheme Scheme { get; set; }
|
||||
Loading…
Reference in New Issue