MVC Web API: Authorization & Authentication

Hi folks,

Now we will discuss a very nice problem which generally happens when we use the Web API.

The problem is authorization and authentication for Web API resources.

Problem: We have a set of methods and a few of them are exposed to authenticated and registered applications (or you can say authenticated users) and the rest are for any others.

Solution

So let's start.

Step 1

Open the Visual Studio 2012.

"File" -> "New" -> "Project...".

Then chose "Web" under "Templates" depending on your familiar language, C# or VB.Net. I will choose C#.

MVC1.jpg

Then set the name of the application and click OK.

Then select the "Web API" and "Razor" in the view engine.

MVC2.jpg
Step 2


Now in the next step we need to create a delegating handler that will help to process the authenticated the request.

Right-click on the project  then select "Add" -> "Class" then name it "ApplicationAuthenticationHandler.cs".

MVC3.jpg
 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Web;  
  5. namespace MonsterApiInc  
  6. {  
  7.     public class ApplicationAuthenticationHandler  
  8.     {  
  9.     }  
  10. }

MVC3.5.jpg

Then override the method "SendAsync".

  1. public class ApplicationAuthenticationHandler : DelegatingHandler  
  2. {  
  3.     protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)  
  4.     {  
  5.         // Write your Authentication code here  
  6.         return base.SendAsync(request, cancellationToken);  
  7.     }  
  8. } 

So I will write the following code to authenticate the application:

  1. public class ApplicationAuthenticationHandler : DelegatingHandler  
  2. {  
  3.     // Http Response Messages  
  4.     private const string InvalidToken = "Invalid Authorization-Token";  
  5.     private const string MissingToken = "Missing Authorization-Token";  
  6.     protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)  
  7.     {  
  8.         // Write your Authentication code here  
  9.         IEnumerable<string> monsterApiKeyHeaderValues = null;  
  10.         // Checking the Header values  
  11.         if (request.Headers.TryGetValues("X-MonsterAppApiKey"out monsterApiKeyHeaderValues))  
  12.         {  
  13.             string[] apiKeyHeaderValue = monsterApiKeyHeaderValues.First().Split(':');  
  14.             // Validating header value must have both APP ID & APP key  
  15.             if (apiKeyHeaderValue.Length == 2)  
  16.             {  
  17.                 // Code logic after authenciate the application.  
  18.                 var appID = apiKeyHeaderValue[0];  
  19.                 var AppKey = apiKeyHeaderValue[1];  
  20.                 if (appID.Equals("MosterIPhoneX123") && AppKey.Equals("ThisMonsterIsPersist"))  
  21.                 {  
  22.                     var userNameClaim = new Claim(ClaimTypes.Name, appID);  
  23.                     var identity = new ClaimsIdentity(new[] { userNameClaim }, "MonsterAppApiKey");  
  24.                     var principal = new ClaimsPrincipal(identity);  
  25.                     Thread.CurrentPrincipal = principal;  
  26.                     if (System.Web.HttpContext.Current != null)  
  27.                     {  
  28.                         System.Web.HttpContext.Current.User = principal;  
  29.                     }  
  30.                 }  
  31.                 else  
  32.                 {  
  33.                     // Web request cancel reason APP key is NULL  
  34.                     return requestCancel(request, cancellationToken, InvalidToken);  
  35.                 }  
  36.             }  
  37.             else  
  38.             {  
  39.                 // Web request cancel reason missing APP key or APP ID  
  40.                 return requestCancel(request, cancellationToken, MissingToken);  
  41.             }  
  42.         }  
  43.         else  
  44.         {  
  45.             // Web request cancel reason APP key missing all parameters  
  46.             return requestCancel(request, cancellationToken, MissingToken);  
  47.         }  
  48.         return base.SendAsync(request, cancellationToken);  
  49.     }  
  50.     private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)  
  51.     {  
  52.         CancellationTokenSource _tokenSource = new CancellationTokenSource();  
  53.         cancellationToken = _tokenSource.Token;  
  54.         _tokenSource.Cancel();  
  55.         HttpResponseMessage response = new HttpResponseMessage();  
  56.         response = request.CreateResponse(HttpStatusCode.BadRequest);  
  57.         response.Content = new StringContent(message);  
  58.         return base.SendAsync(request, cancellationToken).ContinueWith(task =>  
  59.         {  
  60.             return response;  
  61.         });  
  62.     }  
  63. } 

