Wiring up a custom authentication method with OWIN in Web API Part 3: the components

Introduction

In the previous post we looked at HTTP headers in code. We saw how to collect the headers from a request and a response and how to check for the presence of a certain header type. We also built a simple tester C# console application that sends a web request to our Web API controller and sets the necessary custom authentication header.

In this post we’ll start building the components necessary for the OWIN middleware.

Preparation

Let’s remove all the security related demo code from CustomersController to begin with. Normally your controllers should have no security related logic. Here’s the revised controller:

public class CustomersController : ApiController
{
	[Authorize]
	public IHttpActionResult Get()
	{
		IList<Customer> customers = new List<Customer>();
		customers.Add(new Customer() { Name = "Nice customer", Address = "USA", Telephone = "123345456" });
		customers.Add(new Customer() { Name = "Good customer", Address = "UK", Telephone = "9878757654" });
		customers.Add(new Customer() { Name = "Awesome customer", Address = "France", Telephone = "34546456" });
		return Ok<IList<Customer>>(customers);
	}
}

Note, however, that we’ve decorated the Get method with the familiar Authorize attribute. We only want people with the correct PIN to be able to view the customers list.

Then insert a folder called Authentication to the Web API project. We’ll put all our authentication related classes there.

Finally install the Microsoft.Owin.Security DLL from NuGet:

Install microsoft owin security DLL from NuGet

This package includes the security related classes in OWIN without which we cannot build the custom authentication mechanism.

AuthenticationOptions

The OWIN namespace has a number of security related classes that we’ll see in this post. The first one is called AuthenticationOptions. It is a base class for all types of authentication options, such as CookieAuthenticationOptions, GoogleAuthenticationOptions or OpenIdConnectAuthenticationOptions. If you’d like to build your own OWIN authentication then this is the starting point.

The minimum requirement here is to derive from the AuthenticationOptions which has no virtual methods. You only need to have a constructor which accepts a string parameter which describes the authentication type. However, you are free to insert any properties and constructors as you wish. Here we’ll keep it at a minimum.

Add a class called PinBasedAuthenticationOptions into the Authentication folder:

public class PinBasedAuthenticationOptions : AuthenticationOptions
{
	public PinBasedAuthenticationOptions() : base("x-company-auth")
	{}		
}

We simply declare our authentication type. It doesn’t need to be the same as the header value. We might as well have put “Custom” or “MickeyMouse” as the descriptor.

The authentication handler

The next class that we need to derive from is AuthenticationHandler. It is the base class for the work carried out by OWIN authentication middleware. We’ll see shortly that the implemented authentication middleware must return an authentication handler that derives from AuthenticationHandler.

AuthenticationHandler is a generic class with a type parameter. The type parameter must derive from AuthenticationOptions we discussed above. The minimum requirement is to implement the authentication logic in the AuthenticateCoreAsync method. It returns a Task of AuthenticationTicket object, i.e. it is implicitly assumed that the authentication logic will be called asynchronously in some resource intensive operation such as a database lookup.

An AuthenticationTicket, as its name implies “contains user identity information as well as additional authentication state” according to the MSDN documentation. The constructor of this object requires a claims identity – represented by the ClaimsIdentity object – and the authentication properties, which are contained by the AuthenticationProperties object.

Claims-based authentication is a large topic and there are multiple posts dedicated to it on this blog. If you don’t know what claims are then you can start here. You can also check out the Security and Cryptography index page referred to at the end of this post to find claims-related posts. Basically a claim in the world of authentication and authorisation can be defined as a statement about an entity, typically a user. A claim can be very fine grained:

  • Tom is an administrator
  • Tom’s email address is tom@yahoo.com
  • Tom lives in Los Angeles
  • Tom’s allowed to view sales figures between 2009 and 2012
  • Tom’s allowed to wipe out the universe

Claims come in key-value pairs such as “name – Andras”, “age – 18 every year”, “favourite day – Monday” and that is how claims are built in code as well. As AuthenticationTicket requires a ClaimsIdentity object it’s inevitable that you deal with claims if you’d like to set your own OWIN authentication logic.

Add a class called PinAuthenticationHandler into the Authentication folder:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;

namespace CustomAuthenticationOwin.Authentication
{
	public class PinAuthenticationHandler : AuthenticationHandler<PinBasedAuthenticationOptions>
	{
		protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
		{
			bool authorized = await Task<bool>.Run(() => IsAuthorised(Request.Headers));
			if (authorized)
			{
				AuthenticationProperties authProperties = new AuthenticationProperties();
				authProperties.IssuedUtc = DateTime.UtcNow;
				authProperties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
				authProperties.AllowRefresh = true;
				authProperties.IsPersistent = true;
				IList<Claim> claimCollection = new List<Claim>
				{
					new Claim(ClaimTypes.Name, "Andras")
					, new Claim(ClaimTypes.Country, "Sweden")
					, new Claim(ClaimTypes.Gender, "M")
					, new Claim(ClaimTypes.Surname, "Nemes")
					, new Claim(ClaimTypes.Email, "hello@me.com")
					, new Claim(ClaimTypes.Role, "IT")
				};
				ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection, "Custom");
				AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, authProperties);
				return ticket;
			}

			return null;
		}

		private bool IsAuthorised(IHeaderDictionary requestHeaders)
		{
			string[] acceptLanguageValues;
			bool acceptLanguageHeaderPresent = requestHeaders.TryGetValue("x-company-auth", out acceptLanguageValues);
			if (acceptLanguageHeaderPresent)
			{
				string[] elementsInHeader = acceptLanguageValues.ToList()[0].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
				if (elementsInHeader.Length == 2)
				{
					int pin;
					if (int.TryParse(elementsInHeader[1], out pin))
					{
						if (pin >= 500000)
						{
							return true;
						}
					}
				}
			}

			return false;
		}
	}
}

We first check if the user is authorised in the IsAuthorised function. You’ll see that the headers are contained in a slightly different object: IHeaderDictionary, which is the header container in the Owin world. It’s really not much different from the HttpRequestHeaders object we saw before. You might also notice that testing for the presence of a specific header type is performed by the TryGetValue function as opposed to TryGetValues we saw before. However, the two functions are otherwise identical in usage. If the header is present then we dissect its value and evaluate the PIN.

If the user is authorised then we build the AuthenticationTicket object. The code shows an example of building an AuthenticationProperties object. Those are of course not meaningful in our demo specifically. We also build a list of claims and you’ll see that a claim consists of a key and a value as noted above. The ClaimTypes class contains a list of well-known claim types such as Name, Email, Role etc. Those are all strings, therefore you can specify your own claim category as you wish, such as “www.mycompany.com/claims/favourite-colour”.

We return null in case authentication fails.

In the next post we’ll add our Owin middleware and test how it works.

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

Advertisement

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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.

%d bloggers like this: