skip to Main Content

I’m exploring Minimal APIs in .Net 6, and trying to apply a custom Authorization Filter to the endpoint (via Attributes or Extensions).
But it seems to me, I am doing something wrong, or it’s simply not designed to work in that way (and it’s sad if so).
Couldn’t find anything in the docs besides the default usage of [Authorize] attribute in Minimal APIs.

Here is the Filter

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    //Checking tokens
}

And if I try to apply it at Controller level, it works fine

[CustomAuthorize]
public class CustomController : ControllerBase
{
    //Necessary routing
}

But if I switch to Minimap APIs notation and try to use attributes

app.MapGet("/customEndpoint", 
        [CustomAuthorize] async ([FromServices] ICustomService customService, Guid id) => 
            await customService.GetCustomStuff(id));

or even an extension method

app.MapGet("/customEndpoint", 
        async ([FromServices] ICustomService customService, Guid id) => 
            await customService.GetCustomStuff(id)).WithMetadata(new CustomAuthorizeAttribute());

It just doesn’t work. The filter doesn’t even being constructed.

What did I miss or did wrong?
Thx in advance

2

Answers


  1. I think you won’t be able to inject action filter in minimal api, you can use 3 alternative approches.

    1. Create a custom middleware and inject it in startup class, it would check every request and do the intended work as you filter is doing. You can put a check for the request path there if you only need to validate a specific controller/endpoint.

    2. The second approach is you can inject httpcontext in minimal api like this, from that extract jwt token and validate that, if found not ok reject that request.


     app.MapGet("/customEndpoint", async (HttpContext context, ICustomService service) =>
     {
         var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
         if (string.isNullOrEmpty(token) || <not a valid token>) return Results.Unauthorized();    
         // do some work 
         return Results.Ok(result);
     });
    

    as @Dai suggested, you can extract token in this way also

    AuthenticationHeaderValue.TryParse(context.Request.Headers["Authorization"], out var parsed ) && parsed.Scheme == "BearerOrWhatever" ? parsed.Parameter : null
    
    1. You can register the filter globally from startup.cs.
    Login or Signup to reply.
  2. You can write a custom authorization filter for Minimal API in .NET 6.0

    Here is how I tend to approach it – by using Policy-based authorization in ASP.NET Core

    Step 1: Create a Requirement

    A requirement implements IAuthorizationRequirement

    public class AdminRoleRequirement : IAuthorizationRequirement
    {
         public AdminRoleRequirement(string role) => Role = role;
         public string Role { get; set; }
    }
    

    Note: A requirement doesn’t need to have data or properties.

    Step 2: Create a Requirement Handler

    A requirement handler implements AuthorizationHandler<T>

     public class AdminRoleRequirementHandler : AuthorizationHandler<AdminRoleRequirement>
     {
        public AdminRoleRequirementHandler(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
        {
            if (context.User.HasClaim(c => c.Value == requirement.Role))
            {
                context.Succeed(requirement);
            }
            else
            {
    
                _httpContextAccessor.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                _httpContextAccessor.HttpContext.Response.ContentType = "application/json";
                await _httpContextAccessor.HttpContext.Response.WriteAsJsonAsync(new { StatusCode = StatusCodes.Status401Unauthorized, Message = "Unauthorized. Required admin role." });
                await _httpContextAccessor.HttpContext.Response.CompleteAsync();
    
                context.Fail();
    
            }
    
        }
        private readonly IHttpContextAccessor _httpContextAccessor;
    }
    

    Note: HandleRequirementAsync method returns no value. The status of either success or failure is indicated by calling context.Succeed(IAuthorizationRequirement requirement) and passing the requirement that has been successfully validated or by calling context.Fail() to indicate AuthorizationHandlerContext.HasSucceeded will never return true, even if all requirements are met.

    Step 3: Configure Your Policy in the Authorization Service

     builder.Services.AddAuthorization(o =>
     {
            o.AddPolicy("AMIN", p => p.AddRequirements(new AdminRoleRequirement("AMIN")));
     });
    

    Step 4: Add Your Requirement Handler to DI

     builder.Services.AddSingleton<IAuthorizationHandler, AdminRoleRequirementHandler>();
    

    Step 5: Apply Policy to Endpoints

     app.MapGet("/helloworld", () => "Hello World!").RequireAuthorization("AMIN");
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search