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 how-to How to use FastEndpoints in ASP.NET Core Take advantage of the free open-source FastEndpoints library to build fast and lean APIs in your ASP.NET Core applications. By Joydip Kanjilal Jul 11, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks how-to How to use Refit to consume APIs in ASP.NET Core Take advantage of Refit REST library to simplify API consumption and make your code cleaner, more efficient, and easier to maintain. By Joydip Kanjilal Jul 04, 2024 10 mins C# Microsoft .NET Software Deployment how-to When to use an abstract class vs. interface in C# Understanding the differences between an abstract class and interface is key to designing loosely coupled and extensible applications. By Joydip Kanjilal Jun 20, 2024 10 mins Small and Medium Business Microsoft .NET C# how-to 6 security best practices for ASP.NET Core Learn the best practices and built-in safeguards for preventing attacks and protecting sensitive data in your ASP.NET Core web applications. By Joydip Kanjilal Jun 07, 2024 6 mins C# Microsoft .NET Web Development Resources Videos