A better way to handle role permissions in ASP.NET Identity – IsInRole vs. HasPermission

ASP.NET Identity is currently the main framework used to add authentication and authorization capabilities to an ASP.NET site. After ASP.NET Identity is integrated with an ASP.NET project it creates a few database tables where relevant user data can be stored. Let’s look at some of these tables:

AspNetUsers – the table where application users are stored
AspNetRoles – this is where we store application roles (you can also think of them as groups)
AspNetUserRoles – a mapping table where we store information about what users belong to what roles

So far, so good. What we get out of the box is a way to create users, create some roles and assign users to roles. This follows the best-practice where we want to eventually assign application permissions to roles instead of individual users.

But how exactly do we handle application permissions given the functionality that ASP.NET Identity provides for us out of the box? Most developers and projects at this point follow the pattern that we’re so used to from other areas in IT – we check if a user belongs to a certain role and if so, we allow that particular operation to proceed. In ASP.NET this is done using the Authorize attribute (allow the operation to proceed if the current user belongs to a certain role):

What is wrong with this approach? Let’s consider a few scenarios:

  • Let’s say that you’re working on a somewhat complex site that has 20 roles. It could totally be the case then that you’d have certain functions where, let’s say, 15 of those roles would be allowed access … so that code would look something like this:

    This is getting ugly and quite unmanageable.

  • Imagine that your boss comes and requires a permission update – “Allow RoleX to access function A.” The only answer to that given the default implementation is “sure, but that requires a project recompilation and code push to production”. Do that a few times and management will want a better way to handle this aspect of the site.
  • Consider this: an audit request comes down – “We need to know exactly what RoleZ can do in the application”. Good luck with that one – you’re now stuck running searches across the entire project code trying to see where that role might appear in an Authorize attribute.

I think you get the picture. The default approach using the Authorize attribute in ASP.NET has all sorts of problems. We’re basically asking the question “Is the user in this role” and based on the answer to that question we’re hardcoding behavior into our application code.

Let’s look at this problem from a different point of view. Instead of checking whether the user belongs to a role let’s ask this question instead – “does the current user have the particular permission that’s required to access this particular function“?

It’s pretty clear that the Authorize attribute won’t help us here anymore – we need another tool, another attribute. We need to customize the framework’s behavior and this is probably the main reason why most projects will just stick to the default behavior since it’s so much simpler (at least initially).

A possible attribute to give us what we need could look like this:

… and the implementation for the HasPermission attribute might look like this:

The function CHECK_IF_USER_OR_ROLE_HAS_PERMISSION is where we determine whether this user or role has the particular permission we’re looking for in order to allow that operation to proceed. What we do in that function is based on how we actually store our permissions data and how we tie that to application roles.

Consider for example the following database tables:

