diff --git a/src/JWT/Builder/JwtBuilder.cs b/src/JWT/Builder/JwtBuilder.cs index d8fee52ba..3edf0b5cd 100644 --- a/src/JWT/Builder/JwtBuilder.cs +++ b/src/JWT/Builder/JwtBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reflection; using JWT.Algorithms; using static JWT.Internal.EncodingHelper; using static JWT.Serializers.JsonSerializerFactory; @@ -247,6 +248,22 @@ public string Encode() return _encoder.Encode(_jwt.Header, _jwt.Payload, _secrets?[0]); } + public string Encode(T payload) + { + EnsureCanEncode(); + + var payloadAsDictionary = payload.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public) + .ToDictionary(prop => prop.Name, prop => prop.GetValue(payload, null)); + + foreach (var keyValuePair in payloadAsDictionary) + { + _jwt.Payload.Add(keyValuePair.Key, keyValuePair.Value); + } + + return Encode(); + } + /// /// Decodes a token using the supplied dependencies. /// diff --git a/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs b/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs index d2db6b4cd..0d82e06a7 100644 --- a/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs +++ b/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs @@ -1,6 +1,8 @@ #if SYSTEM_TEXT_JSON using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; @@ -118,7 +120,15 @@ private static void HandleValue(Utf8JsonWriter writer, string key, object object } default: { - writer.WriteNullValue(); + var payloadPartAsDictionary = objectValue.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public) + .ToDictionary(prop => prop.Name, prop => prop.GetValue(objectValue, null)); + writer.WriteStartObject(); + foreach (var payloadPartKeyValue in payloadPartAsDictionary) + { + HandleValue(writer, payloadPartKeyValue.Key, payloadPartKeyValue.Value); + } + writer.WriteEndObject(); break; } } diff --git a/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs b/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs index 58ae096d4..f560d932a 100644 --- a/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs +++ b/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs @@ -208,6 +208,36 @@ public void Encode_Should_Return_Token_With_Custom_Extra_Headers() token.Should() .Be(TestData.TokenWithCustomTypeHeader3, "because the same data encoded with the same key must result in the same token"); } + + [TestMethod] + public void Encode_Should_Return_Token_With_Custom_Extra_Headers_Full_Payload() + { + const string key = TestData.Secret; + + var token = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSecret(key) + .AddHeader("version", 1) + .Encode(TestData.Customer); + + token.Should() + .Be(TestData.TokenWithCustomTypeHeader3, "because the same data encoded with the same key must result in the same token"); + } + + [TestMethod] + public void Encode_Should_Return_Token_Nested_Data() + { + const string key = TestData.Secret; + + var token = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSecret(key) + .AddClaim("Data", TestData.Customer) + .Encode(); + + token.Should() + .Be(TestData.TokenWithNestedData, "because the same data encoded with the same key must result in the same token"); + } [TestMethod] public void Encode_With_Custom_Factory_Return_Token() diff --git a/tests/JWT.Tests.Common/Models/TestData.cs b/tests/JWT.Tests.Common/Models/TestData.cs index c1183d080..ca2347e7d 100644 --- a/tests/JWT.Tests.Common/Models/TestData.cs +++ b/tests/JWT.Tests.Common/Models/TestData.cs @@ -29,6 +29,7 @@ public static class TestData public const string TokenWithCustomTypeHeader = "eyJ0eXAiOiJmb28iLCJhbGciOiJIUzI1NiJ9.eyJGaXJzdE5hbWUiOiJKZXN1cyIsIkFnZSI6MzN9.vubwuLxx_7AWGvo-Y8XF_l7XP1WOv5obJulIk3RlVdk"; public const string TokenWithCustomTypeHeader2 = "eyJraWQiOiI0MiIsInR5cCI6IkpXVCIsImFsZyI6IkhTMjU2In0.eyJGaXJzdE5hbWUiOiJKZXN1cyIsIkFnZSI6MzN9.sPwGfDyhArZCmWRHTxm0xzNeG1gCf-qXhz21PdUxS4k"; public const string TokenWithCustomTypeHeader3 = "eyJ2ZXJzaW9uIjoxLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJGaXJzdE5hbWUiOiJKZXN1cyIsIkFnZSI6MzN9.TSQb2zVBJL9uY6mIdBKFaEooR-0OPjR-FPqY7hzwzwU"; + public const string TokenWithNestedData = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJEYXRhIjp7IkZpcnN0TmFtZSI6Ikplc3VzIiwiQWdlIjozM319.IDx9z7phNsm-0MQLpZ1fREcUtkKtAVnSV6MSeP2R88U"; public const string TokenWithExp = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJGaXJzdE5hbWUiOiJKZXN1cyIsIkFnZSI6MzMsImV4cCI6MTYwNTgzNDI1NX0.dOkG1StO33Ae0qFQbHLslvSsCV6ThLofjc885egDnuY"; public const string TokenWithNbf = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJGaXJzdE5hbWUiOiJKZXN1cyIsIkFnZSI6MzMsIm5iZiI6MTYwNTgzNDI1NX0.iuxTYx6CMcNaxgvPn8pfnPFDhIZceKB0PrIZgkmHFbg"; public const long TokenTimestamp = 1605834255;