Wiring up a custom authentication method with OWIN in Web API Part 5: abstracting away the auth logic

Introduction

In the previous post we built the necessary OWIN components for our custom authentication logic: the middleware and the extension method that’s attached to the IAppBuilder object. We also diverged a little and discussed how to use the Use extension method to inject the necessary objects into the constructor of the middleware class.

In this post we’ll extend PinAuthenticationHandler so that the actual authentication logic can be easily changed.

Loosely coupled dependencies

In particular I don’t like that PinAuthenticationHandler is tightly coupled to the authentication logic in the IsAuthorised method. The logic itself is very simple but we still have a tight coupling. If we want to replace it with something else then we have to do it directly within this method. If you’re not sure why this can be a bad strategy you should read about the dependency inversion principle (DIP) and the dependency injection pattern available here. It’s a large topic that’s worth exploring.

PinAuthenticationHandler is responsible for implementing the PIN evaluation logic. It would be better if we could remove this burden from this class and delegate it to a specialised one that can be replaced at ease. The standard way of solving this problem is by way of abstractions. We’ll let PinAuthenticationHandler depend on an abstraction, i.e. an abstract base class or an interface and let the caller inject the correct implementation.

Externalising the PIN evaluation

Insert the following interface into the Authentication folder of the demo project:

public interface IPinAuthenticationService
{
	bool IsAuthorised(string userId, int pin);
}

Add the following implementation to the same folder. You’ll recognise our simple PIN evaluation logic:

public class DummyPinAuthenticationService : IPinAuthenticationService
{
	public bool IsAuthorised(string userId, int pin)
	{
		return pin >= 500000;			
	}
}

A more realistic implementation would probably check the ID and the PIN in some database.

The next step is to introduce the IPinAuthenticationService abstraction into PinAuthenticationHandler in some way. DIP comes in a couple of different shapes and constructor injection is probably the most common. The term “constructor injection” may sound scary but it really only means that we extend the constructor of an object with the dependencies it requires. We thereby inform the rest of the world that PinAuthenticationHandler will require an IPinAuthenticationService in order to carry out its tasks.

Therefore we extend PinAuthenticationHandler so that its constructor requires an IPinAuthenticationService. We’ll then delegate the evaluation to the abstraction within the IsAuthorised method:

namespace CustomAuthenticationOwin.Authentication
{
	public class PinAuthenticationHandler : AuthenticationHandler<PinBasedAuthenticationOptions>
	{
		private readonly IPinAuthenticationService _pinAuthenticationService;

		public PinAuthenticationHandler(IPinAuthenticationService pinAuthenticationService)
		{
			if (pinAuthenticationService == null) throw new ArgumentNullException("PinAuthenticationService");
			_pinAuthenticationService = pinAuthenticationService;
		}

		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))
					{
						return _pinAuthenticationService.IsAuthorised(elementsInHeader[0], pin);
					}
				}
			}

			return false;
		}
	}
}

We need to propagate this change to PinBasedAuthMiddleware as well since its CreateHandler returns a PinAuthenticationHandler:

namespace CustomAuthenticationOwin.Authentication
{
	public class PinBasedAuthMiddleware : AuthenticationMiddleware<PinBasedAuthenticationOptions>
	{
		private readonly IPinAuthenticationService _pinAuthenticationService;

		public PinBasedAuthMiddleware(OwinMiddleware nextMiddleware, PinBasedAuthenticationOptions authOptions, IPinAuthenticationService pinAuthenticationService) 
			: base(nextMiddleware, authOptions)
		{
			if (pinAuthenticationService == null) throw new ArgumentNullException("PinAuthenticationService");
			_pinAuthenticationService = pinAuthenticationService;
		}

		protected override AuthenticationHandler<PinBasedAuthenticationOptions> CreateHandler()
		{
			return new PinAuthenticationHandler(_pinAuthenticationService);
		}
	}
}

If you try to run the demo app you’ll get a runtime exception:

The class ‘CustomAuthenticationOwin.Authentication.PinBasedAuthMiddleware’ does not have a constructor taking 2 arguments.

You should understand what the above message means based on our experiments that finished off the previous post. We have to modify the extension method that calls upon IAppBuilder to use the PinBasedAuthMiddleware middleware:

public static class PinAuthenticationExtension
{
	public static void UsePinBasedAuthentication(this IAppBuilder appBuilder, IPinAuthenticationService pinAuthenticationService)
	{
		appBuilder.Use<PinBasedAuthMiddleware>(new PinBasedAuthenticationOptions(), pinAuthenticationService);
	}
}

At this point you should get the following compilation error:

Error 1 No overload for method ‘UsePinBasedAuthentication’ takes 0 arguments

The last piece in the chain is to update Startup.cs so that we pass an IPinAuthenticationService into the UsePinBasedAuthentication extension method:

public void Configuration(IAppBuilder appBuilder)
{
	appBuilder.UsePinBasedAuthentication(new DummyPinAuthenticationService());
}

You can now start the demo app and the tester C# console application. You should get the same behaviour as before the abstraction was introduced but now the PIN evaluation logic is hidden behind an abstraction.

This was the last post in this series. I hope you’ve found it useful.

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

Advertisements

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

One Response to Wiring up a custom authentication method with OWIN in Web API Part 5: abstracting away the auth logic

  1. Bryan D Hobbs says:

    Thank you very much for putting together these last three articles on custom owin middleware. There wasn’t much out there on the topic, and the articles I did find did not explain it adequately. Cheers!

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

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

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: