简体   繁体   English

ASP.NET core 2.2:当配置了多个身份验证方案时,ChallengeResult 的预期行为是什么?

[英]ASP.NET core 2.2: what is the expected behaviour of ChallengeResult when there are multiple authentication schemes configured?

We are trying to understand what is the expected handling of a ChallengeResult when there are multiple authentication schemes registered.我们试图了解当注册多个身份验证方案时,对 ChallengeResult 的预期处理是什么。

We need to handle such a scenario because we have an ASP.NET core 2.2 app exposing some action methods (we use the MVC middleware) that must be used by an angularjs SPA which relies on cookies authentication and some third parties applications which use an authentication mechanism based on the Authorization HTTP request header.我们需要处理这样的场景,因为我们有一个 ASP.NET 核心 2.2 应用程序公开了一些操作方法(我们使用 MVC 中间件),必须由依赖 cookie 身份验证的 angularjs SPA 和一些使用身份验证的第三方应用程序使用基于 Authorization HTTP 请求头的机制。 Please notice that the involved action methods are the same for both the users , this means that each one of them must allow authentication using both the cookie and the custom scheme based on Authorization HTTP request header.请注意,涉及的操作方法对于两个用户是相同的,这意味着他们每个人都必须允许使用 cookie 和基于授权 HTTP 请求标头的自定义方案进行身份验证。 We know that probably this is not an optimal design but we cannot modify the overall architecture.我们知道这可能不是最佳设计,但我们无法修改整体架构。

This documentation seems to confirm that what we would like to achieve is entirely possible using ASP.NET core 2.2. 该文档似乎证实了使用 ASP.NET core 2.2 完全可以实现我们想要实现的目标。 Unfortunately, the cookie authentication used by the UI app and the custom authentication used by the third parties must behave differently in case of an authentication challenge and their expected behaviors are not compatible with each other: the UI app should redirect the user to a login form, while a thir party application expects a raw 401 status code response.不幸的是,UI 应用程序使用的 cookie 身份验证和第三方使用的自定义身份验证必须在身份验证质询的情况下表现不同,并且它们的预期行为彼此不兼容:UI 应用程序应将用户重定向到登录表单,而第三方应用程序需要原始 401 状态代码响应。 The documentation linked above does not offer a clear explanation of the ChallengeResult handling, so we decided to experiment with a test application.上面链接的文档没有提供对 ChallengeResult 处理的清晰解释,因此我们决定尝试一个测试应用程序。

We created two fake authentication handlers:我们创建了两个假身份验证处理程序:

public class FooAuthenticationHandler : IAuthenticationHandler
  {
    private HttpContext _context;

    public Task<AuthenticateResult> AuthenticateAsync()
    {
      return Task.FromResult(AuthenticateResult.Fail("Foo failed"));
    }

    public Task ChallengeAsync(AuthenticationProperties properties)
    {
      _context.Response.StatusCode = StatusCodes.Status403Forbidden;
      return Task.CompletedTask;
    }

    public Task ForbidAsync(AuthenticationProperties properties)
    {
      return Task.CompletedTask;
    }

    public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
    {
      _context = context;
      return Task.CompletedTask;
    }
  }
public class BarAuthenticationHandler : IAuthenticationHandler
  {
    private HttpContext _context;

    public Task<AuthenticateResult> AuthenticateAsync()
    {
      return Task.FromResult(AuthenticateResult.Fail("Bar failed"));
    }

    public Task ChallengeAsync(AuthenticationProperties properties)
    {
      _context.Response.StatusCode = StatusCodes.Status500InternalServerError;
      return Task.CompletedTask;
    }

    public Task ForbidAsync(AuthenticationProperties properties)
    {
      return Task.CompletedTask;
    }

    public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
    {
      _context = context;
      return Task.CompletedTask;
    }
  }

We registered the authentication schemas inside ConfigureServices method as follows:我们在 ConfigureServices 方法中注册了身份验证模式,如下所示:

public void ConfigureServices(IServiceCollection services)
    {
      services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

      services.AddAuthentication(options => 
      {
        options.DefaultChallengeScheme = "Bar";
        options.AddScheme<FooAuthenticationHandler>("Foo", "Foo scheme");
        options.AddScheme<BarAuthenticationHandler>("Bar", "Bar scheme");
      });
    }

