diff --git a/docs/validating-tokens.md b/docs/validating-tokens.md index 4fe7f8e4..807f46c7 100644 --- a/docs/validating-tokens.md +++ b/docs/validating-tokens.md @@ -75,6 +75,7 @@ This library provides the following constraints: * `Lcobucci\JWT\Validation\Constraint\SignedWith`: verifies if the token was signed with the expected signer and key * `Lcobucci\JWT\Validation\Constraint\StrictValidAt`: verifies presence and validity of the claims `iat`, `nbf`, and `exp` (supports leeway configuration) * `Lcobucci\JWT\Validation\Constraint\LooseValidAt`: verifies the claims `iat`, `nbf`, and `exp`, when present (supports leeway configuration) +* `Lcobucci\JWT\Validation\Constraint\HasClaimWithValue`: verifies that a custom claim has the expected value (not recommended when comparing cryptographic hashes) Example code for adding a constraint to the configuration object: diff --git a/src/Validation/Constraint/CannotValidateARegisteredClaim.php b/src/Validation/Constraint/CannotValidateARegisteredClaim.php new file mode 100644 index 00000000..4afb7cb7 --- /dev/null +++ b/src/Validation/Constraint/CannotValidateARegisteredClaim.php @@ -0,0 +1,17 @@ +claim = $claim; + $this->expectedValue = $expectedValue; + } + + public function assert(Token $token): void + { + if (! $token instanceof UnencryptedToken) { + throw new ConstraintViolation('You should pass a plain token'); + } + + $claims = $token->claims(); + + if (! $claims->has($this->claim)) { + throw new ConstraintViolation('The token does not have the claim "' . $this->claim . '"'); + } + + if ($claims->get($this->claim) !== $this->expectedValue) { + throw new ConstraintViolation('The claim "' . $this->claim . '" does not have the expected value'); + } + } +} diff --git a/test/unit/Validation/Constraint/HasClaimWithValueTest.php b/test/unit/Validation/Constraint/HasClaimWithValueTest.php new file mode 100644 index 00000000..3e4e9864 --- /dev/null +++ b/test/unit/Validation/Constraint/HasClaimWithValueTest.php @@ -0,0 +1,112 @@ +expectException(CannotValidateARegisteredClaim::class); + $this->expectExceptionMessage( + 'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value' + ); + + new HasClaimWithValue($claim, 'testing'); + } + + /** @return iterable */ + public function registeredClaims(): iterable + { + foreach (Token\RegisteredClaims::ALL as $claim) { + yield $claim => [$claim]; + } + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenClaimIsNotSet(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The token does not have the claim "claimId"'); + + $constraint = new HasClaimWithValue('claimId', 'claimValue'); + $constraint->assert($this->buildToken()); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenClaimValueDoesNotMatch(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('The claim "claimId" does not have the expected value'); + + $constraint = new HasClaimWithValue('claimId', 'claimValue'); + $constraint->assert($this->buildToken(['claimId' => 'Some wrong value'])); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldRaiseExceptionWhenTokenIsNotAPlainToken(): void + { + $this->expectException(ConstraintViolation::class); + $this->expectExceptionMessage('You should pass a plain token'); + + $constraint = new HasClaimWithValue('claimId', 'claimValue'); + $constraint->assert($this->createMock(Token::class)); + } + + /** + * @test + * + * @covers ::__construct + * @covers ::assert + * + * @uses \Lcobucci\JWT\Token\DataSet + * @uses \Lcobucci\JWT\Token\Plain + * @uses \Lcobucci\JWT\Token\Signature + */ + public function assertShouldNotRaiseExceptionWhenClaimMatches(): void + { + $token = $this->buildToken(['claimId' => 'claimValue']); + $constraint = new HasClaimWithValue('claimId', 'claimValue'); + + $constraint->assert($token); + $this->addToAssertionCount(1); + } +}