added predis and eseye back in.

This commit is contained in:
2020-12-25 11:28:41 +00:00
parent 0ddd298350
commit 017f72b42e
670 changed files with 60992 additions and 10 deletions

View File

@@ -0,0 +1,4 @@
# Contributing
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
Please do not submit any Pull Requests here. It will be automatically closed.

View File

@@ -0,0 +1 @@
patreon: FlorentMorselli

View File

@@ -0,0 +1,3 @@
Please do not submit any Pull Requests here. It will be automatically closed.
You should submit it here: https://github.com/web-token/jwt-framework/pulls

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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';
/**
* @var bool
*/
private $protectedHeader = false;
/**
* @var string[]
*/
private $supportedAlgorithms;
/**
* @param string[] $supportedAlgorithms
*/
public function __construct(array $supportedAlgorithms, bool $protectedHeader = false)
{
$this->supportedAlgorithms = $supportedAlgorithms;
$this->protectedHeader = $protectedHeader;
}
/**
* {@inheritdoc}
*/
public function checkHeader($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,93 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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';
/**
* @var bool
*/
private $protectedHeader = false;
/**
* @var string
*/
private $audience;
public function __construct(string $audience, bool $protectedHeader = false)
{
$this->audience = $audience;
$this->protectedHeader = $protectedHeader;
}
/**
* {@inheritdoc}
*/
public function checkClaim($value): void
{
$this->checkValue($value, InvalidClaimException::class);
}
/**
* {@inheritdoc}
*/
public function checkHeader($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;
}
/**
* @param mixed $value
*
* @throws InvalidClaimException if the claim is invalid
* @throws InvalidHeaderException if the header is invalid
*/
private function checkValue($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,32 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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.
*
* @param mixed $value
*
* @throws InvalidClaimException if the claim is invalid
*/
public function checkClaim($value): void;
/**
* The method returns the claim to be checked.
*/
public function supportedClaim(): string;
}

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use function array_key_exists;
use function count;
/**
* This manager handles as many claim checkers as needed.
*/
class ClaimCheckerManager
{
/**
* @var ClaimChecker[]
*/
private $checkers = [];
/**
* @param ClaimChecker[] $checkers
*/
public function __construct(array $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
*
* @throws InvalidClaimException
* @throws MissingMandatoryClaimException
*/
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
*
* @throws MissingMandatoryClaimException
*/
private function checkMandatoryClaims(array $mandatoryClaims, array $claims): void
{
if (0 === count($mandatoryClaims)) {
return;
}
$diff = array_keys(array_diff_key(array_flip($mandatoryClaims), $claims));
if (0 !== count($diff)) {
throw new MissingMandatoryClaimException(sprintf('The following claims are mandatory: %s.', implode(', ', $diff)), $diff);
}
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use InvalidArgumentException;
class ClaimCheckerManagerFactory
{
/**
* @var ClaimChecker[]
*/
private $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,23 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use Throwable;
/**
* Exceptions thrown by this component.
*/
interface ClaimExceptionInterface extends Throwable
{
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
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';
/**
* @var int
*/
private $allowedTimeDrift;
/**
* @var bool
*/
private $protectedHeaderOnly;
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
{
$this->allowedTimeDrift = $allowedTimeDrift;
$this->protectedHeaderOnly = $protectedHeaderOnly;
}
/**
* {@inheritdoc}
*
* @throws InvalidClaimException if the claim "exp" is not valid
*/
public function checkClaim($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidClaimException('"exp" must be an integer.', self::NAME, $value);
}
if (time() > $value + $this->allowedTimeDrift) {
throw new InvalidClaimException('The token expired.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
/**
* @param mixed $value
*
* @throws InvalidHeaderException if the claim "exp" is not valid
*/
public function checkHeader($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidHeaderException('"exp" must be an integer.', self::NAME, $value);
}
if (time() > $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,37 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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.
*
* @param mixed $value
*
* @throws InvalidHeaderException if the header parameter is invalid
*/
public function checkHeader($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,169 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use function array_key_exists;
use function count;
use InvalidArgumentException;
use function is_array;
use Jose\Component\Core\JWT;
class HeaderCheckerManager
{
/**
* @var HeaderChecker[]
*/
private $checkers = [];
/**
* @var TokenTypeSupport[]
*/
private $tokenTypes = [];
/**
* HeaderCheckerManager constructor.
*
* @param HeaderChecker[] $checkers
* @param TokenTypeSupport[] $tokenTypes
*/
public function __construct(array $checkers, array $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
*
* @throws InvalidArgumentException if the token format is not valid
*/
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;
}
/**
* @throws InvalidArgumentException if the header contains duplicated entries
*/
private function checkDuplicatedHeaderParameters(array $header1, array $header2): void
{
$inter = array_intersect_key($header1, $header2);
if (0 !== count($inter)) {
throw new InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter))));
}
}
/**
* @param string[] $mandatoryHeaderParameters
*
* @throws MissingMandatoryHeaderParameterException if a mandatory header parameter is missing
*/
private function checkMandatoryHeaderParameters(array $mandatoryHeaderParameters, array $protected, array $unprotected): void
{
if (0 === count($mandatoryHeaderParameters)) {
return;
}
$diff = array_keys(array_diff_key(array_flip($mandatoryHeaderParameters), array_merge($protected, $unprotected)));
if (0 !== count($diff)) {
throw new MissingMandatoryHeaderParameterException(sprintf('The following header parameters are mandatory: %s.', implode(', ', $diff)), $diff);
}
}
/**
* @throws InvalidHeaderException if a protected header parameter is not in the protected header
*/
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);
}
/**
* @throws InvalidHeaderException if the "crit" parameter is not valid or if a critical header parameter cannot be verified
*/
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 (0 !== count($diff)) {
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,88 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use InvalidArgumentException;
class HeaderCheckerManagerFactory
{
/**
* @var HeaderChecker[]
*/
private $checkers = [];
/**
* @var TokenTypeSupport[]
*/
private $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
*
* @throws InvalidArgumentException if an alias is not supported
*/
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,61 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use Exception;
/**
* This exception is thrown by claim checkers when a claim check failed.
*/
class InvalidClaimException extends Exception implements ClaimExceptionInterface
{
/**
* @var string
*/
private $claim;
/**
* @var mixed
*/
private $value;
/**
* @param mixed $value
*/
public function __construct(string $message, string $claim, $value)
{
parent::__construct($message);
$this->claim = $claim;
$this->value = $value;
}
/**
* Returns the claim that caused the exception.
*/
public function getClaim(): string
{
return $this->claim;
}
/**
* Returns the claim value that caused the exception.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use Exception;
/**
* This exception is thrown by header parameter checkers when a header parameter check failed.
*/
class InvalidHeaderException extends Exception
{
/**
* @var string
*/
private $header;
/**
* @var mixed
*/
private $value;
/**
* @param mixed $value
*/
public function __construct(string $message, string $header, $value)
{
parent::__construct($message);
$this->header = $header;
$this->value = $value;
}
/**
* Returns the header parameter that caused the exception.
*/
public function getHeader(): string
{
return $this->header;
}
/**
* Returns the header parameter value that caused the exception.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
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';
/**
* @var int
*/
private $allowedTimeDrift;
/**
* @var bool
*/
private $protectedHeaderOnly;
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
{
$this->allowedTimeDrift = $allowedTimeDrift;
$this->protectedHeaderOnly = $protectedHeaderOnly;
}
/**
* {@inheritdoc}
*
* @throws InvalidClaimException if the claim is invalid
*/
public function checkClaim($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidClaimException('"iat" must be an integer.', self::NAME, $value);
}
if (time() < $value - $this->allowedTimeDrift) {
throw new InvalidClaimException('The JWT is issued in the future.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
/**
* @param mixed $value
*
* @throws InvalidHeaderException if the header parameter is invalid
*/
public function checkHeader($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidHeaderException('The header "iat" must be an integer.', self::NAME, $value);
}
if (time() < $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,93 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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';
/**
* @var bool
*/
private $protectedHeader = false;
/**
* @var array
*/
private $issuers;
public function __construct(array $issuer, bool $protectedHeader = false)
{
$this->issuers = $issuer;
$this->protectedHeader = $protectedHeader;
}
/**
* @param mixed $value
*
* @throws InvalidClaimException if the claim is invalid
*/
public function checkClaim($value): void
{
$this->checkValue($value, InvalidClaimException::class);
}
/**
* @param mixed $value
*
* @throws InvalidHeaderException if the header parameter is invalid
*/
public function checkHeader($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;
}
/**
* @param mixed $value
*
* @throws InvalidClaimException if the claim is invalid
* @throws InvalidHeaderException if the header parameter is invalid
*/
private function checkValue($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);
}
}
}

21
vendor/web-token/jwt-checker/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2019 Spomky-Labs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use Exception;
class MissingMandatoryClaimException extends Exception implements ClaimExceptionInterface
{
/**
* @var string[]
*/
private $claims;
/**
* MissingMandatoryClaimException constructor.
*
* @param string[] $claims
*/
public function __construct(string $message, array $claims)
{
parent::__construct($message);
$this->claims = $claims;
}
/**
* @return string[]
*/
public function getClaims(): array
{
return $this->claims;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
use Exception;
class MissingMandatoryHeaderParameterException extends Exception
{
/**
* @var string[]
*/
private $parameters;
/**
* MissingMandatoryHeaderParameterException constructor.
*
* @param string[] $parameters
*/
public function __construct(string $message, array $parameters)
{
parent::__construct($message);
$this->parameters = $parameters;
}
/**
* @return string[]
*/
public function getParameters(): array
{
return $this->parameters;
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker;
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';
/**
* @var int
*/
private $allowedTimeDrift;
/**
* @var bool
*/
private $protectedHeaderOnly;
public function __construct(int $allowedTimeDrift = 0, bool $protectedHeaderOnly = false)
{
$this->allowedTimeDrift = $allowedTimeDrift;
$this->protectedHeaderOnly = $protectedHeaderOnly;
}
/**
* {@inheritdoc}
*
* @throws InvalidClaimException if the claim "nbf" is not an integer
* @throws InvalidClaimException if the claim "nbf" restrict the use of the token
*/
public function checkClaim($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidClaimException('"nbf" must be an integer.', self::NAME, $value);
}
if (time() < $value - $this->allowedTimeDrift) {
throw new InvalidClaimException('The JWT can not be used yet.', self::NAME, $value);
}
}
public function supportedClaim(): string
{
return self::NAME;
}
/**
* @param mixed $value
*
* @throws InvalidHeaderException if the claim "nbf" is not an integer
* @throws InvalidHeaderException if the claim "nbf" restrict the use of the token
*/
public function checkHeader($value): void
{
if (!is_float($value) && !is_int($value)) {
throw new InvalidHeaderException('"nbf" must be an integer.', self::NAME, $value);
}
if (time() < $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;
}
}

15
vendor/web-token/jwt-checker/README.md vendored Normal file
View File

@@ -0,0 +1,15 @@
PHP JWT Checker Component
=========================
This repository is a sub repository of [the JWT Framework](https://github.com/web-token/jwt-framework) project and is READ ONLY.
**Please do not submit any Pull Request here.**
You should go to [the main repository](https://github.com/web-token/jwt-framework) instead.
# Documentation
The official documentation is available as https://web-token.spomky-labs.com/
# Licence
This software is release under [MIT licence](LICENSE).

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\AlgorithmChecker;
use Jose\Component\Checker\InvalidHeaderException;
use PHPUnit\Framework\TestCase;
/**
* @group HeaderChecker
* @group functional
*
* @internal
*/
class AlgorithmHeaderCheckerTest extends TestCase
{
/**
* @test
*/
public function anAlgorithmMustBeAString(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('"alg" must be a string.');
$checker = new AlgorithmChecker(['foo']);
$checker->checkHeader(1);
}
/**
* @test
*/
public function theAlgorithmHeaderIsNotAllowed(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('Unsupported algorithm.');
$checker = new AlgorithmChecker(['foo']);
$checker->checkHeader('bar');
}
/**
* @test
*/
public function theAlgorithmHeaderIsSupported(): void
{
$checker = new AlgorithmChecker(['foo']);
$checker->checkHeader('foo');
static::assertFalse($checker->protectedHeaderOnly());
static::assertEquals('alg', $checker->supportedHeader());
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\AudienceChecker;
use Jose\Component\Checker\InvalidClaimException;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group functional
*
* @internal
*/
class AudienceClaimCheckerTest extends TestCase
{
/**
* @test
*/
public function anAudienceClaimMustBeAStringOrAnArrayOfStrings(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkClaim(1);
}
/**
* @test
*/
public function theAudienceClaimIsNotKnown(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkClaim('bar');
}
/**
* @test
*/
public function theAudienceClaimListDoesNotContainTheCurrentAudience(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkClaim(['bar']);
}
/**
* @test
*/
public function theAudienceClaimIsSupported(): void
{
$checker = new AudienceChecker('foo');
$checker->checkClaim('foo');
$checker->checkClaim(['foo']);
static::assertEquals('aud', $checker->supportedClaim());
}
}

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\AudienceChecker;
use Jose\Component\Checker\InvalidHeaderException;
use PHPUnit\Framework\TestCase;
/**
* @group HeaderChecker
* @group functional
*
* @internal
*/
class AudienceHeaderCheckerTest extends TestCase
{
/**
* @test
*/
public function anAudienceHeaderMustBeAStringOrAnArrayOfStrings(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkHeader(1);
}
/**
* @test
*/
public function theAudienceHeaderIsNotKnown(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkHeader('bar');
}
/**
* @test
*/
public function theAudienceHeaderListDoesNotContainTheCurrentAudience(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('Bad audience.');
$checker = new AudienceChecker('foo');
$checker->checkHeader(['bar']);
}
/**
* @test
*/
public function theAudienceHeaderIsSupported(): void
{
$checker = new AudienceChecker('foo');
$checker->checkHeader('foo');
$checker->checkHeader(['foo']);
static::assertFalse($checker->protectedHeaderOnly());
static::assertEquals('aud', $checker->supportedHeader());
}
}

View File

@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use InvalidArgumentException;
use Jose\Component\Checker\AudienceChecker;
use Jose\Component\Checker\ClaimCheckerManagerFactory;
use Jose\Component\Checker\ExpirationTimeChecker;
use Jose\Component\Checker\IssuedAtChecker;
use Jose\Component\Checker\NotBeforeChecker;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group functional
*
* @internal
*/
class ClaimCheckerManagerFactoryTest extends TestCase
{
/**
* @var null|ClaimCheckerManagerFactory
*/
private $claimCheckerManagerFactory;
/**
* @test
*/
public function theAliasListOfTheClaimCheckerManagerFactoryIsAvailable(): void
{
static::assertEquals(['exp', 'iat', 'nbf', 'aud'], $this->getClaimCheckerManagerFactory()->aliases());
}
/**
* @test
*/
public function theAliasDoesNotExist(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The claim checker with the alias "foo" is not supported.');
$this->getClaimCheckerManagerFactory()->create(['foo']);
}
/**
* @test
*/
public function iCanCreateAClaimCheckerManager(): void
{
$manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']);
static::assertCount(4, $manager->getCheckers());
}
/**
* @test
*/
public function iCanCheckValidPayloadClaims(): void
{
$payload = [
'exp' => time() + 3600,
'iat' => time() - 1000,
'nbf' => time() - 100,
'foo' => 'bar',
];
$expected = $payload;
unset($expected['foo']);
$manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']);
$result = $manager->check($payload);
static::assertEquals($expected, $result);
}
/**
* @test
*/
public function theMandatoryClaimsAreNotSet(): void
{
$this->expectException(\Jose\Component\Checker\MissingMandatoryClaimException::class);
$this->expectExceptionMessage('The following claims are mandatory: bar.');
$payload = [
'exp' => time() + 3600,
'iat' => time() - 1000,
'nbf' => time() - 100,
'foo' => 'bar',
];
$expected = $payload;
unset($expected['foo']);
$manager = $this->getClaimCheckerManagerFactory()->create(['exp', 'iat', 'nbf', 'aud']);
$manager->check($payload, ['exp', 'foo', 'bar']);
}
private function getClaimCheckerManagerFactory(): ClaimCheckerManagerFactory
{
if (null === $this->claimCheckerManagerFactory) {
$this->claimCheckerManagerFactory = new ClaimCheckerManagerFactory();
$this->claimCheckerManagerFactory->add('exp', new ExpirationTimeChecker());
$this->claimCheckerManagerFactory->add('iat', new IssuedAtChecker());
$this->claimCheckerManagerFactory->add('nbf', new NotBeforeChecker());
$this->claimCheckerManagerFactory->add('aud', new AudienceChecker('My Service'));
}
return $this->claimCheckerManagerFactory;
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\AudienceChecker;
use Jose\Component\Checker\ClaimCheckerManager;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group unit
*
* @internal
*/
final class ClaimCheckerManagerTest extends TestCase
{
/**
* @covers \Jose\Component\Checker\AudienceChecker
* @covers \Jose\Component\Checker\ClaimCheckerManager
* @test
*/
public function getCheckers(): void
{
$checkers = [
new AudienceChecker('some-expected-audience'),
];
$expectedCheckers = [
'aud' => $checkers[0],
];
$sut = new ClaimCheckerManager($checkers);
static::assertEquals(
$expectedCheckers,
$sut->getCheckers()
);
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\ExpirationTimeChecker;
use Jose\Component\Checker\InvalidClaimException;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group functional
*
* @internal
*/
class ExpirationTimeClaimCheckerTest extends TestCase
{
/**
* @test
*/
public function theExpirationTimeClaimMustBeAnInteger(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('"exp" must be an integer.');
$checker = new ExpirationTimeChecker();
$checker->checkClaim('foo');
}
/**
* @test
*/
public function theExpirationTimeIsInThePast(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('The token expired.');
$checker = new ExpirationTimeChecker();
$checker->checkClaim(time() - 1);
}
/**
* @test
*/
public function theExpirationTimeIsInTheFutur(): void
{
$checker = new ExpirationTimeChecker();
$checker->checkClaim(time() + 3600);
static::assertEquals('exp', $checker->supportedClaim());
}
}

View File

@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use InvalidArgumentException;
use Jose\Component\Checker\AudienceChecker;
use Jose\Component\Checker\HeaderCheckerManagerFactory;
use Jose\Component\Checker\InvalidHeaderException;
use Jose\Component\Checker\IssuerChecker;
use Jose\Component\Checker\MissingMandatoryHeaderParameterException;
use Jose\Component\Checker\Tests\Stub\OtherToken;
use Jose\Component\Checker\Tests\Stub\Token;
use Jose\Component\Checker\Tests\Stub\TokenSupport;
use PHPUnit\Framework\TestCase;
/**
* @group HeaderChecker
* @group functional
*
* @internal
*/
class HeaderCheckerManagerFactoryTest extends TestCase
{
/**
* @var null|HeaderCheckerManagerFactory
*/
private $headerCheckerManagerFactory;
/**
* @test
*/
public function theAliasListOfTheHeaderCheckerManagerFactoryIsAvailable(): void
{
static::assertEquals(['aud', 'iss'], $this->getHeaderCheckerManagerFactory()->aliases());
}
/**
* @test
*/
public function aHeaderMustNotContainDuplicatedHeaderParameters(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The header contains duplicated entries: alg.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['alg' => 'foo'];
$unprotected = ['alg' => 'foo'];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theTokenHasCriticalHeaderNotSatisfied(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('One or more header parameters are marked as critical, but they are missing or have not been checked: alg.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['crit' => ['alg']];
$unprotected = [];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theHeaderIsSuccessfullyChecked(): void
{
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['crit' => ['aud'], 'aud' => 'My Service'];
$unprotected = ['iss' => 'Another Service'];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theCriticalHeaderParameterMustBeProtected(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('The header parameter "crit" must be protected.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['aud' => 'My Service'];
$unprotected = ['crit' => ['aud']];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theCriticalHeaderParameterMustBeAListOfHeaderParameters(): void
{
$this->expectException(InvalidHeaderException::class);
$this->expectExceptionMessage('The header "crit" must be a list of header parameters.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['aud' => 'My Service', 'crit' => true];
$unprotected = [];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theHeaderContainsUnknownParametersAndIsSuccessfullyChecked(): void
{
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['foo' => 'bar', 'iss' => 'Another Service'];
$unprotected = [];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
/**
* @test
*/
public function theHeaderDoesNotContainSomeMandatoryParameters(): void
{
$this->expectException(MissingMandatoryHeaderParameterException::class);
$this->expectExceptionMessage('The following header parameters are mandatory: mandatory.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['aud' => 'Audience', 'iss' => 'Another Service'];
$unprotected = ['foo' => 'bar'];
$token = new Token(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0, ['aud', 'iss', 'mandatory']);
}
/**
* @test
*/
public function iTryToCheckATokenThatIsNotSupported(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported token type.');
$headerCheckerManager = $this->getHeaderCheckerManagerFactory()->create(['aud', 'iss']);
$payload = [];
$protected = ['foo' => 'bar'];
$unprotected = [];
$token = new OtherToken(json_encode($payload), $protected, $unprotected);
$headerCheckerManager->check($token, 0);
}
private function getHeaderCheckerManagerFactory(): HeaderCheckerManagerFactory
{
if (null === $this->headerCheckerManagerFactory) {
$this->headerCheckerManagerFactory = new HeaderCheckerManagerFactory();
$this->headerCheckerManagerFactory->add('aud', new AudienceChecker('My Service', true));
$this->headerCheckerManagerFactory->add('iss', new IssuerChecker(['Another Service']));
$this->headerCheckerManagerFactory->addTokenTypeSupport(new TokenSupport());
}
return $this->headerCheckerManagerFactory;
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\InvalidClaimException;
use Jose\Component\Checker\IssuedAtChecker;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group functional
*
* @internal
*/
class IssuedAtClaimCheckerTest extends TestCase
{
/**
* @test
*/
public function anIssuedAtClaimMustBeAnInteger(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('"iat" must be an integer.');
$checker = new IssuedAtChecker();
$checker->checkClaim('foo');
}
/**
* @test
*/
public function theIssuedAtClaimIsInTheFutur(): void
{
$this->expectException(InvalidClaimException::class);
$this->expectExceptionMessage('The JWT is issued in the future.');
$checker = new IssuedAtChecker();
$checker->checkClaim(time() + 3600);
}
/**
* @test
*/
public function theIssuedAtClaimIsInThePast(): void
{
$checker = new IssuedAtChecker();
$checker->checkClaim(time() - 3600);
static::assertEquals('iat', $checker->supportedClaim());
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\NotBeforeChecker;
use PHPUnit\Framework\TestCase;
/**
* @group ClaimChecker
* @group functional
*
* @internal
*/
class NotBeforeClaimCheckerTest extends TestCase
{
/**
* @test
*/
public function theNotBeforeClaimMustBeAnInteger(): void
{
$this->expectException(\Jose\Component\Checker\InvalidClaimException::class);
$this->expectExceptionMessage('"nbf" must be an integer.');
$checker = new NotBeforeChecker();
$checker->checkClaim('foo');
}
/**
* @test
*/
public function theNotBeforeClaimIsInTheFutur(): void
{
$this->expectException(\Jose\Component\Checker\InvalidClaimException::class);
$this->expectExceptionMessage('The JWT can not be used yet.');
$checker = new NotBeforeChecker();
$checker->checkClaim(time() + 3600);
}
/**
* @test
*/
public function theNotBeforeClaimIsInThePast(): void
{
$checker = new NotBeforeChecker();
$checker->checkClaim(time() - 3600);
static::assertEquals('nbf', $checker->supportedClaim());
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests\Stub;
use Jose\Component\Core\JWT;
class OtherToken implements JWT
{
/**
* @var null|string
*/
private $payload;
/**
* @var array
*/
private $protectedHeader;
/**
* @var array
*/
private $unprotectedHeader;
public function __construct(?string $payload, array $protectedHeader, array $unprotectedHeader)
{
$this->payload = $payload;
$this->protectedHeader = $protectedHeader;
$this->unprotectedHeader = $unprotectedHeader;
}
public function getPayload(): ?string
{
return $this->payload;
}
public function getProtectedHeader(): array
{
return $this->protectedHeader;
}
public function getUnprotectedHeader(): array
{
return $this->unprotectedHeader;
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests\Stub;
use Jose\Component\Core\JWT;
class Token implements JWT
{
/**
* @var null|string
*/
private $payload;
/**
* @var array
*/
private $protectedHeader;
/**
* @var array
*/
private $unprotectedHeader;
public function __construct(?string $payload, array $protectedHeader, array $unprotectedHeader)
{
$this->payload = $payload;
$this->protectedHeader = $protectedHeader;
$this->unprotectedHeader = $unprotectedHeader;
}
public function getPayload(): ?string
{
return $this->payload;
}
public function getProtectedHeader(): array
{
return $this->protectedHeader;
}
public function getUnprotectedHeader(): array
{
return $this->unprotectedHeader;
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests\Stub;
use InvalidArgumentException;
use Jose\Component\Checker\TokenTypeSupport;
use Jose\Component\Core\JWT;
class TokenSupport implements TokenTypeSupport
{
/**
* @throws InvalidArgumentException if the token is not supported
*/
public function retrieveTokenHeaders(JWT $jwt, int $index, array &$protectedHeader, array &$unprotectedHeader): void
{
if (!$jwt instanceof Token) {
throw new InvalidArgumentException('Unsupported token.');
}
$protectedHeader = $jwt->getProtectedHeader();
$unprotectedHeader = $jwt->getUnprotectedHeader();
}
public function supports(JWT $jwt): bool
{
return $jwt instanceof Token;
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace Jose\Component\Checker\Tests;
use Jose\Component\Checker\UnencodedPayloadChecker;
use PHPUnit\Framework\TestCase;
/**
* @group HeaderChecker
* @group functional
*
* @internal
*/
class UnencodedPayloadHeaderCheckerTest extends TestCase
{
/**
* @test
*/
public function theB64HeaderMustBeAnBoolean(): void
{
$this->expectException(\Jose\Component\Checker\InvalidHeaderException::class);
$this->expectExceptionMessage('"b64" must be a boolean.');
$checker = new UnencodedPayloadChecker();
$checker->checkHeader('foo');
}
/**
* @test
*/
public function theB64HeaderIsABoolean(): void
{
$checker = new UnencodedPayloadChecker();
$checker->checkHeader(true);
$checker->checkHeader(false);
static::assertTrue($checker->protectedHeaderOnly());
static::assertEquals('b64', $checker->supportedHeader());
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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.
*/
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,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
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';
/**
* {@inheritdoc}
*
* @throws InvalidHeaderException if the header parameter "b64" is not a boolean
*/
public function checkHeader($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;
}
}

View File

@@ -0,0 +1,41 @@
{
"name": "web-token/jwt-checker",
"description": "Checker component of the JWT Framework.",
"type": "library",
"license": "MIT",
"keywords": ["JWS", "JWT", "JWE", "JWA", "JWK", "JWKSet", "Jot", "Jose", "RFC7515", "RFC7516", "RFC7517", "RFC7518", "RFC7519", "RFC7520", "Bundle", "Symfony"],
"homepage": "https://github.com/web-token",
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky"
},{
"name": "All contributors",
"homepage": "https://github.com/web-token/jwt-checker/contributors"
}
],
"autoload": {
"psr-4": {
"Jose\\Component\\Checker\\": ""
}
},
"require": {
"web-token/jwt-core": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
},
"extra": {
"branch-alias": {
"v1.0": "1.0.x-dev",
"v1.1": "1.1.x-dev",
"v1.2": "1.2.x-dev",
"v1.3": "1.3.x-dev",
"v2.0": "2.0.x-dev",
"v2.1": "2.1.x-dev"
}
},
"config": {
"sort-packages": true
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="true"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./</directory>
<exclude>
<directory>./vendor</directory>
<directory>./Tests</directory>
<directory suffix="Test.php">./src</directory>
</exclude>
</whitelist>
</filter>
</phpunit>