Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core override controller level Authorize attribute for a specific action

Here is an example controller to explain the case

[Authorize]
public class AccountController : ControllerBase
{
    [AllowAnonymous]
    [Authorize(Policy = "SpecificPolicy")]
    public string MethodA() {}

    public string MethodB() {}
}
  • MethodA should only be authorized via "SpecificPolicy".
  • MethodB should be authorized via the Authorized attribute

The issue I'm having is that if I remove the AllowAnonymous attribute then Authorize on the controller takes precedence which I don't want for MethodA.

When I keep AllowAnonymous for MethodA then Authorize(Policy = "SpecificPolicy") is ignored.

like image 249
Exocomp Avatar asked Mar 05 '23 06:03

Exocomp


1 Answers

When I keep AllowAnonymous for MethodA then Authorize(Policy = "SpecificPolicy") is ignored.

[AllowAnonymous] bypasses all other authorization attributes. When you have it with other authorize attributes at the same time, all other attributes are ignored, even other attributes are the-more-specific method level.

For example:

[AllowAnonymous]
public class DashboardController : Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }
}

/dashboard will be open/public.

The issue I'm having is that if I remove the AllowAnonymous attribute then Authorize on the controller takes precedence which I don't want for MethodA.

When you have multiple authorize attributes, all of them need to be satisfied before you can make the call to the method. In your case, both [Authorize] and [Authorize(Policy = "SpecificPolicy")] must pass before access is granted.

If you don't want [Authorize] to take the precedence, you can only apply it to method B:

public class AccountController : ControllerBase
{
    [Authorize(Policy = "SpecificPolicy")]
    public string MethodA() {}

    [Authorize]
    public string MethodB() {}
}

I want to avoid putting specific [Authorize] attributes on actions since that Controller has lots of actions but a single action that has it's own authorize rule.

Then this might be good time for you to separate MethodA into Areas.

For example:

You still have [Authorize] on your AccountController, but just take out the MethodA:

[Authorize]
public class AccountController : ControllerBase
{
    public string MethodB() {}
}

Then you create an Area for MethodA:

[Area("specific")]
[Authorize(Policy = "SpecificPolicy")]
public abstract class SpecificControllerBase : ControllerBase
{ }

public class AccountController : SpecificationControllerBase
{
    public string MethodA() {}
}

Lastly you need to register the area route in your Startup.cs:

app.UseMvc(routes =>
{
    ...

    routes.MapRoute(
        name: "areaRoute",
        template: "{area:exists}/{controller=dashboard}/{action=index}/{id?}");

    routes.MapRoute(
        name: "default",
        template: "{controller=home}/{action=index}/{id?}");
});
like image 57
David Liang Avatar answered Mar 08 '23 01:03

David Liang