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

Introduction

In the previous post we built upon our MVC claims transformation demo. Specifically we saw how to take the incoming claims and add a couple of custom ones by pretending that we query some data store. We wired up all the demo code in the HomeController.

The goal of this closing post is to reorganise the claims-related code into OWIN middleware.

OWIN middleware

We took up OWIN middleware in a couple of posts on this blog before. If you’re entirely new to this topic you can skim through this introductory series. Make sure you understand the app function, the Startup class, the Invoke method and its special signature and how to build OWIN middleware yourself.

We’ll go through this in steps.

Add a folder called Middleware to the solution. We’ll need some extension that we can wire up with the IAppBuilder object. The MVC5 default project includes a Startup.cs – in fact there are 2 of them as we’re talking about a partial class, one called Startup.cs located at the root of the solution and one called Startup.Auth.cs that you’ll find in the App_Start folder. We’re more interested in Startup.cs which currently is very thin:

public partial class Startup
{
	public void Configuration(IAppBuilder app)
	{			
		ConfigureAuth(app);
	}
}

Here we want to be able to write t.ex. app.UseClaimsTransformationComponent(args).

That sounds like an extension method. If you’re not sure how extension methods work start here. Let’s add a class called AppBuilderExtensions to the Middleware folder with the following content:

using ClaimsTransformationInOwinDemo.Middleware;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Owin
{
	public static class AppBuilderExtensions
	{
		public static void UseClaimsTransformationComponent(this IAppBuilder appBuilder)
		{
			appBuilder.Use<ClaimsTransformationComponent>();
		}
	}
}

I’ve copied all the code so that you won’t miss any single detail. Notice how the namespace is set to “Owin” so that the extension method will be found when we add it to Startup.cs in a short while. We don’t have ClaimsTransformationComponent yet so let’s add it to the Middleware folder too:

using ClaimsTransformationInOwinDemo.ClaimsTransformation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using ApplicationFunction = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;

namespace ClaimsTransformationInOwinDemo.Middleware
{
	public class ClaimsTransformationComponent
	{
		private readonly ApplicationFunction _nextComponent;

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

		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)
		{
			ClaimsTransformationService service = new ClaimsTransformationService();
			IEnumerable<Claim> finalClaims = await service.TransformInititalClaims(initialClaims);			
			return finalClaims;
		}
	}
}

If you’ve gone through the introductory posts on OWIN referenced above you’ll recognise a couple of important elements to an OWIN middleware:

  • The Invoke method that accepts the environment dictionary
  • We save and execute the “next” component in the middleware chain

The rest of the code will look familiar to you from the previous post. We had most of it in the HomeController’s Index action. We won’t need it there any more so let’s return to a simpler form there:

public ActionResult Index()
{
	ClaimsPrincipal claimsPrincipal = User as ClaimsPrincipal;
	foreach (Claim claim in claimsPrincipal.Claims)
	{
		Debug.WriteLine("Claim type in HomeController: {0}, claim value type: {1}, claim value: {2}", claim.Type, claim.ValueType, claim.Value);
	}
	return View();
}

We can finally add the call to use our new middleware in Startup.cs:

public partial class Startup
{
	public void Configuration(IAppBuilder app)
	{			
		ConfigureAuth(app);
		app.UseClaimsTransformationComponent();
	}
}

Run the application and watch the messages in the Debug window. Before logging in you may see messages like the following:

User authenticated in OWIN middleware: False
Claim type in OWIN: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value:
Claim type in HomeController: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value:

Log in to the application and you’ll see that the messages are different:

User authenticated in OWIN middleware: True

Claim type in OWIN: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 46b0cbfc-74b7-4269-858b-d98972fd1a66
Claim type in OWIN: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: andras.nemes@apicasystem.com
Claim type in OWIN: http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: ASP.NET Identity
Claim type in OWIN: AspNet.Identity.SecurityStamp, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 88293e8f-0399-44f0-9e3e-73ddd7206c95

Claim type in HomeController: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 46b0cbfc-74b7-4269-858b-d98972fd1a66
Claim type in HomeController: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: andras.nemes@apicasystem.com
Claim type in HomeController: http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: ASP.NET Identity
Claim type in HomeController: AspNet.Identity.SecurityStamp, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 88293e8f-0399-44f0-9e3e-73ddd7206c95

We’ve successfully added the transformation logic to our application in form of an OWIN middleware.

We’ll diverge a little in the next post which also finishes this series. We’ll see how to add a dependency to our middleware. In particular we’ll aim to factor out the call to…

ClaimsTransformationService service = new ClaimsTransformationService();

…so that the code becomes more “compliant” with the dependency inversion principle.

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.

3 Responses to Handling claims transformation in an OWIN middleware in .NET MVC part 3

  1. Pingback: Handling claims transformation in an OWIN middleware in .NET MVC part 3 | Brian By Experience

  2. Jonas says:

    Hi Andras,

    First let me thank you for writting good articles about security in ASP.NET & OWIN/Katana.

    Im have followed your articles and now have a OWIN middleware which tranforms claims using a ClaimsTransformationService, but I wonder how I can perist the claims transformations in the OWIN cookie. Currently I can see in the debug window that transformation are working in OWIN middleware but not in Homecontroller Index action method.

    It seems like the initial claims are serialized after calling the AuthenticationManager.SignIn method which issues a cookie, and the final claims including the transformed claims are never included in the cookie.

    How can claim transformations uwin a OWIN middleware persist in a cookie and be available later in the pipeline (like in the Homecontroller Index action method)?

  3. I could not get this to work. Claims added to the Thread.CurrentPrincipal did not propagate to the controller (different threads?). What *does* work: The environtment dictionary has a “Server.User” witch holds the current Identity. Adding claims to those Identity’s get all the way to the controller.

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

Elliot Balynn's Blog

A directory of wonderful thoughts

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: