diff --git a/src/JsonWebToken/JwtReader.cs b/src/JsonWebToken/JwtReader.cs index f81b914d8..ced7aabd2 100644 --- a/src/JsonWebToken/JwtReader.cs +++ b/src/JsonWebToken/JwtReader.cs @@ -346,7 +346,7 @@ private TokenValidationResult TryReadJwe( } else { - if (decryptionResult.Status == TokenValidationStatus.MalformedToken) + if (decryptionResult.Status == TokenValidationStatus.MalformedToken && !policy.HasValidation) { // The decrypted payload is not a nested JWT jwe = new Jwt(header, compressed ? decompressedBytes.ToArray() : decryptedBytes.ToArray(), decryptionKey); diff --git a/src/JsonWebToken/SignatureValidationPolicy.cs b/src/JsonWebToken/SignatureValidationPolicy.cs index cd01f70ec..ba9046093 100644 --- a/src/JsonWebToken/SignatureValidationPolicy.cs +++ b/src/JsonWebToken/SignatureValidationPolicy.cs @@ -22,6 +22,11 @@ public abstract class SignatureValidationPolicy /// Allows to ignore the signature, whatever ther is an algorithm defined or not. /// public static readonly SignatureValidationPolicy IgnoreSignature = new IgnoreSignatureValidationContext(); + + /// + /// Gets whether the signature validation is enabled. + /// + public abstract bool IsEnabled { get; } /// /// Try to validate the token signature. @@ -58,6 +63,9 @@ public DefaultSignatureValidationPolicy(IKeyProvider keyProvider, SignatureAlgor _algorithm = algorithm; } + /// + public override bool IsEnabled => true; + public override SignatureValidationResult TryValidateSignature(JwtHeader header, ReadOnlySpan contentBytes, ReadOnlySpan signatureSegment) { if (contentBytes.IsEmpty && signatureSegment.IsEmpty) @@ -122,6 +130,9 @@ public override SignatureValidationResult TryValidateSignature(JwtHeader header, private sealed class NoSignatureValidationContext : SignatureValidationPolicy { + /// + public override bool IsEnabled => true; + public override SignatureValidationResult TryValidateSignature(JwtHeader header, ReadOnlySpan contentBytes, ReadOnlySpan signatureSegment) { return (contentBytes.Length == 0 && signatureSegment.Length == 0) || (signatureSegment.IsEmpty && header.SignatureAlgorithm == SignatureAlgorithm.None) @@ -132,6 +143,9 @@ public override SignatureValidationResult TryValidateSignature(JwtHeader header, private sealed class IgnoreSignatureValidationContext : SignatureValidationPolicy { + /// + public override bool IsEnabled => false; + public override SignatureValidationResult TryValidateSignature(JwtHeader header, ReadOnlySpan contentBytes, ReadOnlySpan signatureSegment) { return SignatureValidationResult.Success(); diff --git a/src/JsonWebToken/TokenValidationPolicy.cs b/src/JsonWebToken/TokenValidationPolicy.cs index eab4ea2d3..6fa7ec095 100644 --- a/src/JsonWebToken/TokenValidationPolicy.cs +++ b/src/JsonWebToken/TokenValidationPolicy.cs @@ -80,7 +80,7 @@ internal TokenValidationPolicy( /// /// Gets whether the has validation. /// - public bool HasValidation => _validators.Length != 0; + public bool HasValidation => _control != 0 || _validators.Length != 0 || SignatureValidationPolicy.IsEnabled; /// /// Gets whether the issuer 'iss' is required. diff --git a/src/JsonWebToken/TokenValidationPolicyBuilder.cs b/src/JsonWebToken/TokenValidationPolicyBuilder.cs index 0eb31b8b0..b7a2ae5ae 100644 --- a/src/JsonWebToken/TokenValidationPolicyBuilder.cs +++ b/src/JsonWebToken/TokenValidationPolicyBuilder.cs @@ -171,7 +171,7 @@ public TokenValidationPolicyBuilder RequireSignature(Jwk key) throw new InvalidOperationException($"The key does not define an 'alg' parameter. Use the method {nameof(RequireSignature)} with a {nameof(Jwk)} and a {nameof(SignatureAlgorithm)}."); } - return RequireSignature(key, null); + return RequireSignature(key, (SignatureAlgorithm?)null); } /// @@ -180,7 +180,29 @@ public TokenValidationPolicyBuilder RequireSignature(Jwk key) /// /// /// - public TokenValidationPolicyBuilder RequireSignature(Jwk key, SignatureAlgorithm? algorithm) => RequireSignature(new Jwks(key), algorithm); + public TokenValidationPolicyBuilder RequireSignature(Jwk key, SignatureAlgorithm? algorithm) + => RequireSignature(new Jwks(key), algorithm); + + /// + /// Requires a valid signature. + /// + /// + /// + /// + public TokenValidationPolicyBuilder RequireSignature(Jwk key, string? algorithm) + { + if (algorithm is null) + { + throw new ArgumentNullException(nameof(algorithm)); + } + + if (!SignatureAlgorithm.TryParse(Utf8.GetBytes(algorithm), out var alg)) + { + throw new NotSupportedException($"The algorithm '{alg}' is not supported."); + } + + return RequireSignature(new Jwks(key), alg); + } /// /// Requires a valid signature. @@ -210,7 +232,8 @@ public TokenValidationPolicyBuilder RequireSignature(Jwk key) /// /// /// - public TokenValidationPolicyBuilder RequireSignature(Jwks keySet, SignatureAlgorithm? algorithm) => RequireSignature(new StaticKeyProvider(keySet), algorithm); + public TokenValidationPolicyBuilder RequireSignature(Jwks keySet, SignatureAlgorithm? algorithm) + => RequireSignature(new StaticKeyProvider(keySet), algorithm); /// /// Requires a valid signature. diff --git a/test/JsonWebToken.Tests/JsonWebTokenReaderTests.cs b/test/JsonWebToken.Tests/JsonWebTokenReaderTests.cs index b5e450057..93f2887a9 100644 --- a/test/JsonWebToken.Tests/JsonWebTokenReaderTests.cs +++ b/test/JsonWebToken.Tests/JsonWebTokenReaderTests.cs @@ -191,6 +191,49 @@ public void ReadJwt_CriticalHeader(string jwt, TokenValidationStatus expected) Assert.Equal(expected, result.Status); } + [Theory] + [InlineData("eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.4VETXwjtEQIHzctz2FTAef8iHvk8ShfMJrRvDNVISdUh9Zju4tl75w.o0IVPs65CR8B0b6fxH3mow.p8DIesdqyemto-EKiHSA19jiobfS6sR4kfe4PGEyruI.VtIn9WFytiZNjP7wXBeNNg")] + public void Issue504_Valid(string jwt) + { + var reader = new JwtReader(new SymmetricJwk("R9MyWaEoyiMYViVWo8Fk4T")); + var policy = new TokenValidationPolicyBuilder() + .IgnoreSignature() + .Build(); + + var result = reader.TryReadToken(jwt, policy); + Assert.Equal(TokenValidationStatus.Success, result.Status); + } + + [Theory] + [InlineData("eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.4VETXwjtEQIHzctz2FTAef8iHvk8ShfMJrRvDNVISdUh9Zju4tl75w.o0IVPs65CR8B0b6fxH3mow.p8DIesdqyemto-EKiHSA19jiobfS6sR4kfe4PGEyruI.VtIn9WFytiZNjP7wXBeNNg", true, false, false)] + [InlineData("eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.4VETXwjtEQIHzctz2FTAef8iHvk8ShfMJrRvDNVISdUh9Zju4tl75w.o0IVPs65CR8B0b6fxH3mow.p8DIesdqyemto-EKiHSA19jiobfS6sR4kfe4PGEyruI.VtIn9WFytiZNjP7wXBeNNg", false, true, false)] + [InlineData("eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.4VETXwjtEQIHzctz2FTAef8iHvk8ShfMJrRvDNVISdUh9Zju4tl75w.o0IVPs65CR8B0b6fxH3mow.p8DIesdqyemto-EKiHSA19jiobfS6sR4kfe4PGEyruI.VtIn9WFytiZNjP7wXBeNNg", false, false, true)] + public void Issue504_Invalid(string jwt, bool requireAudience, bool requireSignature, bool requireOther) + { + var reader = new JwtReader(new SymmetricJwk("R9MyWaEoyiMYViVWo8Fk4T")); + var builder = new TokenValidationPolicyBuilder(); + if (requireAudience) + { + builder.RequireAudience("test"); + } + if (requireSignature) + { + builder.RequireSignature(SymmetricJwk.FromBase64Url("R9MyWaEoyiMYViVWo8Fk4TUGWiSoaW6U1nOqXri8_XU"), "HS256"); + } + else + { + builder.IgnoreSignature(); + } + if (requireOther) + { + builder.AddValidator(new FakeValidator()); + } + + var policy = builder.Build(); + var result = reader.TryReadToken(jwt, policy); + Assert.Equal(TokenValidationStatus.MalformedToken, result.Status); + } + [Theory] [InlineData("eyJhbGciOiJub25lIn0.eyJleHAiOjk5MDAwMDAwMDAsIm5iZiI6MTUwMDAwMDAwMH0.")] [InlineData("eyJhbGciOiJub25lIn0.eyJleHAiOjk5MDAwMDAwMDB9.")] @@ -238,7 +281,7 @@ public void Issue489_NoValidation_Valid(string jwt) .Build(); var result = reader.TryReadToken(jwt, policy); - Assert.Equal(TokenValidationStatus.Success , result.Status); + Assert.Equal(TokenValidationStatus.Success, result.Status); } private HttpResponseMessage BackchannelRequestToken(HttpRequestMessage req) @@ -325,4 +368,12 @@ public bool TryHandle(JwtHeader heade, string headerName) return _value; } } + + internal class FakeValidator : IValidator + { + public TokenValidationResult TryValidate(Jwt jwt) + { + return TokenValidationResult.MalformedToken(); + } + } } \ No newline at end of file