Home > Mobile >  Is there an issue with the custom object JSON serialization implementation of System.IdentityModel.T
Is there an issue with the custom object JSON serialization implementation of System.IdentityModel.T

Time:04-03

We migrated from .Net Core (dotnet core) 3.1 to .Net 6. We were using the System.IdentityModel.Tokens.Jwt to create a payload and generate a security token with that payload.

Our application has yet to be migrated from Newtonsoft.Json to System.Text.Json due to a lot of nonstandard property serialization that is currently favoring the former. The custom claim value contains an object that was previously serialized properly by adhering to the camelCase contract resolver that was specified in the Startup.cs configuration with regards to JSON serialization.

We upgraded from version 5.5.0 of System.IdentityModel.Tokens.Jwt to version 6.16.0 and the serialization is behaving differently.

We are using a mixture of IdentityModel well-known claims along with a custom claim. The custom claim is the only one that is an object and also the only one behaving this way. All other claims are primitive types and are written to the token as specified and expected.

This is an example of the code that is not working:

 var payload = new JwtPayload()
            {
                {JwtRegisteredClaimNames.Iss, issuer},
                {JwtRegisteredClaimNames.Iat, now},
                {JwtRegisteredClaimNames.Nbf, now},
                {JwtRegisteredClaimNames.Exp, exp},
                {JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")},
                {"role", user.UserType},
                {"customClaim", customClaimObjectInstance }
            };

var jwt = new JwtSecurityToken(_jwtHeader, payload);

/* Token below contains a valid base64 encoded JWT Token with
   the customClaim property containing pascal values that match
   the properties of the C# Poco object and not at all following       
   either default convention or even any JsonProperty description 
   attributes such as [JsonProperty("name")] that may decorate each 
   property of the custom object */
var token = _jwtSecurityTokenHandler.WriteToken(jwt);

My first hunch was such that it may be related to a conflict with default library of System.Text.Json. I proceeded to troubleshoot by adding the [JsonPropertyName("name")] attribute to some of the properties but did not succeed. I expected that if System.Text.Json was being used that at least those description attributes would be respected or consulted during the serialization of the claim object.

I also tried serializing the value with Newtonsoft JsonConverter.Serialize function and use the serialized value as the value of the claim key-value-pair. However, the stringified object quotes were escaped and found plenty of escaping characters ("****") all over the value which was undesired.

CodePudding user response:

After some time searching online and trying to come up with the right keywords to search google and GitHub and I finally got to what I, for now, consider a workaround and not a long-term solution.

The clue was provided by this open issue on Github. I simply, to my interpretation, forced the use of the Newtonsoft serializing and deserializing delegates by specifying the following lines before instantiation of payload variable posted in the question:

JsonExtensions.Serializer = JsonConvert.SerializeObject;
JsonExtensions.Deserializer = JsonConvert.DeserializeObject;

This was the first indication of potentially System.Text.Json being forced from deep within a library. It may also be an indication that the time has come to prioritize the migration to System.Text.Json from Newtonsoft.Json.

I hope this workaround helps somebody else get to this short-term patch, and not spend as much as I did.

If I find anymore concrete ideas or clues about this matter, I will update this answer.

The code below works

    /* This was the key to achieving the prior result, however 
       temporary it may be. */
        JsonExtensions.Serializer = JsonConvert.SerializeObject;
        JsonExtensions.Deserializer = JsonConvert.DeserializeObject;
        
        var payload = new JwtPayload()
        {
            {JwtRegisteredClaimNames.Iss, issuer},
            {JwtRegisteredClaimNames.Iat, now},
            {JwtRegisteredClaimNames.Nbf, now},
            {JwtRegisteredClaimNames.Exp, exp},
            {JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")},
            {"role", user.UserType},
            { "customClaim", customClaimObjectInstance}
       };

       var jwt = new JwtSecurityToken(_jwtHeader, payload);
       var token = _jwtSecurityTokenHandler.WriteToken(jwt);

I am thankful for the issue on github, but more importantly to the solution suggested here.

  • Related