Today I had to answer to a GitHub issue created by one of my readers who asked me how to properly implement a Role Authorization filter on the controller methods of TestMakerFree, the sample app shown in my ASP.NET Core 2 and Angular 5 book.
What he basically wanted to do is to change this:
1 2 3 4 5 6 |
[HttpPut] [Authorize] public IActionResult Put([FromBody]QuizViewModel model) { // todo... } |
into this:
1 2 3 4 5 6 |
[HttpPut] [Authorize(Roles = "Administrators")] public IActionResult Put([FromBody]QuizViewModel model) { // todo... } |
He also asked to explain if he would be forced to put the role info within the token's claims - which didn't seem a good practice to him for security reasons - or if he could use the user's unique ID (which is already present in the token) to fetch the role entirely from the back-end.
Since the answer could be useful to other people who are struggling to find a way to make the ASP.NET Identity Roles work in ASP.NET Core, I thought that I could also publish it within a dedicated post... and here we are!
Implementing Roles in a token-based Auth pattern
As we might already know, the concept of "roles" (and role-based checks) is mostly a Microsoft thing which does not exist outside of the IPrincipal
pattern used by the ASP.NET Identity framework. In any "non-Microsoft" scenario, which now also includes most of the ASP.NET Core token-based authentication patterns, whenever we want to check for the existence of a given claim we can simply query the claims collection for what you are looking for. Needless to say, we have to do that manually... or, to better say that, using the tools provided by the back-end framework we're using.
ASP.NET Core gives us a chance to easily do that using the new policy-based pattern, which allows a handy attribute-based shortcut that strongly resembles the [Authorize(Roles = "RoleName")] syntax we're already used to: we'll basically just have to replace Roles with Policy, define such policy and then create our token Claims accordingly. If we're looking for a replacement of the above attribute, there's an high chance that this new pattern will help us to fix our specific needs with little changes in how we're already used to work.
For additional info regarding this, we strongly suggest to read this page from the ASP.NET Core official docs.
However, if we really want (or need) to implement the old-fashioned role-based checks from the MS Identity framework, we can still do that by using the RoleClaimType and/or the RoleClaimTypeRetriever properties of the TokenValidationParameters class, depending if we want to put the "roles" claim in our token or not.
In case we don't mind adding it, we just need to define the RoleClaimType property and add the claim accordingly; in case we don't, we can implement the RoleClaimTypeRetriever property, which is basically an handler for a retrieval function (method) which will perform our logic/queries to fetch the user Role using the info that we already know - such as the user's unique ID, which should be more than enough - assuming that we have the vanilla ASP.NET Identity tables in our Database.
However, we should be wary that implementing a RoleClaimTypeRetriever function could easily lead our app to some (hopefully minimal) performance issues - especially if we'll be using a sync SQL query on every login/permission check in order to retrieve the user Role.
Well, that's it for now: in case you need further assistance you can also take a look at this great article from leastpriviledge.com, which explains (almost) everything you need to know regarding this topic.