Skip to content

Commit

Permalink
Merge pull request #1053 from freebuu/has-claim-constraint
Browse files Browse the repository at this point in the history
HasClaim constraint
  • Loading branch information
lcobucci authored Apr 11, 2024
2 parents 22cf2f4 + ca8d773 commit 08071d8
Show file tree
Hide file tree
Showing 3 changed files with 115 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 @@ -90,5 +90,6 @@ This library provides the following constraints:
* `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)
* `Lcobucci\JWT\Validation\Constraint\HasClaim`: verifies that a **custom claim** is present

You may also create your [own validation constraints](extending-the-library.md#validation-constraints).
35 changes: 35 additions & 0 deletions src/Validation/Constraint/HasClaim.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?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 HasClaim implements Constraint
{
/** @param non-empty-string $claim */
public function __construct(private readonly string $claim)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}
}

public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}

$claims = $token->claims();

if (! $claims->has($this->claim)) {
throw ConstraintViolation::error('The token does not have the claim "' . $this->claim . '"', $this);
}
}
}
79 changes: 79 additions & 0 deletions tests/Validation/Constraint/HasClaimTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Tests\Validation\Constraint;

use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint\CannotValidateARegisteredClaim;
use Lcobucci\JWT\Validation\Constraint\HasClaim;
use Lcobucci\JWT\Validation\ConstraintViolation;

/**
* @covers \Lcobucci\JWT\Validation\ConstraintViolation
* @covers \Lcobucci\JWT\Validation\Constraint\HasClaim
*
* @uses \Lcobucci\JWT\Token\DataSet
* @uses \Lcobucci\JWT\Token\Plain
* @uses \Lcobucci\JWT\Token\Signature
*/
final class HasClaimTest extends ConstraintTestCase
{
/**
* @test
* @dataProvider registeredClaims
*
* @covers \Lcobucci\JWT\Validation\Constraint\CannotValidateARegisteredClaim
*
* @param non-empty-string $claim
*/
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 HasClaim($claim);
}

/** @return iterable<non-empty-string, array{non-empty-string}> */
public static function registeredClaims(): iterable
{
foreach (Token\RegisteredClaims::ALL as $claim) {
yield $claim => [$claim];
}
}

/** @test */
public function assertShouldRaiseExceptionWhenClaimIsNotSet(): void
{
$constraint = new HasClaim('claimId');

$this->expectException(ConstraintViolation::class);
$this->expectExceptionMessage('The token does not have the claim "claimId"');

$constraint->assert($this->buildToken());
}

/** @test */
public function assertShouldRaiseExceptionWhenTokenIsNotAPlainToken(): void
{
$token = $this->createMock(Token::class);
$constraint = new HasClaim('claimId');

$this->expectException(ConstraintViolation::class);
$this->expectExceptionMessage('You should pass a plain token');

$constraint->assert($token);
}

/** @test */
public function assertShouldNotRaiseExceptionWhenClaimMatches(): void
{
$token = $this->buildToken(['claimId' => 'claimValue']);
$constraint = new HasClaim('claimId');

$constraint->assert($token);
$this->addToAssertionCount(1);
}
}

0 comments on commit 08071d8

Please sign in to comment.