Using JWTs with the ASP.NET UserManager

Last night I got stuck trying to fix a bug in a REST API that really drove me nuts, so I’ll share in the hopes it will save someone else a headache.
The service uses JWTs for authentication, and are issued in an authentication controller, while the data is provided by another controller. I have a CreateToken method that creates an instance of ApplicationUser for the requested user, and adds some claims.

                        var claims = new[]
                        {
                              new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                              new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                              new Claim(JwtRegisteredClaimNames.Email, user.Email)
                        }.Union(userClaims);

I’m sure this looks familiar if you’ve used JWTs in .NET. The method creates a valid token, and returns it to the user. The user can then take that token and pass it along to a different controller for authentication. In my case, I had customized the ApplicationUser to include a property called TenantID, and in the controller, I needed to create an instance of the ApplicationUser, and use the TenantID.

var user = await _userManager.GetUserAsync(User);

The problem was that the GetUserAsync(User) always returned null. After screwing around with it for way too long, I went to GitHub to see what GetUserAsync was actually doing.

https://github.com/aspnet/Identity/blob/dev/src/Core/UserManager.cs

It calls GetUserID, and passes in the ClaimsPrincipal.

        public virtual string GetUserId(ClaimsPrincipal principal)
	        {
	            if (principal == null)
	            {
	                throw new ArgumentNullException(nameof(principal));
	            }
	            return principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType);
	        }

It wants to find the first claim associated with the UserIdClaimType….Which is what?

JwtRegisteredClaimNames.Sub

When I was creating the token, I was giving the Sub claim the user’s username, but ASP.NET identity was searching the database for the Id field in the AspNetUsers table in SQL.

UGH.

So I changed the assignment when the token is created, and everything seems to work fine now.

                        var claims = new[]
                        {
                              new Claim(JwtRegisteredClaimNames.Sub, user.Id),
                              new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
                              new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                              new Claim(JwtRegisteredClaimNames.Email, user.Email)
                        }.Union(userClaims);