2

how do I pass and Object in a JWT claim in Asp.NetCore WebApi e.g lets say I want to pass an object of Gender with an ID and Name.

        private IActionResult GenerateJwtToken(string email, ApplicationUser user)
        {

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[] 
                {
                    new Claim(ClaimTypes.Name, user.Id.ToString()),
                    new Claim(ClaimTypes.Email, user.Email.ToString()),
                    new Claim(ClaimTypes.GivenName, user.FirstName.ToString()),
                    new Claim(ClaimTypes.UserData, user.LastName.ToString()),
                    new Claim(ClaimTypes.StreetAddress, user.Address.ToString()),
                    new Claim(ClaimTypes.DateOfBirth, user.DateOfBirth.ToString()),
                    new Claim(ClaimTypes.Rsa, user.FileName.ToString()),
                    new Claim(ClaimTypes.Gender, user.Sex.ToString()),
                    new Claim(ClaimTypes.Country, user.Country.ToString()),
                    new Claim(ClaimTypes.StateOrProvince, user.state.ToString()),
                    new Claim(ClaimTypes.Locality, user.LGA.ToString()),
                    new Claim(ClaimTypes.Hash, user.HairColor.ToString()),
                    new Claim(ClaimTypes.Sid, user.GenoType.Name.ToString()),
                    new Claim(ClaimTypes.Spn, user.BloodGroup.ToString()),
                    new Claim(ClaimTypes.System, user.Religion.ToString()),
                    new Claim(ClaimTypes.NameIdentifier, user.NKName.ToString()),
                    new Claim(ClaimTypes.OtherPhone, user.NKPhoneNumber.ToString()),
                    new Claim(ClaimTypes.SerialNumber, user.NKAddress.ToString()),
                    new Claim(ClaimTypes.GroupSid, user.NKRelationship.ToString()),


                }),

                Expires = DateTime.UtcNow.AddDays(7),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };


            var token = tokenHandler.CreateToken(tokenDescriptor);
            var tokenString = tokenHandler.WriteToken(token);

            return Ok(new{ token = tokenString });


        }

Please if there is any better way of getting the current logged in user details aside from Claims, i would love to know about it. Thanks in advance

[HttpGet("/api/profile")]
public IActionResult Index()
{
    var claimIdentity = this.User.Identity as ClaimsIdentity;
    IEnumerable<Claim> claims = claimIdentity.Claims;

    var model = new IndexViewModel
    {
        Id = claimIdentity.FindFirst(ClaimTypes.Name).Value,
        Email = claimIdentity.FindFirst(ClaimTypes.Email).Value,
        FirstName = claimIdentity.FindFirst(ClaimTypes.GivenName).Value,
        LastName = claimIdentity.FindFirst(ClaimTypes.UserData).Value,
        Address = claimIdentity.FindFirst(ClaimTypes.StreetAddress).Value,
        DateOfBirth = claimIdentity.FindFirst(ClaimTypes.DateOfBirth).Value,
        FileName = claimIdentity.FindFirst(ClaimTypes.Rsa)?.Value,
    };

    return Ok(model);
}

4 Answers 4

5

Since the claim can store only string values, you need to represent your objects as a string. For example, you can use Json.net nuget package to serialize/deserialize your object to/from a json format. It could be like that:

string genderStr = JsonConvert.SerializeObject(gender);
// {
//   "Id": 1,
//   "Name": "Male"
// }
Gender genderObj = JsonConvert.DeserializeObject<Gender>(genderStr);

if there is any better way of getting the current logged in user details aside from Claims

As I see your claims are stored in the token and it is OK to get it from claims like you do. Another option could be to store user details in some storage e.g. DB. In this case you would need to take user id from claims and get all details from your storage.

Both options are worth considering. Using the first option you will increase the token size and decrease amount of DB requests. Using the second option you will decrease the token size and increase amount of DB requests.

Sign up to request clarification or add additional context in comments.

1 Comment

thank you very much, I took the user id from the claims and got all the details from the storage.
1

This is the first question that pops up when searching and answers are quite bad. Object can be properly added as JSON, not only as string as other suggested.

You can pass a 3rd parameter to Claim constructor "JsonClaimValueTypes.Json".

var myClaims = new List<Claim>
        {
            new(ClaimTypes.Name, user.UserName ?? string.Empty),
            new(ClaimTypes.NameIdentifier, user.Id),                
            new("my-claim", JsonSerializer.Serialize(myClaimObject), JsonClaimValueTypes.Json)
        };

So instead of having something ugly like this in JWT:

"my-claim": "[{\"id\": 1, \"value\": \"a\"}, {\"id\": 2, \"value\": \"b\"}]"

You will have this:

"my-claim": [{"id": 1, "value": "a"}, {"id": 2, "value": "b"}]

This way the "my-claim" key will hold a proper JSON object, not a plain string.

Comments

0

I took the user id from the claims and got the details from the storage like this

[HttpGet("/api/profile")]
public async Task < IActionResult > Index() {

   var userId = caller.Claims.Single(c => c.Type == ClaimTypes.Name);
   var user = await userManager.Users.Include(s => s.Sex)
              .SingleOrDefaultAsync(c => c.Id == userId.Value);

    return Ok(user);

}    

Comments

0

This is how I added a whole object containing user properties to a list of claims. I just added the name with the serialized object to the userClaims list. Finally, You can just pass the List<Claim> to the ClaimsIdentity object.

 List<Claim> userClaims = new();
 userClaims.Add(new Claim(nameof(user), JsonConvert.SerializeObject(user)));
 var claimsIdentity = new ClaimsIdentity(userClaims)

When extracting the token you can do something like this to cast it to a object, in my case the user object of type User.

var jwtToken = (JwtSecurityToken)validatedToken;
var user = JsonConvert.DeserializeObject<User>(jwtToken.Claims.First(x => x.Type == "user").Value);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.