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
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

 

namespace MonsterApiInc

{

    public class ApplicationAuthenticationHandler

    {

 

    }

}

MVC3.5.jpg

Then override the method "SendAsync".

public class ApplicationAuthenticationHandler : DelegatingHandler

{
   
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)

    {

        // Write your Authentication code here

        return base.SendAsync(request, cancellationToken);

    }

}


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

    public class ApplicationAuthenticationHandler : DelegatingHandler

    {

        // Http Response Messages

        private const string InvalidToken = "Invalid Authorization-Token";

        private const string MissingToken = "Missing Authorization-Token";

 

        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)

        {

            // Write your Authentication code here

            IEnumerable<string> monsterApiKeyHeaderValues = null;

 

            // Checking the Header values

            if (request.Headers.TryGetValues("X-MonsterAppApiKey", out monsterApiKeyHeaderValues))

            {

                string[] apiKeyHeaderValue = monsterApiKeyHeaderValues.First().Split(':');

 

                // Validating header value must have both APP ID & APP key

                if (apiKeyHeaderValue.Length == 2)

                {

                    // Code logic after authenciate the application.

                    var appID = apiKeyHeaderValue[0];

                    var AppKey = apiKeyHeaderValue[1];

 

                    if (appID.Equals("MosterIPhoneX123") && AppKey.Equals("ThisMonsterIsPersist"))

                    {

                        var userNameClaim = new Claim(ClaimTypes.Name, appID);

                        var identity = new ClaimsIdentity(new[] { userNameClaim }, "MonsterAppApiKey");

                        var principal = new ClaimsPrincipal(identity);

                        Thread.CurrentPrincipal = principal;

 

                        if (System.Web.HttpContext.Current != null)

                        {

                            System.Web.HttpContext.Current.User = principal;

                        }

                    }

                    else

                    {

                        // Web request cancel reason APP key is NULL

                        return requestCancel(request, cancellationToken, InvalidToken);

                    }

                }

                else

                {

                    // Web request cancel reason missing APP key or APP ID

                    return requestCancel(request, cancellationToken, MissingToken);

                }

            }

            else

            {

                // Web request cancel reason APP key missing all parameters

                return requestCancel(request, cancellationToken, MissingToken);

            }

 

            return base.SendAsync(request, cancellationToken);

        }

 

        private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)

        {

            CancellationTokenSource _tokenSource = new CancellationTokenSource();

            cancellationToken = _tokenSource.Token;

            _tokenSource.Cancel();

            HttpResponseMessage response = new HttpResponseMessage();

 

            response = request.CreateResponse(HttpStatusCode.BadRequest);

            response.Content = new StringContent(message);

            return base.SendAsync(request, cancellationToken).ContinueWith(task =>

            {

                return response;

            });

        }

    }

Step 3

Now to register the delegating handler.

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

MVC4.jpg

public class WebApiApplication : System.Web.HttpApplication
{
   
protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        
WebApiConfig.Register(GlobalConfiguration.Configuration);
        
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        
RouteConfig.RegisterRoutes(RouteTable.Routes);
        
BundleConfig.RegisterBundles(BundleTable.Bundles);
 
       GlobalConfiguration.Configuration.MessageHandlers.Add(new
        ApplicationAuthenticationHandler());
       }
}


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.

[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "MonsterValue1", "MonsterValue2" };
}

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.

namespace MonsterApiInc.Controllers

{

    public class ValuesController : ApiController

    {

        // GET api/values

        [Authorize]

        public IEnumerable<string> Get()

        {

            return new string[] { "MonsterValue1", "MonsterValue2" };

        }

 

        // GET api/values/5

        public string Get(int id)

        {

            return "MonsterValue";

        }

 

        // 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)

        {

        }

    }

}

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.

 

// Web request cancel reason APP key missing all parameters

return requestCancel(request, cancellationToken, MissingToken);

 

private System.Threading.Tasks.Task<HttpResponseMessage> requestCancel(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken, string message)

{

    CancellationTokenSource _tokenSource = new CancellationTokenSource();

    cancellationToken = _tokenSource.Token;

    _tokenSource.Cancel();

    HttpResponseMessage response = new HttpResponseMessage();

 

    response = request.CreateResponse(HttpStatusCode.BadRequest);

    response.Content = new StringContent(message);

    return base.SendAsync(request, cancellationToken).ContinueWith(task =>

    {

        return response;

    });

}

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 !!!
 

Next Recommended Readings
Red Ant Corp
Founded in 2012 by Red Ant Corp, Red Ant Corp & Associates Consulting (Red Ant Corp).