Skip to content

Endpoint with AllowAnonymous() returns 401 when API version is unsupported or unspecified #1131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
naegelejd opened this issue Apr 22, 2025 · 2 comments
Open
1 task done
Assignees

Comments

@naegelejd
Copy link

naegelejd commented Apr 22, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I am using Minimal APIs with Asp.Versioning.Mvc 8.1.0.

I have Authentication and Authorization enabled with default JWT-Bearer Authentication scheme and default Authorization policy (the only requirement is DenyAnonymousAuthorizationRequirement).

As expected, with Authentication/Authorization disabled, I get a 400 Bad Request response when I provide an invalid API version in a client request (or omit the API version altogether).

However, if I enable Authentication/Authorization, and use .AllowAnonymous() to bypass the authorization requirement on an endpoint, I get a 401 Unauthorized response for the same requests.

Is this the expected behavior? Either way, is there a way to work around this so that we can return a 401 response?

Expected Behavior

When I use .AllowAnonymous() on an endpoint, I expect to receive a 401 Bad Request when a client request omits the API version or provides an invalid API version.

Steps To Reproduce

Minimal server to reproduce:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Asp.Versioning;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddAuthorization();
builder.Services.AddOptions<AuthorizationOptions>().Configure((authOptions) =>
{
    authOptions.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});


builder.Services.AddApiVersioning(options =>
{
    options.ApiVersionReader = new QueryStringApiVersionReader("api-version");
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

var api = app.NewVersionedApi();
api.MapGet("/foo", () => "Hello World!").AllowAnonymous().HasApiVersion(1.0);

app.Run();

Demo:

$ curl -i http://localhost:5080/foo?api-version=1.0
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Tue, 22 Apr 2025 20:49:15 GMT
Server: Kestrel
Transfer-Encoding: chunked

$ curl -i http://localhost:5080/foo?api-version=1.1
HTTP/1.1 401 Unauthorized
Content-Length: 0
Date: Tue, 22 Apr 2025 20:49:18 GMT
Server: Kestrel
WWW-Authenticate: Bearer

$ curl -i http://localhost:5080/foo
HTTP/1.1 401 Unauthorized
Content-Length: 0
Date: Tue, 22 Apr 2025 20:49:29 GMT
Server: Kestrel
WWW-Authenticate: Bearer

Exceptions (if any)

No response

.NET Version

9.0.102

Anything else?

No response

@commonsensesoftware
Copy link
Collaborator

commonsensesoftware commented Apr 22, 2025

I might be missing something, but I don't see a mapping for 1.1. My best guess is that endpoint selection is being short-circuited and returning 401 before API versioning has a chance to return 400.

If you add support for 1.1, then I expect it to work the same way as 1.0, which seems to be correct:

var root = api.MapGroup("/foo").HasApiVersion(1.0).HasApiVersion(1.1);

@naegelejd
Copy link
Author

naegelejd commented Apr 23, 2025

@commonsensesoftware

I slightly simplified my example above, removing the group so we can just focus on these lines:

var api = app.NewVersionedApi();
api.MapGet("/foo", () => "Hello World!").AllowAnonymous().HasApiVersion(1.0);

What response should I get when I run curl http://localhost:5080/foo?
I would expect either 200 Ok, or 400 Bad Request because I'm missing the required, valid API version (1.0). But instead, I'm getting a 401 Unauthorized, which is simply incorrect.

If I remove ApiVersioning and run that command, I get a 200 Ok:

app.MapGet("/foo", () => "Hello World!").AllowAnonymous();

Perhaps something is being short-circuited but it would seem to be a bug because it's short-circuiting the .AllowAnonymous(). The documentation for AllowAnonymous says "This will bypass all authorization checks for the endpoint including the default authorization policy and fallback authorization policy".

I'm happy to work around it if you have any insights. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants