Implementing an Authorization Attribute for WCF Web API

code, asp.net 0 comments suggest edit

If you’re not familiar with WCF Web API, it’s a framework with nice HTTP abstractions used to expose simple HTTP services over the web. Its focus is targeted at applications that provide HTTP services for various clients such as mobile devices, browsers, desktop applications.

In some ways, it’s similar to ASP.NET MVC as it was developed with testability and extensibility in mind. There are some concepts that are similar to ASP.NET MVC, but with a twist. For example, where ASP.NET MVC has filters, WCF has operation handlers.

security

One question that comes up often with Web API is how do you authenticate requests? Well, you run Web API on ASP.NET (Web API also supports a self-host model), one approach you could take is to write an operation handler and attach it to a set of operations (an operation is analogous to an ASP.NET MVC action).

However, some folks like the ASP.NET MVC approach of slapping on an AuthorizeAttribute. In this blog post, I’ll show you how to write an attribute, RequireAuthorizationAttribute, for WCF Web API that does something similar.

One difference is that in the WCF Web API case, the attribute simply provides metadata, but not the the behavior, for authorization. If you wanted to use the existing ASP.NET MVC AuthorizeAttribute in the same way, you could do that as well, but I leave that as an exercise for the reader.

I’ll start with the easiest part, the attribute.

[AttributeUsage(AttributeTargets.Method)]
public class RequireAuthorizationAttribute : Attribute
{
    public string Roles { get; set; }
}

For now, it only applies to methods (operations). Later, we can update it to apply to classes as well if we so choose. I’m still learning the framework so I didn’t want to go bite off too much all at once.

The next step is to write an operation handler. When properly configured, the operation handler runs on every request for the operation that it applies to.

public class AuthOperationHandler 
      : HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>
{
  RequireAuthorizationAttribute _authorizeAttribute;

  public AuthOperationHandler(RequireAuthorizationAttribute authorizeAttribute)
    : base("response")
  {
    _authorizeAttribute = authorizeAttribute;
  }

  protected override HttpRequestMessage OnHandle(HttpRequestMessage input)
  {
    IPrincipal user = Thread.CurrentPrincipal;
    if (!user.Identity.IsAuthenticated)
    {
      throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    if (_authorizeAttribute.Roles == null)
    {
      return input;
    }

    var roles = _authorizeAttribute.Roles.Split(new[] { " " }, 
      StringSplitOptions.RemoveEmptyEntries);
    if (roles.Any(role => user.IsInRole(role)))
    {
      return input;
    }

    throw new HttpResponseException(HttpStatusCode.Unauthorized);
  }
}

Notice that the code accesses HttpContext.Current. This restricts this operation handler to only work within ASP.NET applications. Hey, I write what I know! Many folks replied to me that I should use Thread.CurrentPrincipal. My brain must have been off when I wrote this to not think of it. :)

Then all we do is ensure that the user is authenticated and in one of the specified roles if any role is specified. Very simple straightforward code at this point.

The final step is to associate this operation handler with some operations. In general, when you build a Web API application, the application author writes a configuration class that derives from WebApiConfiguration and either sets it as the default configuration, or passes it to a service route.

Within that configuration class, the author can specify an action that gets called on every request and gives the configuration class a chance to map a set of operation handlers to an operation.

For example, in a sample Web API app, I added the following configuration class.

public class CommentsConfiguration : WebApiConfiguration
{
    public CommentsConfiguration()
    {
        EnableTestClient = true;

        RequestHandlers = (c, e, od) =>
        {
            // TODO: Configure request operation handlers
        };

        this.AppendAuthorizationRequestHandlers();
    }
}

The RequestHandlers is a property of type Action<Collection<HttpOperationHandler>, ServiceEndpoint, HttpOperationDescription>

In general, it would be up to the application author to wire up the authentication operation handler I wrote to the appropriate actions. But I wanted to provide a method that helps with that. That’s the AppendAuthorizationRequestHandlers method in there, which is an extension method I wrote.

