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: 🙂

 

Configure ASP.NET MVC controller to use SSL or Not to use SSL

This article explains how to configure a controller or a controller action to use SSL or Not to use SSL or use SSL only on a production server

If you are developing an asp.net mvc app that uses SSL for some of its pages, you need a robust way to make sure that all the requests for that page will use https:// connections. Luckily for us microsoft has come up with the RequireHttps Attribute. When you decorate a controller action with the RequireHttps Attribute, all the requests for that action will be forced to use SSL.

But there’s a catch. If you use that attribute, the page will ask for an SSL connection even on the visual studio development server (casini) and as casini does not support SSL, you cannot debug the page 🙁

So we need to customize the RequireHttpsAttribute to order it to ask for SSL only for remote servers and not in localhost. Luckily for us .net framework 2.0 source is available so we can get the attributes source and modify it.

 

Here’s the modified code to use SSL only for remote hosts

namespace System.Web.Mvc
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Web.Mvc.Resources;

    [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
        Justification = "Unsealed because type contains virtual extensibility points.")]
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class RequireHttpsProdAttribute : FilterAttribute, IAuthorizationFilter
    {

        public virtual void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            //modified to check for local requests since cant use ssl for dev server. (casini)
            if (!filterContext.HttpContext.Request.IsSecureConnection && !filterContext.HttpContext.Request.IsLocal)
            {
                HandleNonHttpsRequest(filterContext);
            }
        }

        protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
        {
            // only redirect for GET requests, otherwise the browser might not propagate the verb and request
            // body correctly.

            if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("Requests to the given url must use SSL");
            }

            // redirect to HTTPS version of page
            string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
            filterContext.Result = new RedirectResult(url);
        }

    }
}

 

Save the above code as RequirehttpsAttribute.cs and …. that’s it.

Now go to your controller action and add the following attribute to it [RequireHttpsProd(Order = 1)]

Adding the attribute to the controller will apply it to all the controller actions in that controller.

Now there’s another issue. When the user is in a page with SSL, all the relative links he visit from it will be https://… ones. So how to force user to use a non SSL connections to some pages in your site ?

It’s simple just turn the require https attribute upside down. Like this 🙂

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

namespace Web
{
    public class NotRequireHttpsAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var response = filterContext.HttpContext.Response;

            if (request.IsSecureConnection && !request.IsLocal)
            {
                string redirectUrl = request.Url.ToString().Replace("https:", "http:");
                response.Redirect(redirectUrl);
            }
            base.OnActionExecuting(filterContext);
        }
    }
}

 

Save the above code as NotRequireHttpsAttribute.cs and apply the attribute NotRequireHttps to controllers or actions that need non SSL connections.

Well that’s it,

 

Happy Coding…