Handling claims transformation in an OWIN middleware in .NET MVC part 4

Introduction

In the previous post we turned our claims transformation code into OWIN middleware. We went through the process of creating a component – the ClaimsTransformationComponent – and registering it with OWIN in Startup.cs. We set the current claims principal in our component which is then available within the controllers.

In this post which finishes this series we’ll diverge a little from our main topic and take a look at how we can add dependencies to an OWIN component.

Loosely coupled dependencies

In particular I don’t like the hard dependency introduced by the following bit of code:

ClaimsTransformationService service = new ClaimsTransformationService();

…which we currently have in the Transform method of ClaimsTransformationComponent. It introduces a hard dependency on ClaimsTransformationService and increases the coupling between the OWIN component class and the concrete claims transformation class. 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.

One practical issue with the current implementation is that we’re tied to the claims transformation logic as it’s implemented in ClaimsTransformationService.cs. The middleware component is responsible for constructing an instance of ClaimsTransformationService which immediately establishes the hard dependency we mentioned above. It would be better if we could remove this burden from ClaimsTransformationComponent. The standard way of solving this problem is by way of abstractions. We’ll let ClaimsTransformationComponent depend on an abstraction – an abstract base class or an interface – instead of the concrete ClaimsTransformationService.

Let’s go step by step. First we’ll create our abstraction. Insert the following interface to the ClaimsTransformation folder:

public interface IClaimsTransformationService
{
	Task<IEnumerable<Claim>> TransformInititalClaims(IEnumerable<Claim> initialClaims);
}

You’ll recognise the method signature from ClaimsTransformationService. We can now let ClaimsTransformationService implement the interface:

public class ClaimsTransformationService : IClaimsTransformationService

That’s the only change we need to apply to the ClaimsTransformationService.

Next we’ll turn to the ClaimsTransformationComponent. 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 ClaimsTransformationComponent will require an IClaimsTransformationService in order to carry out its tasks.

Here’s the revised component:

public class ClaimsTransformationComponent
{
	private readonly ApplicationFunction _nextComponent;
	private readonly IClaimsTransformationService _claimsTransformationService;

	public ClaimsTransformationComponent(ApplicationFunction appFunc, IClaimsTransformationService claimsTransformationService)
	{
		if (appFunc == null) throw new ArgumentNullException("AppFunc of next component");
		if (claimsTransformationService == null) throw new ArgumentNullException("ClaimsTransformationService");
		_nextComponent = appFunc;
		_claimsTransformationService = claimsTransformationService;
	}

	public async Task Invoke(IDictionary<string, object> environment)
	{
		ClaimsPrincipal claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;
		if (claimsPrincipal != null)
		{
			ClaimsIdentity claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity;
			Debug.WriteLine("User authenticated in OWIN middleware: {0}", claimsIdentity.IsAuthenticated);
			IEnumerable<Claim> claimsCollection = claimsPrincipal.Claims;
			foreach (Claim claim in claimsCollection)
			{
				Debug.WriteLine("Claim type in OWIN: {0}, claim value type: {1}, claim value: {2}", claim.Type, claim.ValueType, claim.Value);
			}
			IEnumerable<Claim> finalClaims = await Transform(claimsCollection);
			ClaimsPrincipal extendedPrincipal = new ClaimsPrincipal(new ClaimsIdentity(finalClaims, "CustomAuthType"));
			Thread.CurrentPrincipal = extendedPrincipal;
		}
		await _nextComponent(environment);
	}

	private async Task<IEnumerable<Claim>> Transform(IEnumerable<Claim> initialClaims)
	{
		IEnumerable<Claim> finalClaims = await _claimsTransformationService.TransformInititalClaims(initialClaims);			
		return finalClaims;
	}
}

We haven’t changed much. We include the IClaimsTransformationService object in the constructor and then delegate the claims transformation to this object in the Transform method. We’ve now got rid of the concrete dependency.

If you compile the project now you should get an error saying that ClaimsTransformationComponent doesn’t have a constructor that accepts a single parameter. So we’ll now turn our attention to the UseClaimsTransformationComponent extension method in AppBuilderExtensions.cs. We’ll extend the method signature with the IClaimsTransformationService object and pass it into ClaimsTransformationComponent as follows:

namespace Owin
{
	public static class AppBuilderExtensions
	{
		public static void UseClaimsTransformationComponent(this IAppBuilder appBuilder, IClaimsTransformationService claimsTransformationService)
		{
			if (claimsTransformationService == null) throw new ArgumentNullException("ClaimsTransformationService");
			appBuilder.Use<ClaimsTransformationComponent>(claimsTransformationService);
		}
	}
}

We can pass in an arbitrary array of objects into the Use method. However, the parameters must match the constructor or the middleware class. The middleware must have at least one parameter of type…

Func<IDictionary<string, object>, Task>

…and it must come first. All other dependencies must be listed afterwards and you must pass in those parameters in the Use method.

The code…

appBuilder.Use<ClaimsTransformationComponent>(claimsTransformationService);

…implies that we have a constructor of ClaimsTransformationComponent which has the following parameters:

Func<IDictionary<string, object>, Task>, IClaimsTransformationService

…which is true. You can try extending the Use method as follows to pretend that we want to pass a boolean paramter:

appBuilder.Use<ClaimsTransformationComponent>(claimsTransformationService, true);

You’ll get a runtime error when you try to run the application:

An exception of type ‘System.MissingMethodException’ occurred in Microsoft.Owin.dll but was not handled in user code

Additional information: The class ‘ClaimsTransformationInOwinDemo.Middleware.ClaimsTransformationComponent’ does not have a constructor taking 3 arguments.

As soon as you add a boolean to the constructor…

public ClaimsTransformationComponent(ApplicationFunction appFunc, IClaimsTransformationService claimsTransformationService, bool b)

…the code will pass. We don’t actually need that boolean parameter, it was just for demo purposes.

The compiler should now complain that Startup.cs cannot resolve the call to UseClaimsTransformationComponent as we have to pass in an IClaimsTransformationService object. The easiest solution looks as follows:

app.UseClaimsTransformationComponent(new ClaimsTransformationService());

Startup.cs is the entry point into an MVC5 application so I think it’s fine to construct concrete dependencies there. This is called poor man’s dependency injection as we haven’t used any IoC container to resolve our dependencies but I think that it’s not necessary to complicate the code any further.

This is the end of this series, I hope you’ve enjoyed it.

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.

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.