Resource/Action based Authorization for OWIN (and MVC and Web API)

Authorization is hard – much harder than authentication because it is so application specific. Microsoft went through several iterations of authorization plumbing in .NET, e.g. PrincipalPermission, IsInRole, Authorization configuration element and AuthorizeAttribute. All of the above are horrible approaches and bad style since they encourage you to mix business and authorization logic (aka role names inside your business code).

WIF’s ClaimsPrincipalPermission and ClaimsAuthorizationManager tried to provide better separation of concerns – while this was a step in the right direction, the implementation was “sub-optimal” – based on a CLR permission attribute, exception based, no async, bad for unit testing etc…

In the past Brock and me worked on more modern versions that integrate nicer with frameworks like Web API and MVC, but with the advent of OWIN/Katana there was a chance to start over…

Resource Authorization Manager & Context
We are mimicking the WIF resource/action based authorization approach – which proved to be general enough to build your own logic on top. We removed the dependency on System.IdentityModel and made the interface async (since you probably will need to do I/O at some point). This is the place where you will centralize your authorization policy:

public interface IResourceAuthorizationManager

{

    Task<bool> CheckAccessAsync(ResourceAuthorizationContext context);

}

 

(there is also a ResourceAuthorizationManager base class with some easy to use helpers for returning true/false and evaluations)

The context allows you to describe the actions and resources as lists of claims:

public class ResourceAuthorizationContext
{
    public IEnumerable<Claim> Action { get; set; }
    public IEnumerable<Claim> Resource { get; set; }
    public ClaimsPrincipal Principal { get; set; }
}

 

Middleware
The corresponding middleware makes the authorization manager available in the OWIN enviroment:

public void Configuration(IAppBuilder app)
{
    var cookie = new CookieAuthenticationOptions
    {
        AuthenticationType = "Cookie",
        ExpireTimeSpan = TimeSpan.FromMinutes(20),
        LoginPath = new PathString("/Login"),
    };
    app.UseCookieAuthentication(cookie);
 
    app.UseResourceAuthorization(new ChinookAuthorization());
}

 

Usage
Since the authorization manager is now available from the environment (key: idm:resourceAuthorizationManager) you can get ahold of it from anywhere in the pipeline, construct the context and call the CheckAccessAsync method.

The Web API and MVC integration packages provide a ResourceAuthorize attribute for declarative checks:

[ResourceAuthorize(ChinookResources.AlbumActions.View, ChinookResources.Album)]

 

And several extension methods for HttpContextBase and HttpRequestMessage, e.g.:

if (!HttpContext.CheckAccess(
    ChinookResources.AlbumActions.Edit,
    ChinookResources.Album,
    id.ToString()))
{
    return new HttpUnauthorizedResult();
}

 

or..

var result = Request.CheckAccess(
    ChinookResources.AlbumActions.Edit,
    ChinookResources.Album,
    id.ToString());

 

Testing authorization policy
Separating authorization policy from controllers and business logic is a good thing, centralizing the policy into a single place also has the nice benefit that you can now write unit tests against your authorization rules, e.g.:

[TestMethod]
public void Authenticated_Admin_Can_Edit_Album()
{
    var ctx = new ResourceAuthorizationContext(User("test", "Admin"),
        ChinookResources.AlbumActions.Edit,
        ChinookResources.Album);
    Assert.IsTrue(subject.CheckAccessAsync(ctx).Result);
}

 

or…

[TestMethod]
public void Authenticated_Manager_Cannot_Edit_Track()
{
    var ctx = new ResourceAuthorizationContext(User("test", "Manager"),
        ChinookResources.TrackActions.Edit,
        ChinookResources.Track);
    Assert.IsFalse(subject.CheckAccessAsync(ctx).Result);
}

 

Code, Samples, Nuget
The authorization manager, context, middleware and integration packages are part of Thinktecture.IdentityModel – see here.

The corresponding Nuget packages are:

..and here’s a sample using MVC (if anyone wants to add a Web API to it – send me a PR).

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

6 Responses to Resource/Action based Authorization for OWIN (and MVC and Web API)

  1. larry says:

    I downloaded the ResourceAuthorizationSample and am seeing a problem with the Claim.Properties property.

    If I edit the InMemoryClaimsRepository’s GetClaimsForUser method and add a test key-value pair onto the Claim.Properties property, it is not there when I later check for it in the ChinookAuthorization’s CheckAccessAsync override method. The Claim is there but the Properties property is empty.

    Is there anything special I need to do to have the Claims.Property “persist” in the Claim?

  2. Raf says:

    1) So this is your recommended way to perform granular authorization?

    I’m still new to the IdentityServer.3 approach but I certainly will use it on the application I’m working on. And I am trying to understand what is the best possible way to perform granular authorization.

    My app has tons of actions/resources so I will need to store this inside a database so Im guessing I will have to implement my own version of ResourceAuthorizationManager.

    2) If I were to use this approach with IdentityServer.3 that means I will have to use the claims (or scopes?) returned on the token instead of having my own ClaimRepository implementation. Right?

  3. Khaled Hammouda says:

    How do I combine resource authorization with OAuth bearer token authentication?

    Previously I had this in my OWIN configuration:

    app.UseOAuthBearerAuthentication( new OAuthBearerAuthenticationOptions {} );

    And my controller:

    [Authorize]
    public class IssuesController : ApiController {

    }

    The [Authorize] attribute caused the OAuth token validation to be enforced, so this part was fine.

    Now I have:

    app.UseOAuthBearerAuthentication( new OAuthBearerAuthenticationOptions {} );
    app.UseResourceAuthorization( new MyAuthorizationManager() );

    [ResourceAuthorize( “Access”, “Things” )]
    public class IssuesController : ApiController {

    }

    But now the OAuth token validation doesn’t work. If I add the [Authorize] attribute to the controller it works. So is this how it’s supposed to be:

    [Authorize]
    [ResourceAuthorize( “Access”, “Things” )]
    public class IssuesController : ApiController {

    }

    Seems a bit redundant to me to require to authorization attributes to have this work, because from your example you seem to be able to achieve both resource authorization and cookie authentication by just using the ResourceAuthorizeAttribute.

  4. Pingback: Common authentication/authorization between .NET4.0 and .NET4.5 web applications | Low Level Design

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 )

Google+ photo

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

Connecting to %s