.net core decoded JWT claim type doesn't match the ones used while generation

Viewed 360

I'm generating a JWT token in .net core 3.1 like so

    private string GenerateJwtToken(User user)
    {
        var tokenHandler = new JwtSecurityTokenHandler();

        var key = Encoding.ASCII.GetBytes(_authenticationSettings.Secret);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.NameIdentifier, user.Username),
                new Claim(ClaimTypes.Email, user.Email),
                new Claim(ClaimTypes.GivenName, user.FirstName)
            }),
            Expires = DateTime.UtcNow.AddDays(_authenticationSettings.AuthTokenValidityInDays),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);

        return tokenHandler.WriteToken(token);
    }

And in my unit tests I'm trying to test it like so

    [Fact]
    public async Task Should_Authenticate_With_Valid_CredentialsAsync()
    {
        var tokenString = await _sut.Authenticate(new User 
        { 
            Username = "test",
            Email = "test@fakemail.com",
            FirstName = "Someone"
        });
        var jwtHandler = new JwtSecurityTokenHandler();
        Assert.True(jwtHandler.CanReadToken(tokenString));

        var token = new JwtSecurityToken(tokenString);
        Assert.Equal("test", token.Claims.First(claim => claim.Type == ClaimTypes.NameIdentifier).Value);
    }

The first Assert passes however the second one fails because of a null reference exception. I've inspected the code and looked at the username claim which is of type nameid. It seems like the value of the key ClaimTypes.NameIdentifier is http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier during the tests instead of nameid.

Can someone help explain what's happening and how to fix it?

2 Answers

Okay, so after a lot more digging around I finally found the answer. So what I needed to do was something like this (which gets a ClaimsPrincipal which can be queried with static types)

        var validationParameters = new TokenValidationParameters
        {
            IssuerSigningKey = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("my-super-secret-test-key")), SecurityAlgorithms.HmacSha256Signature).Key,
            ValidateAudience = false,
            ValidateIssuer = false,
        };

        SecurityToken validatedToken;
        JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
        var user = handler.ValidateToken(tokenString, validationParameters, out validatedToken);

        Assert.Equal("test", user.FindFirst(ClaimTypes.NameIdentifier).Value);

ClaimTypes.NameIdentifier is always the same, the SAML claim including the namespace. However, that doesn't mean that CreateJwtSecurityToken will use that string value to present the claim in JWT.

You can use JwtSecurityTokenHandler.OutboundClaimTypeMap to get the mapping between ClaimType (which comes from SOAP identities) to the the value used in JWT.

Related