Standardise your basic user data and add it to your ClaimsPrincipal for easy access!
Recently I was working on a web api 2.0 project that used a bearer token for authentication and passed a UserId in the claims for the generated ClaimsPrincipal.
Each controller in the project was accessing the User property from the ApiController base class to get the UserId from the claims and creating various other data to pass to a CQRS style query. Some (simplified) sample code follows;
var userId = ((ClaimsPrincipal)User).Claims.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier); var userRegion = GetUserRegionForUser(userId); var userAccountName = GetUserAccountNameForUser(userId); ... Call an external query with userRegion and userAccountType to assemble data to return
Doesn’t seem too bad, but this kind of code was littered throughout the project and in some cases only the UserId was passed to the external query which was then getting the other data itself. Clearly we needed a unifying UserData class.
The UserData was only ever going to be three simple pieces of information (UserId, UserRegion and UserAccountName) and seemed like an ideal candidate for adding directly to the ClaimsPrinipal. You might think that a custom ClaimsPrincipal that inherits from a base class and adds UserData would be the way to go, but on his blog, Dominick Baier, suggests that deriviation is not ideal and I tend to agree.
You could simply add the three pieces of information as separate claims and then read them back again in your controller:
var userId = ((ClaimsPrincipal)User).Claims.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier); var userRegion = ((ClaimsPrincipal)User).Claims.SingleOrDefault(x => x.Type == "UserRegion"); var userAccountName = ((ClaimsPrincipal)User).Claims.SingleOrDefault(x => x.Type == "UserAccountName");
but this approach is messy and doesn’t give us much more than the code we are trying to change.
Using extensions
The answer, as suggested in Dominick’s blog, relies on extension methods. Careful use of these allows us to not only get and set the UserData class on the principal but also to check for its existence.
public static class ClaimsPrincipalExtensions { private const string UserRegion = "UserRegion"; private const string UserAccountName = "UserAccountName"; public static bool HasUserData(this ClaimsPrincipal principal) { return principal.HasClaim(claim => claim.Type == UserRegion); } public static UserData GetUserData(this ClaimsPrincipal principal) { var userId = principal.Claims.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value; var userRegion = principal.Claims.SingleOrDefault(x => x.Type == UserRegion)?.Value; var userAccountName = principal.Claims.SingleOrDefault(x => x.Type == UserAccountName)?.Value; return new UserData(userId, userRegion, userAccountName); } public static void AddSiteDetails(this ClaimsPrincipal principal, UserData userData) { principal.Identities.First().AddClaim(new Claim(ClaimTypes.NameIdentifier, siteAccessDetails.UserId)); principal.Identities.First().AddClaim(new Claim(UserRegion, userData.UserRegion)); principal.Identities.First().AddClaim(new Claim(UserAccountName, userData.UserAccountName)); } }
Getting the UserData
Now we can access the UserData class from the principal with
var userData = ((ClaimsPrincipal)User).GetUserData();
or, not relying on the ApiController base class
var userData = ClaimsPrincipal.Current.GetUserData();
Creating the claims transformation
But how do we add the claims in the first place, especially considering we are using Owin middleware? The easiest way is to use the freely available NuGet package IdentityModel Owin ClaimsTransformation (the simple source for which is available here if you want to hand craft it).
Having installed this package, add a claims transformer class:
public interface IClaimsTransformer { Task<ClaimsPrincipal> TransformWithSiteDetails(ClaimsPrincipal principal); } public class ClaimsTransformer : IClaimsTransformer { private readonly IUserDataProvider userDataProvider; public ClaimsTransformer(IUserDataProvider userDataProvider) { this.userDataProvider = userDataProvider; } public Task<ClaimsPrincipal> TransformWithUserData(ClaimsPrincipal principal) { if (principal.Identity.IsAuthenticated && !principal.HasUserData()) { principal.AddUserData(userDataProvider.GetDataByClaimsPrincipal(principal)); } return Task.FromResult(principal); } }
Here I am passing in a UserDataProvider that can get me the user data from wherever it originates and using that to populate the principal.
Integrating with Owin middleware
Next add the transformer to your Startup.cs class (after your authentication middleware)
app.UseClaimsTransformation(incoming => Container.Resolve<IClaimsTransformer>().TransformWithUserData(incoming));
Note that I am resolving the claimsTransformer from our IOC container, you can do it whichever way you choose.
And that’s it! Your authenticated controllers will now have the UserData contained in the ClaimsPrincipal and can easily be accessed or tested for.
I hope that helps.
Nice article. I was doing exactly that in my project, but the implementation you and IdentityModel present seems more elegant.
There’s only one issue, when I inject a micro ORM database (NPOCO) into where your IUserDataProvider is, it blows up, complaining something about ClaimPrincipal. I wasn’t even touching the claim principal, just calling the database and assign result to a local variable. Not sure why. You probably didn’t see it if you do database DI in the next layer from IUserDataProvider
I ended up changing the transformation class to something like this
public static IAppBuilder UseClaimsTransformation(this IAppBuilder app, string idType,
Func<string, Task<List>> transformation)