save
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;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Exception;
|
||||
|
||||
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
|
||||
{
|
||||
public static function create(string $claim): self
|
||||
{
|
||||
return new self(
|
||||
'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
use function in_array;
|
||||
|
||||
final class HasClaimWithValue implements Constraint
|
||||
{
|
||||
private string $claim;
|
||||
|
||||
/** @var mixed */
|
||||
private $expectedValue;
|
||||
|
||||
/** @param mixed $expectedValue */
|
||||
public function __construct(string $claim, $expectedValue)
|
||||
{
|
||||
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
|
||||
throw CannotValidateARegisteredClaim::create($claim);
|
||||
}
|
||||
|
||||
$this->claim = $claim;
|
||||
$this->expectedValue = $expectedValue;
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token instanceof UnencryptedToken) {
|
||||
throw 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,28 @@
|
||||
<?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
|
||||
{
|
||||
private string $id;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
$this->id = $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,29 @@
|
||||
<?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 string[] */
|
||||
private array $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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -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,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
use Lcobucci\Clock\Clock;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
|
||||
|
||||
final class LooseValidAt implements ValidAtInterface
|
||||
{
|
||||
private Clock $clock;
|
||||
private DateInterval $leeway;
|
||||
|
||||
public function __construct(Clock $clock, ?DateInterval $leeway = null)
|
||||
{
|
||||
$this->clock = $clock;
|
||||
$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,28 @@
|
||||
<?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
|
||||
{
|
||||
private string $audience;
|
||||
|
||||
public function __construct(string $audience)
|
||||
{
|
||||
$this->audience = $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,28 @@
|
||||
<?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
|
||||
{
|
||||
private string $subject;
|
||||
|
||||
public function __construct(string $subject)
|
||||
{
|
||||
$this->subject = $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,37 @@
|
||||
<?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
|
||||
{
|
||||
private Signer $signer;
|
||||
private Signer\Key $key;
|
||||
|
||||
public function __construct(Signer $signer, Signer\Key $key)
|
||||
{
|
||||
$this->signer = $signer;
|
||||
$this->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,86 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
use Lcobucci\Clock\Clock;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
|
||||
|
||||
final class StrictValidAt implements ValidAtInterface
|
||||
{
|
||||
private Clock $clock;
|
||||
private DateInterval $leeway;
|
||||
|
||||
public function __construct(Clock $clock, ?DateInterval $leeway = null)
|
||||
{
|
||||
$this->clock = $clock;
|
||||
$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,25 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
use DateInterval;
|
||||
use Lcobucci\Clock\Clock;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
|
||||
/** @deprecated Use \Lcobucci\JWT\Validation\Constraint\LooseValidAt */
|
||||
final class ValidAt implements Constraint
|
||||
{
|
||||
private LooseValidAt $constraint;
|
||||
|
||||
public function __construct(Clock $clock, ?DateInterval $leeway = null)
|
||||
{
|
||||
$this->constraint = new LooseValidAt($clock, $leeway);
|
||||
}
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
$this->constraint->assert($token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Lcobucci\JWT\Validation;
|
||||
|
||||
use Lcobucci\JWT\Exception;
|
||||
use RuntimeException;
|
||||
|
||||
use function get_class;
|
||||
|
||||
final class ConstraintViolation extends RuntimeException implements Exception
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var class-string<Constraint>|null
|
||||
*/
|
||||
public ?string $constraint = null;
|
||||
|
||||
public static function error(string $message, Constraint $constraint): self
|
||||
{
|
||||
$exception = new self($message);
|
||||
$exception->constraint = get_class($constraint);
|
||||
|
||||
return $exception;
|
||||
}
|
||||
}
|
||||
@@ -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,46 @@
|
||||
<?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
|
||||
{
|
||||
/** @var ConstraintViolation[] */
|
||||
private array $violations = [];
|
||||
|
||||
public static function fromViolations(ConstraintViolation ...$violations): self
|
||||
{
|
||||
$exception = new self(self::buildMessage($violations));
|
||||
$exception->violations = $violations;
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
/** @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 $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user