Youlounn
Youlounn

Reputation: 43

Why my custom AuthorizationHandler is executed twice with an AuthorizationFilterContextSealed object as context.Resource?

I have developed an OData endpoint in .NET 6.0 and I need to add a custom parameterized Authorization filter. Thanks to this documentation, I created customs AuthorizeAttribute, IAuthorizationPolicyProvider and AuthorizationHandler with its requirement. The problem is that HandleRequirementAsync function is called three times when I execute a request:

I didn't found a lot of information about this AuthorizationFilterContextSealed class...

Does someone know why these calls are done?

Is there a way to avoid them? Otherwise, what is the best practice to manage them? Because I cannot cast context.Resource as AuthorizationFilterContextSealed since this class is internal.

Thanks :)

Upvotes: 4

Views: 1275

Answers (2)

Kenzo De Ridder
Kenzo De Ridder

Reputation: 81

In our .NET8.0 app, this was happening because we had the following:

services.AddControllers(o =>
{
    o.EnableEndpointRouting = false;
});
...
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

Setting EnableEndpointRouting to true (which is the default) made it so our handlers weren't called for AuthorizationFilterContextSealed resource objects anymore.

I'm not sure anymore why we disabled endpoint routing to begin with, but I'm guessing it had something to do with an older version of the OData library.

Upvotes: 3

MSingh-13
MSingh-13

Reputation: 41

I had faced a similar issue with my .NET8 application. I had added a global AuthorizeFilter to ensure that the users had a specific role. I also had an Authorize attribute with a custom policy on my controller. On debugging I found that the HandleRequirementAsync in my AuthorizationHandler was being executed twice for every request to the controller's actions. The first time the context.Resource was a DefaultHttpContext as expected. The second time it was AuthorizationFilterContextSealed.

I came across the below issue on github which explained the issue and provided a solution. https://github.com/dotnet/aspnetcore/issues/32518

I am quoting Pranav's answer for reference.

Yup, this is by design. Starting in 3.1, authorization attributes are handled by the authorization middleware instead of being evaluated by MVC's authorization filter. However if you explicitly add an AuthorizationFilter, it would get separately executed.

Instead of adding a AuthorizationFilter, consider using using RequireAuthorization on an endpoint: e.g.

endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}")
   .RequireAuthorization("some_policy");

This would a substitute to adding a global auth filter.

After applying the above solution, my AuthorizationHandler was called only once per request.

In addition to above, in order to apply the same global policy on attribute based routes, I had to call RequireAuthorize on the result of an explicit call to MapControllers(). If you are using .NET8, the Map* methods can be called on the WebApplication object (app) returned by the WebApplicationBuilder.

app.MapControllers()
    .RequireAuthorization(policy);

Upvotes: 2

Related Questions