public static void AppendAuthorizationRequestHandlers(
  this WebApiConfiguration config)
{
  var requestHandlers = config.RequestHandlers;
  config.RequestHandlers = (c, e, od) =>
  {
    if (requestHandlers != null)
    {
      requestHandlers(c, e, od); // Original request handler
    }
    var authorizeAttribute = od.Attributes.OfType<RequireAuthorizationAttribute>()
      .FirstOrDefault();
    if (authorizeAttribute != null)
    {
      c.Add(new AuthOperationHandler(authorizeAttribute));
    }
  };
}

Since I didn’t want to stomp on the existing request handlers, I set the RequestHandlers property to a new action that calls the existing action (if any) and then does my custom registration logic.

I’ll admit, I couldn’t help thinking that if RequestHandlers was an event, rather than an action, that sort of logic could be handled for me. Winking
smile Have events fallen out of favor? They do work well to decouple code in this sort of scenario, but I digress.

The interesting part here is that the action’s third parameter, od, is an HttpOperationDescription. This is a description of the operation that includes access to such things as the attributes applied to the method! I simply look to see if the operation has the RequireAuthorizationAttribute applied and if so, I add the AuthOperationHandler I wrote earlier to the operation’s collection of operation handlers.

With this in place, I can now write a service that looks like this:

[ServiceContract]
public class CommentsApi
{
    [WebGet]
    public IQueryable<Comment> Get()
    {
        return new[] { new Comment 
        { 
            Title = "This is neato", 
            Body = "Ok, not as neat as I originally thought." } 
        }.AsQueryable();
    }

    [WebGet(UriTemplate = "auth"), RequireAuthorization]
    public IQueryable<Comment> GetAuth()
    {
        return new[] { new Comment 
        { 
            Title = "This is secured neato", 
            Body = "Ok, a bit neater than I originally thought." } 
        }.AsQueryable();
    }
}

And route to the Web API service like so:

public class Global : HttpApplication
{
  protected void Application_Start(object sender, EventArgs e)
  {
    RouteTable.Routes.MapServiceRoute<CommentsApi>("comments",
      new CommentsConfiguration());
  }
}

With this in place, a request for /comments allows anonymous, but a request for /comments/auth requires authentication.

If you’re interested in checking this code out, I pushed it to my CodeHaacks Github repository as a sample. I won’t make this into a NuGet package until it’s been thoroughly vetted by the WCF Web API team because it’s very likely I have no idea what I’m doing. I’d rather one of those folks make a NuGet package for this. Smile

And if you’re wondering why I’m writing about Web API, we’re all part of the same larger team now, so I figured it’s good to take a peek at what my friends are up to.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

