Pages

Wednesday, 10 October 2012

WebAPI and Subscriber Authentication by Custom HTTP Headers

Recently I've been experimenting with WebAPI, part of the ASP.NET 4 framework. Whilst providing a great way to provide HTTP services, most HTTP services that are provided by companies are on a subscription basis. With this in mind, how can I best secure my HTTP Services for consumption by a paying subscriber?

The easy answer is to pass in an authentication token or credentials with each call to your HTTP service. How you do this is important, particularly for GET methods.

The best solution I've come up with so far is to add a custom authentication header to calls to my HTTP service, and provide a mechanism in my WebAPI MVC application to check the authentication provided in the header of each call.

First, we need to build our WebAPI MVC application! To do this:

Start Visual Studio and select New Project from the Start page. Or, from the File menu, select New and then Project.

In the Templates pane, select Installed Templates and expand the Visual C# node. Under Visual C#, select Web. In the list of project templates, select ASP.NET MVC 4 Web Application. Name the project "NewsSubscriptionService" and click OK.

In the New ASP.NET MVC 4 Project dialog, select Web API and click OK. For the purposes of this example, don't tick the Create a unit test project box.

Created for you as part of this project will be a default API controller, located in the Controllers folder and called ValuesController.cs. You will need to open this file to create our test GET method.
In the ValuesController.cs file, please replace the class section with:
public class ValuesController : ApiController
    {
        // GET api/values
        [ValidateSubscriber]
        public string Get()
        {
            return "Authenticated";
        }
    }
What does this do? Well, it creates a GET method called Get, which returns a string value. You will see from the code that an attribute has been assigned to the method called ValidateSubscriber. This is what you will create next, and does the job of validating the authentication token passed in the HTTP request to our method. The benefit of this approach is you can secure each HTTP method in your application by applying the attribute as required. 


In your solution, create a folder called Authentication and within this folder create a class file called ValidateSubscriber.cs
In the ValidateSubscriber.cs file, replace the ValidateSubscriber class with:
public class ValidateSubscriber : System.Web.Http.AuthorizeAttribute
    {
        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            
            if (actionContext.Request.Headers.GetValues("authenticationToken") != null)
            {
                // get value from header
                string authenticationToken = Convert.ToString(actionContext.Request.Headers.GetValues("authenticationToken").FirstOrDefault());
                // This is a hard-coded token, but in reality you would do a check against a subscriber database or something of that ilk.
                if (authenticationToken != "326ED626-0E95-4C75-A456-B42E605FF929")
                {
                    HttpContext.Current.Response.AddHeader("authenticationToken", authenticationToken);
                    HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized");
                    actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
                    return;
                }

                HttpContext.Current.Response.AddHeader("authenticationToken", authenticationToken);
                HttpContext.Current.Response.AddHeader("AuthenticationStatus", "Authorized");
                return;
            }
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed);
            actionContext.Response.ReasonPhrase = "Please provide valid inputs";
            base.OnAuthorization(actionContext);
        }
    }
You will see from the code that requests to any HTTP service secured by this attribute is intercepted and a custom HTTP header value is looked for called authenticationToken. For this example, a hard-coded Guid value is expected, but in reality you would plumb this in to a database or data store call to check the supplied Guid is valid. Each subscriber should have a different Guid too!


This is all you need to secure your HTTP method!
A quick test is to call this secured method in a HTML page:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="/Scripts/jquery-1.7.1.min.js"></script>
    <script type="text/javascript" src="/Scripts/knockout-2.1.0.js"></script>
    <script type="text/javascript" src="/Scripts/jquery.unobtrusive-ajax.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $(".button").click(function () {
                $.ajax({
                    
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader("authenticationToken", "326ED626-0E95-4C75-A456-B42E605FF929");
                    },
                    url: "/api/Values/Get",
                    type: "GET",
                    statusCode: {
                        200: function (result) {
                            $("#Data").html(result);
                        },
                        401: function (result) {
                            $("#Data").html("Access was denied");
                        }
                    }
                });
            });
        });
    </script>
</head>
<body>
    <div id="Data"></div>
    
    <input type="submit" class="button" value="test" />
    
</body>
</html>

What this does is create a request to our HTTP method and display the string value that is returned by the method. You can test the authentication by changing the Guid value added to the request in the beforeSend function.
An example solution is available for download here

3 comments:

  1. Hey thanks for the example.. you know, I looked all over, and although there was obviously lots of examples of filters / custom filters and so, they all seemed to revolved around forms authentication or other 3rd party identity managers, when all I needed was to allow a user to login, return a guid/api key if successful, and then a way to pass that through to the REST calls to authenticate them/identify them. Using this approach with Azure Caching, so thanks for a simple and clear example!

    ReplyDelete
  2. Hi

    I want to call this api from another webapp using Javascript. I am trying to pass authheader from javascript. But not able to pass authheader. Is it a cross domain issue??How do I resolve this??

    ReplyDelete
  3. Hey Yogeeta, you have to use CORS

    http://codebetter.com/johnvpetersen/2012/04/02/making-your-asp-net-web-apis-secure/

    ReplyDelete