From 4241ac5de4f9ed5c3af780efc642331a86ff894a Mon Sep 17 00:00:00 2001 From: Markus Hartung Date: Wed, 25 Jan 2023 00:37:31 +0100 Subject: [PATCH] Fix decoding/encoding payloads with attribute decoration #456 --- src/JWT/Builder/JwtBuilder.cs | 34 ++++++++++++- src/JWT/JWT.csproj | 2 +- .../Builder/JwtBuilderDecodeTests.cs | 48 +++++++++++++++++++ tests/JWT.Tests.Common/Models/TestData.cs | 14 ++++++ 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/JWT/Builder/JwtBuilder.cs b/src/JWT/Builder/JwtBuilder.cs index 36f6c61f4..3d486ec80 100644 --- a/src/JWT/Builder/JwtBuilder.cs +++ b/src/JWT/Builder/JwtBuilder.cs @@ -3,6 +3,7 @@ using System.Reflection; using JWT.Algorithms; using JWT.Serializers; +using Newtonsoft.Json; using static JWT.Internal.EncodingHelper; namespace JWT.Builder @@ -276,7 +277,7 @@ public string Encode(Type payloadType, object payload) EnsureCanEncode(); var dic = payloadType.GetProperties(BindingFlags.Instance | BindingFlags.Public) - .ToDictionary(prop => prop.Name, prop => prop.GetValue(payload, null)); + .ToDictionary(info => GetPropName(info, _jsonSerializerFactory), prop => prop.GetValue(payload, null)); foreach (var pair in dic) { @@ -286,6 +287,37 @@ public string Encode(Type payloadType, object payload) return Encode(); } + private static string GetPropName(MemberInfo prop, IJsonSerializerFactory jsonSerializerFactory) + { + var customAttributes = prop.GetCustomAttributes(true); + foreach (var attribute in customAttributes) + { + if (jsonSerializerFactory.Create() is JsonNetSerializer) + { + if (attribute is JsonPropertyAttribute jsonNetProperty) + { + return jsonNetProperty.PropertyName; + } + } +#if MODERN_DOTNET + else if (jsonSerializerFactory.Create() is SystemTextSerializer) + { + if (attribute is System.Text.Json.Serialization.JsonPropertyNameAttribute systemTextSerializerProperty) + { + return systemTextSerializerProperty.Name; + } + } +#endif + else + { + throw new NotSupportedException( + $"{jsonSerializerFactory.Create().GetType().Name} is not supported"); + } + } + + return prop.Name; + } + /// /// Decodes a token using the supplied dependencies. /// diff --git a/src/JWT/JWT.csproj b/src/JWT/JWT.csproj index 7513c1e4a..9038b73e5 100644 --- a/src/JWT/JWT.csproj +++ b/src/JWT/JWT.csproj @@ -20,7 +20,7 @@ Alexander Batishchev, John Sheehan, Michael Lehenbauer jwt;json;authorization CC0-1.0 - 10.0.1 + 10.0.2 10.0.0.0 10.0.0.0 JWT diff --git a/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs b/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs index dbafb02cf..f67bd0082 100644 --- a/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs +++ b/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs @@ -433,5 +433,53 @@ public void Decode_ToDictionary_Without_Serializer_Should_Throw_Exception() action.Should() .Throw(); } + + [TestMethod] + public void Encode_Decode_ToJsonNetDecoratedType_Should_UseDecoratedName_Bug456() + { + var token = JwtBuilder.Create() + .WithAlgorithm(new NoneAlgorithm()) + .WithJsonSerializer(new JsonNetSerializer()); + + var model = new TestData.TestDataJsonNetDecorated + { + AccessToken = "abc123", + }; + + var encoded = token.Encode(model); + Assert.IsNotNull(encoded); + + token = JwtBuilder.Create() + .WithAlgorithm(new NoneAlgorithm()) + .WithJsonSerializer(new JsonNetSerializer()); + + var payloadDecoded = token.Decode(encoded); + Assert.AreEqual(model.AccessToken, payloadDecoded.AccessToken); + } +#if NETSTANDARD2_0 || NET6_0 || NET7_0 + + [TestMethod] + public void Encode_Decode_ToSystemTextSerializerDecoratedType_Should_UseDecoratedName_Bug456() + { + var token = JwtBuilder.Create() + .WithAlgorithm(new NoneAlgorithm()) + .WithJsonSerializer(new SystemTextSerializer()); + + var model = new TestData.TestDataSystemTextSerializerDecorated + { + AccessToken = "abc123", + }; + + var encoded = token.Encode(model); + Assert.IsNotNull(encoded); + + token = JwtBuilder.Create() + .WithAlgorithm(new NoneAlgorithm()) + .WithJsonSerializer(new SystemTextSerializer()); + + var payloadDecoded = token.Decode(encoded); + Assert.AreEqual(model.AccessToken, payloadDecoded.AccessToken); + } +#endif } } diff --git a/tests/JWT.Tests.Common/Models/TestData.cs b/tests/JWT.Tests.Common/Models/TestData.cs index 94da079e8..9ffeb270a 100644 --- a/tests/JWT.Tests.Common/Models/TestData.cs +++ b/tests/JWT.Tests.Common/Models/TestData.cs @@ -17,6 +17,20 @@ public static class TestData Age = 33 }; + public class TestDataJsonNetDecorated + { + [Newtonsoft.Json.JsonProperty("AT")] + public string AccessToken { get; set; } + } + +#if NETSTANDARD2_0 || NET6_0 || NET7_0 + public class TestDataSystemTextSerializerDecorated + { + [System.Text.Json.Serialization.JsonPropertyName("AT")] + public string AccessToken { get; set; } + } +#endif + public const string Secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk"; public const string Secret2 = "QWORIJkmQWEDIHbjhOIHAUSDFOYnUGWEYT";