20
Oct
11

WCF web api + developer APi keys

I found myself with the requirement of needing custom authorization for a rest-style web API. The requirements would be something along the lines of Netflix, Twitter, etc. APIs whereby there is a need to identify an application making calls to the service. Most of these APIs seem to follow a familiar pattern of dishing out a developer API key consisting of a private key and a public key. If you’re not familiar with the basics of public key cryptography see http://en.wikipedia.org/wiki/Public-key_cryptography.

This post is not about that, though! The purpose for this is to show the code I ended with to integrate the solution into WCF Web API (http://wcf.codeplex.com/). An additional requirement was that some of the API required the auth and some of it didn’t.

I also investigated the OAuth 1.0 spec and it appeared to support my scenario but with extra, unnecessary steps. (I haven’t yet investigated OAuth 2.0 fully).

There appears to be a lot of information floating around on this subject (see http://weblogs.asp.net/cibrax/archive/2011/04/15/http-message-channels-in-wcf-web-apis-preview-4.aspx and http://haacked.com/archive/2011/10/19/implementing-an-authorization-attribute-for-wcf-web-api.aspx, amongst others).

Anyway, I’ll run through the steps of what I did for this so far….

So first, I created a blank ASP.NET MVC3 application. I added some routes in the RegisterRoutes method in Global.asax.cs, one for each API:

   1: routes.Add(new ServiceRoute("api/authedapi",

   2:     new HttpServiceHostFactory

   3:         {

   4:             Configuration =

   5:                 new AuthHttpConfiguration(Container)

   6:                     {

   7:                         EnableTestClient = true,

   8:                         CreateInstance = ResolveServiceInstances

   9:                     }

  10:         },

  11:     typeof (AuthedApi)));

  12: routes.Add(new ServiceRoute("api/keys",

  13:     new HttpServiceHostFactory

  14:         {

  15:             Configuration =

  16:                 new HttpConfiguration

  17:                     {

  18:                         EnableTestClient = true,

  19:                         CreateInstance = ResolveServiceInstances

  20:                     }

  21:         },

  22:     typeof (ApiKeys)));

Things to note about this code are:

  • I have set the CreateInstance delegate to a function which uses my IOC container to resolve the types.
  • The configuration for the un-authed api is just the standard way to set up a WCF web API route – the authedapi, however uses a custom Configuration type derived from HttpConfiguration. It does this in order to set up a custom ServiceAuthorizationManager object on the Service Authorization Behavior:
   1: public class AuthHttpConfiguration : HttpConfiguration

   2: {

   3:     private readonly IUnityContainer _container;

   4:  

   5:     public AuthHttpConfiguration(IUnityContainer container)

   6:     {

   7:         _container = container;

   8:     }

   9:  

  10:     protected override void OnConfigureServiceHost(HttpServiceHost serviceHost)

  11:     {

  12:         ServiceDebugBehavior serviceDebugBehavior = serviceHost.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();

  13:         if (serviceDebugBehavior != null)

  14:             serviceDebugBehavior.IncludeExceptionDetailInFaults = true;

  15:  

  16:         foreach (var behavior in serviceHost.Description.Behaviors.OfType<ServiceAuthorizationBehavior>())

  17:         {

  18:             behavior.ServiceAuthorizationManager = _container.Resolve<MyServiceAuthorizationManager>();

  19:         }

  20:         base.OnConfigureServiceHost(serviceHost);

  21:     }

  22: }

Also note that I use an IOC container to resolve the types. (WCF web API lends itself nicely to the use of dependency injection and unit testing).

Plugging in the custom authorization manager (http://msdn.microsoft.com/en-us/library/ms731774.aspx) allows each requests auth to be handled in a central location. Authorization decisions are made in the CheckAccessCore method, which returns true when access is granted and false when access is denied.

So, here’s the shell of MyServiceAuthorizationManager…

   1: public class MyServiceAuthorizationManager : ServiceAuthorizationManager

   2:  {

   3:      private readonly IApiKeyRepository _apiKeyRepository;

   4:  

   5:      public MyServiceAuthorizationManager(IApiKeyRepository repository)

   6:      {

   7:          _apiKeyRepository = repository;

   8:      }

   9:  

  10:      protected override bool CheckAccessCore(OperationContext operationContext)

  11:      {

  12:         // logic to determine auth status for request...

  13:      } 

  14: }

The logic in CheckAccessCore is something like the following in my scenario:

  • Retrieve the auth-related data from the http request (probably from the http auth header or contained in the query string). This will contain the public key and also the request signature.
  • This contains the public key so look up the private key from the public key in a key repository (The public key is sent in the request. The private key is stored separately by both parties).
  • Generate a signature using the public and private keys and compare it to the one sent in the request.
  • If they match return true, otherwise return false.

This seems to work for me for now but I don’t yet know how this compares with the other methods or indeed whether OAuth 2.0 will better facilitate the requirements for this scenario.

About these ads

3 Responses to “WCF web api + developer APi keys”


  1. January 7, 2013 at 6:26 am

    After looking at a few of the articles on your blog, I seriously appreciate your way of writing a
    blog. I added it to my bookmark webpage list and will be
    checking back soon. Please visit my web site as well and let
    me know your opinion.


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


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: