Using Claims-based Authorization in MVC and Web API

.NET 4.5 ships with a claims-based authorization infrastructure around the ClaimsAuthorizationManager class. I have written about that before, and I am still a fan of that approach (not necessary of the non-existent tooling).

Claims-based authorization encourages you to have a clean separation of business and authorization code – and that’s much better than sprinkling role checks all over your code base.

The problem is, that the corresponding API is not very approachable, especially in the face of “modern” application development like MVC or Web API. The main problem here is that the classes that ship with .NET 4.5 to invoke authorization are based on a code access security permission (ClaimsPrincipalPermission) and calling the CheckAccess method will throw a SecurityException instead of returning a boolean.

This approach typically gets in your way, e.g. ClaimsPrincipalPermission gets invoked directly by the CLR, which means it will also run in unit tests, and unhandled exceptions short-circuit your processing pipeline.

That said, all the base APIs in .NET 4.5 allow using claims-based authorization in a much more sensible way, you just have to write your own plumbing. Since I am working mostly with MVC and Web API these days, I decided to do that.

Thinktecture.IdentityModel contains an authorization filter called ClaimsAuthorizeAttribute (well – strictly speaking two filters – one for Web API, one for MVC) to make the connection to ClaimsAuthorizationManager.

You can either use them as a global authorization filter, e.g.

MVC

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

{

    filters.Add(new HandleErrorAttribute());

    filters.Add(new ClaimsAuthorizeAttribute());

}

Web API

public static void Register(HttpConfiguration config)

{

    config.Routes.MapHttpRoute(

        name: “DefaultApi”,

        routeTemplate: “api/{controller}/{id}”,

        defaults: new { id = RouteParameter.Optional }

    );

 

    // add global authorization filter

    config.Filters.Add(new ClaimsAuthorizeAttribute());

}

 

In that case the registered authorization manager will be invoked for every request with a resource/http method pair (Web API) or a controller/action pair (MVC).

..or you can decorate controllers and actions with it. With that approach you can also pass in explicit resource/operation pairs, e.g.:

[ClaimsAuthorize("Read", "SomeData")]

public string Get()

{

    return “somedata”;

}

You can also pass in multiple resource descriptions, if that makes sense for your authorization logic:

[ClaimsAuthorize("View", "StreetAddress", "TelephoneNumber")]

public ActionResult Contact()

{

    ViewBag.Message = “Your contact page.”;

 

    return View();

}

…or you can invoke the authorization manager imperatively:

public string Get(int id)

{

    var isAllowed =
      ClaimsAuthorization.CheckAccess(“Get”, “CustomerId”
, id.ToString());

 

    …

}

 

Note: The WIF team made the unfortunate design decision to provide a default implementation of ClaimsAuthorizationManager that always returns true. That means, if you (or your customers) forget to register the authorization manager in configuration, all your access checks will always succeed. And that’s really bad.
Fortunately my ClaimsAuthorization class above (that is also used by the attributes internally) checks that. It will throw an exception when no custom ClaimsAuthorizationManager is configured, so you directly know what’s wrong.

The binaries are on Nuget, and you can find the full sample here.

HTH

This entry was posted in .NET Security, ASP.NET, IdentityModel, WebAPI. Bookmark the permalink.

