In order to perform authentication of Web API, we can use basic authentication. This authentication can either be performed by using HTTP handlers or by using the message handlers. Both handler types are different but message handlers are more specific to the Web APIs. In this article, we will see how we can use the message handlers to perform the basic authentication of the user.
To start with, we will create a blank solution and add a new project of type Web API. Next, we add a Web API type Controller and add a simple method GetData that will take input an integer value and return a string. So, the Web API will look like the following.
- public class TestController : ApiController
- {
- public string GetData(int Id)
- {
- return ("You entered: " + Id);
- }
- }
Next, we will create a new message handler. We add a new class named MessageHandlerAuthentication and derive it from DelegatingHandler type class. This class contains a virtual method of the following type.
- public class MessageHandlerAuthentication : DelegatingHandler
- {
- protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
-
- }
- }
In Web API, before a request reaches the Controller, it has to pass through a series of message handlers. Our custom handler that we added above will also be one of the same types and will get added into the request pipeline. So, before the request reaches Controller, this handler will be executed and the SendAsync method above will contain the logic to validate the request. If successfully validated, the request will be allowed to further passed to the next handler in the pipeline, by "base.SendAsync" method call in the above method.
When the request is validated successfully, we need to attach an IPrincipal type object to the current Thread and current HttpContext. So we add a new class named CustomPrincipal and derive it from IPrincipal. We also add a new property named UserRole to keep the current users' role in it. Now, the code will look like the following,
- public class CustomPrincipal : IPrincipal
- {
- public CustomPrincipal(string userName)
- {
- UserName = userName;
- Identity = new GenericIdentity(userName);
- }
-
- public string UserName { get; set; }
- public IIdentity Identity { get; set; }
- public bool IsInRole(string role)
- {
- if (role.Equals("user"))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- }
Next, we will add the custom validation logic. Since we are using basic authentication, we will add a method which will read the authorization header and validate the credentials provided. If the credentials are valid, then it will set the IPrincipal object in the current thread and current http context. So, the code will look like the following.
- public class MessageHandlerAuthentication : DelegatingHandler
- {
- string _userName = "";
-
- private bool ValidateCredentials(AuthenticationHeaderValue authenticationHeaderVal)
- {
- try
- {
- if (authenticationHeaderVal != null && !String.IsNullOrEmpty(authenticationHeaderVal.Parameter))
- {
- string[] decodedCredentials = Encoding.ASCII.GetString(Convert.FromBase64String(authenticationHeaderVal.Parameter)).Split(new[] { ':' });
-
- if (decodedCredentials[0].Equals("jasminder") && decodedCredentials[1].Equals("jasminder"))
- {
- _userName = "Jasminder Singh";
- return true;
- }
- }
- return false;
- }
- catch
- {
- return false;
- }
- }
-
- protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- if (ValidateCredentials(request.Headers.Authorization))
- {
- IPrincipal principal = new GenericPrincipal(new GenericIdentity("Jasminder Singh"), new string[] { "Admin" });
- Thread.CurrentPrincipal = principal;
- HttpContext.Current.User = principal;
- }
-
- var response = await base.SendAsync(request, cancellationToken);
- if (response.StatusCode == HttpStatusCode.Unauthorized && !response.Headers.Contains("WwwAuthenticate"))
- {
- response.Headers.Add("WwwAuthenticate", "Basic");
- }
-
- return response;
- }
- }
Now, we will add the message handler to the Web API pipeline, using the following code in the Register method of WebApiConfig.cs file.
- public static void Register(HttpConfiguration config)
- {
-
-
-
- config.MapHttpAttributeRoutes();
-
- config.Routes.MapHttpRoute(
- name: "DefaultApi",
- routeTemplate: "api/{controller}/{id}",
- defaults: new { id = RouteParameter.Optional }
- );
-
- GlobalConfiguration.Configuration.MessageHandlers.Add(new MessageHandlerAuthentication());
- }
The authentication mechanism is in place but we also need to make sure that the method does not get hit, if the credentials are not valid. To do this, we will add the Authorize attribute on the controller method. If the credentials are not valid, the request will not hit the method.
- public class TestController : ApiController
- {
- [Authorize]
- public string GetData(int Id)
- {
- return ("You entered: " + Id);
- }
- }
Run the application and make a request using Postman app. The request will first hit the message handler and if validated, will further call the method; else, it will return http status code 401 un-authorized message.
Further, in order to set the authorization based on roles, we just need to add the role names in the authorize attribute. While generating the IPrincipal object, we already provided the role name. So, when the Controller method is to be called, the role specified in authorize attribute is verified against the role we provided in the IPrincipal object.
- public class TestController : ApiController
- {
- [Authorize(Roles = "Admin")]
- public string GetData(int Id)
- {
- return ("You entered: " + Id);
- }
- }
So, this was about basic authentication using message handler in Web API. Hope you enjoyed reading it. Happy coding...!!!