In “traditional” .NET with its IPrincipal interface and IsInRole method, developers were encouraged to write code like this:
public void AddCustomer(Customer customer)
{
if (Thread.CurrentPrincipal.IsInRole(“Sales”))
{
// add customer
}
}
In code reviews I’ve seen tons of code like this. What I don’t like about this is, that two concerns in your application get tightly coupled: business and security logic.
But what happens when the security requirements change – and they will (e.g. members of the sales role and some other people from different roles need to create customers)? Well – since your security logic is sprinkled across your project you need to change the security checks in all relevant places (and make sure you don’t forget one) and you need to re-test, re-stage and re-deploy the complete app. This is clearly not what we want.
WIF’s claims-based authorization encourages developers to separate business code and authorization policy evaluation. This is a good thing. So the same security check with WIF’s out-of-the box APIs would look like this:
public void AddCustomer(Customer customer)
{
try
{
ClaimsPrincipalPermission.CheckAccess(“Customer”, “Add”);
// add customer
}
catch (SecurityException ex)
{
// access denied
}
}
You notice the fundamental difference? The security check only describes what the code is doing (represented by a resource/action pair) – and does not state who is allowed to invoke the code. As I mentioned earlier – the who is most probably changing over time – the what most probably not.
The call to ClaimsPrincipalPermission hands off to another class called the ClaimsAuthorizationManager. This class handles the evaluation of your security policy and is ideally in a separate assembly to allow updating the security logic independently from the application logic (and vice versa).
The claims authorization manager features a method called CheckAccess that retrieves three values (wrapped inside an AuthorizationContext instance) – action (“add”), resource (“customer”) and the principal (including its claims) in question. CheckAccess then evaluates those three values and returns true/false.
I really like the separation of concerns part here. Unfortunately there is not much support from Microsoft beyond that point. And without further tooling and abstractions the CheckAccess method quickly becomes *very* complex. But still I think that is the way to go.
In the next post I will tell you what I don’t like about it (and how to fix it).
Pingback: Approaches to (Server-side) Authorization | www.leastprivilege.com
Pingback: Using Claims-based Authorization in MVC and Web API | www.leastprivilege.com
Hi again Dominick…I’m running into one issue when using my ClaimsAuthorization manager. By using the current Principal I want the CheckAccess method to return true when some criteria about roles and permissions are met and in the last line return false(default) for every request that never made it to find a true(access granted). But the client app seems to be needing the authorization of the CheckAccess while establishing connection and the “return false” statement is causing an Access Denied Exception. Is there a way to bypass that first check with a true and after that only return true for the request I programmatically I allow???
Thanks again…
Well – I guess you need to check what exactly the client is sending in this first request and special case it.
Hello Dominick.
I know this post is a bit dated, bit it’s still relevant and nicely put together anyway. In the past, I had great success with IsInRole() by “hacking” resource/action pairs into roles. So, in your example, rather than doing IsInRole(“Sales”), I would instead do IsInRole(“customers/add”). This gave me access check globally, both imperatively from external assemblies, MVC controllers, Razor views, etc. through IsInRole(“customers/add”), as well as declarative through [Authorize(Roles = “customers/add”)] attribute, all while being able to re-assign user permissions without changing the code or recompiling assemblies, so it worked extremely well.
Now I’m still trying to get my head around the new OWIN-based way of doing authentication/authorization in MVC 5. The part where I get stuck is how to easily manage “super users” (i.e. users who can do everything.) In the past, I would simply derive from GenericPrincipal and override IsInRole with something like return base.IsInRole(“*”) || base.IsInRole(role); so IsInRole would always return true for users in the “*” ‘role’, regardless what resource they try to access. I have found doing this very thing in the new ASP.NET Identity model is not so trivial. I believe I am supposed to derive ClaimsAuthorizationManager and override CheckAccess, but I’m not sure how to “plug” this derived class into the pipeline. Documentation on how to do this is next to nonexistent. Do you happen to know a sample on how to do this?
Thanks.
Hey,
I don’t have the 100% perfect answer for you – right now I use this approach
https://github.com/thinktecture/Thinktecture.AuthorizationServer/blob/master/samples/Flows/ResourceServer%20(Web%20API%20v2)/Startup.cs
(see the sample – it makes use of ResourceActionAuthorize – I am working on something more “OWIN like”).
For your “superuser” – yes I would special case that in the CheckAccess method – if the user is in some special role e.g. always return true – or whatever makes sense to you.