'Is there any way to avoid challenging all registered authentication schemes when one of them succeeds?

I am facing an issue related to handling multiple authentication schemes in an asp.net core application which exposes API. In particular, I am attempting to make two different authentication modes available, by registering two schemes, each with its own handler. At the moment my configuration code (simplified) looks like this:

public class Startup {
  public void ConfigureServices(IServiceCollection services) {
    services
      .AddAuthentication()
      .AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>("Basic", o => {});
      .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthHandler>("ApiKey", o => {});

    services.AddAuthorization(o => {
      o.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("Basic", "ApiKey").Build();
    });
  }

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    ...
    app.UseAuthentication();
    app.UseAuthorization();
    ...
  }
}

public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> {
  protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { 
   Console.WriteLine("Challenging Basic Authentication");
     if( ... ) {
       return AuthenticateResult.Success(...);
     }
     return AuthenticateResult.Fail(...);
  }  
}

public class ApiKeyAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> {
  protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { 
   Console.WriteLine("Challenging ApiKey Authentication");
     if( ... ) {
       return AuthenticateResult.Success(...);
     }
     return AuthenticateResult.Fail(...);
  }  
}

The ApiKeyAuthHandler authenticates requests depending on a custom header (say X-ApiKey) value, the BasicAuthHandler authenticates requests depending on credentials provided through Authorization: Basic BASE_ENCODED_USERNAME_AND_PASSWORD header.

Both handlers work fine: I can provide Authorization header with basic auth credentials and I can provide an api-key through the custom header. Generally speaking, the two registered handlers are executed sequentially, in the same order I've registered them. So far, so good.

However, when I provide both basic credentials in Authorization header and api-key through X-ApiKey header, I noticed that

  • the first handler (BasicAuthHandler) is executed, returning AuthenticateResult.Success
  • the second handler (ApiKeyAuthHandler) is executed, even the first already authenticated the request successfylly, also returning AuthenticateResult.Success
  • the identity finally available in my controller is the one built by the second handler

I'm wondering why the second handler is executed after the first completed authentication successfully: I googled about this but can't found any configuration flags to enable a sort of short-circuit after the first success result.

The reason seems to be this foreach statement that iterates over all available authentication schemes regardless of theirs results.

Does anyone have any idea of a way to challenge available authentication scheme while no one is successful, stopping when the first succeeds? No doubt I could implement a CompositeAuthHandler in order to manage this requirement, but I would prefer an already-made, official solution to my problem.

A note about the context which justifies the requirement of short-circuit handler evaluation: the example provided above is simplified and uses two very cheap authentication handlers, but the real case I'm facing off involves at least one very slow handler, that perform paid calls to an external system - so I would like to avoid calling it for requests already authenticated by former handlers.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source