Jamie Locatis
Web Developer

Stop Using Unsafe Strings to Refer to User Roles In Your ASP.NET Application

Jul 6, 2018
ASP.NET MVC .NET C#

Switch your user role references throughout your code base to an enum, instead of using the base ASP.NET implementation of just referencing the string role name. This helps to guarantee you won’t have any code errors when the application compiles. Also, learn how to easily create a custom ASP.NET MVC authorization attribute that will allow you to make use of this enum.

Introduction

The base ASP.NET authorization attribute offers some robust access control throughout your application. This is important if you need to easily block certain users from seeing certain parts of your application. The base implementation of this can often cause problems, especially when working with a larger code base. Typically roles will be stored in your database with a name (a string), which can be referenced in code by the string that role has. When the application gets a user, it has a list of strings representing the roles the user is assigned. A standard Identity implementation will let you check a user role in a controller or in a view like so...

var test = User.IsInRole("OtherTestRole");

These roles will probably look something like this in your database…

ASP.NET Identity Roles Database Example
ASP.NET Identity Roles Database Example

Make the Enum

Let’s map those strings to an enum...

public enum AuthorizeRoles
{
    Administrator,
    AnotherTestRole,
    OtherTestRole
}

Check User Role

There are a number of different ways you could override the current ASP.NET Identity User that you have access to, giving you a place to override the IsInRole method we used in the example above to check user role. To simplify this tutorial, let's just quickly create a User Helper that we can reference whenever we need to check user role. Inside of this new UserHelper class, let's write a quick method that gets the current HttpContext (so we can get to the current logged in User) and checks if they have any of our enum roles.

public bool UserIsInRoles(params AuthorizeRoles[] roles)
{
    var httpContext = HttpContext.Current;

    if (roles.Any(r => httpContext.User.IsInRole(r.ToString())))
    {
        return true;
    }

    return false;
}

Now anywhere in our application we can just make a call like this to check user role...

var hasRole = UserHelper.UserIsInRoles(AuthorizeRoles.OtherTestRole, AuthorizeRoles.AnotherTestRole);

The above code would return true if the currently logged in user has either OtherTestRole or AnotherTestRole associated with their account. This is much safer than the built in alternative, where you would pass these roles in as strings. Any typo in those strings wouldn't break until someone actually executed that code, whereas a typo in any of your enums will break when you compile your code. Plus you will also have the benefit of IntelliSense in Visual Studio.

Custom Authorization

Now let’s create our own custom authorize attribute that will do the same as above, check for given roles using the enum instead of a string...

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public CustomAuthorizeAttribute(params AuthorizeRoles[] roles)
    {
        AuthorizedRoles = roles;
    }

    public AuthorizeRoles[] AuthorizedRoles { get; set; }

    protected override bool AuthorizeCore(HttpContextBase 
    httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContext));
        }

        if (AuthorizedRoles.Any(r => 
        httpContext.User.IsInRole(r.ToString())))
        {
            return true;
        }

        return false;
    }
}

Our new attribute inherits from ASP.NET MVC’s AuthroizeAttribute class. It's constructor simply accepts an array of our attribute enum as it's parameters. By overriding the AuthorizeCore method of the AuthorizeAttribute, we can write our custom check to see if the user has any of the provided roles. Of course we are still just calling .ToString() on the enum, but at least it is only happening here, greatly limiting the chance for any error (and again the compiler will catch a mistyped enum and throw an error, it won’t ever catch a mistyped string!).

Now we can call this attribute wherever we could normally call the Authorize attribute like so...

public class TestController : Controller
{
    [CustomAuthorize(AuthorizeRoles.OtherTestRole)]
    public ActionResult Index()
    {
        return View();
    }
}

Simple, easy, and safe!

Share on Twitter!
If you liked this article and think others should read it, please share it on Twitter!