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.
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.