Authorize Attribute with Multiple Roles

C#asp.net MvcAuthorization

C# Problem Overview


I would like to add Authorization to a controller, for multiple Roles at once.

Normally that would look like this:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

But I have stored my Roles in consts, since they might change or be extended at some point.

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

I cannot do this, since the string must be known at compile time:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

Is there a way to circumvent the problem?

I COULD write a const which simply contains "RoleA,RoleB,RoleC" - but I dislike magic strings and this is a magic string. Changing the name of a Role and forgetting to change the combined string would be a disaster.

I am using MVC5. ASP.NET Identity and the Role are known at compile time.

C# Solutions


Solution 1 - C#

Try to create custom authorize attribute like this.

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = string.Join(",", roles);
    }
}

Assuming your roles will be the same for multiple controllers, create a helper class:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

Then use it like so:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}

Solution 2 - C#

The best and simplest way I found to resolve this problem is just to concatenate roles in the Authorize attribute.

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

with CustomRole a class with constant strings like this :

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}

Solution 3 - C#

Make sure you are deriving your custom attribute class off System.Web.Mvc.AuthorizeAttribute and NOT System.Web.Http.AuthorizeAttribute.

I ran into the same problem. Once I changed it, everything worked.

You may also want to add the following to your custom attribute class:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 

Solution 4 - C#

What i did is the answer in @Tieson

I tweak a little in his answer. Instead of string.Join why not convert it to list?

Here is my answer:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

And then check the if the role is valid overriding OnAuthorization

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);
                
                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

And there you have it, it is now validating if the role is authorized to access the resource

Solution 5 - C#

I feel like a custom authorize attribute is overkill for this issue unless you have a large amount of roles.

Since the string must be known at compile time, why not make a static Role class that contains public strings of the roles you have defined, and then add comma separated strings with certain roles that you want to authorize:

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

And then you can use the Authorize Attribute like so on the Controller Class or the Controller Method (or both):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChristian SauerView Question on Stackoverflow
Solution 1 - C#MacGyverView Answer on Stackoverflow
Solution 2 - C#ChristopheHvdView Answer on Stackoverflow
Solution 3 - C#Jerry FineganView Answer on Stackoverflow
Solution 4 - C#Christopher EnriquezView Answer on Stackoverflow
Solution 5 - C#Robert TuttleView Answer on Stackoverflow