joydip_kanjilal
Contributor

Implement HTTP authentication in Web API

opinion
Oct 08, 20155 mins
Software Development

Take advantage of HTTP authentication to secure your Web API

In this article I would present a discussion on implementing HTTP authentication in Web API. There are two ways in which you can implement HTTP authentication in your Web Api. These include:

  • Forms authentication
  • Basic authentication

We wouldn’t consider Windows authentication as a feasible strategy as you cannot expose your service over the Internet if you leverage Windows authentication.

Securing Web Api using Forms Authentication

Forms authentication uses the ASP.Net membership provider and uses standard HTTP cookies instead of the Authorization header. Forms authentication is not that REST-friendly as it uses cookies, and the clients would need to manage cookies to consume services that take advantage of forms authentication, which is vulnerable to cross-site forgery attacks. This is why you would need to implement CSRF measures if you use forms authentication. Forms authentication doesn’t use encryption to secure the user’s credentials. Hence, this is not a secure strategy unless you run your Web API over SSL.

Secure Web API using basic authentication

Basic authentication sends the user’s credentials in plaint text over the wire. If you were to use basic authentication, you should use your Web API over a Secure Socket Layer (SSL). When using basic authentication, we would pass the user’s credentials or the authentication token in the header of the HTTP request. The service at the server side would need to parse the header to retrieve the authentication token. If the request is not a valid request, the server returns HTTP 401, meaning an unauthorized response.

Let’s explore how we can perform basic authentication using an action filter. To do this, you should create a class that derives the System.Web.Http.Filters.ActionFilterAttribute class as shown below:

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        private Boolean IsUserValid(Dictionary<string, string> credentials)

        {

            if (credentials["UserName"].Equals("joydip") && credentials["Password"].Equals("joydip123"))

                return true;

            return false;

        }

         private Dictionary<string, string> ParseRequestHeaders(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            Dictionary<string, string> credentials = new Dictionary<string, string>();

             var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

            httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

             string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

            string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

            string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

             credentials.Add("UserName", username);

            credentials.Add("Password", password);

             return credentials;

        }

         public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                     Dictionary<string, string> credentials = ParseRequestHeaders(actionContext);

                     if (IsUserValid(credentials))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                 }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage

(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

We check if the authorization header is present; if not, an HTTP 401 or “unauthorized” response is returned.

The next step is to validate the user credentials passed via the authorization request header from the client. Before we do that, we should know how the Web API is to be called from the client. For this, I’ve prepared a test method. The test method uses the HttpClient class to call the Web API. Note that the user names are converted to Base64 string format before they are passed. The test method is given below.

[TestMethod]

        public void BasicAuthenticationTest()

        {

            string username = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip"));

            string password = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip123"));

            HttpClient client = new HttpClient();

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", username + ":" + password);

            var result = client.GetAsync(new Uri("http://localhost/IDG/api/default/")).Result;

           Assert.IsTrue(result.IsSuccessStatusCode);

        }

As you can see in the above code snippet, the user credentials are passed using the authorization header.

Now that the client is ready, let’s complete the implementation of the BasicAuthenicationFilter class. Inside the OnActionExecuting method we would need to parse the header value in this class and check if the credentials supplied from the client match. For now, let’s assume that the user name and the password has values of joydip and joydip123, respectively (they are hard-coded). Here’s the complete code of the BasicAuthenticationFilter class that incorporates the validation of the user credentials.

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

                    var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

                    httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

                    string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

                    string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

                    string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

                    if (username.Equals("joydip") && password.Equals("joydip123"))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

In your controller class you should specify the attribute appropriately. Note that the BasicAuthentication attribute here refers to the BasicAuthenticationAttribute class we implemented.

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable<string> Get()

        {

            return new string[] { "Joydip", "Kanjilal" };

        }

    }

Now, a bit of configuration — you need to configure the attribute so that calls to your controller would be filtered appropriately for the authentication to work.

 public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(

                name: "DefaultApi",

                routeTemplate: "api/{controller}/{id}",

                defaults: new { id = RouteParameter.Optional }

            );

            config.Formatters.Remove(config.Formatters.XmlFormatter);

            GlobalConfiguration.Configuration.Filters.Add(new BasicAuthenticationAttribute());

        }

    }

And you are done! When you execute the test case, the test passes.

You should anyway ensure that the credentials are not hard-coded; rather, they should be stored in a database and you should retrieve them and validate in the OnActionExecuting method of the BasicAuthenticationAttribute class.

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author