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. Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos