primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use function in_array;
use function is_string;
/**
* This class is a header parameter checker. When the "alg" header parameter is present, it will check if the value is
* within the allowed ones.
*/
final class AlgorithmChecker implements HeaderChecker
{
private const HEADER_NAME = 'alg';
/**
* @param string[] $supportedAlgorithms
*/
public function __construct(
private readonly array $supportedAlgorithms,
private readonly bool $protectedHeader = false
) {
}
public function checkHeader(mixed $value): void
{
if (! is_string($value)) {
throw new InvalidHeaderException('"alg" must be a string.', self::HEADER_NAME, $value);
}
if (! in_array($value, $this->supportedAlgorithms, true)) {
throw new InvalidHeaderException('Unsupported algorithm.', self::HEADER_NAME, $value);
}
}
public function supportedHeader(): string
{
return self::HEADER_NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeader;
}
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use function in_array;
use function is_array;
use function is_string;
/**
* This class is a header parameter and claim checker. When the "aud" header parameter or claim is present, it will
* check if the value is within the allowed ones.
*/
final class AudienceChecker implements ClaimChecker, HeaderChecker
{
private const CLAIM_NAME = 'aud';
public function __construct(
private readonly string $audience,
private readonly bool $protectedHeader = false
) {
}
public function checkClaim(mixed $value): void
{
$this->checkValue($value, InvalidClaimException::class);
}
public function checkHeader(mixed $value): void
{
$this->checkValue($value, InvalidHeaderException::class);
}
public function supportedClaim(): string
{
return self::CLAIM_NAME;
}
public function supportedHeader(): string
{
return self::CLAIM_NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeader;
}
private function checkValue(mixed $value, string $class): void
{
if (is_string($value) && $value !== $this->audience) {
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
}
if (is_array($value) && ! in_array($this->audience, $value, true)) {
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
}
if (! is_array($value) && ! is_string($value)) {
throw new $class('Bad audience.', self::CLAIM_NAME, $value);
}
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use InvalidArgumentException;
use function call_user_func;
use function is_callable;
/**
* @see \Jose\Tests\Component\Checker\CallableCheckerTest
*/
final class CallableChecker implements ClaimChecker, HeaderChecker
{
/**
* @param string $key The claim or header parameter name to check.
* @param callable(mixed $value): bool $callable The callable function that will be invoked.
*/
public function __construct(
private readonly string $key,
private $callable,
private readonly bool $protectedHeaderOnly = true
) {
if (! is_callable($this->callable)) { // @phpstan-ignore-line
throw new InvalidArgumentException('The $callable argument must be a callable.');
}
}
public function checkClaim(mixed $value): void
{
if (call_user_func($this->callable, $value) !== true) {
throw new InvalidClaimException(sprintf('The "%s" claim is invalid.', $this->key), $this->key, $value);
}
}
public function supportedClaim(): string
{
return $this->key;
}
public function checkHeader(mixed $value): void
{
if (call_user_func($this->callable, $value) !== true) {
throw new InvalidHeaderException(sprintf('The "%s" header is invalid.', $this->key), $this->key, $value);
}
}
public function supportedHeader(): string
{
return $this->key;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeaderOnly;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
interface ClaimChecker
{
/**
* When the token has the applicable claim, the value is checked. If for some reason the value is not valid, an
* InvalidClaimException must be thrown.
*/
public function checkClaim(mixed $value): void;
/**
* The method returns the claim to be checked.
*/
public function supportedClaim(): string;
}

View File

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use function array_key_exists;
use function count;
/**
* This manager handles as many claim checkers as needed.
*
* @see \Jose\Tests\Component\Checker\ClaimCheckerManagerTest
*/
class ClaimCheckerManager
{
/**
* @var ClaimChecker[]
*/
private array $checkers = [];
/**
* @param ClaimChecker[] $checkers
*/
public function __construct(iterable $checkers)
{
foreach ($checkers as $checker) {
$this->add($checker);
}
}
/**
* This method returns all checkers handled by this manager.
*
* @return ClaimChecker[]
*/
public function getCheckers(): array
{
return $this->checkers;
}
/**
* This method checks all the claims passed as argument. All claims are checked against the claim checkers. If one
* fails, the InvalidClaimException is thrown.
*
* This method returns an array with all checked claims. It is up to the implementor to decide use the claims that
* have not been checked.
*
* @param string[] $mandatoryClaims
*/
public function check(array $claims, array $mandatoryClaims = []): array
{
$this->checkMandatoryClaims($mandatoryClaims, $claims);
$checkedClaims = [];
foreach ($this->checkers as $claim => $checker) {
if (array_key_exists($claim, $claims)) {
$checker->checkClaim($claims[$claim]);
$checkedClaims[$claim] = $claims[$claim];
}
}
return $checkedClaims;
}
private function add(ClaimChecker $checker): void
{
$claim = $checker->supportedClaim();
$this->checkers[$claim] = $checker;
}
/**
* @param string[] $mandatoryClaims
*/
private function checkMandatoryClaims(array $mandatoryClaims, array $claims): void
{
if (count($mandatoryClaims) === 0) {
return;
}
$diff = array_keys(array_diff_key(array_flip($mandatoryClaims), $claims));
if (count($diff) !== 0) {
throw new MissingMandatoryClaimException(sprintf(
'The following claims are mandatory: %s.',
implode(', ', $diff)
), $diff);
}
}
}

View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use InvalidArgumentException;
/**
* @see \Jose\Tests\Component\Checker\ClaimCheckerManagerFactoryTest
*/
class ClaimCheckerManagerFactory
{
/**
* @var ClaimChecker[]
*/
private array $checkers = [];
/**
* This method creates a Claim Checker Manager and populate it with the claim checkers found based on the alias. If
* the alias is not supported, an InvalidArgumentException is thrown.
*
* @param string[] $aliases
*/
public function create(array $aliases): ClaimCheckerManager
{
$checkers = [];
foreach ($aliases as $alias) {
if (! isset($this->checkers[$alias])) {
throw new InvalidArgumentException(sprintf(
'The claim checker with the alias "%s" is not supported.',
$alias
));
}
$checkers[] = $this->checkers[$alias];
}
return new ClaimCheckerManager($checkers);
}
/**
* This method adds a claim checker to this factory.
*/
public function add(string $alias, ClaimChecker $checker): void
{
$this->checkers[$alias] = $checker;
}
/**
* Returns all claim checker aliases supported by this factory.
*
* @return string[]
*/
public function aliases(): array
{
return array_keys($this->checkers);
}
/**
* Returns all claim checkers supported by this factory.
*
* @return ClaimChecker[]
*/
public function all(): array
{
return $this->checkers;
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Throwable;
/**
* Exceptions thrown by this component.
*/
interface ClaimExceptionInterface extends Throwable
{
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Psr\Clock\ClockInterface;
use function is_float;
use function is_int;
/**
* This class is a claim checker. When the "exp" is present, it will compare the value with the current timestamp.
*/
final class ExpirationTimeChecker implements ClaimChecker, HeaderChecker
{
private const NAME = 'exp';
private readonly ClockInterface $clock;
public function __construct(
private readonly int $allowedTimeDrift = 0,
private readonly bool $protectedHeaderOnly = false,
?ClockInterface $clock = null,
) {
if ($clock === null) {
trigger_deprecation(
'web-token/jwt-library',
'3.3.0',
'The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".'
);
$clock = new InternalClock();
}
$this->clock = $clock;
}
public function checkClaim(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidClaimException('"exp" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now > $value + $this->allowedTimeDrift) {
throw new InvalidClaimException('The token expired.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
public function checkHeader(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidHeaderException('"exp" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now > $value + $this->allowedTimeDrift) {
throw new InvalidHeaderException('The token expired.', self::NAME, $value);
}
}
public function supportedHeader(): string
{
return self::NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeaderOnly;
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
interface HeaderChecker
{
/**
* This method is called when the header parameter is present. If for some reason the value is not valid, an
* InvalidHeaderException must be thrown.
*/
public function checkHeader(mixed $value): void;
/**
* The method returns the header parameter to be checked.
*/
public function supportedHeader(): string;
/**
* When true, the header parameter to be checked MUST be set in the protected header of the token.
*/
public function protectedHeaderOnly(): bool;
}

View File

@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use InvalidArgumentException;
use Jose\Component\Core\JWT;
use function array_key_exists;
use function count;
use function is_array;
class HeaderCheckerManager
{
/**
* @var HeaderChecker[]
*/
private array $checkers = [];
/**
* @var TokenTypeSupport[]
*/
private array $tokenTypes = [];
/**
* HeaderCheckerManager constructor.
*
* @param HeaderChecker[] $checkers
* @param TokenTypeSupport[] $tokenTypes
*/
public function __construct(iterable $checkers, iterable $tokenTypes)
{
foreach ($checkers as $checker) {
$this->add($checker);
}
foreach ($tokenTypes as $tokenType) {
$this->addTokenTypeSupport($tokenType);
}
}
/**
* This method returns all checkers handled by this manager.
*
* @return HeaderChecker[]
*/
public function getCheckers(): array
{
return $this->checkers;
}
/**
* This method checks all the header parameters passed as argument. All header parameters are checked against the
* header parameter checkers. If one fails, the InvalidHeaderException is thrown.
*
* @param string[] $mandatoryHeaderParameters
*/
public function check(JWT $jwt, int $index, array $mandatoryHeaderParameters = []): void
{
foreach ($this->tokenTypes as $tokenType) {
if ($tokenType->supports($jwt)) {
$protected = [];
$unprotected = [];
$tokenType->retrieveTokenHeaders($jwt, $index, $protected, $unprotected);
$this->checkDuplicatedHeaderParameters($protected, $unprotected);
$this->checkMandatoryHeaderParameters($mandatoryHeaderParameters, $protected, $unprotected);
$this->checkHeaders($protected, $unprotected);
return;
}
}
throw new InvalidArgumentException('Unsupported token type.');
}
private function addTokenTypeSupport(TokenTypeSupport $tokenType): void
{
$this->tokenTypes[] = $tokenType;
}
private function add(HeaderChecker $checker): void
{
$header = $checker->supportedHeader();
$this->checkers[$header] = $checker;
}
private function checkDuplicatedHeaderParameters(array $header1, array $header2): void
{
$inter = array_intersect_key($header1, $header2);
if (count($inter) !== 0) {
throw new InvalidArgumentException(sprintf(
'The header contains duplicated entries: %s.',
implode(', ', array_keys($inter))
));
}
}
/**
* @param string[] $mandatoryHeaderParameters
*/
private function checkMandatoryHeaderParameters(
array $mandatoryHeaderParameters,
array $protected,
array $unprotected
): void {
if (count($mandatoryHeaderParameters) === 0) {
return;
}
$diff = array_keys(
array_diff_key(array_flip($mandatoryHeaderParameters), array_merge($protected, $unprotected))
);
if (count($diff) !== 0) {
throw new MissingMandatoryHeaderParameterException(sprintf(
'The following header parameters are mandatory: %s.',
implode(', ', $diff)
), $diff);
}
}
private function checkHeaders(array $protected, array $header): void
{
$checkedHeaderParameters = [];
foreach ($this->checkers as $headerParameter => $checker) {
if ($checker->protectedHeaderOnly()) {
if (array_key_exists($headerParameter, $protected)) {
$checker->checkHeader($protected[$headerParameter]);
$checkedHeaderParameters[] = $headerParameter;
} elseif (array_key_exists($headerParameter, $header)) {
throw new InvalidHeaderException(sprintf(
'The header parameter "%s" must be protected.',
$headerParameter
), $headerParameter, $header[$headerParameter]);
}
} else {
if (array_key_exists($headerParameter, $protected)) {
$checker->checkHeader($protected[$headerParameter]);
$checkedHeaderParameters[] = $headerParameter;
} elseif (array_key_exists($headerParameter, $header)) {
$checker->checkHeader($header[$headerParameter]);
$checkedHeaderParameters[] = $headerParameter;
}
}
}
$this->checkCriticalHeader($protected, $header, $checkedHeaderParameters);
}
private function checkCriticalHeader(array $protected, array $header, array $checkedHeaderParameters): void
{
if (array_key_exists('crit', $protected)) {
if (! is_array($protected['crit'])) {
throw new InvalidHeaderException(
'The header "crit" must be a list of header parameters.',
'crit',
$protected['crit']
);
}
$diff = array_diff($protected['crit'], $checkedHeaderParameters);
if (count($diff) !== 0) {
throw new InvalidHeaderException(sprintf(
'One or more header parameters are marked as critical, but they are missing or have not been checked: %s.',
implode(', ', array_values($diff))
), 'crit', $protected['crit']);
}
} elseif (array_key_exists('crit', $header)) {
throw new InvalidHeaderException('The header parameter "crit" must be protected.', 'crit', $header['crit']);
}
}
}

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use InvalidArgumentException;
/**
* @see \Jose\Tests\Component\Checker\HeaderCheckerManagerFactoryTest
*/
class HeaderCheckerManagerFactory
{
/**
* @var HeaderChecker[]
*/
private array $checkers = [];
/**
* @var TokenTypeSupport[]
*/
private array $tokenTypes = [];
/**
* This method creates a Header Checker Manager and populate it with the header parameter checkers found based on
* the alias. If the alias is not supported, an InvalidArgumentException is thrown.
*
* @param string[] $aliases
*/
public function create(array $aliases): HeaderCheckerManager
{
$checkers = [];
foreach ($aliases as $alias) {
if (! isset($this->checkers[$alias])) {
throw new InvalidArgumentException(sprintf(
'The header checker with the alias "%s" is not supported.',
$alias
));
}
$checkers[] = $this->checkers[$alias];
}
return new HeaderCheckerManager($checkers, $this->tokenTypes);
}
/**
* This method adds a header parameter checker to this factory. The checker is uniquely identified by an alias. This
* allows the same header parameter checker to be added twice (or more) using several configuration options.
*/
public function add(string $alias, HeaderChecker $checker): void
{
$this->checkers[$alias] = $checker;
}
/**
* This method adds a token type support to this factory.
*/
public function addTokenTypeSupport(TokenTypeSupport $tokenType): void
{
$this->tokenTypes[] = $tokenType;
}
/**
* Returns all header parameter checker aliases supported by this factory.
*
* @return string[]
*/
public function aliases(): array
{
return array_keys($this->checkers);
}
/**
* Returns all header parameter checkers supported by this factory.
*
* @return HeaderChecker[]
*/
public function all(): array
{
return $this->checkers;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use DateTimeImmutable;
use Psr\Clock\ClockInterface;
/**
* @internal
*/
final class InternalClock implements ClockInterface
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Exception;
/**
* This exception is thrown by claim checkers when a claim check failed.
*/
class InvalidClaimException extends Exception implements ClaimExceptionInterface
{
public function __construct(
string $message,
private readonly string $claim,
private readonly mixed $value
) {
parent::__construct($message);
}
/**
* Returns the claim that caused the exception.
*/
public function getClaim(): string
{
return $this->claim;
}
/**
* Returns the claim value that caused the exception.
*/
public function getValue(): mixed
{
return $this->value;
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Exception;
/**
* This exception is thrown by header parameter checkers when a header parameter check failed.
*/
class InvalidHeaderException extends Exception
{
public function __construct(
string $message,
private readonly string $header,
private readonly mixed $value
) {
parent::__construct($message);
}
/**
* Returns the header parameter that caused the exception.
*/
public function getHeader(): string
{
return $this->header;
}
/**
* Returns the header parameter value that caused the exception.
*/
public function getValue(): mixed
{
return $this->value;
}
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
/**
* @see \Jose\Tests\Component\Checker\IsEqualCheckerTest
*/
final class IsEqualChecker implements ClaimChecker, HeaderChecker
{
/**
* @param string $key The claim or header parameter name to check.
* @param mixed $value The expected value.
* @param bool $protectedHeaderOnly [optional] Whether the header parameter MUST be protected.
* This option has no effect for claim checkers.
*/
public function __construct(
private readonly string $key,
private readonly mixed $value,
private readonly bool $protectedHeaderOnly = true
) {
}
public function checkClaim(mixed $value): void
{
if ($value !== $this->value) {
throw new InvalidClaimException(sprintf('The "%s" claim is invalid.', $this->key), $this->key, $value);
}
}
public function supportedClaim(): string
{
return $this->key;
}
public function checkHeader(mixed $value): void
{
if ($value !== $this->value) {
throw new InvalidHeaderException(sprintf('The "%s" header is invalid.', $this->key), $this->key, $value);
}
}
public function supportedHeader(): string
{
return $this->key;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeaderOnly;
}
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Psr\Clock\ClockInterface;
use function is_float;
use function is_int;
/**
* This class is a claim checker. When the "iat" is present, it will compare the value with the current timestamp.
*/
final class IssuedAtChecker implements ClaimChecker, HeaderChecker
{
private const NAME = 'iat';
private readonly ClockInterface $clock;
public function __construct(
private readonly int $allowedTimeDrift = 0,
private readonly bool $protectedHeaderOnly = false,
?ClockInterface $clock = null,
) {
if ($clock === null) {
trigger_deprecation(
'web-token/jwt-library',
'3.3.0',
'The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".'
);
$clock = new InternalClock();
}
$this->clock = $clock;
}
public function checkClaim(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidClaimException('"iat" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now < $value - $this->allowedTimeDrift) {
throw new InvalidClaimException('The JWT is issued in the future.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
public function checkHeader(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidHeaderException('The header "iat" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now < $value - $this->allowedTimeDrift) {
throw new InvalidHeaderException('The JWT is issued in the future.', self::NAME, $value);
}
}
public function supportedHeader(): string
{
return self::NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeaderOnly;
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use function in_array;
use function is_string;
/**
* This class is a header parameter and claim checker. When the "iss" header parameter or claim is present, it will
* check if the value is within the allowed ones.
*/
final class IssuerChecker implements ClaimChecker, HeaderChecker
{
private const CLAIM_NAME = 'iss';
public function __construct(
private readonly array $issuers,
private readonly bool $protectedHeader = false
) {
}
public function checkClaim(mixed $value): void
{
$this->checkValue($value, InvalidClaimException::class);
}
public function checkHeader(mixed $value): void
{
$this->checkValue($value, InvalidHeaderException::class);
}
public function supportedClaim(): string
{
return self::CLAIM_NAME;
}
public function supportedHeader(): string
{
return self::CLAIM_NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeader;
}
private function checkValue(mixed $value, string $class): void
{
if (! is_string($value)) {
throw new $class('Invalid value.', self::CLAIM_NAME, $value);
}
if (! in_array($value, $this->issuers, true)) {
throw new $class('Unknown issuer.', self::CLAIM_NAME, $value);
}
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Exception;
class MissingMandatoryClaimException extends Exception implements ClaimExceptionInterface
{
/**
* MissingMandatoryClaimException constructor.
*
* @param string[] $claims
*/
public function __construct(
string $message,
private readonly array $claims
) {
parent::__construct($message);
}
/**
* @return string[]
*/
public function getClaims(): array
{
return $this->claims;
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Exception;
class MissingMandatoryHeaderParameterException extends Exception
{
/**
* MissingMandatoryHeaderParameterException constructor.
*
* @param string[] $parameters
*/
public function __construct(
string $message,
private readonly array $parameters
) {
parent::__construct($message);
}
/**
* @return string[]
*/
public function getParameters(): array
{
return $this->parameters;
}
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Psr\Clock\ClockInterface;
use function is_float;
use function is_int;
/**
* This class is a claim checker. When the "nbf" is present, it will compare the value with the current timestamp.
*/
final class NotBeforeChecker implements ClaimChecker, HeaderChecker
{
private const NAME = 'nbf';
private readonly ClockInterface $clock;
public function __construct(
private readonly int $allowedTimeDrift = 0,
private readonly bool $protectedHeaderOnly = false,
?ClockInterface $clock = null,
) {
if ($clock === null) {
trigger_deprecation(
'web-token/jwt-library',
'3.3.0',
'The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".'
);
$clock = new InternalClock();
}
$this->clock = $clock;
}
public function checkClaim(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidClaimException('"nbf" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now < $value - $this->allowedTimeDrift) {
throw new InvalidClaimException('The JWT can not be used yet.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
public function checkHeader(mixed $value): void
{
if (! is_float($value) && ! is_int($value)) {
throw new InvalidHeaderException('"nbf" must be an integer.', self::NAME, $value);
}
$now = $this->clock->now()
->getTimestamp();
if ($now < $value - $this->allowedTimeDrift) {
throw new InvalidHeaderException('The JWT can not be used yet.', self::NAME, $value);
}
}
public function supportedHeader(): string
{
return self::NAME;
}
public function protectedHeaderOnly(): bool
{
return $this->protectedHeaderOnly;
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use Jose\Component\Core\JWT;
interface TokenTypeSupport
{
/**
* This method will retrieve the protect and unprotected headers of the token for the given index. The index is
* useful when the token is serialized using the Json General Serialization mode. For example the JWE Json General
* Serialization Mode allows several recipients to be set. The unprotected headers correspond to the share
* unprotected header and the selected recipient header.
*
* @param array<string, mixed> $protectedHeader
* @param array<string, mixed> $unprotectedHeader
*/
public function retrieveTokenHeaders(
JWT $jwt,
int $index,
array &$protectedHeader,
array &$unprotectedHeader
): void;
/**
* This method returns true if the token in argument is supported, otherwise false.
*/
public function supports(JWT $jwt): bool;
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Jose\Component\Checker;
use function is_bool;
/**
* This class is a header parameter checker. When the "b64" is present, it will check if the value is a boolean or not.
*
* The use of this checker will allow the use of token with unencoded payload.
*/
final class UnencodedPayloadChecker implements HeaderChecker
{
private const HEADER_NAME = 'b64';
public function checkHeader(mixed $value): void
{
if (! is_bool($value)) {
throw new InvalidHeaderException('"b64" must be a boolean.', self::HEADER_NAME, $value);
}
}
public function supportedHeader(): string
{
return self::HEADER_NAME;
}
public function protectedHeaderOnly(): bool
{
return true;
}
}