I wanted to do this with a new policy, but I could not figure out how, as even if I inherit my new AuthorizationRequirement from RolesAuthorizationRequirement
, and then implement everything needed to replace the default handler, I'd still have to provide the roles to the requirement when I AddPolicy in Program.cs, which I don't have.
Luckily, my need does not require any additional data in a new AuthorizationRequirement class, so I can forgo that and just totally override the default handler. So, in Program.cs:
builder.Services
.AddAuthorization()
.AddSingleton<IAuthorizationHandler, NonproductionAuthorizeHandler>() // note this makes NonproductionAuthorizeHandler override the default handler
...
And the new handler:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace MyNamespace.Server.Helpers;
/// <summary>
/// Handles authorization to allow access to attribute-specified roles in any environment,
/// as well as special logic that need not be specified in
/// any AuthorizationAttribute (which may appear on any controller or controller method).
/// </summary>
public class SpecialLogicAuthorizeHandler : AuthorizationHandler<RolesAuthorizationRequirement>
{
private readonly IWebHostEnvironment environment;
public SpecialLogicAuthorizeHandler(/* optional injected dependencies */) {}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RolesAuthorizationRequirement requirement)
{
if (requirement.AllowedRoles.Any(context.User.IsInRole) || IsAllowedBySpecialLogic())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private bool IsAllowedBySpecialLogic(){
// whatever you want here
}
}
With that, it is always effective for all AuthorizationAttributes and no policy is needed.
[Authorize(Roles = "MyRole1")]
[HttpGet]
public async Task<string> Get(){
...
}
/// the user can access the above if they satisfy the special logic OR if they are in MyRole1.