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
- this results in the route being called even if an AuthenticationHandler returns
- 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.