Permissions (Id, Description) – this table stores the list of all permissions we support in our application. The values from the ‘Description’ column are the values we’d use in our code as parameters to the HasPermission attribute (we could certainly check permissions by Id instead of Description but using the description can make the code more readable – we’d immediately know if we have the right permission in place for a given operation.

AspNetRolePermissions (RoleId, PermissionId) – a mapping table where we link roles to permissions. If a row exists in this table it would mean that the role has that particular permission – otherwise, that role does not have the permission.

With this table structure we now have enough information for CHECK_IF_USER_OR_ROLE_HAS_PERMISSION to determine if a user or role has a particular permission. We know the current user or role (we get that from ASP.NET Identity). We know the permission we’re checking for – that’s passed in to our custom attribute. If we find an entry in AspNetRolePermissions for that role/permission combination we allow the operation to proceed.

So what did we do here? We went from the default implementation where we answered the question “is the user in this role” (with the hardcoded answer basically driving our application permissions implementation) and we moved to a more flexible approach where we answer the question “does the user/role have this permission” where the permission data is stored in metadata tables outside of the actual application code.

Is this a better approach? Let’s consider again the original list of problems with the default approach:

  • It does not matter how complex our application is or how many roles are involved – we only need to check whether the current user/role has the permission needed to perform that operation (we will not have a long list of comma-separated roles in our new attribute).
  • Does management want to modify some permissions? No problem – we just need to update data in AspNetRolePermissions and the application will pick it up right away – no compilations or code releases needed for this. For bonus points – build an admin web interface around the new permission metadata tables and allow management to directly manage application permissions.
  • Permission audits? No problem – we just need to check our permissions metadata tables to see what permissions a particular user or role has.

This particular article focused on ASP.NET Identity but a similar problem exists in previous ASP.NET authentication frameworks and probably other such similar frameworks in other languages. It’s pretty clear that what we get out of the box with such frameworks is not always ideal. Instead of hardcoding such critical data in the application code spend some time upfront to come up with a site architecture that allows privileged users to maintain this type of data without having to dig through application code – dynamic, flexible site architectures are a win-win for everybody.

36 thoughts on “A better way to handle role permissions in ASP.NET Identity – IsInRole vs. HasPermission

  1. Another approach is to treat Permissions as Roles. I.e. you would have a custom RoleProvider or similar whose “GetRolesForUser” method returns a list of Permissions. Then you can use all the existing authorization code – AuthorizeAttribute, PrincipalPermissionAttribute etc unchanged.

    • The main problem with that approach is that you would be assigning permissions now directly to users (since the old structures for roles are now used for permissions instead) … so that leads to management problems in a different way.

  2. I think you misunderstood what I’m proposing. You keep the data model that you propose (Users are mapped m-n to Roles; Roles are mapped m-n to Permissions). You then need a custom admin tool to manage this data model. But at runtime, all you need to do is return a list of Permissions from the GetRolesForUser method, after joining Users-UserRoles-Roles-RolePermissions-Permissions.

    • As described in the article the mapping between roles and permissions could possibly be done in a table such as AspNetRolePermissions which will have entries for every role/permission combination. If an entry is found for a given role and permission then it means that the role in question has that particular permission.

  3. Great Post, just what I was looking for. I couldn’t work out what to search for and then stumbled across this!

    I think I understand where Joe is coming from though, instead of querying the database every time a user accesses a new Action you would have all the required information inside the user object. Would this not provide a faster method of checking permissions with less overhead from contacting the database? What do you think Cristian?

    • Regarding Joe’s answer: I’m not quite sure where that user object would reside that would skip trips to the database in order to lookup permissions. If I’m not mistaken I remember that even the default ASP.NET Identity implementation still hits the db for each user request to determine if the user is in the requested role.

      In the custom implementation I describe you have to write custom code to determine if a user has a certain permission. One approach for example would be at time of login to store all user’s permissions as the result of a LINQ query in the session object and then lookup future permissions that way without hitting the db. HTTP as we all know is a stateless protocol so we either have to keep state in memory on the web server using the session object (or a dedicated session store service such as Redis or AWS DynamoDB) or we have to hit the main database for info on each request.

  4. Hi,

    Thanks for interesting article. But it seems that AspNetRolePermissions is already implemented in ASP.NET Identity Framework as AspNetRoleClaims. Please correct me if I am wrong.

    • I’m familiar with AspNetUserClaims (and working with claims is not the same as what I’m proposing here) but I don’t know where AspNetRoleClaims comes from … there is not much information on the web in reference to that name.

  5. Pretty sure you just reinvented claims based authorization. Of course in a full blown claims system, there would be IDs all over the place and checks to make sure your application had access to those claims by a claims server, yada, yada, yada. But in essence you have recreated what claims is supposed to do for you. Unfortunately the current versions of ASP.Net identity ,with MVC 5, did not implement AspNetRoleClaims (it’s there in MVC 6 I believe).

    I also don’t think you want to redirect them to the login page for a missing claim (permission). Instead you want to return HTTP 403, perhaps using an IActionFilter as recommended on this SO (http://stackoverflow.com/a/2578795).

  6. So, pulling the data out from AspNetRolePermissions table and checking against the records will all be done inside the HasPermissionAttribute?

    • In theory yes but it all depends exactly on how you implement it. For example, in one app where we used this approach we saved a particular user’s set of permissions to that user’s Session object so that we’d not need to hit the db on each lookup to see if the user had a particular permission or not.

  7. Great post!! I am just working on migrating our current app to MVC and Identity. I used old membership provider and roles worked great. With new app, we need more granularity on permissions and been looking exactly similar to what you proposed. It really great article with neat detail proposal.

    As you said in the reply, it’s good idea to store the set of permission use has in the users object as list and check against it when HASpermission is called. Only issue with that and similar session approaches is that if the one of the user permission was removed after the user is logged in, it won’t taken into account that permission is removed and will continue to allow the user to do that permission until session expires right!!

    • You’re correct … the way I described it permissions for a user would stick in the user’s session until that times out. It’s the eternal struggle between caching (performance) and the need for up-to-the-second realtime information. A possible approach there would be at the time when a permission change is made (in some sort of admin area of the site) to track down those permission objects in sessions and invalidate them, causing them to load fresh from the database at the next request.

  8. Why are you reinventing the wheel in regard to claims authentication? Have you ever used OAuth? Nice attempt at being a manager with unrealistic requests. Lol

    • OAuth does not tie into this really because we’re just authenticating and authorizing users against a single website … there are no multiple sites involved and no need to implement a separate OAuth server.

    • I think it’s important to clarify here that OAuth is not an implementation; it’s a loose specification for how clients can securely communicate with resources (web services) over HTTPS. While OAuth does specify the use of claims, usually pased via session cookies, to exchange information between client and server, the nature of those claims or how they may dictate what a end user is allowed to do in a system is completely left up to the implementationof the app. Considering that OAuth does not account for the identity of an end user, you would need an implementation of OAuth2 such as OpenID Connect to first attribute claims to an identity; then Cristian’s proposal here is still very relevant. For example, having an array of roles in an identity claim must be interpreted by the web service – mapping to permissions against specific actions exposed by that service improves maintainability. Whether dealing with a single website, a web application calling multiple web services or otherwise, OAuth is not equivalent to a role-base access control mechanism and Microsoft has never baked in a full impelementation of role-base access control within ASP.NET Identity because they traditionally provide limited “hello-world” templated features, leaving the demanding nuance of meeting real-world use cases up to software developers.

  9. Hello Cristian, thank you for the insight on this subject. Do you have the code for “CHECK_IF_USER_OR_ROLE_HAS_PERMISSION” function by any chance? Thank you.

    • The implementation of that function is specific to the application so that’s why I didn’t include it. In that function you’d most likely hit a backend database to determine whether a user belongs to a role that has a particular permission.

  10. I don’t see how this approach is better than just using the out of the box roles method. According to your code, you still have to hardcode the permissions just like roles. For instance, if a user has permissionA, permissionB, permissionC. You would have to hardcode these attributes to the controller action method such as [HasPermission(“PermissionA”)] , [HasPermission(“PermissionB”)], [HasPermission(“PermissionC”)].

    So how is it better compared to out-of-the-box-role method?

    • With the ‘permission’ approach (vs. the default role approach) there is really no need to have multiple permissions being checked on a given controller action. We can have individual permissions created for each controller action and then in the db, where we control what role has what permission, we determine the roles that are allowed access to that particular controller action. For example – for controller A with action X we simply have a permission called A_X that applies strictly to that controller action.

      The modified method is better than the original ‘is in role’ check because of the location of permission management. With the original approach (where we check whether a user belongs to a certain role) we have to hardcode those checks directly for each controller action. What if you want to see what permissions a given role has? You have to search the entire codebase to find them. What if you want to give a particular role a new permission? You need a code change & deploy for that.

      With the new method the permission check for a given controller action does not change again – we check to see who has the permission for that action – and in the db table where we manage them we can dynamically make changes without needing code updates … much, much better management experience.

  11. Cristian, do we need to register the custom HasPermission attribute method to the filterconfig.cs file’s RegisterGlobalFilters? Thank you.

  12. Hi. And about the isInRole in the view? If I want hide some part of view if the user not has the X permission? How I get this?

    tk’s

  13. Hi,
    Thanks for this excellent post.

    The table name AspNetRolePermissions says that you are linking a Role to Permission(s).

    If one wants to give more flexibility in the application, would it possible to link Users to Permissions? For instance, even if User1 belongs Group1, I want to give this extra permission for her.

    What do you think? Doable with this same table structure?

    Thanks
    Bilal

    • You could probably set it up that way (you’re writing custom code so you could do whatever you want) but … if you have users receive permissions directly and also from role membership you’ll probably end up with a very complex and hard to manage system. It might be more flexible but it will be complex and complexity is the enemy of security.

  14. This is great and I’m using something almost exactly the same. My only issue is that it seems like my roles are getting cached server side. Have you ran into this issue?

    • I guess it depends on your particular implementation of the process described above. In one of the solutions where we used this we specifically wanted to cache all the permissions that a logged in user had (so we would not have to hit the database backend on each request) so we put that permission collection as an object in the session management system we were using.

  15. i am developing an erp system for my college and i am new to asp.net mvc.
    i have roles like director coordinator,faculty mentor,admin and many more.
    what i want is
    user has roles and roles has permission
    i want to add a user assign him/her a role and giving permissions to users.
    this has to be done dynamically no hard coding of roles and permission

    please provide a sample code

Leave a Reply to Joe Cancel reply