Authorize WebAPI request with ActionFilterAttribute

Action filters let you use pre and post processing logic which can be applied globally, to an action method or Controller class. These are generally used for applying logic for logging, caching, authorization etc. which can be shared across Controllers.

For adding Authorization to a WebAPI, we can use ActionFilterAttributes to enable strict access for a particular role or user. The ActionFilterAttribute class below implements the authorization logic.

The base ActionFilterAttribute class has the following methods that you can override as per the MS documentation:

  • OnActionExecuting – This method is called before a controller action is executed.
  • OnActionExecuted – This method is called after a controller action is executed.
  • OnResultExecuting – This method is called before a controller action result is executed.
  • OnResultExecuted – This method is called after a controller action result is executed.

As an example, you have a class called TokenFilter that inherits from ActionFilterAttribute. This class overrides the following methods OnActionExecuting and OnActionExecuted as below:

public class TokenFilter : ActionFilterAttribute
{


	public override void OnActionExecuting(HttpActionContext actionContext)
	{
		try
		{
			if (CheckValidHeaders(actionContext))
			{
				var auth_token = actionContext.Request.Headers.GetValues("access-token").FirstOrDefault();
				
				var ValidateObj = MyUtils.ValidateToken(auth_token);
				if (!ValidateObj.IsAuthenticated)
				{
					var error = MyUtils.GetNotSignedInErrorMessage();
					var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, error);
					actionContext.Response = response;
				}
				else
				{
					if (!ValidateObj.IsDurationValid)
					{
						auth_token = MyUtils.GenerateToken(email);
						actionContext.Request.Headers.Remove("access-token");
						actionContext.Request.Headers.Add("access-token", auth_token);
					}

					HttpContext.Current.Items.Add("access-token", auth_token);
					
				}
			}
			else
			{
				var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "error message");
				actionContext.Response = response;
			}
		}
		catch (Exception ex)
		{
			var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "error message");
			actionContext.Response = response;
		}
	}

The above code OnActionExecuting will check for valid Headers in the request, Validates the auth token and also generates the token if required. The new auth token is again injected into the Request Headers.

The OnActionExecuted method is as below:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    var auth_token = HttpContext.Current.Items["access-token"].ToString();
    actionExecutedContext.Response.Headers.Add("access-token", auth_token);
    var email = HttpContext.Current.Items["emailid"].ToString();
    actionExecutedContext.Response.Headers.Add("emailid", email);
}

Similary, other custom headers can be used to Authorize the request.

The below Authorize Filter class will authorize the user based on email id:

public class AuthorizeUserAttribute : ActionFilterAttribute
{
	bool IsinRole = false;
	public override void OnActionExecuting(HttpActionContext actionContext)
	{
		if (HttpContext.Current.Request.Headers["emailid"] != null)
		{
			var email = HttpContext.Current.Request.Headers["emailid"].ToString();
			 IsinRole = MyUtils.Checkroles(email, "admin");
			if(!IsinRole)
			{
				var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized, "error message");
				actionContext.Response = response;
			}
		}

	}
}

The below controller will use the TokenFilter and AuthorizeUser attributes to use the Filter out Unauthorized users as below:

[TokenFilter]
[AuthorizeUser]
public class ValuesController : ApiController
{

}

So any Action method that gets called for this Controller, has to have a valid token and a valid role Authorized to access this Controller.


Get great detals on Mobile Phones at Alibaba.

What are Content-type and Accept Headers

I’ve often felt confused with the difference between the Content-type and Accept Headers that are passed along with a request to an API. I’ve used a fake Json REST API called JsonPlaceHolder to show the Json request and response with a Post request.

Accept Header tells the API that it is expecting the response in the specified media type e.g. application/json or application/xml.
Accept: application/json

And Content-Type tells the API about the media type of the request being sent in the request body e.g. application/json.
Content-Type: application/json

Both headers are sent along with the call to the API from the Client. Below I’m using the Postman Client to test the scenario for json request.
For the scenario, to test the response in XML format, the API should support the format provided in the Accept Header.

The 2nd image above shows the Json body being passed in the Post request for Creating the resource.

For a standard HTML Post request, the Content-Type Header will be one of the Standard types:

  1. application/x-www-form-urlencoded for simple ASCII text
  2. multipart/form-data for file upload support or non-ASCII text

Deploy WebAPI with TFS Build Definition

To deploy and Publish a WebAPI or MVC application, these 2 steps should work most of the time:

  1. Visual Studio Build
  2. Publish Build Artifacts

If you right-click on your WebAPI Project, go to “Package/Publish Web” tab and check the Items to deploy drop-down, the default value selected is:
Only files needed to run this application

The below solution produces the same output as when you build the WebAPI in Visual Studio.

This is the cue, that let me find the solution to build my WebAPI Project. The WebAPI Project consists of multiple Class Libraries with Business and DAL layers as well.

Follow the steps as below:

  • Add /p:DeployOnBuild=true /p:OutDir=”$(build.artifactstagingdirectory)\” arguments in Visual Studio Build step:
  • Change “Path to Publish” of Publish Build Artifacts task to $(build.artifactstagingdirectory)\_PublishedWebsites\ProjectName:

I’ve written another post on Creating Build Definition here.