Home > Software engineering >  AllowAnonymous not working with ASP.NET Core 6.0 Web API
AllowAnonymous not working with ASP.NET Core 6.0 Web API

Time:08-27

I have the following authentication configuration in ASP.NET Core 6.0, with a custom authentication scheme:

// Enable authentication, add a custom scheme and set it as the default
builder.Services.AddAuthentication(opts =>
        opts.DefaultAuthenticateScheme = "Custom")
    .AddScheme<CustomAuthSchemeOptions, CustomAuthSchemeHandler>("Custom", null);

// stuff...

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

// Use attribute routing for the Web API
app.MapControllers();

Options are empty, while the CustomAuthSchemeHandler can be something as simple as this:

public class CustomAuthSchemeHandler: AuthenticationHandler<CustomAuthSchemeOptions>
{
    public CustomAuthSchemeHandler(
        IOptionsMonitor<CustomAuthSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        Console.WriteLine("Authenticating..."); // the logger, yeah yeah...
        var principal = new ClaimsPrincipal(new ClaimsIdentity("Test"));
        var ticket = new AuthenticationTicket(principal, "Custom");
        return AuthenticateResult.Success(ticket);
    }
}

Now, what I understand (although apparently undocumented) is that if I set the default authentication scheme as in the code above, authentication is automatically enforced on all controllers and actions.

Now what if I wanted to have a controller/action with no authentication?

My understanding is that there are two options:

  • Remove the default authentication scheme and use the [Authorize] attribute explicitly where needed

  • Use the [AllowAnonymous] attribute to disable authentication in specific contexts

The first option works, but I can't get the [AllowAnonymous] approach to work.

The following code behaves as if the [AllowAnonymous] wasn't there, i.e. the authentication scheme handler is called anyway and the user is authenticated.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet("test")]
    [AllowAnonymous]
    // ^^^^^^^^^^^^^
    public string Get()
    {
        return HttpContext.User.Identity.IsAuthenticated ? "YES" : "NO";
    }
}

What am I missing?

Thanks!

CodePudding user response:

The AllowAnonymous attribute "allows anonymous" as its name implies, but also allows authorization, that's why your handler is called... it should work even when your handler can't authenticate (that's what the attribute does on the middleware)

PS: It also makes the middleware ignore the Authorize attribute if both are present, but not the default authentication scheme

CodePudding user response:

Adding to Jcl's answer, that pointed me in the right direction, here's a summary of how to use authentication schemes and authorization in ASP.NET Core 6.0.

This is an excerpt from the blog post I've written after this question.

  • you can define many authentication schemes, whose handlers contain the actual authentication code
  • if you choose a default authentication scheme...
    • the authentication handler will be called at each request, no matter what
    • to actually enforce authorization, use RequireAuthorization() or [Require]
    • to selectively disable authorization where already enabled, use [AllowAnonymous]
  • if you don't set a default scheme...
    • you must explicitly choose an authentication scheme when enabling authorization, with [Require(AuthenticationSchemes = "...")]
    • the authentication handler will then be run only where you enabled authorization
    • to simplify the syntax and simply use [Authorize], define a default authorization policy at startup
  • Related