19 responses

  1. Avatar for RichB
    RichB October 19th, 2011

    So WCF has finally been moved out of the SQL Server group? Yay!
    Or has ASP.Net become part of SQL Server now :-(

  2. Avatar for Oli
    Oli October 19th, 2011

    "It’s focus is targeted". It should read "its focus".

  3. Avatar for Cecil
    Cecil October 19th, 2011

    Check this blog post out. The gitHub repo has an extension method that helps with adding new request handlers
    pfelix.wordpress.com/...

  4. Avatar for Steve Syfuhs
    Steve Syfuhs October 19th, 2011

    You could just replace HttpContext.Current.User with Thread.Principal and (I'm assuming) you could reduce the dependency on being within an ASP.NET application.

  5. Avatar for haacked
    haacked October 20th, 2011

    @lordeagle. Thanks! Fixed. @Steve, fixed as well. Thanks!

  6. Avatar for Pedro
    Pedro October 20th, 2011

    I also don't like the Thread.Principal solution, specially in this new age of asynchronicity, where the underlying thread may change several times.
    The authentication "component" should instead
    a) attach the identity to the HttpRequestMessage (as a property),
    b) output it as a parameter, so that it can be used by downstream handlers or by the operation.
    The authorization component retrieves the identity from the HttpRequestMessage or declares it as an input parameter.
    The post mentioned by Cecil uses this approach.

  7. Avatar for Luke
    Luke October 20th, 2011

    Do you have any insight into recommended auth methods using WebAPI when it will be consumed programmatically? I was playing with it a couple months back and I felt like I was stuck between forcing the client to use a weird forms based hack where they auth, store the cookie, and include the cookie with every request, and implementing a complicated OAuth-style token mechanism. Is there any hope for a simple standard api authentication mechanism to make its way into WebAPI (besides using windows authentication :-)?

  8. Avatar for Cecil
    Cecil October 20th, 2011

    Instead of replacing the RequestHandlers property you could call AddRequestHandlers on the WebApiConfiguration instance and pass it your delegate. I believe that could remove the need for you if(requestHandlers != null) check

  9. Avatar for haacked
    haacked October 20th, 2011

    @Pedro, yeah, good point.
    @Luke, I'll talk to the Web API guys for more guidance. I think for now, if you run on ASP.NET, FormsAuth is always an option. Howard Dierking wrote a bit about using OAuth.
    @Cecil, Ah! I didn't know about that method. I'll take a look. :)

  10. Avatar for babaandthepigman
    babaandthepigman October 20th, 2011

    I haven't really considered the pros and cons yet but I recently prototyped a developer key scenario in WCF WebAPI using ServiceAuthorizationManager. See babaandthepigman.wordpress.com/...

  11. Avatar for Glenn Block
    Glenn Block October 20th, 2011

    On the subject of whether to use an operation handler vs message handler, I put together a post on which to use when here: codebetter.com/...
    There is some overlap but there are clear design goals for each.

  12. Avatar for Glenn Block
    Glenn Block October 21st, 2011

    Luke, we are definitely looking at reusable bits both server and client for handling auth with web api.

  13. Avatar for Joshua Shannon
    Joshua Shannon October 25th, 2011

    I always thought you could stack delegates the same way you stack events (Events are a just a specialized delegate form as I recall with some syntactic sugar). I'm almost certain I've done this in the past.
    Combining delegates on MSDN
    so you could just do config.RequestHandlers += ...???

  14. Avatar for Adam Anderly
    Adam Anderly November 2nd, 2011

    I've been fighting with both MessageHandlers (which Glenn mentions as a good tool for security) and OperationHandlers for authentication.
    I'm really missing the simplicity of MVC Action Filters and how simple it is to create an Authorization action filter.
    What I'd really like to see is the ability to do some sort of request intercepting/filtering as easily as I can add action filters to Mvc.
    Then, be able to register them per operation (like I can per action) or per Api (like I can per Controller) or globally for all Apis Mvc Global Filters.
    Please find a way! I'm debating between using Mvc3 and WebApi right now for creating a new web api and I really wish it was a slam-dunk decision to go with WebApi, but so far it's not.
    Cheers,
    Adam

  15. Avatar for Cory
    Cory November 15th, 2011

    Can someone explain why we have to go this route in the first place? Why don't the attribute [code]
    [PrincipalPermission(SecurityAction.Demand, Role = "myrole")] [/code]
    and the service behavior work using WCF WebApi?
    [code]
    <system.serviceModel>
    <behaviors>
    <serviceBehaviors>
    <behavior>
    <serviceAuthorization principalPermissionMode="UseAspNetRoles"
    roleProviderName="AspNetSqlRoleProvider" />
    </behavior>
    </serviceBehaviors>
    </behaviors>
    [/code]

  16. Avatar for Antony Scott
    Antony Scott December 20th, 2011

    Here is a neat little trick I use for splitting strings. Instead of this ...
    var roles = _authorizeAttribute.Roles.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
    I do this ...
    var roles = _authorizeAttribute.Roles.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    Not much of a saving in this case, but when you've got lots of different characters you want to split on it looks much neater IMO.

  17. Avatar for BB
    BB January 13th, 2012

    @Cory
    Bloody good question!
    I notice nobody's provided an answer, no surprise there.
    In essence we just want to check that HttpContext.Current.User is authorised to call our WCF service/methods.
    Can't have been overlooked, surely....

  18. Avatar for Remy Blaettler
    Remy Blaettler February 3rd, 2012

    Thanks Phil for this example. I've used it to implement my own custom Basic HTTP Authentication module. If someone is interested:
    remy.supertext.ch/...

  19. Avatar for Kader Belbina
    Kader Belbina March 28th, 2012

    I used this to great affect in the web-api but am struggling to do something similar in the new ASP.NET MVC 4 Web Api. Any suggestions?