How to do API Key Verification for REST Services in .NET 4

Recently somebody asked me about doing APIKey verification in .NET 4.  Previously in .NET 3.5 we provided the REST Starter Kit which included a fair bit of server side code to enable a collection of RequestInterceptor objects that we showed doing API Key verification.  In this post I’ll show you how you can be a ServiceAuthorizationManager in WCF to accomplish the same goal.

For more info

What is an API Key and Why do I need to verify it?

When you create a RESTFul service on the public web you have to protect yourself from people who might abuse your service.  After all, once it is available to the public for use by anonymous users if people start abusing the service by invoking it too much or trying to hack into it you need a way to shut off their access to your service.

I recall a few years ago when I was doing ARCast.TV we started seeing a huge spike in download traffic.  We wondered if I had suddenly become very popular, but sadly it turned out to be a buggy add on for Windows Media Center that was repeatedly downloading episodes until it filled up the hard drive, then it would delete them and download again until the users ISP would shut off their connection.

In the world of the web the provider has to pay the bandwidth charges for such bugs so needless to say we weren’t very happy about this.  We decided to block that particular program but we couldn’t block it by IP address since there were many customers around the world using it.  Fortunately they set the User-Agent string so we could filter out their requests.

But what if they didn’t use the User-Agent string?  What if they were a malicious program trying to make my bandwidth charges go so high that I would have no alternative but to shut down the feed?  This is where an API Key becomes useful.

Most public Web APIs ask you to sign up for their developer program even if it is free to access their service.  Once you sign up, they provide you with an API key that you have to pass whenever you call their service.  That way if they detect that your app is buggy or malicious they can simply revoke your API key and you won’t be able to access the service anymore.

How do I verify an API key with a WebHttp Service?

Easy… ready?

  1. You create a list of API Keys and store them in something (XML file, Database etc.)
  2. You implement a ServiceAuthorizationManager for your service that verifies API keys against this datastore
  3. You give your users an API key for their app
  4. You ask users to provide an API key as a query string parameter that they use when they call your service.

In this post we will do the simplest things and in later posts we will consider how you can make this even better with Windows Server AppFabric.

Step 1 – Store Some Keys

In my example, I’m storing some keys in an XML file that I generated using the DataContractSerializer.  The details of how I store and read this file are not important here.  Most people will probably store keys in a database or distributed cache.  As you can see below, I’ve stored the keys in APIKeys.xml

image

Step 2 – Implement a ServiceAuthorizationManager

Implementing a ServiceAuthorizationManager is easy.  Don’t let the sample code about claims confuse you – you don’t have to use claims in your authorization manager.  In my case, I’ve created some simple code that extracts the APIKey from the query string and searches the list of keys to see if there is a match.

The first step is to obtain the APIKey from the QueryString.  To do this I’ve created the following method.

    1:  public string GetAPIKey(OperationContext operationContext)
    2:  {
    3:      // Get the request message
    4:      var request = operationContext.RequestContext.RequestMessage;
    5:   
    6:      // Get the HTTP Request
    7:      var requestProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
    8:   
    9:      // Get the query string
   10:      NameValueCollection queryParams = HttpUtility.ParseQueryString(requestProp.QueryString);
   11:   
   12:      // Return the API key (if present, null if not)
   13:      return queryParams[APIKEY];
   14:  }

Next I created an APIKeys property on the class.  The property getter obtains the list of keys from the cache.  In my simple example, I’m using the ASP.NET cache but with some very minor code changes I could use AppFabric Caching to get a truly scalable distributed cache.  I put them in the cache because I can control the length of time the keys will remain present.  If I revoke a key after a few minutes the list of keys will expire from the cache and the key will be revoked.  This small compromise prevents me from having to look up the keys in a database every time a request comes to a service.

    1:  public List<Guid> APIKeys
    2:  {
    3:      get
    4:      {
    5:          // Get from the cache
    6:          // Could also use AppFabric cache for scalability
    7:          var keys = HttpContext.Current.Cache[APIKEYLIST] as List<Guid>;
    8:   
    9:          if (keys == null)
   10:              keys = PopulateAPIKeys();
   11:   
   12:          return keys;
   13:      }
   14:  }

Next I implement a method to validate the API Key.  For the purposes of the sample I’ve added a global property that allows you to enable/disable API Key Verification from the home page.

    1:  public bool IsValidAPIKey(OperationContext operationContext)
    2:  {
    3:      // if verification is disabled, return true
    4:      if (Global.APIKeyVerification == false)
    5:          return true;
    6:   
    7:      string key = GetAPIKey(operationContext);
    8:   
    9:      Guid apiKey;
   10:   
   11:      // Convert the string into a Guid and validate it
   12:      if (Guid.TryParse(key, out apiKey) && APIKeys.Contains(apiKey))
   13:      {
   14:          return true;
   15:      }
   16:      else
   17:      {
   18:          // Send back an HTML reply
   19:          CreateErrorReply(operationContext, key);
   20:          return false;
   21:      }
   22:  }

Finally I override the CheckAccessCore method.  This is the method that WCF will call to see if I want to allow the call or not.

    1:  protected override bool CheckAccessCore(OperationContext operationContext)
    2:  {
    3:      return IsValidAPIKey(operationContext);
    4:  }

 

 

 

 

Step 3 – Add the Authorization Manager behavior to web.config

Our class is implemented but WCF doesn’t know about it yet.  We need to modify web.config to indicate that we have a serviceAuthorization behavior we want to add.  In .NET 4 we have simplified WCF configuration so you can create a default behavior set that will be applied to services that do not specify any other behavior.  I’m going to take advantage of this to make it really simple.  Look at line 6 – this is where I added the ServiceAuthorization behavior.  Lines 11-15 add new features for WebHttp services in .NET 4 that provide automatic content negotiation and an auto generated help page.

    1:  <system.serviceModel>
    2:    <behaviors>
    3:      <serviceBehaviors>
    4:        <behavior>
    5:          <!-- This behavior enables API Key Verification -->
    6:          <serviceAuthorization serviceAuthorizationManagerType="WCFWebHttp.APIKeyAuthorization, WCFWebHttp" />
    7:        </behavior>
    8:      </serviceBehaviors>
    9:    </behaviors>
   10:    <standardEndpoints>
   11:      <webHttpEndpoint>
   12:        <standardEndpoint
   13:          automaticFormatSelectionEnabled="true"
   14:          helpEnabled="true" />
   15:      </webHttpEndpoint>
   16:    </standardEndpoints>
   17:    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
   18:  </system.serviceModel>

Step 4 - Test it out

Give it a try, run the project from Visual Studio.  When you start it up, API key verification is disabled.

  1. Click link #4 (Get Session 1) using the SessionService class – this will invoke a link using the registered route (no .SVC extension)
  2. The service should return data for session 1
  3. Click on the Enable APIKey Verification button – this will turn on verification
  4. Click on link #3 - (Sessions Collection) – the call will fail because the API key was not provided.
    Note: Sometimes if the request was serviced from the local browser cache it will appear to succeed – just hit F5 to force a call to the server
  5. Now click link #7 – the call will succeed because it includes a valid APIKey query string parameter
    https://localhost:62302/Sessions/?apikey=bda11d91-7ade-4da1-855d-24adfe39d174

Summary

If you plan on exposing a WebHTTP (REST) service on the public web you really should support API Key verification.  This will give you the control to block buggy or malicious apps that are abusing your service.  This sample application gives you everything you need to get started.