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 neededUse 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