16 Responses to Using Claims-based Authorization in MVC and Web API

  1. Christian says:

    How would the claims in your last example (var isAllowed =
    ClaimsAuthorization.CheckAccess(“Get”, “CustomerId”, id.ToString());) look like?

    In my understanding I would have created a claim of type “CustomerId” with e.g. the value 5 if the user is allowed to access user 5.

    How would you check the access in the ClaimsAuthorizationManager?
    If I understand your .CheckAccess method correctly it converts all but the first parameters into “ResourceType”.
    Would I check for the resource “CustomerId” and for the resource “5″? (I would have checked for the presence of “CustomerId” and then compared the values.)

    • well – i wouldn’t do it like that – claims describe the identity of a principal. not his authorization data (this may sometimes overlap).

      What claims would you create when the use can access 1 million of your customers??

      In claims based authz you pass in the intent of the user + his identity. The rest is up to the authz manager.

      • Andy Cohen says:

        I’m questioning this paradigm as well.

        Can you provide an example of how you would implement the ClaimsAuthorization Manager for this particular example? Or perhaps fill in the blanks:

        public override bool CheckAccess(AuthorizationContext context)
        {
        if (ClaimsPrincipal.Current.Identity.IsAuthenticated)
        {

        TraceAuthorizationContext(context);

        Claim actionClaim = context.Action.Where(x => x.Type == “http://application/claims/authorization/action”)
        .FirstOrDefault();

        Claim resourceClaim = context.Resource.Where(x => x.Type == “http://application/claims/authorization/resource”)
        .FirstOrDefault();

        IPrincipal principal = context.Principal;

        string resource = resourceClaim.Value;
        string action = actionClaim.Value;

        // NOTE: Add custom logic here
        // DO SOMETHING HERE FOR THE EXAMPLE:
        /*
        var isAllowed =
        ClaimsAuthorization.CheckAccess(“Get”, “CustomerId”, id.ToString());
        */

        return base.CheckAccess(context);
        }

        return false;
        }

      • Well in pseudocode:

        Evaluate if principal with claims x is allowed to do action y on resource z. Ask additional data stores if you have to.

        What’s questionable?

      • Andy Cohen says:

        WordPress only allows the threads to go so deep, so I’m responding here.

        I guess I the word paradigm was a little out of place. My question is, are you going to have a bunch of if then that statements one on top of each other?

        What I am frustrated by is the lack of guidance on what the best practices are in implementing this. I could certainly write a lookup system that checks to see if the principal who is requesting the action and resource in fact does have access based on their claims – but is that the intention? You keep saying “it’s up to you”. Can you elaborate any here?

      • That’s the intention yes.

        Unfortunately there is no “framework” around that.

      • Andy Cohen says:

        All intentions aside, would you recommend this? (I’m assuming so) I was going to read the Appendix G: “Authorization Strategies” referred to in your Patterns and Practices book, but it looks like it was never added to any of the 3 editions that I have read.

        I can certainly implement my own code here, but I hate to do that when it comes to Authentication or Authorization. I tend to want to rely on tried, tested, and true patterns when it comes to security.

      • It seems Microsoft never added that appendix. I remember we discussed it. But it was hard to give general advice.

        The resource/action pattern is a tested one. You just have to implement it yourself.

        Or start an open source project around it, I am happy to review it/give advice.

  2. I just spent the last week taking apart all the System.Security.Principal objects trying to figure out why the hell I was having trouble gathering group roles from AD, and ended up writing a custom filter attribute to use DirectoryServices.AccountManagement… all the while looking at the Identity’s Claims collection and day dreaming that I was using it instead.

    Anyway, I really liked your article. I’ll take a look at the filter soon.

  3. Michael De Marco says:

    Why don’t you provide a useful example of how the claims authorization manager might determine if a claims principal can actually get that customer id. CheckAccess(“Get”, “CustomerId”, id.ToString());). I downloaded the sample code and below is what you have. Bear in mind i understand your leaving it up to us to determine how the CheckAccess call gets implemented. However, there is something to be said about ‘best practices’. What is a best practice for mapping claims to authorization?Should we put the authorization attributes in a table connected to the relying party and then maybe say if the iclaimsprincipal is in a certain role then they are authorized to do A, B, C. I understand that authorization should not be a part of STS as STS is largely communicating identity not authorization. In true architecture there should be a piece of middleware not STS that maps principals to authorizations so that the relying party does not have to assemble this. If I have ten relying parties and I have to do the same things it’s quite duplicative.

    public override bool CheckAccess(AuthorizationContext context)
    {
    Trace.WriteLine(“\n\nClaimsAuthorizationManager\n_______________________\n”);

    Trace.WriteLine(“\nAction:”);
    Trace.WriteLine(” ” + context.Action.First().Value);

    Trace.WriteLine(“\nResources:”);
    foreach (var resource in context.Resource)
    {
    Trace.WriteLine(” ” + resource.Value);
    }

    Trace.WriteLine(“\nClaims:”);
    foreach (var claim in context.Principal.Claims)
    {
    Trace.WriteLine(” ” + claim.Value);
    }

    return true;
    }

  4. I have written about that before – *how* you actually determine if user A is allowed to do operation X on resource Y is up to you.

    This could be based on roles, this could be row level security inside your DB – or something completely different. Sorry can’t help you here.

    • Michael De Marco says:

      Agreed. However, in the future tell the powers to be to create an abstraction that can be implemented to map identities to authorizations and have that reusable across multiple RP’s.

  5. Rune Trusell says:

    Hi Dominick

    I am trying out claims based authorization. My first problem was how to catch the SecurityException that occurs when the AuthorizationManager.CheckAccess method returns false. I simply cannot get it.

    Then I tryed your IdentityModel, and are currently using the attribute way: [ClaimsAuthorize("SomeAction", "SomeResource")]. This works fine for a user with the right permissions. My AuthorizationManager checks the claims and returns true, and the given method is entered. Hwever, when an unaurhorized user logs on, and the AuthorizationManager returns false (which is correct), a logon-popup is shown. How and where should I control this return value?

    I hope it makes sense, I am totally new into this field.

    All the best,
    Rune

    • I assume this is with basic auth and a browser client (that would be good info to include) ?

      Could you please ask the question on the github issue tracker für identitymodel so other can benefit from that?

      thanks!

      • Rune says:

        Hi,

        This was some front-end issues that I was not aware of. In collaboration with my front-end man, we created our own ClaimsAuthorizeAttribute, deriving from your ClaimsAuthorizeAttribute class. This class handles unauthorized requests by creating a response of type MethodNotAllowed. This way we can handle the 404.

  6. Ibraheem says:

    Thanks for the great article. With regards to the comments that refer to how claims should be mapped to authorizations, IMO a nice pattern would be to have roles contain a collection of authorizations. Your claim could be for a role, or for an authorization. As long as the role contains the authorization, it can be considered as being the authorization. Your CheckAccess(authorization) logic can then use the claim whether its for an authorization explicitly, or for a role that contains the authorization.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s