.Net 6 Rest API Multiple AuthenticationHandlers with anonymous fallback

703 Views Asked by At

I've been trying to accomplish the following, but to no avail:

  • I'm creating a REST Api that allows multiple authentication schemes (like bearer, basic etc.)
  • None of my routes NEED to be authenticated, but the AuthenticationHandlers should be run when an Authorization header is passed
  • When an authorization header is passed and it matches any of the authentication schemes, but the authentication fails, the result should always be unauthorized

In my startup.cs I have the following code:

builder.Services.AddAuthentication()
    .AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("basic", null)
    .AddScheme<BearerAuthenticationHandlerOptions, BearerAuthenticationHandler>("bearer", null);

builder.Services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder("basic", "bearer").RequireAuthenticatedUser().Build();
});

builder.Services.AddControllers(options =>
{
    options.Filters.Add(new AuthorizeFilter());
});

The problem with this is that when I pass no authorization header, it always returns 401 unauthorized. I've tried the following:

  • Add AllowAnonymous as filter on all routes:
    • this results in the route being called even if an AuthenticationHandler returns failed
  • Add another AuthenticationHandler called AnonymousAutnenticationHandler that always succeeds and sets the user to anonymous.
    • this results in the user always becoming anonymous, even if the authentication of the matched "real" authenticationhandler fails
  • Add a fallbackPolicy for the AnonymousAuthenticationHandler
    • This causes the fallback handler to be called even if a handler in the DefaultPolicy fails, resulting in accepted requests when authentication fails.

Now I can come up with 2 solutions:

  • Don't use separate Authentication handlers for different schemes, but create a single handler that handles all schemes
    • This doesn't seem like the intention of the AuthenticationHandler system
  • Have the AnonymousAuthenticationHandler check if an Authorization header is present, and only succeed if it not present
    • Although this works (and is what I'm currently using) it doesn't really feels like the correct solution either, since the Authorization header is not the only way in our api to authenticate, and I have to add all other possible authentication methods to the AnonymousAuthenticationHandler checks when I implement them.

I would expect that an AuthenticateResult.Fail would trump all other AuthenticateResult.Success results, but this doesn't seem to be the case. It feels like I'm doing something fundamentally wrong, but I'm not getting it.

0

There are 0 best solutions below