welcome back to dyb-tech
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
|
||||
interface Constraint
|
||||
{
|
||||
/** @throws ConstraintViolation */
|
||||
public function assert(Token $token): void;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Exception;
|
||||
|
||||
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
|
||||
{
|
||||
/** @param non-empty-string $claim */
|
||||
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',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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
|
||||
{
|
||||
/** @param non-empty-string $claim */
|
||||
public function __construct(private readonly string $claim, private readonly mixed $expectedValue)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if ($claims->get($this->claim) !== $this->expectedValue) {
|
||||
throw ConstraintViolation::error(
|
||||
'The claim "' . $this->claim . '" does not have the expected value',
|
||||
$this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
final class IdentifiedBy implements Constraint
|
||||
{
|
||||
/** @param non-empty-string $id */
|
||||
public function __construct(private readonly string $id)
|
||||
{
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->isIdentifiedBy($this->id)) {
|
||||
throw ConstraintViolation::error(
|
||||
'The token is not identified with the expected ID',
|
||||
$this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
final class IssuedBy implements Constraint
|
||||
{
|
||||
/** @var non-empty-string[] */
|
||||
private readonly array $issuers;
|
||||
|
||||
/** @param non-empty-string ...$issuers */
|
||||
public function __construct(string ...$issuers)
|
||||
{
|
||||
$this->issuers = $issuers;
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
|
||||
throw ConstraintViolation::error(
|
||||
'The token was not issued by the given issuers',
|
||||
$this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Exception;
|
||||
|
||||
final class LeewayCannotBeNegative extends InvalidArgumentException implements Exception
|
||||
{
|
||||
public static function create(): self
|
||||
{
|
||||
return new self('Leeway cannot be negative');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
|
||||
use Psr\Clock\ClockInterface as Clock;
|
||||
|
||||
final class LooseValidAt implements ValidAtInterface
|
||||
{
|
||||
private readonly DateInterval $leeway;
|
||||
|
||||
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
|
||||
{
|
||||
$this->leeway = $this->guardLeeway($leeway);
|
||||
}
|
||||
|
||||
private function guardLeeway(?DateInterval $leeway): DateInterval
|
||||
{
|
||||
if ($leeway === null) {
|
||||
return new DateInterval('PT0S');
|
||||
}
|
||||
|
||||
if ($leeway->invert === 1) {
|
||||
throw LeewayCannotBeNegative::create();
|
||||
}
|
||||
|
||||
return $leeway;
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
$now = $this->clock->now();
|
||||
|
||||
$this->assertIssueTime($token, $now->add($this->leeway));
|
||||
$this->assertMinimumTime($token, $now->add($this->leeway));
|
||||
$this->assertExpiration($token, $now->sub($this->leeway));
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertExpiration(Token $token, DateTimeInterface $now): void
|
||||
{
|
||||
if ($token->isExpired($now)) {
|
||||
throw ConstraintViolation::error('The token is expired', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
|
||||
{
|
||||
if (! $token->isMinimumTimeBefore($now)) {
|
||||
throw ConstraintViolation::error('The token cannot be used yet', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertIssueTime(Token $token, DateTimeInterface $now): void
|
||||
{
|
||||
if (! $token->hasBeenIssuedBefore($now)) {
|
||||
throw ConstraintViolation::error('The token was issued in the future', $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
final class PermittedFor implements Constraint
|
||||
{
|
||||
/** @param non-empty-string $audience */
|
||||
public function __construct(private readonly string $audience)
|
||||
{
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->isPermittedFor($this->audience)) {
|
||||
throw ConstraintViolation::error(
|
||||
'The token is not allowed to be used by this audience',
|
||||
$this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
final class RelatedTo implements Constraint
|
||||
{
|
||||
/** @param non-empty-string $subject */
|
||||
public function __construct(private readonly string $subject)
|
||||
{
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->isRelatedTo($this->subject)) {
|
||||
throw ConstraintViolation::error(
|
||||
'The token is not related to the expected subject',
|
||||
$this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
|
||||
|
||||
final class SignedWith implements SignedWithInterface
|
||||
{
|
||||
public function __construct(private readonly Signer $signer, private readonly Signer\Key $key)
|
||||
{
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token instanceof UnencryptedToken) {
|
||||
throw ConstraintViolation::error('You should pass a plain token', $this);
|
||||
}
|
||||
|
||||
if ($token->headers()->get('alg') !== $this->signer->algorithmId()) {
|
||||
throw ConstraintViolation::error('Token signer mismatch', $this);
|
||||
}
|
||||
|
||||
if (! $this->signer->verify($token->signature()->hash(), $token->payload(), $this->key)) {
|
||||
throw ConstraintViolation::error('Token signature mismatch', $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
final class SignedWithOneInSet implements SignedWithInterface
|
||||
{
|
||||
/** @var array<SignedWithUntilDate> */
|
||||
private readonly array $constraints;
|
||||
|
||||
public function __construct(SignedWithUntilDate ...$constraints)
|
||||
{
|
||||
$this->constraints = $constraints;
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
$errorMessage = 'It was not possible to verify the signature of the token, reasons:';
|
||||
|
||||
foreach ($this->constraints as $constraint) {
|
||||
try {
|
||||
$constraint->assert($token);
|
||||
|
||||
return;
|
||||
} catch (ConstraintViolation $violation) {
|
||||
$errorMessage .= PHP_EOL . '- ' . $violation->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw ConstraintViolation::error($errorMessage, $this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class SignedWithUntilDate implements SignedWithInterface
|
||||
{
|
||||
private readonly SignedWith $verifySignature;
|
||||
private readonly ClockInterface $clock;
|
||||
|
||||
public function __construct(
|
||||
Signer $signer,
|
||||
Signer\Key $key,
|
||||
private readonly DateTimeImmutable $validUntil,
|
||||
?ClockInterface $clock = null,
|
||||
) {
|
||||
$this->verifySignature = new SignedWith($signer, $key);
|
||||
|
||||
$this->clock = $clock ?? new class () implements ClockInterface {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if ($this->validUntil < $this->clock->now()) {
|
||||
throw ConstraintViolation::error(
|
||||
'This constraint was only usable until '
|
||||
. $this->validUntil->format(DateTimeInterface::RFC3339),
|
||||
$this,
|
||||
);
|
||||
}
|
||||
|
||||
$this->verifySignature->assert($token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
|
||||
use Psr\Clock\ClockInterface as Clock;
|
||||
|
||||
final class StrictValidAt implements ValidAtInterface
|
||||
{
|
||||
private readonly DateInterval $leeway;
|
||||
|
||||
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
|
||||
{
|
||||
$this->leeway = $this->guardLeeway($leeway);
|
||||
}
|
||||
|
||||
private function guardLeeway(?DateInterval $leeway): DateInterval
|
||||
{
|
||||
if ($leeway === null) {
|
||||
return new DateInterval('PT0S');
|
||||
}
|
||||
|
||||
if ($leeway->invert === 1) {
|
||||
throw LeewayCannotBeNegative::create();
|
||||
}
|
||||
|
||||
return $leeway;
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token instanceof UnencryptedToken) {
|
||||
throw ConstraintViolation::error('You should pass a plain token', $this);
|
||||
}
|
||||
|
||||
$now = $this->clock->now();
|
||||
|
||||
$this->assertIssueTime($token, $now->add($this->leeway));
|
||||
$this->assertMinimumTime($token, $now->add($this->leeway));
|
||||
$this->assertExpiration($token, $now->sub($this->leeway));
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void
|
||||
{
|
||||
if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) {
|
||||
throw ConstraintViolation::error('"Expiration Time" claim missing', $this);
|
||||
}
|
||||
|
||||
if ($token->isExpired($now)) {
|
||||
throw ConstraintViolation::error('The token is expired', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
|
||||
{
|
||||
if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
|
||||
throw ConstraintViolation::error('"Not Before" claim missing', $this);
|
||||
}
|
||||
|
||||
if (! $token->isMinimumTimeBefore($now)) {
|
||||
throw ConstraintViolation::error('The token cannot be used yet', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws ConstraintViolation */
|
||||
private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
|
||||
{
|
||||
if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
|
||||
throw ConstraintViolation::error('"Issued At" claim missing', $this);
|
||||
}
|
||||
|
||||
if (! $token->hasBeenIssuedBefore($now)) {
|
||||
throw ConstraintViolation::error('The token was issued in the future', $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Exception;
|
||||
use RuntimeException;
|
||||
|
||||
final class ConstraintViolation extends RuntimeException implements Exception
|
||||
{
|
||||
/** @param class-string<Constraint>|null $constraint */
|
||||
public function __construct(
|
||||
string $message = '',
|
||||
public readonly ?string $constraint = null,
|
||||
) {
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
/** @param non-empty-string $message */
|
||||
public static function error(string $message, Constraint $constraint): self
|
||||
{
|
||||
return new self(message: $message, constraint: $constraint::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Exception;
|
||||
use RuntimeException;
|
||||
|
||||
final class NoConstraintsGiven extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Exception;
|
||||
use RuntimeException;
|
||||
|
||||
use function array_map;
|
||||
use function implode;
|
||||
|
||||
final class RequiredConstraintsViolated extends RuntimeException implements Exception
|
||||
{
|
||||
/** @param ConstraintViolation[] $violations */
|
||||
public function __construct(
|
||||
string $message = '',
|
||||
public readonly array $violations = [],
|
||||
) {
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
public static function fromViolations(ConstraintViolation ...$violations): self
|
||||
{
|
||||
return new self(message: self::buildMessage($violations), violations: $violations);
|
||||
}
|
||||
|
||||
/** @param ConstraintViolation[] $violations */
|
||||
private static function buildMessage(array $violations): string
|
||||
{
|
||||
$violations = array_map(
|
||||
static function (ConstraintViolation $violation): string {
|
||||
return '- ' . $violation->getMessage();
|
||||
},
|
||||
$violations,
|
||||
);
|
||||
|
||||
$message = "The token violates some mandatory constraints, details:\n";
|
||||
$message .= implode("\n", $violations);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/** @return ConstraintViolation[] */
|
||||
public function violations(): array
|
||||
{
|
||||
return $this->violations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
interface SignedWith extends Constraint
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
interface ValidAt extends Constraint
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
|
||||
final class Validator implements \Lcobucci\JWT\Validator
|
||||
{
|
||||
public function assert(Token $token, Constraint ...$constraints): void
|
||||
{
|
||||
if ($constraints === []) {
|
||||
throw new NoConstraintsGiven('No constraint given.');
|
||||
}
|
||||
|
||||
$violations = [];
|
||||
|
||||
foreach ($constraints as $constraint) {
|
||||
$this->checkConstraint($constraint, $token, $violations);
|
||||
}
|
||||
|
||||
if ($violations) {
|
||||
throw RequiredConstraintsViolated::fromViolations(...$violations);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param ConstraintViolation[] $violations */
|
||||
private function checkConstraint(
|
||||
Constraint $constraint,
|
||||
Token $token,
|
||||
array &$violations,
|
||||
): void {
|
||||
try {
|
||||
$constraint->assert($token);
|
||||
} catch (ConstraintViolation $e) {
|
||||
$violations[] = $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function validate(Token $token, Constraint ...$constraints): bool
|
||||
{
|
||||
if ($constraints === []) {
|
||||
throw new NoConstraintsGiven('No constraint given.');
|
||||
}
|
||||
|
||||
try {
|
||||
foreach ($constraints as $constraint) {
|
||||
$constraint->assert($token);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (ConstraintViolation) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user