From a1a97a60bc4567bc791d6b7b765d7daa8fc7e76c Mon Sep 17 00:00:00 2001 From: David De Smet <2607383+daviddesmet@users.noreply.github.com> Date: Mon, 23 May 2022 20:20:58 -0500 Subject: [PATCH] Added audience validation test --- src/Paseto/Validators/EqualValidator.cs | 2 +- .../Validators/ExpirationTimeValidator.cs | 2 +- src/Paseto/Validators/IssuedAtValidator.cs | 2 +- src/Paseto/Validators/NotBeforeValidator.cs | 2 +- tests/Paseto.Tests/PasetoBuilderTests.cs | 1 - tests/Paseto.Tests/PasetoValidationTest.cs | 333 +++++++++++------- 6 files changed, 200 insertions(+), 142 deletions(-) diff --git a/src/Paseto/Validators/EqualValidator.cs b/src/Paseto/Validators/EqualValidator.cs index c204da6..3d868d5 100644 --- a/src/Paseto/Validators/EqualValidator.cs +++ b/src/Paseto/Validators/EqualValidator.cs @@ -6,7 +6,7 @@ /// /// The Equality Validator. This class cannot be inherited. /// -/// +/// public sealed class EqualValidator : BaseValidator { /// diff --git a/src/Paseto/Validators/ExpirationTimeValidator.cs b/src/Paseto/Validators/ExpirationTimeValidator.cs index 5412864..2a6cec7 100644 --- a/src/Paseto/Validators/ExpirationTimeValidator.cs +++ b/src/Paseto/Validators/ExpirationTimeValidator.cs @@ -6,7 +6,7 @@ /// /// The ExpirationTime Validator. This class cannot be inherited. /// -/// +/// public sealed class ExpirationTimeValidator : BaseValidator { /// diff --git a/src/Paseto/Validators/IssuedAtValidator.cs b/src/Paseto/Validators/IssuedAtValidator.cs index 0631b5c..0ad0c01 100644 --- a/src/Paseto/Validators/IssuedAtValidator.cs +++ b/src/Paseto/Validators/IssuedAtValidator.cs @@ -6,7 +6,7 @@ /// /// The NotAfter Validator. This class cannot be inherited. /// -/// +/// public sealed class IssuedAtValidator : BaseValidator { /// diff --git a/src/Paseto/Validators/NotBeforeValidator.cs b/src/Paseto/Validators/NotBeforeValidator.cs index 407daf1..9be2dcc 100644 --- a/src/Paseto/Validators/NotBeforeValidator.cs +++ b/src/Paseto/Validators/NotBeforeValidator.cs @@ -6,7 +6,7 @@ /// /// The NotBefore Validator. This class cannot be inherited. /// -/// +/// public sealed class NotBeforeValidator : BaseValidator { /// diff --git a/tests/Paseto.Tests/PasetoBuilderTests.cs b/tests/Paseto.Tests/PasetoBuilderTests.cs index 67abe9a..d22f27e 100644 --- a/tests/Paseto.Tests/PasetoBuilderTests.cs +++ b/tests/Paseto.Tests/PasetoBuilderTests.cs @@ -483,7 +483,6 @@ public void ShouldFailOnLocalDecodeWhenTokenFooterIsInvalid(ProtocolVersion vers } // TODO: Public Decode fails tests, include invalid header v1.remote. - // TODO: Decode with payload validation (success and fails) [Theory(DisplayName = "Should fail on Local Decode when Token's Footer is not valid")] [InlineData(ProtocolVersion.V1, "v1.local.4VyfcVcFAOAbB8yEM1j1Ob7Iez5VZJy5kHNsQxmlrAwKUbOtq9cv39T2fC0MDWafX0nQJ4grFZzTdroMvU772RW-X1oTtoFBjsl_3YYHWnwgqzs0aFc3ejjORmKP4KUM339W3szA28OabR192eRqiyspQ6xPM35NMR-04-FhRJZEWiF0W5oWjPVtGPjeVjm2DI4YtJg.eyJraWQiOiJVYmtLOFk2aXY0R1poRnA2VHgzSVdMV0xmTlhTRXZKY2RUM3pkUjY1WVp4byJ9", "{\"kid\":\"UbkK8Y6iv4GZhFp6Tx3IWLWLfNXSEvJcdT3zdR65YZxo\"}")] diff --git a/tests/Paseto.Tests/PasetoValidationTest.cs b/tests/Paseto.Tests/PasetoValidationTest.cs index bf5ecce..e23f49e 100644 --- a/tests/Paseto.Tests/PasetoValidationTest.cs +++ b/tests/Paseto.Tests/PasetoValidationTest.cs @@ -1,138 +1,197 @@ -using System; -using System.ComponentModel; -using System.Linq; -using FluentAssertions; -using Paseto.Builder; -using Paseto.Cryptography.Key; -using Xunit; - -namespace Paseto.Tests -{ - public sealed class PasetoValidationTest - { - [Theory(DisplayName = "Should succeed on token with valid issuer")] - [InlineData(ProtocolVersion.V3, Purpose.Local)] - [InlineData(ProtocolVersion.V3, Purpose.Public)] - [InlineData(ProtocolVersion.V4, Purpose.Local)] - [InlineData(ProtocolVersion.V4, Purpose.Public)] - public void TokenWithValidIssuerValidationSucceeds(ProtocolVersion version, Purpose purpose) - { - var validationParameters = new PasetoTokenValidationParameters() - { - ValidateIssuer = true, - ValidIssuer = "valid-issuer", - }; - - var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Issuer, "valid-issuer"); - var decoded = new PasetoBuilder() - .Use(version, purpose) - .WithKey(decodeKey) - .Decode(token, validationParameters); - - decoded.IsValid.Should().BeTrue(); - } - - [Theory(DisplayName = "Should fail on token with invalid issuer")] - [InlineData(ProtocolVersion.V3, Purpose.Local)] - [InlineData(ProtocolVersion.V3, Purpose.Public)] - [InlineData(ProtocolVersion.V4, Purpose.Local)] - [InlineData(ProtocolVersion.V4, Purpose.Public)] - public void TokenWithInValidIssuerValidationFails(ProtocolVersion version, Purpose purpose) - { - var validationParameters = new PasetoTokenValidationParameters() - { - ValidateIssuer = true, - ValidIssuer = "valid-issuer", - }; - - var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Issuer, "invalid-issuer"); - var decoded = new PasetoBuilder() - .Use(version, purpose) - .WithKey(decodeKey) - .Decode(token, validationParameters); - - decoded.IsValid.Should().BeFalse(); - } - - [Theory(DisplayName = "Should succeed on token with valid subject")] - [InlineData(ProtocolVersion.V3, Purpose.Local)] - [InlineData(ProtocolVersion.V3, Purpose.Public)] - [InlineData(ProtocolVersion.V4, Purpose.Local)] - [InlineData(ProtocolVersion.V4, Purpose.Public)] - public void TokenWithValidSubjectValidationSucceeds(ProtocolVersion version, Purpose purpose) - { - var validationParameters = new PasetoTokenValidationParameters() - { - ValidateSubject = true, - ValidSubject = "valid-subject", - }; - - var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Subject, "valid-subject"); - var decoded = new PasetoBuilder() - .Use(version, purpose) - .WithKey(decodeKey) - .Decode(token, validationParameters); - - decoded.IsValid.Should().BeTrue(); - } - - [Theory(DisplayName = "Should fail on token with invalid subject")] - [InlineData(ProtocolVersion.V3, Purpose.Local)] - [InlineData(ProtocolVersion.V3, Purpose.Public)] - [InlineData(ProtocolVersion.V4, Purpose.Local)] - [InlineData(ProtocolVersion.V4, Purpose.Public)] - public void TokenWithInValidSubjectValidationFails(ProtocolVersion version, Purpose purpose) - { - var validationParameters = new PasetoTokenValidationParameters() - { - ValidateSubject = true, - ValidSubject = "valid-subject", - }; - - var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Subject, "invalid-subject"); - var decoded = new PasetoBuilder() - .Use(version, purpose) - .WithKey(decodeKey) - .Decode(token, validationParameters); - - decoded.IsValid.Should().BeFalse(); - } - - private static (string token, PasetoKey decodeKey) GenerateToken(ProtocolVersion version, Purpose purpose, string claimName, string claimValue) - { - var builder = new PasetoBuilder().Use(version, purpose); - switch (claimName) - { - case PasetoRegisteredClaimNames.Issuer: - builder.Issuer(claimValue); - break; - case PasetoRegisteredClaimNames.Subject: - builder.Subject(claimValue); - break; - default: - throw new NotImplementedException(); - } - switch (purpose) - { - case Purpose.Local: - { - var key = builder.GenerateSymmetricKey(); - var token = builder - .WithKey(key) - .Encode(); - return (token, key); - } - case Purpose.Public: - { - var keyPair = builder.GenerateAsymmetricKeyPair(Enumerable.Repeat((byte)0x00, 32).ToArray()); - var token = builder - .WithKey(keyPair.SecretKey) - .Encode(); - return (token, keyPair.PublicKey); - } - default: - throw new InvalidEnumArgumentException(); - } - } - } +namespace Paseto.Tests; + +using System; +using System.ComponentModel; +using System.Linq; +using FluentAssertions; +using Paseto.Builder; +using Paseto.Cryptography.Key; +using Xunit; + +public sealed class PasetoValidationTest +{ + [Theory(DisplayName = "Should succeed on token with valid audience")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithValidAudienceValidationSucceeds(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateAudience = true, + ValidAudience = "valid-audience", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Audience, "valid-audience"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeTrue(); + } + + [Theory(DisplayName = "Should fail on token with invalid audience")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithInValidAudienceValidationFails(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateAudience = true, + ValidAudience = "valid-audience", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Audience, "invalid-audience"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeFalse(); + } + + [Theory(DisplayName = "Should succeed on token with valid issuer")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithValidIssuerValidationSucceeds(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateIssuer = true, + ValidIssuer = "valid-issuer", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Issuer, "valid-issuer"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeTrue(); + } + + [Theory(DisplayName = "Should fail on token with invalid issuer")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithInValidIssuerValidationFails(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateIssuer = true, + ValidIssuer = "valid-issuer", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Issuer, "invalid-issuer"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeFalse(); + } + + [Theory(DisplayName = "Should succeed on token with valid subject")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithValidSubjectValidationSucceeds(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateSubject = true, + ValidSubject = "valid-subject", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Subject, "valid-subject"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeTrue(); + } + + [Theory(DisplayName = "Should fail on token with invalid subject")] + [InlineData(ProtocolVersion.V1, Purpose.Local)] + [InlineData(ProtocolVersion.V1, Purpose.Public)] + [InlineData(ProtocolVersion.V2, Purpose.Local)] + [InlineData(ProtocolVersion.V2, Purpose.Public)] + [InlineData(ProtocolVersion.V3, Purpose.Local)] + [InlineData(ProtocolVersion.V3, Purpose.Public)] + [InlineData(ProtocolVersion.V4, Purpose.Local)] + [InlineData(ProtocolVersion.V4, Purpose.Public)] + public void TokenWithInValidSubjectValidationFails(ProtocolVersion version, Purpose purpose) + { + var validationParameters = new PasetoTokenValidationParameters() + { + ValidateSubject = true, + ValidSubject = "valid-subject", + }; + + var (token, decodeKey) = GenerateToken(version, purpose, PasetoRegisteredClaimNames.Subject, "invalid-subject"); + var decoded = new PasetoBuilder() + .Use(version, purpose) + .WithKey(decodeKey) + .Decode(token, validationParameters); + + decoded.IsValid.Should().BeFalse(); + } + + private static (string token, PasetoKey decodeKey) GenerateToken(ProtocolVersion version, Purpose purpose, string claimName, string claimValue) + { + var builder = new PasetoBuilder() + .Use(version, purpose) + .AddClaim(claimName, claimValue); + + switch (purpose) + { + case Purpose.Local: + { + var key = builder.GenerateSymmetricKey(); + var token = builder + .WithKey(key) + .Encode(); + return (token, key); + } + case Purpose.Public: + { + var keyPair = builder.GenerateAsymmetricKeyPair(Enumerable.Repeat((byte)0x00, 32).ToArray()); + var token = builder + .WithKey(keyPair.SecretKey) + .Encode(); + return (token, keyPair.PublicKey); + } + default: + throw new InvalidEnumArgumentException(); + } + } } \ No newline at end of file