A Better way to do API Key verification in WCF WebHTTP services

This post explains a better solution to API Key verification in WCF WebHTTP services, than using a ServiceAuthorizationManager. When a verification fails, this method sends the error response in the same format that the user receives if there were no errors.

If you’ve ever tried to do an API key verification in a webhttp service, chances are you’ve already read this good post by Ron Jacobs about using a ServiceAuthorizationManager to do the authorization. But the problem in that approach is that, when the authentication fails, it sends an HTML response to the client.

But I think if your service is using JSON or XML for request/responses, it should send the errors also in those formats. If not your clients will be confused when your service starts spitting html when something goes wrong.

Here’s the trick, with the .NET 4.0, microsoft has given us the excellent WebFaultException class which serializes the exception to the format that the user receives if there were no errors. So if your service returns JSON your exceptions will also be JSON and if XML, exceptions will also be XML.

But as I experienced, this serialization only works if the exception is thrown within the service class. But not if you throw the exception from a ServiceAuthorizationManager or an HTTPModule. So when you try to throw it from your custom ServiceAuthorizationManager it is sent to the client in the default format of your service and it may not be the format that the user requested.

Here’s the solution

Do the key verification inside your services constructor  !!!

Yes simple as that. But you need to make sure that the InstanceContextMode = InstanceContextMode.PerCall is set on your service to make sure the constructor is called for every request.

Here’s the code of the contructor including the service description:

[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
   [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   public class API : IAPI
   {
       /// <summary>
       /// Initializes a new instance of the REST
       /// </summary>
       public API()
       {
           ValidateKey(); //since we have InstanceContextMode = InstanceContextMode.PerCall, this is called per every api call yay!!!

       }

 

And here’s the ValidateKey() method

void ValidateKey()
{           

    string key = HttpContext.Current.Request.QueryString["apikey"];
    if (string.IsNullOrEmpty(key))
        key = HttpContext.Current.Request.Headers["apikey"];
    if (!string.IsNullOrEmpty(key))
    {
        Guid apiKey;
        Guid hardKey = new Guid("F5D14784-2D9E-4F57-A69E-50FB0551940A");
        // Convert the string into a Guid and validate it
        if (!Guid.TryParse(key, out apiKey) || !apiKey.Equals(hardKey)) //we are not validating yet just hard code one guid
        {
            throw new System.ServiceModel.Web.WebFaultException<string>("Invalid API Key", HttpStatusCode.Forbidden);
        }
    }
}

This method checks the value of the apikey in query string and then in the httpheader named apikey and validate it with the hard coded key (You can modify the code to do a db lookup). If the validation failed, this sends an HTTP 403 to the user with the text “Inavlid API Key”. Note that this allows users without the api key (public access), just remove the if condition that checks for the empty or null of the key variable to prevent that.

Well that’s it:

Happy Coding: :)

 

Prevent forms auth from redirecting to login page in RESTFul WCF

In this post I am going to explain how to prevent a WCF service from sending HTTP 302 redirects to the login.aspx when used with the forms authentication.

RESTFul web services are becoming more popular than the traditional SOAP based .asmx web services and if you still don’t know about then check out this Wikipedia article http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services

Microsoft has a great starter kit for building REST WCF services here

So back to the article now.

If you are using forms authentication to authenticate a WCF REST service, when the user authentication fails, the service sends a http redirect to the login page. But since this is a web service what we want is a login exception not a web page.

The reason for this is that the forms authentication is primarily designed with the web sites in mind and the redirecting to the login page in the event of a login failure is the expected behavior for a site.

But thanks to the extensibility of the WCF we can work around this limitation.

What we need is to catch the http 302(redirect) sent by the forms auth module before sending it to the user, and change it to a http 401 (unauthorized) and send.

So here’s how to do it;

Step 1. Create an HttpModule

First we need to implement a http module. (If you don’t know what that is, think of it as an evil being who catches the responses before they are sent to the user) :)

So here’s the code for that. I named it as AuthRedirectHandler but you can use any name.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace MyRest
{
    class AuthRedirectHandler : IHttpModule
    {

        #region IHttpModule Members

        public void Dispose()
        {

        }

        public void Init(HttpApplication context)
        {
            context.EndRequest+= new EventHandler(context_EndRequest);
        }

        void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            if (app.Response.StatusCode == 302)
            {
                app.Response.ClearHeaders();
                app.Response.ClearContent();
                app.Response.StatusCode = 401;
            }
        }

        #endregion
    }
}

In the above class we catch the response in the context_EndRequest method and if it is a http redirect, clears all the content, headers and set the status code to 401 (unauthorized). Note that the changing the status code alone won’t be enough but this code will clear the cookies as well :( .

Step 2. Modify the web.config to use the HttpModule

Ok now we have the http module and now we want to tell our service to use it to handle the http requests.

And here’s how to do that.

Go to the web.config in your WCF service and add the following to the <system.web> section

  <httpModules>
      <add name="AuthRedirectHandler" type="MyRest.AuthRedirectHandler, MyRest" />
    </httpModules>

Now run your service and it won’t send you redirects to login page anymore.

Happy coding