Step 3

Now to register the delegating handler.

Open the Global.asax file and provide one highlighted line of code.

MVC4.jpg

  1. public class WebApiApplication : System.Web.HttpApplication  
  2. {  
  3.     protected void Application_Start()  
  4.     {  
  5.         AreaRegistration.RegisterAllAreas();  
  6.         WebApiConfig.Register(GlobalConfiguration.Configuration);  
  7.         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
  8.         RouteConfig.RegisterRoutes(RouteTable.Routes);  
  9.         BundleConfig.RegisterBundles(BundleTable.Bundles);  
  10.         GlobalConfiguration.Configuration.MessageHandlers.Add(new  
  11.         ApplicationAuthenticationHandler());  
  12.        }  
  13. }

Step 4

Make the resources authorized for the authenticated application (or users).

[Authorize]

Authorizing the attribute helps to make the resource available only for authorized applications and users.

Put "[Authorize]" before the method.

  1. [Authorize]  
  2. public IEnumerable<string> Get()  
  3. {  
  4.     return new string[] { "MonsterValue1""MonsterValue2" };  
  5. }

If the method does not have "[Authorize]" then it can be used by anonymous request.

MVC5.jpg

Or we can use the "[AllowAnonymous]" attribute for anonymous requests.

  1. namespace MonsterApiInc.Controllers  
  2. {  
  3.     public class ValuesController : ApiController  
  4.     {  
  5.         // GET api/values  
  6.         [Authorize]  
  7.         public IEnumerable<string> Get()  
  8.         {  
  9.             return new string[] { "MonsterValue1""MonsterValue2" };  
  10.         }  
  11.         // GET api/values/5  
  12.         public string Get(int id)  
  13.         {  
  14.             return "MonsterValue";  
  15.         }  
  16.         // POST api/values  
  17.         public void Post([FromBody]string value)  
  18.         {  
  19.         }  
  20.         // PUT api/values/5  
  21.         public void Put(int id, [FromBody]string value)  
  22.         {  
  23.         }  
  24.         // DELETE api/values/5  
  25.         public void Delete(int id)  
  26.         {  
  27.         }  
  28.     }  
  29. } 

Now it's testing time.

Open the Fiddler Tool.

Download Fiddler here.

Build and run the project.

Then open Fiddler and supply the WebAPI resource URL and click "Execute" without header value (AppID and App Key) .
MVC6.jpg

Because we have not supplied the application authentication token we get the Bad Request response that we have set when no header is found.

  1. // Web request cancel reason APP key missing all parameters  
  2. return requestCancel(request, cancellationToken, MissingToken);  
  3. private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)  
  4. {  
  5.     CancellationTokenSource _tokenSource = new CancellationTokenSource();  
  6.     cancellationToken = _tokenSource.Token;  
  7.     _tokenSource.Cancel();  
  8.     HttpResponseMessage response = new HttpResponseMessage();  
  9.     response = request.CreateResponse(HttpStatusCode.BadRequest);  
  10.     response.Content = new StringContent(message);  
  11.     return base.SendAsync(request, cancellationToken).ContinueWith(task =>  
  12.     {  
  13.         return response;  
  14.     });  
  15. }

MVC7.jpg

Now let's the valid authentication token

MVC8.jpg

Then Execute and you will the response.

Go to the left pane and select the response.

MVC9.jpg


Now we can get the response.

MVC10.jpg

If you have any questions then please post the question here, I will try to provide a quick response.

Enjoy !!!