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

Introduction

In the previous post we laid the foundations for this short series. We went through a refresher of claims and OWIN and started building a simple ASP.NET MVC web project to investigate what’s available about the user of the current thread in a controller. We saw that by default the only claim available about a user might be their name, unless they are anonymous of course.

In this post we’ll continue to explore claims of authenticated users.

Authenticated users and their default claims

We can easily check how the output of the code we tested in the previous part changes for authenticated users. Open the demo web app and start it in Visual Studio. Use the Register link in the top right hand corner to register a user and log in automatically. Check the Debug window when you are redirected to the home page. You’ll see something along these lines:

User authenticated: True
Claim type: 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: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: andras.nemes@email.com
Claim type: 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: AspNet.Identity.SecurityStamp, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 88293e8f-0399-44f0-9e3e-73ddd7206c95

I’m building the demo in Visual Studio 2013 but 2012 and 2015 may give different results. Let’s see what each claim means in turn:

  • nameidentifier: this is the unique identifier of the user in the current system. Where is it coming from? The project saved the user in an SQL table. Below you’ll find a description how to find it.
  • name: this is the user name. I had to provide an email address as the user name on the registration page and it was set as my name claim
  • identityprovider: ASP.NET Identity is the successor of the well-know and by now outdated ASP.NET Membership provider. You can find more details about Identity here.
  • SecurityStamp: this is the most difficult one to figure out. I’ve found a useful thread on StackOverflow available here. Here comes a quote from that thread: “So this is basically meant to represent the current snapshot of your user’s credentials. So if nothing changes, the stamp will stay the same. But if the user’s password is changed, or a login is removed (unlink your google/fb account), the stamp will change. This is needed for things like automatically signing users/rejecting old cookies when this occurs, which is a feature that’s coming in 2.0.

As promised let’s see where to find the user name and user identifier claims. The default ASP.NET Identity mechanism will set up the user and membership related tables for you when the first user is registered. The tables look somewhat like the ones that the old Membership mechanism created but they are more streamlined now.

Click the Show All Files icon in the Solution Explorer and expand the App_Data folder. You’ll find the .mdf file there:

ASP NET identity auto created sql tables for users and membership

If you double-click the .mdf file the database will open in the Server Explorer window. Open the Tables folder of the DefaultConnection node, right-click the AspNetUsers table and click Show Table Data:

View the contents of the Users table created by ASP NET identity

The Id column will hold the name identifier claim. There’s also a UserName column all the way to the right which holds the name claim.

A side note: if you’d like to know more about the new Identify framework in ASP.NET MVC5 I have an intro series on that topic starting here.

So now we know what kind of claims are available by default in an ASP.NET MVC5 project. It is possible that the above set of claims will be enough for you in your web project. However, most often than not you’ll want to dig further and find other types of claims in the database and/or suppress some of the existing ones. E.g. you might not need to know the identity provider claim.

Starting with claims transformation

We’re given an initial set of claims and we’d like to build a custom-made one, a list that only contains the claims that we’re interested in. As mentioned before the MVC5 template already includes a lot of security-related infrastructure. In fact if you add a couple of claims into the AspNetUserClaims table in the mdf database mentioned above those will be automatically retrieved for you when a user logs in, i.e. they will be visible in the…

IEnumerable<Claim> claimsCollection = claimsPrincipal.Claims;

…variable. However, for educational purposes we’ll see how to achieve something similar manually. This process helps you understand claims and OWIN better.

Add a new folder called ClaimsTransformation to the project. Insert a class called ClaimsTransformationService into it:

public class ClaimsTransformationService
{		
	public async Task<IEnumerable<Claim>> TransformInititalClaims(IEnumerable<Claim> initialClaims)
	{
		List<Claim> finalClaims = new List<Claim>();
		Claim userIdClaim = (from c in initialClaims where c.Type == ClaimTypes.NameIdentifier select c).FirstOrDefault();
		if (userIdClaim == null) //user not authenticated
		{
			return initialClaims;
		}
		else
		{
			//pretend that we're accessing a database
			await Task.Delay(1000);
			IList<Claim> additionalClaims = new List<Claim>();
			additionalClaims.Add(new Claim("http://www.mycompany.com/claims/favourite-day", "Monday"));
			additionalClaims.Add(new Claim("http://www.mycompany.com/claims/favourite-colour", "Blue"));
			additionalClaims.Add(new Claim("http://www.mycompany.com/claims/occupation", "Developer"));
			finalClaims.AddRange(initialClaims);
			finalClaims.AddRange(additionalClaims);
		}
		return finalClaims;
	}		
}

We first check whether the user ID is available among the initial claims. That indicates that the user is authenticated. If it’s not available then we simply return the incoming list of claims. Otherwise we add a couple of custom claims that we’re pretending to get from a data store. Those could in reality come from a web service, the Amazon file storage, MongoDb, what have you. Also, this is where you can remove some unnecessary claims from the incoming list. Here we simply keep the original list and extend it with some custom-made ones.

Let’s test this service from HomeController with the following modifications:

public async Task<ActionResult> Index()
{
	ClaimsPrincipal claimsPrincipal = User as ClaimsPrincipal;
	if (claimsPrincipal != null)
	{
		ClaimsIdentity claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity;
		Debug.WriteLine("User authenticated: {0}", claimsIdentity.IsAuthenticated);
		IEnumerable<Claim> claimsCollection = claimsPrincipal.Claims;
		foreach (Claim claim in claimsCollection)
		{					
			Debug.WriteLine("Claim type: {0}, claim value type: {1}, claim value: {2}", claim.Type, claim.ValueType, claim.Value);
		}
		await TestClaimsTransformationCode(claimsCollection);
	}
		
	return View();
}

private async Task TestClaimsTransformationCode(IEnumerable<Claim> initialClaims)
{			
	ClaimsTransformationService service = new ClaimsTransformationService();
	IEnumerable<Claim> finalClaims = await service.TransformInititalClaims(initialClaims);
	Debug.WriteLine("Claims after transformation: ");
	foreach (Claim claim in finalClaims)
	{
		Debug.WriteLine("Claim type: {0}, claim value type: {1}, claim value: {2}", claim.Type, claim.ValueType, claim.Value);
	}
}

Start the application and the new service code should be invoked. Make sure you first test the home page without logging in. You’ll see that we have a single claim after the transformation which is expected:

User authenticated: False
Claim type: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value:
Claims after transformation:
Claim type: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value:

Now log in and you’ll see that the additional claims are indeed added to the original ones:

User authenticated: True
Claim type: 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: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: andras.nemes@nosuchemail.com
Claim type: 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: AspNet.Identity.SecurityStamp, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 88293e8f-0399-44f0-9e3e-73ddd7206c95

Claims after transformation:
Claim type: 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: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: andras.nemes@nosuckemail.com
Claim type: 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: AspNet.Identity.SecurityStamp, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: 88293e8f-0399-44f0-9e3e-73ddd7206c95
Claim type: http://www.mycompany.com/claims/favourite-day, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: Monday
Claim type: http://www.mycompany.com/claims/favourite-colour, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: Blue
Claim type: http://www.mycompany.com/claims/occupation, claim value type: http://www.w3.org/2001/XMLSchema#string, claim value: Developer

So far so good. In the next post we’ll see how to convert this into an OWIN middleware.

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 Handling claims transformation in an OWIN middleware in .NET MVC part 2

  1. Hatvani Sándor says:

    Nagyon szeretem a blogját, mert tömörek és aktuálisak a bejegyzései.
    Köszönöm szépen és további jó munkát kívánok Önnek.

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.