Header Ads

ASP.NET MVC5: REST Web API Authorization

When a REST Web API is created to share data across multiple devices e.g. mobile devices, desktop applications or any website, then the authorization of REST Web API becomes a vital aspect in order to protect data sensitivity from any outside breaches.

Today, I shall demonstrate a simple mechanism to authorize a REST Web API without the complex authorization process of OWIN security layer but at the same time benefiting from [Authorize] attribute.


Following are few prerequisites before you proceed any further:  

1) Knowledge of ASP.NET MVC5.  
2) Knowledge of C# programming.  
3) Knowledge of REST Web API.

You can download complete source code or you can follow step by step discussion below. The sample code is developed in Microsoft Visual Studio 2013 Ultimate.

Download Link

Let's begin now:  

1) Create new Web API project and name it "WebApiAuthorization".  
2) Rename "ValueController.cs" file to "WebApiController.cs".
3) Now, in "WebApiController.cs" file replace following code:

using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Net;  
 using System.Net.Http;  
 using System.Web.Http;  
 namespace WebApiAuthorization.Controllers  
 {  
   [Authorize]  
   public class WebApiController : ApiController  
   {  
     // GET api/values  
     public IEnumerable<string> Get()  
     {  
       return new string[] { "Hello REST API", "I am Authorized" };  
     }  
     // GET api/values/5  
     public string Get(int id)  
     {  
       return "Hello Authorized API with ID = " + id;  
     }  
     // POST api/values  
     public void Post([FromBody]string value)  
     {  
     }  
     // PUT api/values/5  
     public void Put(int id, [FromBody]string value)  
     {  
     }  
     // DELETE api/values/5  
     public void Delete(int id)  
     {  
     }  
   }  
 }  

In above code, I simply replace some of existing string values, nothing special is done here.  

4) Now, for the authorization part, I am using HTTP Message Handlers technique, its detail can be studied here. In simple essence, this technique capture HTTP request and respond accordingly. In order to use this technique, we need to inherit "DelegatingHandler" class and then hook its method SendAsync(...) that will process every hit to our REST Web API and verify our allocated authorization or API header key accordingly and then finally set our Principal after successful authorization. Principal will simply set our security context by containing information about the user whom we have claim as authorize user by using Identity Based Authorization, this will allow us to utilize [Authorize] attribute for our web api controller. So, create new folder under project root and name it "Resources->Constants". I like my code architecture clean, so, I am using constants in a resource file.  

5) Now, create a file "Resource->Constants-> ApiInfo.resx" open the file and place following constants in it i.e.


Make sure that Access Modifier is set to Public. This file will contain authorization constants that I will be using to authenticate my REST Web API.

6) Now, create new folder hierarchy under project root i.e. "Helper_Code->Common".
7) Create our authorization file and name it "Helper_Code->Common->AuthorizationHeaderHandler.cs".  
8) Open the file "Helper_Code->Common->AuthorizationHeaderHandler.cs" and replace it with the following piece of code i.e.

//-----------------------------------------------------------------------  
 // <copyright file="AuthorizationHeaderHandler.cs" company="None">  
 //   Copyright (c) Allow to distribute this code.  
 // </copyright>  
 // <author>Asma Khalid</author>  
 //-----------------------------------------------------------------------  
 namespace WebApiAuthorization.Helper_Code.Common  
 {  
   using System;  
   using System.Collections.Generic;  
   using System.Linq;  
   using System.Net.Http;  
   using System.Net.Http.Headers;  
   using System.Security.Claims;  
   using System.Security.Principal;  
   using System.Text;  
   using System.Threading;  
   using System.Threading.Tasks;  
   using System.Web;  
   using WebApiAuthorization.Resources.Constants;  
   /// <summary>  
   /// Authorization for web API class.  
   /// </summary>  
   public class AuthorizationHeaderHandler : DelegatingHandler  
   {  
     #region Send method.  
     /// <summary>  
     /// Send method.  
     /// </summary>  
     /// <param name="request">Request parameter</param>  
     /// <param name="cancellationToken">Cancellation token parameter</param>  
     /// <returns>Return HTTP response.</returns>  
     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)  
     {  
       // Initialization.  
       IEnumerable<string> apiKeyHeaderValues = null;  
       AuthenticationHeaderValue authorization = request.Headers.Authorization;  
       string userName = null;  
       string password = null;  
       // Verification.  
       if (request.Headers.TryGetValues(ApiInfo.API_KEY_HEADER, out apiKeyHeaderValues) &&  
         !string.IsNullOrEmpty(authorization.Parameter))  
       {  
         var apiKeyHeaderValue = apiKeyHeaderValues.First();  
         // Get the auth token  
         string authToken = authorization.Parameter;  
         // Decode the token from BASE64  
         string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));  
         // Extract username and password from decoded token  
         userName = decodedToken.Substring(0, decodedToken.IndexOf(":"));  
         password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);  
         // Verification.  
         if (apiKeyHeaderValue.Equals(ApiInfo.API_KEY_VALUE) &&  
           userName.Equals(ApiInfo.USERNAME_VALUE) &&  
           password.Equals(ApiInfo.PASSWORD_VALUE))  
         {  
           // Setting  
           var identity = new GenericIdentity(userName);  
           SetPrincipal(new GenericPrincipal(identity, null));  
         }  
       }  
       // Info.  
       return base.SendAsync(request, cancellationToken);  
     }  
     #endregion  
     #region Set principal method.  
     /// <summary>  
     /// Set principal method.  
     /// </summary>  
     /// <param name="principal">Principal parameter</param>  
     private static void SetPrincipal(IPrincipal principal)  
     {  
       // setting.  
       Thread.CurrentPrincipal = principal;  
       // Verification.  
       if (HttpContext.Current != null)  
       {  
         // Setting.  
         HttpContext.Current.User = principal;  
       }  
     }  
     #endregion  
   }  
 } 

In above code, "Helper_Code->Common->AuthorizationHeaderHandler.cs" class inherits "DelegatingHandler" class. We have hooked the "SendAsync(...)" method and created a new method "SetPrincipal(...)" to set our authorization principal. Now, let's discuss above code chunk by chunk .i.e. In Method "SetPrincipal(...)" the following code i.e.

     // setting.  
       Thread.CurrentPrincipal = principal;  
       // Verification.  
       if (HttpContext.Current != null)  
       {  
         // Setting.  
         HttpContext.Current.User = principal;  
       }  

The above code will set our authorization principal with Identity Based Authorization model. Let's, dissect "SendAsync(...)" method step by step i.e.

      // Verification.  
       if (request.Headers.TryGetValues(ApiInfo.API_KEY_HEADER, out apiKeyHeaderValues) &&  
         !string.IsNullOrEmpty(authorization.Parameter))  
       {  
 ...  
 }  

The above line of code will verify that whether our authorize header key and credentials are empty or not. I have used combination of both header key and credentials to authorize my REST Web API. If the authorization is successful then following code will extract our authorization information form the HTTP request and store them into local variables i.e.

       var apiKeyHeaderValue = apiKeyHeaderValues.First();  
         // Get the auth token  
         string authToken = authorization.Parameter;  
         // Decode the token from BASE64  
         string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));  
         // Extract username and password from decoded token  
         userName = decodedToken.Substring(0, decodedToken.IndexOf(":"));  
         password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);  

After above code we will verify that whether the provided authorization for REST Web API hit is valid or not with the following code i.e.

       // Verification.  
         if (apiKeyHeaderValue.Equals(ApiInfo.API_KEY_VALUE) &&  
           userName.Equals(ApiInfo.USERNAME_VALUE) &&  
           password.Equals(ApiInfo.PASSWORD_VALUE))  
         {  
             ...  
         }  

If the hit to our REST Web API contains valid authorization credentials and header key then we register our principal with Identity Based Authorization model i.e.

          // Setting  
           var identity = new GenericIdentity(userName);  
           SetPrincipal(new GenericPrincipal(identity, null));  
   
9) Now, Open "Global.asax.cs" file and replace following code in it i.e.

using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 using System.Web.Http;  
 using System.Web.Mvc;  
 using System.Web.Optimization;  
 using System.Web.Routing;  
 using WebApiAuthorization.Helper_Code.Common;  
 namespace WebApiAuthorization  
 {  
   public class WebApiApplication : System.Web.HttpApplication  
   {  
     protected void Application_Start()  
     {  
       AreaRegistration.RegisterAllAreas();  
       GlobalConfiguration.Configure(WebApiConfig.Register);  
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
       RouteConfig.RegisterRoutes(RouteTable.Routes);  
       BundleConfig.RegisterBundles(BundleTable.Bundles);  
       // API authorization registration.  
       GlobalConfiguration.Configuration.MessageHandlers.Add(new AuthorizationHeaderHandler());  
     }  
   }  
 }  

In above code we have registered our authorization class within global configuration.  

10) Now, execute the project and use following link in the browser to see your newly created REST Web API method in action as follow:

 yourlink:port/api/WebApi 


In the above snippet, you will notice that since, now our REST Web API has been authorized, therefore, we cannot directly execute the REST Web API URL in the browser.  

11) Lets, test out REST Web API in REST Web API client. I am using fire fox plugin i.e. "RESTED". At, first, I simply try to hit the REST Web API without any authorization details and I will get following response i.e.


 

12) Now, I will provide the authorization and hit the REST Web API and will get following response i.e.


 

 

That's about it.

Enjoy!! coding.

16 comments:

  1. i find more new information,i like that kind of information,not only i like that post all peoples like that post,because of all given information was very excellent.

    Digital Marketing Company in Chennai

    ReplyDelete
  2. Thank you for the appreciation.

    ReplyDelete
  3. keep sharing your information regularly for my future reference. This content creates a new hope and inspiration with in me.

    Digital Marketing Company in Chennai

    ReplyDelete
  4. I will surely. Thank you once again.

    ReplyDelete
  5. I get requested to do site appraises a considerable measure and once in a while it's out and out shocking to catch wind of the battles and difficulties business visionaries and entrepreneurs confront when attempting to locate the correct web engineer. Edknt Media

    ReplyDelete
  6. I am using VS2015 and I am unable to create ApiInfo.resx, can you plz help me.

    ReplyDelete
    Replies
    1. Right click target project in solution explorer -> click add new item -> type .resx in search box and select resource file name it then click -> resource file will be created.

      Delete
  7. Our credit repair services work to fix past credit mistakes and verify credit report accuracy. Talk to a credit repair expert today!  visit website

    ReplyDelete
  8. Your post is very helpful to get some effective tips to reduce weight properly. You have shared various nice photos of the same. I would like to thank you for sharing these tips. Surely I will try this at home. Keep updating more simple tips like this. privacidadenlared.es

    ReplyDelete
  9. Good post but I was wondering if you could write a litte more on this subject? I’d be very thankful if you could elaborate a little bit further. Appreciate it..! vpn for expats

    ReplyDelete
    Replies
    1. Thank you for reaching out. Do let me know which part need more elaboration. So, I can update accordingly.

      Delete
  10. Regular visits listed here are the easiest method to appreciate your energy, which is why why I am going to the website everyday, searching for new, interesting info. Many, thank you https://www.lemigliorivpn.com

    ReplyDelete
  11. Such a very useful article. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. vpnveteran

    ReplyDelete