Web API 2 security extensibility points part 3: custom message handlers

Introduction

In the previous post we looked at how to implement your own custom authentication filter. Authentication filters – and filters of type IFilter in general – are executed just before your controller action methods are run. We saw how to implement the IAuthenticationFilter interface and how to apply the custom filter both as an attribute and as a global filter.

In this post we’ll look at HTTP message handlers and specifically how to add your own message handler. Message handlers are executed even before any custom filter so they provide an early entry point into the life cycle of a web application. There’s nothing stopping us from adding an initial security check or a full-blown login mechanism already at that stage. We can check e.g. if a mandatory custom header has been provided and reject all incoming HTTP calls that don’t fulfil this requirement up front.

Custom message handler

We’ll build upon the CustomerApi demo Web API application we’ve been working on so far. Adding a custom message handler is a straightforward task. Add a new folder called MessageHandlers to the project and add a new class called CustomAuthenticationMessageHandler. HTTP message handlers are derived from the System.Net.Http.DelegatingHandler class. The DelegatingHandler class has a virtual SendAsync method that you can override. Here’s a stub implementation where we simply call the SendAsync of the base class:

public class CustomAuthenticationMessageHandler : DelegatingHandler
{
	protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
	{
		return base.SendAsync(request, cancellationToken);
	}
}

We saw in the previous post that the CustomAuthenticationFilterAttribute methods returned Task objects adhering to the asynchronous execution model available in .NET 4.5. Here we have a similar case except that we don’t return a void Task object but a Task which has a return type of HttpResponseMessage.

Let’s start by checking how to extract the principal of the HTTP request:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
	IPrincipal incomingPrincipal = request.GetRequestContext().Principal;
	Debug.WriteLine(string.Format("Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: {0}", incomingPrincipal.Identity.IsAuthenticated));

	return base.SendAsync(request, cancellationToken);
}

You’ll see that the GetRequestContext() extension method is available here like we saw before in this series.

Our message handler must be registered in Global.asax like we saw in the case of the custom authentication filter. Here’s how you can do it in Global.asax:

GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomAuthenticationMessageHandler());

If you run the /customers endpoint now then you’ll see the following output in the debug window where we already have the output of the auth filter as well:

Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: False
Incoming principal in custom auth filter AuthenticateAsync method is authenticated: False
Incoming principal in custom auth filter ChallengeAsync method is authenticated: True
Principal authenticated from extension method: True
Principal authenticated from shorthand property: True
Principal authenticated from User: True

The following implementation checks for the presence of a custom header in the call. If the header is missing then the request is rejected:

public class CustomAuthenticationMessageHandler : DelegatingHandler
{
	protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
	{
		IPrincipal incomingPrincipal = request.GetRequestContext().Principal;
		Debug.WriteLine(string.Format("Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: {0}", incomingPrincipal.Identity.IsAuthenticated));
		HttpRequestHeaders requestHeaders = request.Headers;
		IEnumerable<string> authTokenContainer = new List<string>();
		bool tryGetCustomAuthHeader = requestHeaders.TryGetValues("x-custom-header", out authTokenContainer);
		if (!tryGetCustomAuthHeader)
		{
			return await SendError("Custom authentication header is missing.", HttpStatusCode.Forbidden);
		}
		return await base.SendAsync(request, cancellationToken);
	}

	private Task<HttpResponseMessage> SendError(string error, HttpStatusCode code)
	{
		HttpResponseMessage response = new HttpResponseMessage();
		response.Content = new StringContent(error);
		response.StatusCode = code;
		return Task<HttpResponseMessage>.Factory.StartNew(() => response);
	}
}

If you run the application now and navigate to the /customers endpoint then you’ll get an HTTP Forbidden back:

“Custom authentication header is missing.”

You can do much more in a custom message handler. The following code checks for an authentication scheme in the header. Let’s say that we want to have some auth header but we reject Basic auth:

AuthenticationHeaderValue authHeader = request.Headers.Authorization;
if (authHeader == null || authHeader.Scheme == "Basic")
{
	return await SendError("Either no auth header or Basic auth scheme set", HttpStatusCode.Forbidden);
}

We can of course set a dummy generic principal like we did in our authentication filter:

IPrincipal incomingPrincipal = request.GetRequestContext().Principal;
Debug.WriteLine(string.Format("Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: {0}", incomingPrincipal.Identity.IsAuthenticated));
if (!incomingPrincipal.Identity.IsAuthenticated)
{
	IPrincipal genericPrincipal = new GenericPrincipal(new GenericIdentity("Andras", "CustomIdentification"), new string[] { "Admin", "PowerUser" });
	request.GetRequestContext().Principal = genericPrincipal;
}

If you then comment out the test code about the headers so that we only execute the following bit of code…

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{			
	IPrincipal incomingPrincipal = request.GetRequestContext().Principal;
	Debug.WriteLine(string.Format("Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: {0}", incomingPrincipal.Identity.IsAuthenticated));
	if (!incomingPrincipal.Identity.IsAuthenticated)
	{
		IPrincipal genericPrincipal = new GenericPrincipal(new GenericIdentity("Andras", "CustomIdentification"), new string[] { "Admin", "PowerUser" });
		request.GetRequestContext().Principal = genericPrincipal;
	}
	
	return await base.SendAsync(request, cancellationToken);
}

…then /customers will generate the following debug messages:

Principal is authenticated at the start of SendAsync in CustomAuthenticationMessageHandler: False
Incoming principal in custom auth filter AuthenticateAsync method is authenticated: True
Incoming principal in custom auth filter ChallengeAsync method is authenticated: True
Principal authenticated from extension method: True
Principal authenticated from shorthand property: True
Principal authenticated from User: True

You can see that the principal we set in the HTTP message handler carries on to the custom authentication filter.

In the next post we’ll see how to write a custom authorization filter. Authorization filters are invoked after authentication filters and before the call hits your controller actions.

You can view the list of posts on Security and Cryptography here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to Web API 2 security extensibility points part 3: custom message handlers

  1. I know it’s not the focus of the post but instead of “return Task.Factory.StartNew(() => response);” use “return Task.FromResult(response);”

    Not wasting an context switch 😉

    Nice postings. Keep it up! 🙂

    Stefan

  2. Ian Robinson says:

    I couldn’t find a link in the article above to the next post so for anyone who needs it, here is a link to Andras 4th post in this excellent series – https://dotnetcodr.com/2015/07/30/web-api-2-security-extensibility-points-part-4-custom-authorisation-filters/

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.