From 2e2c9c087e0f4eb71b5bfa2840faf16d94962982 Mon Sep 17 00:00:00 2001 From: Markus Hartung Date: Thu, 8 Sep 2022 19:17:40 +0200 Subject: [PATCH] Added option to select serializer (#433) * Bumped all versions * Renamed methods on JwtBuilder * Added DefaultJsonSerializerFactory * Added DelegateJsonSerializerFactory * Updated tests Co-authored-by: Markus Hartung Co-authored-by: Alexander Batishchev --- src/Directory.Build.targets | 8 +-- .../JWT.Extensions.AspNetCore.csproj | 6 +- .../JWT.Extensions.DependencyInjection.csproj | 6 +- .../ServiceCollectionExtensions.cs | 15 ++-- .../Algorithms/DelegateAlgorithmFactory.cs | 7 +- src/JWT/Builder/JwtBuilder.cs | 69 ++++++++++++------- src/JWT/Builder/JwtHeader.cs | 4 +- src/JWT/JWT.csproj | 6 +- .../DictionaryStringObjectJsonConverter.cs | 2 +- .../DefaultJsonSerializerFactory.cs | 19 +++++ .../DelegateJsonSerializerFactory.cs | 29 ++++++++ src/JWT/Serializers/IJsonSerializerFactory.cs | 7 ++ src/JWT/Serializers/JsonNetSerializer.cs | 4 +- src/JWT/Serializers/JsonSerializerFactory.cs | 16 ----- src/JWT/Serializers/SystemTextSerializer.cs | 2 +- .../JwtAuthenticationHandlerTests.cs | 6 +- .../Builder/JwtBuilderDecodeTests.cs | 8 +-- .../Builder/JwtBuilderEncodeTests.cs | 40 ++++++++++- .../DefaultJsonSerializerFactoryTests.cs | 44 ++++++++++++ tests/JWT.Tests.Common/JwtDecoderTests.cs | 8 ++- tests/JWT.Tests.Common/JwtEncoderTests.cs | 27 ++++++-- tests/JWT.Tests.Common/JwtSecurityTests.cs | 8 ++- tests/JWT.Tests.Common/JwtValidatorTests.cs | 5 +- tests/JWT.Tests.Net40/JWT.Tests.Net40.csproj | 2 +- 24 files changed, 250 insertions(+), 98 deletions(-) create mode 100644 src/JWT/Serializers/DefaultJsonSerializerFactory.cs create mode 100644 src/JWT/Serializers/DelegateJsonSerializerFactory.cs create mode 100644 src/JWT/Serializers/IJsonSerializerFactory.cs delete mode 100644 src/JWT/Serializers/JsonSerializerFactory.cs create mode 100644 tests/JWT.Tests.Common/DefaultJsonSerializerFactoryTests.cs diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 3d32dc3af..0930dcbca 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -4,14 +4,8 @@ - $(DefineConstants);SYSTEM_TEXT_JSON + $(DefineConstants);MODERN_DOTNET - - - $(DefineConstants);NEWTONSOFT_JSON - - - \ No newline at end of file diff --git a/src/JWT.Extensions.AspNetCore/JWT.Extensions.AspNetCore.csproj b/src/JWT.Extensions.AspNetCore/JWT.Extensions.AspNetCore.csproj index 2ddf10770..b94df00d2 100644 --- a/src/JWT.Extensions.AspNetCore/JWT.Extensions.AspNetCore.csproj +++ b/src/JWT.Extensions.AspNetCore/JWT.Extensions.AspNetCore.csproj @@ -11,7 +11,7 @@ Alexander Batishchev jwt;json;asp.net;asp.net core;.net core;authorization MIT - 10.0.0-beta3 + 10.0.0-beta4 10.0.0.0 10.0.0.0 JWT.Extensions.AspNetCore @@ -30,8 +30,8 @@ - - + + diff --git a/src/JWT.Extensions.DependencyInjection/JWT.Extensions.DependencyInjection.csproj b/src/JWT.Extensions.DependencyInjection/JWT.Extensions.DependencyInjection.csproj index b8d721153..65072da4b 100644 --- a/src/JWT.Extensions.DependencyInjection/JWT.Extensions.DependencyInjection.csproj +++ b/src/JWT.Extensions.DependencyInjection/JWT.Extensions.DependencyInjection.csproj @@ -11,7 +11,7 @@ Alexander Batishchev jwt;json;asp.net;asp.net core;.net core;authorization;dependenсy injection MIT - 2.2.0-beta3 + 2.2.0-beta4 2.0.0.0 2.0.0.0 JWT @@ -30,8 +30,8 @@ - - + + diff --git a/src/JWT.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/JWT.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 73fbbc037..d5d6ed572 100644 --- a/src/JWT.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/JWT.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -1,15 +1,10 @@ using System; using JWT.Algorithms; using JWT.Internal; +using JWT.Serializers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -#if SYSTEM_TEXT_JSON -using JsonSerializer = JWT.Serializers.SystemTextSerializer; -#elif NEWTONSOFT_JSON -using JsonSerializer = JWT.Serializers.JsonNetSerializer; -#endif - namespace JWT { public static class ServiceCollectionExtensions @@ -17,7 +12,8 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddJwtEncoder(this IServiceCollection services) { services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(p => p.GetRequiredService().Create()); services.TryAddSingleton(); return services; @@ -44,7 +40,8 @@ private static IServiceCollection AddJwtEncoder(this IServiceCollectio public static IServiceCollection AddJwtDecoder(this IServiceCollection services) { services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(p => p.GetRequiredService().Create()); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -60,4 +57,4 @@ public static IServiceCollection AddJwtDecoder(this IServiceCollection return services.AddJwtDecoder(); } } -} \ No newline at end of file +} diff --git a/src/JWT/Algorithms/DelegateAlgorithmFactory.cs b/src/JWT/Algorithms/DelegateAlgorithmFactory.cs index a8fa928d7..82f34aef1 100644 --- a/src/JWT/Algorithms/DelegateAlgorithmFactory.cs +++ b/src/JWT/Algorithms/DelegateAlgorithmFactory.cs @@ -20,16 +20,15 @@ public DelegateAlgorithmFactory(Func algFactory) => /// Creates an instance of with supplied algorithm. /// /// - public DelegateAlgorithmFactory(IJwtAlgorithm algorithm) + public DelegateAlgorithmFactory(IJwtAlgorithm algorithm) : + this(() => algorithm) { if (algorithm is null) throw new ArgumentNullException(nameof(algorithm)); - - _algFactory = () => algorithm; } /// public IJwtAlgorithm Create(JwtDecoderContext context) => _algFactory(); } -} \ No newline at end of file +} diff --git a/src/JWT/Builder/JwtBuilder.cs b/src/JWT/Builder/JwtBuilder.cs index f682beaf3..36f6c61f4 100644 --- a/src/JWT/Builder/JwtBuilder.cs +++ b/src/JWT/Builder/JwtBuilder.cs @@ -2,9 +2,8 @@ using System.Linq; using System.Reflection; using JWT.Algorithms; - +using JWT.Serializers; using static JWT.Internal.EncodingHelper; -using static JWT.Serializers.JsonSerializerFactory; namespace JWT.Builder { @@ -19,7 +18,8 @@ public sealed class JwtBuilder private IJwtDecoder _decoder; private IJwtValidator _validator; - private IJsonSerializer _serializer = CreateSerializer(); + private IJsonSerializerFactory _jsonSerializerFactory = new DefaultJsonSerializerFactory(); + private IBase64UrlEncoder _urlEncoder = new JwtBase64UrlEncoder(); private IDateTimeProvider _dateTimeProvider = new UtcDateTimeProvider(); private ValidationParameters _valParams = ValidationParameters.Default; @@ -75,9 +75,23 @@ public JwtBuilder AddClaim(string name, object value) /// Sets JWT serializer. /// /// Current builder instance - public JwtBuilder WithSerializer(IJsonSerializer serializer) + public JwtBuilder WithJsonSerializer(IJsonSerializer serializer) => + WithJsonSerializerFactory(new DelegateJsonSerializerFactory(serializer)); + + /// + /// Sets JWT serializer. + /// + /// Current builder instance + public JwtBuilder WithJsonSerializer(Func factory) => + WithJsonSerializerFactory(new DelegateJsonSerializerFactory(factory)); + + /// + /// Sets JWT serializer factory. + /// + /// Current builder instance + public JwtBuilder WithJsonSerializerFactory(IJsonSerializerFactory jsonSerializerFactory) { - _serializer = serializer; + _jsonSerializerFactory = jsonSerializerFactory; return this; } @@ -321,42 +335,46 @@ private void TryCreateEncoder() { if (_algorithm is null && _algFactory is null) throw new InvalidOperationException($"Can't instantiate {nameof(JwtEncoder)}. Call {nameof(WithAlgorithm)}."); - if (_serializer is null) - throw new InvalidOperationException($"Can't instantiate {nameof(JwtEncoder)}. Call {nameof(WithSerializer)}"); + + var jsonSerializer = _jsonSerializerFactory.Create(); + if (jsonSerializer is null) + throw new InvalidOperationException($"Can't instantiate {nameof(JwtEncoder)}. Call {nameof(WithJsonSerializer)}"); if (_urlEncoder is null) throw new InvalidOperationException($"Can't instantiate {nameof(JwtEncoder)}. Call {nameof(WithUrlEncoder)}."); if (_algorithm is object) - _encoder = new JwtEncoder(_algorithm, _serializer, _urlEncoder); + _encoder = new JwtEncoder(_algorithm, jsonSerializer, _urlEncoder); else if (_algFactory is object) - _encoder = new JwtEncoder(_algFactory, _serializer, _urlEncoder); + _encoder = new JwtEncoder(_algFactory, jsonSerializer, _urlEncoder); } private void TryCreateDecoder() { TryCreateValidator(); - if (_serializer is null) - throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithSerializer)}."); + var jsonSerializer = _jsonSerializerFactory.Create(); + if (jsonSerializer is null) + throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithJsonSerializer)}."); if (_urlEncoder is null) throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithUrlEncoder)}."); if (_algorithm is object) - _decoder = new JwtDecoder(_serializer, _validator, _urlEncoder, _algorithm); + _decoder = new JwtDecoder(jsonSerializer, _validator, _urlEncoder, _algorithm); else if (_algFactory is object) - _decoder = new JwtDecoder(_serializer, _validator, _urlEncoder, _algFactory); + _decoder = new JwtDecoder(jsonSerializer, _validator, _urlEncoder, _algFactory); else if (!_valParams.ValidateSignature) - _decoder = new JwtDecoder(_serializer, _urlEncoder); + _decoder = new JwtDecoder(jsonSerializer, _urlEncoder); } private void TryCreateDecoderForHeader() { - if (_serializer is null) - throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithSerializer)}."); + var jsonSerializer = _jsonSerializerFactory.Create(); + if (jsonSerializer is null) + throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithJsonSerializer)}."); if (_urlEncoder is null) throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithUrlEncoder)}."); - _decoder = new JwtDecoder(_serializer, _urlEncoder); + _decoder = new JwtDecoder(jsonSerializer, _urlEncoder); } private void TryCreateValidator() @@ -364,12 +382,13 @@ private void TryCreateValidator() if (_validator is object) return; - if (_serializer is null) - throw new InvalidOperationException($"Can't instantiate {nameof(JwtValidator)}. Call {nameof(WithSerializer)}."); + var jsonSerializer = _jsonSerializerFactory.Create(); + if (jsonSerializer is null) + throw new InvalidOperationException($"Can't instantiate {nameof(JwtValidator)}. Call {nameof(WithJsonSerializer)}."); if (_dateTimeProvider is null) throw new InvalidOperationException($"Can't instantiate {nameof(JwtValidator)}. Call {nameof(WithDateTimeProvider)}."); - _validator = new JwtValidator(_serializer, _dateTimeProvider, _valParams); + _validator = new JwtValidator(jsonSerializer, _dateTimeProvider, _valParams); } private void EnsureCanEncode() @@ -382,7 +401,7 @@ private void EnsureCanEncode() throw new InvalidOperationException( "Can't encode a token. Check if you have called all of the following methods:" + Environment.NewLine + $"-{nameof(WithAlgorithm)}" + Environment.NewLine + - $"-{nameof(WithSerializer)}" + Environment.NewLine + + $"-{nameof(WithJsonSerializer)}" + Environment.NewLine + $"-{nameof(WithUrlEncoder)}."); } } @@ -396,7 +415,7 @@ private void EnsureCanDecode() { throw new InvalidOperationException( "Can't decode a token. Check if you have called all of the following methods:" + Environment.NewLine + - $"-{nameof(WithSerializer)}" + Environment.NewLine + + $"-{nameof(WithJsonSerializer)}" + Environment.NewLine + $"-{nameof(WithValidator)}" + Environment.NewLine + $"-{nameof(WithUrlEncoder)}."); } @@ -411,7 +430,7 @@ private void EnsureCanDecodeHeader() { throw new InvalidOperationException( "Can't decode a token header. Check if you have called all of the following methods:" + Environment.NewLine + - $"-{nameof(WithSerializer)}" + Environment.NewLine + + $"-{nameof(WithJsonSerializer)}" + Environment.NewLine + $"-{nameof(WithUrlEncoder)}."); } } @@ -421,7 +440,7 @@ private void EnsureCanDecodeHeader() /// private bool CanEncode() => (_algorithm is object || _algFactory is object) && - _serializer is object && + _jsonSerializerFactory is object && _urlEncoder is object && _jwt.Payload is object; @@ -444,7 +463,7 @@ private bool CanDecodeHeader() if (_urlEncoder is null) return false; - if (_serializer is null) + if (_jsonSerializerFactory is null) return false; return true; diff --git a/src/JWT/Builder/JwtHeader.cs b/src/JWT/Builder/JwtHeader.cs index 8614fd499..08ad1ba72 100644 --- a/src/JWT/Builder/JwtHeader.cs +++ b/src/JWT/Builder/JwtHeader.cs @@ -1,6 +1,6 @@ -#if SYSTEM_TEXT_JSON +#if MODERN_DOTNET using JsonProperty = System.Text.Json.Serialization.JsonPropertyNameAttribute; -#elif NEWTONSOFT_JSON +#else using Newtonsoft.Json; #endif diff --git a/src/JWT/JWT.csproj b/src/JWT/JWT.csproj index 3670d5ca0..7d891e154 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.0-beta6 + 10.0.0-beta7 10.0.0.0 10.0.0.0 JWT @@ -38,8 +38,8 @@ - - + + diff --git a/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs b/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs index 760624758..7fdbf2236 100644 --- a/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs +++ b/src/JWT/Serializers/Converters/DictionaryStringObjectJsonConverter.cs @@ -1,4 +1,4 @@ -#if SYSTEM_TEXT_JSON +#if MODERN_DOTNET using System; using System.Collections.Generic; using System.Linq; diff --git a/src/JWT/Serializers/DefaultJsonSerializerFactory.cs b/src/JWT/Serializers/DefaultJsonSerializerFactory.cs new file mode 100644 index 000000000..86cc3e4d0 --- /dev/null +++ b/src/JWT/Serializers/DefaultJsonSerializerFactory.cs @@ -0,0 +1,19 @@ +namespace JWT.Serializers +{ + public sealed class DefaultJsonSerializerFactory : IJsonSerializerFactory + { + private readonly IJsonSerializer _jsonSerializer; + + public DefaultJsonSerializerFactory() + { +#if MODERN_DOTNET + _jsonSerializer = new SystemTextSerializer(); +#else + _jsonSerializer = new JsonNetSerializer(); +#endif + } + + public IJsonSerializer Create() => + _jsonSerializer; + } +} diff --git a/src/JWT/Serializers/DelegateJsonSerializerFactory.cs b/src/JWT/Serializers/DelegateJsonSerializerFactory.cs new file mode 100644 index 000000000..754376ee6 --- /dev/null +++ b/src/JWT/Serializers/DelegateJsonSerializerFactory.cs @@ -0,0 +1,29 @@ +using System; + +namespace JWT.Serializers +{ + internal sealed class DelegateJsonSerializerFactory : IJsonSerializerFactory + { + private readonly Func _factory; + + public DelegateJsonSerializerFactory(IJsonSerializer jsonSerializer) : + this(() => jsonSerializer) + { + if (jsonSerializer is null) + throw new ArgumentNullException(nameof(jsonSerializer)); + } + + public DelegateJsonSerializerFactory(IJsonSerializerFactory factory) : + this(() => factory.Create()) + { + if (factory is null) + throw new ArgumentNullException(nameof(factory)); + } + + public DelegateJsonSerializerFactory(Func factory) => + _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + + public IJsonSerializer Create() => + _factory(); + } +} diff --git a/src/JWT/Serializers/IJsonSerializerFactory.cs b/src/JWT/Serializers/IJsonSerializerFactory.cs new file mode 100644 index 000000000..e245e8c89 --- /dev/null +++ b/src/JWT/Serializers/IJsonSerializerFactory.cs @@ -0,0 +1,7 @@ +namespace JWT.Serializers +{ + public interface IJsonSerializerFactory + { + IJsonSerializer Create(); + } +} diff --git a/src/JWT/Serializers/JsonNetSerializer.cs b/src/JWT/Serializers/JsonNetSerializer.cs index 4b7864a07..4dbb02115 100644 --- a/src/JWT/Serializers/JsonNetSerializer.cs +++ b/src/JWT/Serializers/JsonNetSerializer.cs @@ -1,5 +1,4 @@ -#if NEWTONSOFT_JSON -using System; +using System; using System.IO; using System.Text; using Newtonsoft.Json; @@ -61,4 +60,3 @@ public object Deserialize(Type type, string json) } } } -#endif diff --git a/src/JWT/Serializers/JsonSerializerFactory.cs b/src/JWT/Serializers/JsonSerializerFactory.cs deleted file mode 100644 index 884861603..000000000 --- a/src/JWT/Serializers/JsonSerializerFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace JWT.Serializers -{ - internal static class JsonSerializerFactory - { - public static IJsonSerializer CreateSerializer() - { -#if SYSTEM_TEXT_JSON - return new SystemTextSerializer(); -#elif NEWTONSOFT_JSON - return new JsonNetSerializer(); -#else - throw new System.NotSupportedException(); -#endif - } - } -} \ No newline at end of file diff --git a/src/JWT/Serializers/SystemTextSerializer.cs b/src/JWT/Serializers/SystemTextSerializer.cs index c66c4d16b..16f9dde0c 100644 --- a/src/JWT/Serializers/SystemTextSerializer.cs +++ b/src/JWT/Serializers/SystemTextSerializer.cs @@ -1,4 +1,4 @@ -#if SYSTEM_TEXT_JSON +#if MODERN_DOTNET using System; using System.Text.Json; using JWT.Serializers.Converters; diff --git a/tests/JWT.Extensions.AspNetCore.Tests/JwtAuthenticationHandlerTests.cs b/tests/JWT.Extensions.AspNetCore.Tests/JwtAuthenticationHandlerTests.cs index 6cb450e54..db4ab3134 100644 --- a/tests/JWT.Extensions.AspNetCore.Tests/JwtAuthenticationHandlerTests.cs +++ b/tests/JWT.Extensions.AspNetCore.Tests/JwtAuthenticationHandlerTests.cs @@ -4,6 +4,7 @@ using AutoFixture; using FluentAssertions; using JWT.Extensions.AspNetCore.Factories; +using JWT.Serializers; using JWT.Tests.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; @@ -13,8 +14,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using static JWT.Serializers.JsonSerializerFactory; - namespace JWT.Extensions.AspNetCore.Tests { [TestClass] @@ -143,5 +142,8 @@ private static async Task CreateHandler(string header) await handler.InitializeAsync(scheme, context); return handler; } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); } } diff --git a/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs b/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs index 267896f6d..904144b3d 100644 --- a/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs +++ b/tests/JWT.Tests.Common/Builder/JwtBuilderDecodeTests.cs @@ -188,11 +188,11 @@ public void Decode_Without_Serializer_Should_Throw_Exception() { Action action = () => JwtBuilder.Create() - .WithSerializer(null) + .WithJsonSerializer((IJsonSerializer)null) .Decode(TestData.Token); action.Should() - .Throw("because token can't be decoded without valid serializer"); + .Throw(); } [TestMethod] @@ -391,13 +391,13 @@ public void Decode_ToDictionary_Without_Serializer_Should_Throw_Exception() Action action = () => JwtBuilder.Create() .WithAlgorithm(TestData.RS256Algorithm) - .WithSerializer(null) + .WithJsonSerializer((IJsonSerializer)null) .WithSecret(TestData.Secret) .MustVerifySignature() .Decode>(TestData.Token); action.Should() - .Throw("because token can't be decoded without valid serializer"); + .Throw(); } } } diff --git a/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs b/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs index a3f3d72c6..101776d73 100644 --- a/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs +++ b/tests/JWT.Tests.Common/Builder/JwtBuilderEncodeTests.cs @@ -7,6 +7,7 @@ using FluentAssertions; using JWT.Algorithms; using JWT.Builder; +using JWT.Serializers; using JWT.Tests.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -268,9 +269,24 @@ public void Encode_Should_Return_ThrowTargetException_Encode_TypesMatch() .AddClaim("ExtraClaim", new { NestedProperty1 = "Foo", NestedProperty2 = 3 }) .Encode(typeof(string), TestData.Customer); - action.Should() - .Throw("Object does not match target type."); + if (IsRunningOnMono()) + { + action.Should() + .Throw("Exception has been thrown by the target of an invocation."); + } + else + { + action.Should() + .Throw("Object does not match target type."); + } } + + /// + /// Copied from: https://stackoverflow.com/a/7077620/2890855 + /// + /// + private static bool IsRunningOnMono() => + Type.GetType("Mono.Runtime") is not null; [TestMethod] public void Encode_Should_Return_Token_With_Custom_Extra_Headers_Full_Payload2() @@ -294,7 +310,7 @@ public void Encode_Should_Return_Token_With_Custom_Extra_Headers_Full_Payload2() }); token.Should() - .Be(TestData.TokenWithCustomTypeHeader3AndClaimNested, "because the same data encoded with the same key must result in the same token"); + .Be(TestData.TokenWithCustomTypeHeader3AndClaimNested, "because the same data encoded with the same key must result in the same token"); } [TestMethod] @@ -326,6 +342,24 @@ public void Encode_With_Custom_Factory_Return_Token() .NotBeNullOrEmpty("because the token should contains some data"); } + [TestMethod] + public void Encode_With_Secret_Should_Return_Valid_Token_Using_Json_Net() + { + var secret = _fixture.Create(); + + var token = JwtBuilder.Create() + .WithAlgorithm(TestData.HMACSHA256Algorithm) + .WithSecret(secret) + .WithJsonSerializer(new JsonNetSerializer()) + .Encode(); + + token.Should() + .NotBeNullOrEmpty("because the token should contains some data"); + token.Split('.') + .Should() + .HaveCount(3, "because the token should consist of three parts"); + } + private sealed class CustomFactory : IAlgorithmFactory { public IJwtAlgorithm Create(JwtDecoderContext context) => diff --git a/tests/JWT.Tests.Common/DefaultJsonSerializerFactoryTests.cs b/tests/JWT.Tests.Common/DefaultJsonSerializerFactoryTests.cs new file mode 100644 index 000000000..a7952406e --- /dev/null +++ b/tests/JWT.Tests.Common/DefaultJsonSerializerFactoryTests.cs @@ -0,0 +1,44 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.Versioning; +using JWT.Serializers; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace JWT.Tests +{ + [TestClass] + public class DefaultJsonSerializerFactoryTests + { + [TestMethod] + public void Create_Should_Return_Correct_Serializer_Per_Runtime_Version() + { + var serializer = CreateSerializer(); + var defaultSerializerClass = serializer.GetType().Name; + var dotNetVersion = GetDotNetVersion(); + + if (String.Equals(dotNetVersion, ".NETFramework,Version=v4.6.2", StringComparison.OrdinalIgnoreCase) || dotNetVersion.StartsWith(".NETCoreApp")) + { + Assert.AreEqual("SystemTextSerializer", defaultSerializerClass); + } + else if (String.Equals(dotNetVersion, ".NETFramework,Version=v4.6.1", StringComparison.OrdinalIgnoreCase)) + { + Assert.AreEqual("JsonNetSerializer", defaultSerializerClass); + } + else + { + Assert.Fail($"Unrecognized dotnet version {dotNetVersion}"); + } + } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); + + private string GetDotNetVersion() => + Assembly.GetExecutingAssembly() + .GetCustomAttributes(typeof(TargetFrameworkAttribute), false) + .Cast() + .Single() + ?.FrameworkName; + } +} diff --git a/tests/JWT.Tests.Common/JwtDecoderTests.cs b/tests/JWT.Tests.Common/JwtDecoderTests.cs index bd7871a15..c7aec970a 100644 --- a/tests/JWT.Tests.Common/JwtDecoderTests.cs +++ b/tests/JWT.Tests.Common/JwtDecoderTests.cs @@ -5,12 +5,11 @@ using JWT.Algorithms; using JWT.Builder; using JWT.Exceptions; +using JWT.Serializers; using JWT.Tests.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; -using static JWT.Serializers.JsonSerializerFactory; - namespace JWT.Tests { [TestClass] @@ -538,5 +537,8 @@ public void DecodeToObject_Should_Throw_Exception_On_Null_NotBefore_Claim() .Throw() .WithMessage("Claim 'nbf' must be a number.", "because the invalid 'nbf' must result in an exception on decoding"); } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); } -} \ No newline at end of file +} diff --git a/tests/JWT.Tests.Common/JwtEncoderTests.cs b/tests/JWT.Tests.Common/JwtEncoderTests.cs index 640770624..8f67455ee 100644 --- a/tests/JWT.Tests.Common/JwtEncoderTests.cs +++ b/tests/JWT.Tests.Common/JwtEncoderTests.cs @@ -1,12 +1,10 @@ -using System; using System.Collections.Generic; using FluentAssertions; using JWT.Algorithms; +using JWT.Serializers; using JWT.Tests.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static JWT.Serializers.JsonSerializerFactory; - namespace JWT.Tests { [TestClass] @@ -91,5 +89,26 @@ public void Encode_With_NoAlgorithm_Should_Encode_To_Token() actual.Should() .Be(expected, "because the same data encoded with the same key must result in the same token"); } + + [TestMethod] + public void Encode_Should_Encode_To_Token_Using_Json_Net() + { + const string key = TestData.Secret; + var toEncode = TestData.Customer; + const string expected = TestData.Token; + + var algorithm = new HMACSHA256Algorithm(); + var urlEncoder = new JwtBase64UrlEncoder(); + var serializer = new JsonNetSerializer(); + var encoder = new JwtEncoder(algorithm, serializer, urlEncoder); + + var actual = encoder.Encode(toEncode, key); + + actual.Should() + .Be(expected, "because the same data encoded with the same key must result in the same token"); + } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); } -} \ No newline at end of file +} diff --git a/tests/JWT.Tests.Common/JwtSecurityTests.cs b/tests/JWT.Tests.Common/JwtSecurityTests.cs index 2f8e04a6a..84111d651 100644 --- a/tests/JWT.Tests.Common/JwtSecurityTests.cs +++ b/tests/JWT.Tests.Common/JwtSecurityTests.cs @@ -2,11 +2,10 @@ using AutoFixture; using FluentAssertions; using JWT.Algorithms; +using JWT.Serializers; using JWT.Tests.Models; using Microsoft.VisualStudio.TestTools.UnitTesting; -using static JWT.Serializers.JsonSerializerFactory; - namespace JWT.Tests { [TestClass] @@ -96,5 +95,8 @@ public void Decode_Should_Throw_Exception_When_Jwt_Contains_HMA_Algorithm_But_RS action.Should() .Throw("because an encryption algorithm can't be changed on decoding"); } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); } -} \ No newline at end of file +} diff --git a/tests/JWT.Tests.Common/JwtValidatorTests.cs b/tests/JWT.Tests.Common/JwtValidatorTests.cs index 0c326ec47..515c3907f 100644 --- a/tests/JWT.Tests.Common/JwtValidatorTests.cs +++ b/tests/JWT.Tests.Common/JwtValidatorTests.cs @@ -2,12 +2,12 @@ using FluentAssertions; using JWT.Algorithms; using JWT.Exceptions; +using JWT.Serializers; using JWT.Tests.Models; using JWT.Tests.Stubs; using Microsoft.VisualStudio.TestTools.UnitTesting; using static JWT.Internal.EncodingHelper; -using static JWT.Serializers.JsonSerializerFactory; namespace JWT.Tests { @@ -378,5 +378,8 @@ public void TryValidate_Should_Return_True_And_Exception_Null_When_Token_Is_Not_ .BeTrue("because token should be valid"); } + + private static IJsonSerializer CreateSerializer() => + new DefaultJsonSerializerFactory().Create(); } } diff --git a/tests/JWT.Tests.Net40/JWT.Tests.Net40.csproj b/tests/JWT.Tests.Net40/JWT.Tests.Net40.csproj index 805adb97f..9e6bffc48 100644 --- a/tests/JWT.Tests.Net40/JWT.Tests.Net40.csproj +++ b/tests/JWT.Tests.Net40/JWT.Tests.Net40.csproj @@ -1,7 +1,7 @@  - net462 + net461