This is our middleware pipeline:这是我们的中间件管道:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }
      else
      {
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
      }

      app.UseHttpsRedirection();

      app.UseAuthentication();

      app.UseMvc();
    }

and finally we created a controller with an action method requiring authentication:最后,我们创建了一个控制器,其动作方法需要身份验证:

[Route("api/[controller]")]
  [ApiController]
  public class ValuesController : ControllerBase
  {
    // GET api/values/5
    [HttpGet("{id}")]
    [Authorize(AuthenticationSchemes = "Foo,Bar")]
    public ActionResult<string> Get(int id)
    {
      return "value";
    }
  }

We noticed that:我们注意到:

  • both the FooAuthenticationHandler and BarAuthenticationHandler are called to handle the ChallengeResult FooAuthenticationHandlerBarAuthenticationHandler都被调用来处理 ChallengeResult
  • the order is FooAuthenticationHandler before BarAuthenticationHandler and depends on the Authorize attribute (if you swap the authentication schemes inside the Authorize attribute then BarAuthenticationHandler is called first)顺序是FooAuthenticationHandlerBarAuthenticationHandler并取决于Authorize属性(如果换成内部的认证方案Authorize属性,那么BarAuthenticationHandler首次调用)
  • the caller gets a raw 500 status code response, but this only depends on the order in which the authorization handlers are called调用者获得原始 500 状态代码响应,但这仅取决于调用授权处理程序的顺序
  • the call to options.DefaultChallengeScheme = "Bar";调用options.DefaultChallengeScheme = "Bar"; matters if and only if inside the [Authorize] attribute the property AuthenticationSchemes is not set.当且仅当[Authorize]属性中设置属性AuthenticationSchemes时才重要。 If you do so, only the BarAuthenticationHandler is called and FooAuthenticationHandler never gets a chance to authenticate the request or handle an authentication challenge.如果这样做,只有BarAuthenticationHandler被称为和FooAuthenticationHandler从来没有得到一个机会来验证请求或处理一个认证挑战。

So, the question basically is: when you have such a scenario, how are you expected to handle the possible "incompatibility" of different authentication schemes regarding ChallengeResult handling since they get both called ?因此,问题基本上是:当您遇到这种情况时,您希望如何处理有关 ChallengeResult 处理的不同身份验证方案的可能“不兼容”,因为它们被调用了?

In our opinion is fine that both have a chance to authenticate the request, but we would like to know if it is possible to decide which one should handle the authentication challenge.在我们看来,两者都有机会对请求进行身份验证是可以的,但我们想知道是否有可能决定哪一个应该处理身份验证挑战。

Thanks for helping !感谢您的帮助!

You should not specify the schemes on the Authorize attribute.您不应在 Authorize 属性上指定方案。 Instead, specify one scheme as the default, and setup a forward selector.相反,指定一个方案作为默认方案,并设置一个前向选择器。

The implementation of the selector depends on your case, but usually you can somehow figure out which scheme was used in a request.选择器的实现取决于您的情况,但通常您可以以某种方式找出请求中使用的方案。

For example, here is an example from the setup of an OpenID Connect scheme.例如,以下是 OpenID Connect 方案设置的示例。

o.ForwardDefaultSelector = ctx =>
{
    // If the current request is for this app's API
    // use JWT Bearer authentication instead
    return ctx.Request.Path.StartsWithSegments("/api")
        ? JwtBearerDefaults.AuthenticationScheme
        : null;
};

So what it does is forward challenges (and well, everything) to the JWT handler if the route starts with /api.因此,如果路由以 /api 开头,那么它所做的就是将挑战(以及一切)转发给 JWT 处理程序。 You can do any kind of checks there, headers etc.您可以在那里进行任何类型的检查,标题等。

So in this case OpenID Connect and Cookies are setup as defaults for everything, but if a call is received that is going to the API, use JWT authentication.因此,在这种情况下,OpenID Connect 和 Cookies 被设置为所有内容的默认值,但如果接收到将要发送到 API 的调用,请使用 JWT 身份验证。

The example here forwards all the "actions" you can do with authentication (challenge, forbid etc.).此处的示例转发了您可以通过身份验证执行的所有“操作”(挑战、禁止等)。 You can also setup forward selectors for just challenges etc.您还可以为挑战等设置前向选择器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM