Skip to content

Commit

Permalink
Add constraint for private claim validation
Browse files Browse the repository at this point in the history
  • Loading branch information
James Beamish-White authored and lcobucci committed Aug 17, 2022
1 parent df7c37a commit 817f241
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/validating-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
17 changes: 17 additions & 0 deletions src/Validation/Constraint/CannotValidateARegisteredClaim.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Validation\Constraint;

use InvalidArgumentException;
use Lcobucci\JWT\Exception;

final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
{
public static function create(string $claim): self
{
return new self(
'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value'
);
}
}
47 changes: 47 additions & 0 deletions src/Validation/Constraint/HasClaimWithValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Validation\Constraint;

use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;

use function in_array;

final class HasClaimWithValue implements Constraint
{
private string $claim;

/** @var mixed */
private $expectedValue;

/** @param mixed $expectedValue */
public function __construct(string $claim, $expectedValue)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}

$this->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');
}
}
}
112 changes: 112 additions & 0 deletions test/unit/Validation/Constraint/HasClaimWithValueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Validation\Constraint;

use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;

/** @coversDefaultClass \Lcobucci\JWT\Validation\Constraint\HasClaimWithValue */
final class HasClaimWithValueTest extends ConstraintTestCase
{
/**
* @test
* @dataProvider registeredClaims
*
* @covers ::__construct
* @covers \Lcobucci\JWT\Validation\Constraint\CannotValidateARegisteredClaim
*/
public function registeredClaimsCannotBeValidatedUsingThisConstraint(string $claim): void
{
$this->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<string, array{string}> */
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);
}
}

0 comments on commit 817f241

Please sign in to comment.