first commit

This commit is contained in:
2024-11-05 12:22:50 +01:00
commit e5682a3912
19641 changed files with 2948548 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
Copyright (c) 2014-2015, Luís Otávio Cobucci Oblonczyk
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,4 @@
<?php
class_exists(\Lcobucci\JWT\Token\Plain::class, false) || class_alias(\Lcobucci\JWT\Token::class, \Lcobucci\JWT\Token\Plain::class);
class_exists(\Lcobucci\JWT\Token\Signature::class, false) || class_alias(\Lcobucci\JWT\Signature::class, \Lcobucci\JWT\Token\Signature::class);

View File

@@ -0,0 +1,7 @@
<?php
if (PHP_VERSION_ID < 70300 && ! class_exists('JsonException')) {
class JsonException extends Exception
{
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Lcobucci\Clock;
use DateTimeImmutable;
use DateTimeZone;
use function interface_exists;
if (! interface_exists(Clock::class)) {
interface Clock
{
/** @return DateTimeImmutable */
public function now();
}
final class FrozenClock implements Clock
{
/** @var DateTimeImmutable */
private $now;
public function __construct(DateTimeImmutable $now)
{
$this->now = $now;
}
/** @return self */
public static function fromUTC()
{
return new self(new DateTimeImmutable('now', new DateTimeZone('UTC')));
}
public function setTo(DateTimeImmutable $now)
{
$this->now = $now;
}
public function now()
{
return $this->now;
}
}
final class SystemClock implements Clock
{
/** @var DateTimeZone */
private $timezone;
public function __construct(DateTimeZone $timezone)
{
$this->timezone = $timezone;
}
/** @return self */
public static function fromUTC()
{
return new self(new DateTimeZone('UTC'));
}
/** @return self */
public static function fromSystemTimezone()
{
return new self(new DateTimeZone(date_default_timezone_get()));
}
public function now()
{
return new DateTimeImmutable('now', $this->timezone);
}
}
}

View File

@@ -0,0 +1,57 @@
{
"name": "lcobucci/jwt",
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"type": "library",
"authors": [
{
"name": "Luís Otávio Cobucci Oblonczyk",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"keywords": [
"JWT",
"JWS"
],
"license": [
"BSD-3-Clause"
],
"require": {
"php": "^5.6 || ^7.0",
"ext-mbstring": "*",
"ext-openssl": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7 || ^7.3",
"squizlabs/php_codesniffer": "~2.3",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
"mikey179/vfsstream": "~1.5"
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
},
"files": [
"compat/class-aliases.php",
"compat/json-exception-polyfill.php",
"compat/lcobucci-clock-polyfill.php"
]
},
"autoload-dev": {
"psr-4": {
"Lcobucci\\JWT\\": [
"test/unit",
"test/functional"
]
}
},
"suggest": {
"lcobucci/clock": "*"
},
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
}
}

View File

@@ -0,0 +1,592 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use DateTimeImmutable;
use Lcobucci\JWT\Claim\Factory as ClaimFactory;
use Lcobucci\JWT\Parsing\Encoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\RegisteredClaimGiven;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_diff;
use function array_filter;
use function array_key_exists;
use function array_merge;
use function array_shift;
use function count;
use function current;
use function in_array;
use function is_array;
use function is_bool;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* This class makes easier the token creation process
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Builder
{
/**
* The token header
*
* @var array
*/
private $headers = ['typ'=> 'JWT', 'alg' => 'none'];
/**
* The token claim set
*
* @var array
*/
private $claims = [];
/**
* The data encoder
*
* @var Encoder
*/
private $encoder;
/**
* The factory of claims
*
* @var ClaimFactory
*/
private $claimFactory;
/**
* @var Signer|null
*/
private $signer;
/**
* @var Key|null
*/
private $key;
/**
* Initializes a new builder
*
* @param Encoder $encoder
* @param ClaimFactory $claimFactory
*/
public function __construct(
Encoder $encoder = null,
ClaimFactory $claimFactory = null
) {
$this->encoder = $encoder ?: new Encoder();
$this->claimFactory = $claimFactory ?: new ClaimFactory();
}
/**
* Configures the audience
*
* @deprecated This method has been wrongly added and doesn't exist on v4
* @see Builder::permittedFor()
*
* @param string $audience
* @param bool $replicateAsHeader
*
* @return Builder
*/
public function canOnlyBeUsedBy($audience, $replicateAsHeader = false)
{
return $this->permittedFor($audience, $replicateAsHeader);
}
/**
* Configures the audience
*
* @param list<string|bool> $audiences A list of audiences and, optionally, the instruction to replicate as header
*
* @return Builder
*/
public function permittedFor(...$audiences)
{
$claim = RegisteredClaims::AUDIENCE;
$replicateAsHeader = false;
if ($audiences !== [] && is_bool($audiences[count($audiences) - 1])) {
$replicateAsHeader = array_pop($audiences);
}
$audiences = array_filter($audiences, 'is_string');
$configured = array_key_exists($claim, $this->claims) ? $this->claims[$claim] : [];
$toAppend = array_diff($audiences, $configured);
return $this->setRegisteredClaim($claim, array_merge($configured, $toAppend), $replicateAsHeader);
}
/**
* Configures the audience
*
* @deprecated This method will be removed on v4
* @see Builder::permittedFor()
*
* @param string $audience
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setAudience($audience, $replicateAsHeader = false)
{
return $this->permittedFor($audience, $replicateAsHeader);
}
/**
* Configures the expiration time
*
* @param int|DateTimeImmutable $expiration
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function expiresAt($expiration, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('exp', $this->convertToDate($expiration), $replicateAsHeader);
}
/**
* @param int|DateTimeImmutable $value
*
* @return DateTimeImmutable
*/
private function convertToDate($value)
{
if (! $value instanceof DateTimeImmutable) {
trigger_error('Using integers for registered date claims is deprecated, please use DateTimeImmutable objects instead.', E_USER_DEPRECATED);
return new DateTimeImmutable('@' . $value);
}
return $value;
}
/**
* Configures the expiration time
*
* @deprecated This method will be removed on v4
* @see Builder::expiresAt()
*
* @param int|DateTimeImmutable $expiration
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setExpiration($expiration, $replicateAsHeader = false)
{
return $this->expiresAt($expiration, $replicateAsHeader);
}
/**
* Configures the token id
*
* @param string $id
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function identifiedBy($id, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('jti', (string) $id, $replicateAsHeader);
}
/**
* Configures the token id
*
* @deprecated This method will be removed on v4
* @see Builder::identifiedBy()
*
* @param string $id
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setId($id, $replicateAsHeader = false)
{
return $this->identifiedBy($id, $replicateAsHeader);
}
/**
* Configures the time that the token was issued
*
* @param int|DateTimeImmutable $issuedAt
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function issuedAt($issuedAt, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('iat', $this->convertToDate($issuedAt), $replicateAsHeader);
}
/**
* Configures the time that the token was issued
*
* @deprecated This method will be removed on v4
* @see Builder::issuedAt()
*
* @param int|DateTimeImmutable $issuedAt
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setIssuedAt($issuedAt, $replicateAsHeader = false)
{
return $this->issuedAt($issuedAt, $replicateAsHeader);
}
/**
* Configures the issuer
*
* @param string $issuer
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function issuedBy($issuer, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('iss', (string) $issuer, $replicateAsHeader);
}
/**
* Configures the issuer
*
* @deprecated This method will be removed on v4
* @see Builder::issuedBy()
*
* @param string $issuer
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setIssuer($issuer, $replicateAsHeader = false)
{
return $this->issuedBy($issuer, $replicateAsHeader);
}
/**
* Configures the time before which the token cannot be accepted
*
* @param int|DateTimeImmutable $notBefore
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function canOnlyBeUsedAfter($notBefore, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('nbf', $this->convertToDate($notBefore), $replicateAsHeader);
}
/**
* Configures the time before which the token cannot be accepted
*
* @deprecated This method will be removed on v4
* @see Builder::canOnlyBeUsedAfter()
*
* @param int|DateTimeImmutable $notBefore
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setNotBefore($notBefore, $replicateAsHeader = false)
{
return $this->canOnlyBeUsedAfter($notBefore, $replicateAsHeader);
}
/**
* Configures the subject
*
* @param string $subject
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function relatedTo($subject, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('sub', (string) $subject, $replicateAsHeader);
}
/**
* Configures the subject
*
* @deprecated This method will be removed on v4
* @see Builder::relatedTo()
*
* @param string $subject
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setSubject($subject, $replicateAsHeader = false)
{
return $this->relatedTo($subject, $replicateAsHeader);
}
/**
* Configures a registered claim
*
* @param string $name
* @param mixed $value
* @param boolean $replicate
*
* @return Builder
*/
protected function setRegisteredClaim($name, $value, $replicate)
{
$this->configureClaim($name, $value);
if ($replicate) {
trigger_error('Replicating claims as headers is deprecated and will removed from v4.0. Please manually set the header if you need it replicated.', E_USER_DEPRECATED);
$this->headers[$name] = $value;
}
return $this;
}
/**
* Configures a header item
*
* @param string $name
* @param mixed $value
*
* @return Builder
*/
public function withHeader($name, $value)
{
$this->headers[(string) $name] = $value;
return $this;
}
/**
* Configures a header item
*
* @deprecated This method will be removed on v4
* @see Builder::withHeader()
*
* @param string $name
* @param mixed $value
*
* @return Builder
*/
public function setHeader($name, $value)
{
return $this->withHeader($name, $value);
}
/**
* Configures a claim item
*
* @deprecated This method has been wrongly added and doesn't exist on v4
* @see Builder::withClaim()
*
* @param string $name
* @param mixed $value
*
* @return Builder
*/
public function with($name, $value)
{
return $this->withClaim($name, $value);
}
/**
* @param string $name
* @param mixed $value
*
* @return Builder
*/
private function configureClaim($name, $value)
{
$this->claims[(string) $name] = $value;
return $this;
}
/**
* Configures a claim item
*
* @param string $name
* @param mixed $value
*
* @return Builder
*
* @throws RegisteredClaimGiven
*/
public function withClaim($name, $value)
{
if (in_array($name, RegisteredClaims::ALL, true)) {
trigger_error('The use of the method "withClaim" is deprecated for registered claims. Please use dedicated method instead.', E_USER_DEPRECATED);
}
return $this->forwardCallToCorrectClaimMethod($name, $value);
}
private function forwardCallToCorrectClaimMethod($name, $value)
{
switch ($name) {
case RegisteredClaims::ID:
return $this->identifiedBy($value);
case RegisteredClaims::EXPIRATION_TIME:
return $this->expiresAt($value);
case RegisteredClaims::NOT_BEFORE:
return $this->canOnlyBeUsedAfter($value);
case RegisteredClaims::ISSUED_AT:
return $this->issuedAt($value);
case RegisteredClaims::ISSUER:
return $this->issuedBy($value);
case RegisteredClaims::AUDIENCE:
return $this->permittedFor($value);
default:
return $this->configureClaim($name, $value);
}
}
/**
* Configures a claim item
*
* @deprecated This method will be removed on v4
* @see Builder::withClaim()
*
* @param string $name
* @param mixed $value
*
* @return Builder
*/
public function set($name, $value)
{
return $this->forwardCallToCorrectClaimMethod($name, $value);
}
/**
* Signs the data
*
* @deprecated This method will be removed on v4
* @see Builder::getToken()
*
* @param Signer $signer
* @param Key|string $key
*
* @return Builder
*/
public function sign(Signer $signer, $key)
{
if (! $key instanceof Key) {
trigger_error('Implicit conversion of keys from strings is deprecated. Please use InMemory or LocalFileReference classes.', E_USER_DEPRECATED);
$key = new Key($key);
}
$this->signer = $signer;
$this->key = $key;
return $this;
}
/**
* Removes the signature from the builder
*
* @deprecated This method will be removed on v4
* @see Builder::getToken()
*
* @return Builder
*/
public function unsign()
{
$this->signer = null;
$this->key = null;
return $this;
}
/**
* Returns the resultant token
*
* @return Token
*/
public function getToken(Signer $signer = null, Key $key = null)
{
if ($signer === null || $key === null) {
trigger_error('Not specifying the signer and key to Builder#getToken() is deprecated. Please move the arguments from Builder#sign() to Builder#getToken().', E_USER_DEPRECATED);
}
$signer = $signer ?: $this->signer;
$key = $key ?: $this->key;
if ($signer instanceof Signer) {
$signer->modifyHeader($this->headers);
}
$headers = new DataSet(
$this->headers,
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->convertItems($this->headers)))
);
$claims = new DataSet(
$this->claims,
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->convertItems($this->claims)))
);
return new Token(
$headers,
$claims,
$this->createSignature($headers->toString() . '.' . $claims->toString(), $signer, $key),
['', ''],
$this->claimFactory
);
}
/**
* @param array<string, mixed> $items
*
* @return array<string, mixed>
*/
private function convertItems(array $items)
{
foreach (RegisteredClaims::DATE_CLAIMS as $name) {
if (! array_key_exists($name, $items) || ! $items[$name] instanceof DateTimeImmutable) {
continue;
}
$items[$name] = $items[$name]->getTimestamp();
}
$audience = RegisteredClaims::AUDIENCE;
if (array_key_exists($audience, $items) && is_array($items[$audience]) && count($items[$audience]) === 1) {
$items[$audience] = current($items[$audience]);
}
return $items;
}
/**
* @param string $payload
*
* @return Signature
*/
private function createSignature($payload, Signer $signer = null, Key $key = null)
{
if ($signer === null || $key === null) {
return Signature::fromEmptyData();
}
$hash = $signer->sign($payload, $key)->hash();
return new Signature($hash, $this->encoder->base64UrlEncode($hash));
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use JsonSerializable;
/**
* Basic interface for token claims
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
interface Claim extends JsonSerializable
{
/**
* Returns the claim name
*
* @return string
*/
public function getName();
/**
* Returns the claim value
*
* @return mixed
*/
public function getValue();
/**
* Returns the string representation of the claim
*
* @return string
*/
public function __toString();
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use Lcobucci\JWT\Claim;
/**
* The default claim
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class Basic implements Claim
{
/**
* @var string
*/
private $name;
/**
* @var mixed
*/
private $value;
/**
* Initializes the claim
*
* @param string $name
* @param mixed $value
*/
public function __construct($name, $value)
{
$this->name = $name;
$this->value = $value;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getValue()
{
return $this->value;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize()
{
return $this->value;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return (string) $this->value;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use Lcobucci\JWT\Claim;
use Lcobucci\JWT\ValidationData;
/**
* Validatable claim that checks if value is strictly equals to the given data
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class EqualsTo extends Basic implements Claim, Validatable
{
/**
* {@inheritdoc}
*/
public function validate(ValidationData $data)
{
if ($data->has($this->getName())) {
return $this->getValue() === $data->get($this->getName());
}
return true;
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use DateTimeImmutable;
use Lcobucci\JWT\Claim;
use Lcobucci\JWT\Token\RegisteredClaims;
use function current;
use function in_array;
use function is_array;
/**
* Class that create claims
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class Factory
{
/**
* The list of claim callbacks
*
* @var array
*/
private $callbacks;
/**
* Initializes the factory, registering the default callbacks
*
* @param array $callbacks
*/
public function __construct(array $callbacks = [])
{
$this->callbacks = array_merge(
[
'iat' => [$this, 'createLesserOrEqualsTo'],
'nbf' => [$this, 'createLesserOrEqualsTo'],
'exp' => [$this, 'createGreaterOrEqualsTo'],
'iss' => [$this, 'createEqualsTo'],
'aud' => [$this, 'createEqualsTo'],
'sub' => [$this, 'createEqualsTo'],
'jti' => [$this, 'createEqualsTo']
],
$callbacks
);
}
/**
* Create a new claim
*
* @param string $name
* @param mixed $value
*
* @return Claim
*/
public function create($name, $value)
{
if ($value instanceof DateTimeImmutable && in_array($name, RegisteredClaims::DATE_CLAIMS, true)) {
$value = $value->getTimestamp();
}
if ($name === RegisteredClaims::AUDIENCE && is_array($value)) {
$value = current($value);
}
if (!empty($this->callbacks[$name])) {
return call_user_func($this->callbacks[$name], $name, $value);
}
return $this->createBasic($name, $value);
}
/**
* Creates a claim that can be compared (greator or equals)
*
* @param string $name
* @param mixed $value
*
* @return GreaterOrEqualsTo
*/
private function createGreaterOrEqualsTo($name, $value)
{
return new GreaterOrEqualsTo($name, $value);
}
/**
* Creates a claim that can be compared (greator or equals)
*
* @param string $name
* @param mixed $value
*
* @return LesserOrEqualsTo
*/
private function createLesserOrEqualsTo($name, $value)
{
return new LesserOrEqualsTo($name, $value);
}
/**
* Creates a claim that can be compared (equals)
*
* @param string $name
* @param mixed $value
*
* @return EqualsTo
*/
private function createEqualsTo($name, $value)
{
return new EqualsTo($name, $value);
}
/**
* Creates a basic claim
*
* @param string $name
* @param mixed $value
*
* @return Basic
*/
private function createBasic($name, $value)
{
return new Basic($name, $value);
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use Lcobucci\JWT\Claim;
use Lcobucci\JWT\ValidationData;
/**
* Validatable claim that checks if value is greater or equals the given data
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class GreaterOrEqualsTo extends Basic implements Claim, Validatable
{
/**
* {@inheritdoc}
*/
public function validate(ValidationData $data)
{
if ($data->has($this->getName())) {
return $this->getValue() >= $data->get($this->getName());
}
return true;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use Lcobucci\JWT\Claim;
use Lcobucci\JWT\ValidationData;
/**
* Validatable claim that checks if value is lesser or equals to the given data
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class LesserOrEqualsTo extends Basic implements Claim, Validatable
{
/**
* {@inheritdoc}
*/
public function validate(ValidationData $data)
{
if ($data->has($this->getName())) {
return $this->getValue() <= $data->get($this->getName());
}
return true;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Claim;
use Lcobucci\JWT\ValidationData;
/**
* Basic interface for validatable token claims
*
* @deprecated This interface will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
interface Validatable
{
/**
* Returns if claim is valid according with given data
*
* @param ValidationData $data
*
* @return boolean
*/
public function validate(ValidationData $data);
}

View File

@@ -0,0 +1,178 @@
<?php
namespace Lcobucci\JWT;
use Closure;
use Lcobucci\JWT\Parsing\Decoder;
use Lcobucci\JWT\Parsing\Encoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\None;
use Lcobucci\JWT\Validation\Constraint;
/**
* Configuration container for the JWT Builder and Parser
*
* Serves like a small DI container to simplify the creation and usage
* of the objects.
*/
final class Configuration
{
/** @var Parser */
private $parser;
/** @var Signer */
private $signer;
/** @var Key */
private $signingKey;
/** @var Key */
private $verificationKey;
/** @var Validator */
private $validator;
/** @var Closure(): Builder */
private $builderFactory;
/** @var Constraint[] */
private $validationConstraints = [];
private function __construct(
Signer $signer,
Key $signingKey,
Key $verificationKey,
Encoder $encoder = null,
Decoder $decoder = null
) {
$this->signer = $signer;
$this->signingKey = $signingKey;
$this->verificationKey = $verificationKey;
$this->parser = new Parser($decoder ?: new Decoder());
$this->validator = new Validation\Validator();
$this->builderFactory = static function () use ($encoder) {
return new Builder($encoder ?: new Encoder());
};
}
/** @return self */
public static function forAsymmetricSigner(
Signer $signer,
Key $signingKey,
Key $verificationKey,
Encoder $encoder = null,
Decoder $decoder = null
) {
return new self(
$signer,
$signingKey,
$verificationKey,
$encoder,
$decoder
);
}
/** @return self */
public static function forSymmetricSigner(
Signer $signer,
Key $key,
Encoder $encoder = null,
Decoder $decoder = null
) {
return new self(
$signer,
$key,
$key,
$encoder,
$decoder
);
}
/** @return self */
public static function forUnsecuredSigner(
Encoder $encoder = null,
Decoder $decoder = null
) {
$key = InMemory::plainText('');
return new self(
new None(),
$key,
$key,
$encoder,
$decoder
);
}
/** @param callable(): Builder $builderFactory */
public function setBuilderFactory(callable $builderFactory)
{
if (! $builderFactory instanceof Closure) {
$builderFactory = static function() use ($builderFactory) {
return $builderFactory();
};
}
$this->builderFactory = $builderFactory;
}
/** @return Builder */
public function builder()
{
$factory = $this->builderFactory;
return $factory();
}
/** @return Parser */
public function parser()
{
return $this->parser;
}
public function setParser(Parser $parser)
{
$this->parser = $parser;
}
/** @return Signer */
public function signer()
{
return $this->signer;
}
/** @return Key */
public function signingKey()
{
return $this->signingKey;
}
/** @return Key */
public function verificationKey()
{
return $this->verificationKey;
}
/** @return Validator */
public function validator()
{
return $this->validator;
}
public function setValidator(Validator $validator)
{
$this->validator = $validator;
}
/** @return Constraint[] */
public function validationConstraints()
{
return $this->validationConstraints;
}
public function setValidationConstraints(Constraint ...$validationConstraints)
{
$this->validationConstraints = $validationConstraints;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotDecodeContent extends RuntimeException implements Exception
{
/**
* @param JsonException $previous
*
* @return self
*/
public static function jsonIssues(JsonException $previous)
{
return new self('Error while decoding from JSON', 0, $previous);
}
/** @return self */
public static function invalidBase64String()
{
return new self('Error while decoding from Base64Url, invalid base64 characters detected');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotEncodeContent extends RuntimeException implements Exception
{
/**
* @param JsonException $previous
*
* @return self
*/
public static function jsonIssues(JsonException $previous)
{
return new self('Error while encoding to JSON', 0, $previous);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Lcobucci\JWT;
if (PHP_MAJOR_VERSION === 5) {
interface Exception
{
}
} else {
interface Exception extends \Throwable
{
}
}

View File

@@ -0,0 +1,175 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use DateTimeImmutable;
use InvalidArgumentException;
use Lcobucci\JWT\Parsing\Decoder;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\RegisteredClaims;
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
use RuntimeException;
use function array_key_exists;
use function is_array;
/**
* This class parses the JWT strings and convert them into tokens
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Parser
{
/**
* The data decoder
*
* @var Decoder
*/
private $decoder;
/**
* Initializes the object
*
* @param Decoder $decoder
*/
public function __construct(Decoder $decoder = null)
{
$this->decoder = $decoder ?: new Decoder();
}
/**
* Parses the JWT and returns a token
*
* @param string $jwt
*
* @return Token
*
* @throws InvalidArgumentException When JWT is not a string or is invalid.
* @throws RuntimeException When something goes wrong while decoding
*/
public function parse($jwt)
{
$data = $this->splitJwt($jwt);
$header = $this->parseHeader($data[0]);
$claims = $this->parseClaims($data[1]);
$signature = $this->parseSignature($header, $data[2]);
foreach ($claims as $name => $value) {
if (isset($header[$name])) {
$header[$name] = $value;
}
}
return new Token(
new DataSet($header, $data[0]),
new DataSet($claims, $data[1]),
$signature,
['', '']
);
}
/**
* Splits the JWT string into an array
*
* @param string $jwt
*
* @return array
*
* @throws InvalidArgumentException When JWT is not a string or is invalid
*/
protected function splitJwt($jwt)
{
if (!is_string($jwt)) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
$data = explode('.', $jwt);
if (count($data) != 3) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
return $data;
}
/**
* Parses the header from a string
*
* @param string $data
*
* @return array
*
* @throws UnsupportedHeaderFound When an invalid header is informed
*/
protected function parseHeader($data)
{
$header = (array) $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (isset($header['enc'])) {
throw UnsupportedHeaderFound::encryption();
}
return $this->convertItems($header);
}
/**
* Parses the claim set from a string
*
* @param string $data
*
* @return array
*/
protected function parseClaims($data)
{
$claims = (array) $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
return $this->convertItems($claims);
}
/**
* @param array<string, mixed> $items
*
* @return array<string, mixed>
*/
private function convertItems(array $items)
{
foreach (RegisteredClaims::DATE_CLAIMS as $name) {
if (! array_key_exists($name, $items)) {
continue;
}
$items[$name] = new DateTimeImmutable('@' . ((int) $items[$name]));
}
if (array_key_exists(RegisteredClaims::AUDIENCE, $items) && ! is_array($items[RegisteredClaims::AUDIENCE])) {
$items[RegisteredClaims::AUDIENCE] = [$items[RegisteredClaims::AUDIENCE]];
}
return $items;
}
/**
* Returns the signature from given data
*
* @param array $header
* @param string $data
*
* @return Signature
*/
protected function parseSignature(array $header, $data)
{
if ($data == '' || !isset($header['alg']) || $header['alg'] == 'none') {
return Signature::fromEmptyData();
}
$hash = $this->decoder->base64UrlDecode($data);
return new Signature($hash, $data);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Parsing;
use JsonException;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use RuntimeException;
use function json_decode;
use function json_last_error;
use function json_last_error_msg;
/**
* Class that decodes data according with the specs of RFC-4648
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*/
class Decoder
{
/**
* Decodes from JSON, validating the errors (will return an associative array
* instead of objects)
*
* @param string $json
* @return mixed
*
* @throws RuntimeException When something goes wrong while decoding
*/
public function jsonDecode($json)
{
if (PHP_VERSION_ID < 70300) {
$data = json_decode($json);
if (json_last_error() != JSON_ERROR_NONE) {
throw CannotDecodeContent::jsonIssues(new JsonException(json_last_error_msg()));
}
return $data;
}
try {
return json_decode($json, false, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotDecodeContent::jsonIssues($exception);
}
}
/**
* Decodes from base64url
*
* @param string $data
* @return string
*/
public function base64UrlDecode($data)
{
if ($remainder = strlen($data) % 4) {
$data .= str_repeat('=', 4 - $remainder);
}
return base64_decode(strtr($data, '-_', '+/'));
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Parsing;
use JsonException;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use RuntimeException;
use function json_encode;
use function json_last_error;
use function json_last_error_msg;
/**
* Class that encodes data according with the specs of RFC-4648
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*/
class Encoder
{
/**
* Encodes to JSON, validating the errors
*
* @param mixed $data
* @return string
*
* @throws RuntimeException When something goes wrong while encoding
*/
public function jsonEncode($data)
{
if (PHP_VERSION_ID < 70300) {
$json = json_encode($data);
if (json_last_error() != JSON_ERROR_NONE) {
throw CannotEncodeContent::jsonIssues(new JsonException(json_last_error_msg()));
}
return $json;
}
try {
return json_encode($data, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotEncodeContent::jsonIssues($exception);
}
}
/**
* Encodes to base64url
*
* @param string $data
* @return string
*/
public function base64UrlEncode($data)
{
return str_replace('=', '', strtr(base64_encode($data), '+/', '-_'));
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use Lcobucci\JWT\Signer\Key;
/**
* This class represents a token signature
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Signature
{
/**
* The resultant hash
*
* @var string
*/
protected $hash;
/** @var string */
private $encoded;
/**
* Initializes the object
*
* @param string $hash
* @param string $encoded
*/
public function __construct($hash, $encoded = '')
{
$this->hash = $hash;
$this->encoded = $encoded;
}
/** @return self */
public static function fromEmptyData()
{
return new self('', '');
}
/**
* Verifies if the current hash matches with with the result of the creation of
* a new signature with given data
*
* @param Signer $signer
* @param string $payload
* @param Key|string $key
*
* @return boolean
*/
public function verify(Signer $signer, $payload, $key)
{
return $signer->verify($this->hash, $payload, $key);
}
/**
* Returns the current hash as a string representation of the signature
*
* @deprecated This method has been removed from the public API in v4
* @see Signature::hash()
*
* @return string
*/
public function __toString()
{
return $this->hash;
}
/** @return string */
public function hash()
{
return $this->hash;
}
/** @return string */
public function toString()
{
return $this->encoded;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use InvalidArgumentException;
use Lcobucci\JWT\Signer\Key;
/**
* Basic interface for token signers
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
interface Signer
{
/**
* Returns the algorithm id
*
* @return string
*/
public function getAlgorithmId();
/**
* Apply changes on headers according with algorithm
*
* @param array $headers
*/
public function modifyHeader(array &$headers);
/**
* Returns a signature for given data
*
* @param string $payload
* @param Key|string $key
*
* @return Signature
*
* @throws InvalidArgumentException When given key is invalid
*/
public function sign($payload, $key);
/**
* Returns if the expected hash matches with the data and key
*
* @param string $expected
* @param string $payload
* @param Key|string $key
*
* @return boolean
*
* @throws InvalidArgumentException When given key is invalid
*/
public function verify($expected, $payload, $key);
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signature;
use Lcobucci\JWT\Signer;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Base class for signers
*
* @deprecated This class will be removed on v4
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
abstract class BaseSigner implements Signer
{
/**
* {@inheritdoc}
*/
public function modifyHeader(array &$headers)
{
$headers['alg'] = $this->getAlgorithmId();
}
/**
* {@inheritdoc}
*/
public function sign($payload, $key)
{
return new Signature($this->createHash($payload, $this->getKey($key)));
}
/**
* {@inheritdoc}
*/
public function verify($expected, $payload, $key)
{
return $this->doVerify($expected, $payload, $this->getKey($key));
}
/**
* @param Key|string $key
*
* @return Key
*/
private function getKey($key)
{
if (is_string($key)) {
trigger_error('Implicit conversion of keys from strings is deprecated. Please use InMemory or LocalFileReference classes.', E_USER_DEPRECATED);
$key = new Key($key);
}
return $key;
}
/**
* Creates a hash with the given data
*
* @internal
*
* @param string $payload
* @param Key $key
*
* @return string
*/
abstract public function createHash($payload, Key $key);
/**
* Performs the signature verification
*
* @internal
*
* @param string $expected
* @param string $payload
* @param Key $key
*
* @return boolean
*/
abstract public function doVerify($expected, $payload, Key $key);
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotSignPayload extends InvalidArgumentException implements Exception
{
/**
* @pararm string $error
*
* @return self
*/
public static function errorHappened($error)
{
return new self('There was an error while creating the signature: ' . $error);
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter;
use Lcobucci\JWT\Signer\Ecdsa\SignatureConverter;
use const OPENSSL_KEYTYPE_EC;
/**
* Base class for ECDSA signers
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
abstract class Ecdsa extends OpenSSL
{
/**
* @var SignatureConverter
*/
private $converter;
public function __construct(SignatureConverter $converter = null)
{
$this->converter = $converter ?: new MultibyteStringConverter();
}
/**
* {@inheritdoc}
*/
public function createHash($payload, Key $key)
{
return $this->converter->fromAsn1(
parent::createHash($payload, $key),
$this->getKeyLength()
);
}
/**
* {@inheritdoc}
*/
public function doVerify($expected, $payload, Key $key)
{
return parent::doVerify(
$this->converter->toAsn1($expected, $this->getKeyLength()),
$payload,
$key
);
}
/**
* Returns the length of each point in the signature, so that we can calculate and verify R and S points properly
*
* @internal
*/
abstract public function getKeyLength();
/**
* {@inheritdoc}
*/
final public function getKeyType()
{
return OPENSSL_KEYTYPE_EC;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Lcobucci\JWT\Signer\Ecdsa;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class ConversionFailed extends InvalidArgumentException implements Exception
{
/** @return self */
public static function invalidLength()
{
return new self('Invalid signature length.');
}
/** @return self */
public static function incorrectStartSequence()
{
return new self('Invalid data. Should start with a sequence.');
}
/** @return self */
public static function integerExpected()
{
return new self('Invalid data. Should contain an integer.');
}
}

View File

@@ -0,0 +1,133 @@
<?php
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2018 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*
* @link https://github.com/web-token/jwt-framework/blob/v1.2/src/Component/Core/Util/ECSignature.php
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use function bin2hex;
use function dechex;
use function hex2bin;
use function hexdec;
use function mb_strlen;
use function mb_substr;
use function str_pad;
use const STR_PAD_LEFT;
/**
* ECDSA signature converter using ext-mbstring
*
* @internal
*/
final class MultibyteStringConverter implements SignatureConverter
{
const ASN1_SEQUENCE = '30';
const ASN1_INTEGER = '02';
const ASN1_MAX_SINGLE_BYTE = 128;
const ASN1_LENGTH_2BYTES = '81';
const ASN1_BIG_INTEGER_LIMIT = '7f';
const ASN1_NEGATIVE_INTEGER = '00';
const BYTE_SIZE = 2;
public function toAsn1($signature, $length)
{
$signature = bin2hex($signature);
if (self::octetLength($signature) !== $length) {
throw ConversionFailed::invalidLength();
}
$pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
$pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
$lengthR = self::octetLength($pointR);
$lengthS = self::octetLength($pointS);
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
$asn1 = hex2bin(
self::ASN1_SEQUENCE
. $lengthPrefix . dechex($totalLength)
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
. self::ASN1_INTEGER . dechex($lengthS) . $pointS
);
return $asn1;
}
private static function octetLength($data)
{
return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE);
}
private static function preparePositiveInteger($data)
{
if (mb_substr($data, 0, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
return self::ASN1_NEGATIVE_INTEGER . $data;
}
while (mb_substr($data, 0, self::BYTE_SIZE, '8bit') === self::ASN1_NEGATIVE_INTEGER
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT) {
$data = mb_substr($data, 2, null, '8bit');
}
return $data;
}
public function fromAsn1($signature, $length)
{
$message = bin2hex($signature);
$position = 0;
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_SEQUENCE) {
throw ConversionFailed::incorrectStartSequence();
}
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
$position += self::BYTE_SIZE;
}
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$points = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT) . str_pad($pointS, $length, '0', STR_PAD_LEFT));
return $points;
}
private static function readAsn1Content($message, &$position, $length)
{
$content = mb_substr($message, $position, $length, '8bit');
$position += $length;
return $content;
}
private static function readAsn1Integer($message, &$position)
{
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
throw ConversionFailed::integerExpected();
}
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
}
private static function retrievePositiveInteger($data)
{
while (mb_substr($data, 0, self::BYTE_SIZE, '8bit') === self::ASN1_NEGATIVE_INTEGER
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
$data = mb_substr($data, 2, null, '8bit');
}
return $data;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
/**
* Signer for ECDSA SHA-256
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha256 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'ES256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha256';
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
{
return 64;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
/**
* Signer for ECDSA SHA-384
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha384 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'ES384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha384';
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
{
return 96;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
/**
* Signer for ECDSA SHA-512
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha512 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'ES512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha512';
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
{
return 132;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Lcobucci\JWT\Signer\Ecdsa;
/**
* Manipulates the result of a ECDSA signature (points R and S) according to the
* JWA specs.
*
* OpenSSL creates a signature using the ASN.1 format and, according the JWA specs,
* the signature for JWTs must be the concatenated values of points R and S (in
* big-endian octet order).
*
* @internal
*
* @see https://tools.ietf.org/html/rfc7518#page-9
* @see https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
*/
interface SignatureConverter
{
/**
* Converts the signature generated by OpenSSL into what JWA defines
*
* @param string $signature
* @param int $length
*
* @return string
*/
public function fromAsn1($signature, $length);
/**
* Converts the JWA signature into something OpenSSL understands
*
* @param string $points
* @param int $length
*
* @return string
*/
public function toAsn1($points, $length);
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
/**
* Base class for hmac signers
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
abstract class Hmac extends BaseSigner
{
/**
* {@inheritdoc}
*/
public function createHash($payload, Key $key)
{
return hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true);
}
/**
* {@inheritdoc}
*/
public function doVerify($expected, $payload, Key $key)
{
if (!is_string($expected)) {
return false;
}
return hash_equals($expected, $this->createHash($payload, $key));
}
/**
* Returns the algorithm name
*
* @internal
*
* @return string
*/
abstract public function getAlgorithm();
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/**
* Signer for HMAC SHA-256
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Sha256 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'HS256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha256';
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/**
* Signer for HMAC SHA-384
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Sha384 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'HS384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha384';
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/**
* Signer for HMAC SHA-512
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Sha512 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'HS512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return 'sha512';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidKeyProvided extends InvalidArgumentException implements Exception
{
/**
* @param string $details
*
* @return self
*/
public static function cannotBeParsed($details)
{
return new self('It was not possible to parse your key, reason: ' . $details);
}
/** @return self */
public static function incompatibleKey()
{
return new self('This key is not compatible with this signer');
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
use Exception;
use InvalidArgumentException;
use Lcobucci\JWT\Signer\Key\FileCouldNotBeRead;
use SplFileObject;
use function strpos;
use function substr;
/**
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 3.0.4
*/
class Key
{
/**
* @var string
*/
protected $content;
/**
* @var string
*/
private $passphrase;
/**
* @param string $content
* @param string $passphrase
*/
public function __construct($content, $passphrase = '')
{
$this->setContent($content);
$this->passphrase = $passphrase;
}
/**
* @param string $content
*
* @throws InvalidArgumentException
*/
private function setContent($content)
{
if (strpos($content, 'file://') === 0) {
$content = $this->readFile($content);
}
$this->content = $content;
}
/**
* @param string $content
*
* @return string
*
* @throws InvalidArgumentException
*/
private function readFile($content)
{
$path = substr($content, 7);
try {
$file = new SplFileObject($path);
} catch (Exception $exception) {
throw FileCouldNotBeRead::onPath($path, $exception);
}
$content = '';
while (! $file->eof()) {
$content .= $file->fgets();
}
return $content;
}
/** @return string */
public function contents()
{
return $this->content;
}
/** @return string */
public function passphrase()
{
return $this->passphrase;
}
/**
* @deprecated This method is no longer part of the public interface
* @see Key::contents()
*
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @deprecated This method is no longer part of the public interface
* @see Key::passphrase()
*
* @return string
*/
public function getPassphrase()
{
return $this->passphrase;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Exception;
use InvalidArgumentException;
if (PHP_MAJOR_VERSION === 7) {
final class FileCouldNotBeRead extends InvalidArgumentException implements Exception
{
/** @return self */
public static function onPath(string $path, \Throwable $cause = null)
{
return new self(
'The path "' . $path . '" does not contain a valid key file',
0,
$cause
);
}
}
} else {
final class FileCouldNotBeRead extends InvalidArgumentException implements Exception
{
/**
* @param string $path
* @param \Exception|null $cause
*
* @return self
*/
public static function onPath($path, \Exception $cause = null)
{
return new self(
'The path "' . $path . '" does not contain a valid key file',
0,
$cause
);
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Signer\Key;
use function base64_decode;
final class InMemory extends Key
{
/**
* @param string $contents
* @param string $passphrase
*
* @return self
*/
public static function plainText($contents, $passphrase = '')
{
return new self($contents, $passphrase);
}
/**
* @param string $contents
* @param string $passphrase
*
* @return self
*/
public static function base64Encoded($contents, $passphrase = '')
{
$decoded = base64_decode($contents, true);
if ($decoded === false) {
throw CannotDecodeContent::invalidBase64String();
}
return new self($decoded, $passphrase);
}
/**
* @param string $path
* @param string $passphrase
*
* @return InMemory
*
* @throws FileCouldNotBeRead
*/
public static function file($path, $passphrase = '')
{
return new self('file://' . $path, $passphrase);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key;
use function file_exists;
use function strpos;
use function substr;
final class LocalFileReference extends Key
{
const PATH_PREFIX = 'file://';
/**
* @param string $path
* @param string $passphrase
*
* @return self
*
* @throws FileCouldNotBeRead
*/
public static function file($path, $passphrase = '')
{
if (strpos($path, self::PATH_PREFIX) === 0) {
$path = substr($path, 7);
}
if (! file_exists($path)) {
throw FileCouldNotBeRead::onPath($path);
}
$key = new self('', $passphrase);
$key->content = self::PATH_PREFIX . $path;
return $key;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
/**
* A utilitarian class that encapsulates the retrieval of public and private keys
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*
* @deprecated Since we've removed OpenSSL from ECDSA there's no reason to use this class
*/
class Keychain
{
/**
* Returns a private key from file path or content
*
* @param string $key
* @param string $passphrase
*
* @return Key
*/
public function getPrivateKey($key, $passphrase = null)
{
return new Key($key, $passphrase);
}
/**
* Returns a public key from file path or content
*
* @param string $certificate
*
* @return Key
*/
public function getPublicKey($certificate)
{
return new Key($certificate);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Lcobucci\JWT\Signer;
final class None extends BaseSigner
{
public function getAlgorithmId()
{
return 'none';
}
public function createHash($payload, Key $key)
{
return '';
}
public function doVerify($expected, $payload, Key $key)
{
return $expected === '';
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use function is_resource;
use function openssl_error_string;
use function openssl_free_key;
use function openssl_pkey_get_details;
use function openssl_pkey_get_private;
use function openssl_pkey_get_public;
use function openssl_sign;
use function openssl_verify;
abstract class OpenSSL extends BaseSigner
{
public function createHash($payload, Key $key)
{
$privateKey = $this->getPrivateKey($key->getContent(), $key->getPassphrase());
try {
$signature = '';
if (! openssl_sign($payload, $signature, $privateKey, $this->getAlgorithm())) {
throw CannotSignPayload::errorHappened(openssl_error_string());
}
return $signature;
} finally {
openssl_free_key($privateKey);
}
}
/**
* @param string $pem
* @param string $passphrase
*
* @return resource
*/
private function getPrivateKey($pem, $passphrase)
{
$privateKey = openssl_pkey_get_private($pem, $passphrase);
$this->validateKey($privateKey);
return $privateKey;
}
/**
* @param $expected
* @param $payload
* @param $key
* @return bool
*/
public function doVerify($expected, $payload, Key $key)
{
$publicKey = $this->getPublicKey($key->getContent());
$result = openssl_verify($payload, $expected, $publicKey, $this->getAlgorithm());
openssl_free_key($publicKey);
return $result === 1;
}
/**
* @param string $pem
*
* @return resource
*/
private function getPublicKey($pem)
{
$publicKey = openssl_pkey_get_public($pem);
$this->validateKey($publicKey);
return $publicKey;
}
/**
* Raises an exception when the key type is not the expected type
*
* @param resource|bool $key
*
* @throws InvalidArgumentException
*/
private function validateKey($key)
{
if (! is_resource($key)) {
throw InvalidKeyProvided::cannotBeParsed(openssl_error_string());
}
$details = openssl_pkey_get_details($key);
if (! isset($details['key']) || $details['type'] !== $this->getKeyType()) {
throw InvalidKeyProvided::incompatibleKey();
}
}
/**
* Returns the type of key to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function getKeyType();
/**
* Returns which algorithm to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function getAlgorithm();
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer;
use const OPENSSL_KEYTYPE_RSA;
/**
* Base class for RSASSA-PKCS1 signers
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
abstract class Rsa extends OpenSSL
{
final public function getKeyType()
{
return OPENSSL_KEYTYPE_RSA;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
/**
* Signer for RSA SHA-256
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha256 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'RS256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return OPENSSL_ALGO_SHA256;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
/**
* Signer for RSA SHA-384
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha384 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'RS384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return OPENSSL_ALGO_SHA384;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
/**
* Signer for RSA SHA-512
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.1.0
*/
class Sha512 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
{
return 'RS512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
{
return OPENSSL_ALGO_SHA512;
}
}

View File

@@ -0,0 +1,430 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
use DateTimeImmutable;
use DateTimeInterface;
use Generator;
use Lcobucci\JWT\Claim\Factory;
use Lcobucci\JWT\Claim\Validatable;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\RegisteredClaims;
use OutOfBoundsException;
use function current;
use function func_num_args;
use function in_array;
use function is_array;
use function sprintf;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Basic structure of the JWT
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Token
{
/**
* The token headers
*
* @var DataSet
*/
private $headers;
/**
* The token claim set
*
* @var DataSet
*/
private $claims;
/**
* The token signature
*
* @var Signature
*/
private $signature;
/**
* @internal This serves just as compatibility layer
*
* @var Factory
*/
private $claimFactory;
/**
* Initializes the object
*
* @param array|DataSet $headers
* @param array|DataSet $claims
* @param Signature|null $signature
* @param array $payload
* @param Factory|null $claimFactory
*/
public function __construct(
$headers = ['alg' => 'none'],
$claims = [],
Signature $signature = null,
array $payload = ['', ''],
Factory $claimFactory = null
) {
$this->headers = $this->convertToDataSet($headers, $payload[0]);
$this->claims = $this->convertToDataSet($claims, $payload[1]);
$this->signature = $signature ?: Signature::fromEmptyData();
$this->claimFactory = $claimFactory ?: new Factory();
}
/**
* @param array|DataSet $data
* @param string $payload
*/
private function convertToDataSet($data, $payload)
{
if ($data instanceof DataSet) {
return $data;
}
return new DataSet($data, $payload);
}
/** @return DataSet */
public function headers()
{
return $this->headers;
}
/**
* Returns the token headers
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::headers()
*
* @return array
*/
public function getHeaders()
{
$items = [];
foreach ($this->headers->all() as $name => $value) {
if (! in_array($name, RegisteredClaims::ALL, true) || ! $this->claims->has($name)) {
$items[$name] = $value;
continue;
}
$items[$name] = $this->claimFactory->create($name, $value);
}
return $items;
}
/**
* Returns if the header is configured
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::headers()
* @see DataSet::has()
*
* @param string $name
*
* @return boolean
*/
public function hasHeader($name)
{
return $this->headers->has($name);
}
/**
* Returns the value of a token header
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::headers()
* @see DataSet::has()
*
* @param string $name
* @param mixed $default
*
* @return mixed
*
* @throws OutOfBoundsException
*/
public function getHeader($name, $default = null)
{
if (func_num_args() === 1 && ! $this->headers->has($name)) {
throw new OutOfBoundsException(sprintf('Requested header "%s" is not configured', $name));
}
return $this->headers->get($name, $default);
}
/** @return DataSet */
public function claims()
{
return $this->claims;
}
/**
* Returns the token claim set
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::claims()
*
* @return array
*/
public function getClaims()
{
$items = [];
foreach ($this->claims->all() as $name => $value) {
$items[$name] = $this->claimFactory->create($name, $value);
}
return $items;
}
/**
* Returns if the claim is configured
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::claims()
* @see DataSet::has()
*
* @param string $name
*
* @return boolean
*/
public function hasClaim($name)
{
return $this->claims->has($name);
}
/**
* Returns the value of a token claim
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::claims()
* @see DataSet::get()
*
* @param string $name
* @param mixed $default
*
* @return mixed
*
* @throws OutOfBoundsException
*/
public function getClaim($name, $default = null)
{
if (func_num_args() === 1 && ! $this->claims->has($name)) {
throw new OutOfBoundsException(sprintf('Requested header "%s" is not configured', $name));
}
$value = $this->claims->get($name, $default);
if ($value instanceof DateTimeImmutable && in_array($name, RegisteredClaims::DATE_CLAIMS, true)) {
return $value->getTimestamp();
}
if ($name === RegisteredClaims::AUDIENCE && is_array($value)) {
if (count($value) > 1) {
trigger_error('You will only get the first array entry as a string. Use Token::claims()->get() instead.', E_USER_DEPRECATED);
}
return current($value);
}
return $value;
}
/**
* Verify if the key matches with the one that created the signature
*
* @deprecated This method has been removed from the interface in v4.0
* @see \Lcobucci\JWT\Validation\Validator
*
* @param Signer $signer
* @param Key|string $key
*
* @return boolean
*/
public function verify(Signer $signer, $key)
{
if ($this->headers->get('alg') !== $signer->getAlgorithmId()) {
return false;
}
return $this->signature->verify($signer, $this->getPayload(), $key);
}
/**
* Validates if the token is valid
*
* @deprecated This method has been removed from the interface in v4.0
* @see \Lcobucci\JWT\Validation\Validator
*
* @param ValidationData $data
*
* @return boolean
*/
public function validate(ValidationData $data)
{
foreach ($this->getValidatableClaims() as $claim) {
if (!$claim->validate($data)) {
return false;
}
}
return true;
}
/**
* Determine if the token is expired.
*
* @param DateTimeInterface|null $now Defaults to the current time.
*
* @return bool
*/
public function isExpired(DateTimeInterface $now = null)
{
if (! $this->claims->has('exp')) {
return false;
}
if ($now === null) {
trigger_error('Not providing the current time is deprecated. Please pass an instance of DateTimeInterface.', E_USER_DEPRECATED);
}
$now = $now ?: new DateTimeImmutable();
return $now >= $this->claims->get(RegisteredClaims::EXPIRATION_TIME);
}
/**
* @param string $audience
*
* @return bool
*/
public function isPermittedFor($audience)
{
return in_array($audience, $this->claims->get(RegisteredClaims::AUDIENCE, []), true);
}
/**
* @param string $id
*
* @return bool
*/
public function isIdentifiedBy($id)
{
return $this->claims->get(RegisteredClaims::ID) === $id;
}
/**
* @param string $subject
*
* @return bool
*/
public function isRelatedTo($subject)
{
return $this->claims->get(RegisteredClaims::SUBJECT) === $subject;
}
/**
* @param list<string> $issuers
*
* @return bool
*/
public function hasBeenIssuedBy(...$issuers)
{
return in_array($this->claims->get(RegisteredClaims::ISSUER), $issuers, true);
}
/**
* @param DateTimeInterface $now
*
* @return bool
*/
public function hasBeenIssuedBefore(DateTimeInterface $now)
{
return $now >= $this->claims->get(RegisteredClaims::ISSUED_AT);
}
/**
* @param DateTimeInterface $now
*
* @return bool
*/
public function isMinimumTimeBefore(DateTimeInterface $now)
{
return $now >= $this->claims->get(RegisteredClaims::NOT_BEFORE);
}
/**
* Yields the validatable claims
*
* @return Generator
*/
private function getValidatableClaims()
{
foreach ($this->getClaims() as $claim) {
if ($claim instanceof Validatable) {
yield $claim;
}
}
}
/**
* Returns the token payload
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::payload()
*
* @return string
*/
public function getPayload()
{
return $this->payload();
}
/**
* Returns the token payload
*
* @return string
*/
public function payload()
{
return $this->headers->toString() . '.' . $this->claims->toString();
}
/** @return Signature */
public function signature()
{
return $this->signature;
}
/**
* Returns an encoded representation of the token
*
* @deprecated This method has been removed from the interface in v4.0
* @see Token::toString()
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
/** @return string */
public function toString()
{
return $this->headers->toString() . '.'
. $this->claims->toString() . '.'
. $this->signature->toString();
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Lcobucci\JWT\Token;
use function array_key_exists;
final class DataSet
{
/** @var array<string, mixed> */
private $data;
/** @var string */
private $encoded;
/**
* @param array<string, mixed> $data
* @param string $encoded
*/
public function __construct(array $data, $encoded)
{
$this->data = $data;
$this->encoded = $encoded;
}
/**
* @param string $name
* @param mixed|null $default
*
* @return mixed|null
*/
public function get($name, $default = null)
{
return $this->has($name) ? $this->data[$name] : $default;
}
/**
* @param string $name
*
* @return bool
*/
public function has($name)
{
return array_key_exists($name, $this->data);
}
/** @return array<string, mixed> */
public function all()
{
return $this->data;
}
/** @return string */
public function toString()
{
return $this->encoded;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidTokenStructure extends InvalidArgumentException implements Exception
{
/** @return self */
public static function missingOrNotEnoughSeparators()
{
return new self('The JWT string must have two dots');
}
/**
* @param string $part
*
* @return self
*/
public static function arrayExpected($part)
{
return new self($part . ' must be an array');
}
/**
* @param string $value
*
* @return self
*/
public static function dateIsNotParseable($value)
{
return new self('Value is not in the allowed date format: ' . $value);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Lcobucci\JWT\Token;
use Lcobucci\JWT\Token;
use function class_alias;
class_exists(Plain::class, false) || class_alias(Token::class, Plain::class);

View File

@@ -0,0 +1,24 @@
<?php
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
use function sprintf;
final class RegisteredClaimGiven extends InvalidArgumentException implements Exception
{
const DEFAULT_MESSAGE = 'Builder#withClaim() is meant to be used for non-registered claims, '
. 'check the documentation on how to set claim "%s"';
/**
* @param string $name
*
* @return self
*/
public static function forClaim($name)
{
return new self(sprintf(self::DEFAULT_MESSAGE, $name));
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace Lcobucci\JWT\Token;
/**
* Defines the list of claims that are registered in the IANA "JSON Web Token Claims" registry
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1
*/
interface RegisteredClaims
{
const ALL = [
self::AUDIENCE,
self::EXPIRATION_TIME,
self::ID,
self::ISSUED_AT,
self::ISSUER,
self::NOT_BEFORE,
self::SUBJECT,
];
const DATE_CLAIMS = [
self::ISSUED_AT,
self::NOT_BEFORE,
self::EXPIRATION_TIME,
];
/**
* Identifies the recipients that the JWT is intended for
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
const AUDIENCE = 'aud';
/**
* Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.4
*/
const EXPIRATION_TIME = 'exp';
/**
* Provides a unique identifier for the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
const ID = 'jti';
/**
* Identifies the time at which the JWT was issued
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.6
*/
const ISSUED_AT = 'iat';
/**
* Identifies the principal that issued the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
const ISSUER = 'iss';
/**
* Identifies the time before which the JWT MUST NOT be accepted for processing
*
* https://tools.ietf.org/html/rfc7519#section-4.1.5
*/
const NOT_BEFORE = 'nbf';
/**
* Identifies the principal that is the subject of the JWT.
*
* https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
const SUBJECT = 'sub';
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Lcobucci\JWT\Token;
use Lcobucci\JWT\Signature as SignatureImpl;
use function class_alias;
class_exists(Signature::class, false) || class_alias(SignatureImpl::class, Signature::class);

View File

@@ -0,0 +1,15 @@
<?php
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class UnsupportedHeaderFound extends InvalidArgumentException implements Exception
{
/** @return self */
public static function encryption()
{
return new self('Encryption is not supported yet');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
interface Constraint
{
/** @throws ConstraintViolation */
public function assert(Token $token);
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class IdentifiedBy implements Constraint
{
/** @var string */
private $id;
/** @param string $id */
public function __construct($id)
{
$this->id = $id;
}
public function assert(Token $token)
{
if (! $token->isIdentifiedBy($this->id)) {
throw new ConstraintViolation(
'The token is not identified with the expected ID'
);
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
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 $issuers;
/** @param list<string> $issuers */
public function __construct(...$issuers)
{
$this->issuers = $issuers;
}
public function assert(Token $token)
{
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
throw new ConstraintViolation(
'The token was not issued by the given issuers'
);
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class LeewayCannotBeNegative extends InvalidArgumentException implements Exception
{
/** @return self */
public static function create()
{
return new self('Leeway cannot be negative');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class PermittedFor implements Constraint
{
/** @var string */
private $audience;
public function __construct($audience)
{
$this->audience = $audience;
}
public function assert(Token $token)
{
if (! $token->isPermittedFor($this->audience)) {
throw new ConstraintViolation(
'The token is not allowed to be used by this audience'
);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class RelatedTo implements Constraint
{
/** @var string */
private $subject;
public function __construct($subject)
{
$this->subject = $subject;
}
public function assert(Token $token)
{
if (! $token->isRelatedTo($this->subject)) {
throw new ConstraintViolation(
'The token is not related to the expected subject'
);
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class SignedWith implements Constraint
{
/** @var Signer */
private $signer;
/** @var Signer\Key */
private $key;
public function __construct(Signer $signer, Signer\Key $key)
{
$this->signer = $signer;
$this->key = $key;
}
public function assert(Token $token)
{
if ($token->headers()->get('alg') !== $this->signer->getAlgorithmId()) {
throw new ConstraintViolation('Token signer mismatch');
}
if (! $this->signer->verify((string) $token->signature(), $token->getPayload(), $this->key)) {
throw new ConstraintViolation('Token signature mismatch');
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\Clock\Clock;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class ValidAt implements Constraint
{
/** @var Clock */
private $clock;
/** @var DateInterval */
private $leeway;
public function __construct(Clock $clock, DateInterval $leeway = null)
{
$this->clock = $clock;
$this->leeway = $this->guardLeeway($leeway);
}
/** @return DateInterval */
private function guardLeeway(DateInterval $leeway = null)
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token)
{
$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)
{
if ($token->isExpired($now)) {
throw new ConstraintViolation('The token is expired');
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(Token $token, DateTimeInterface $now)
{
if (! $token->isMinimumTimeBefore($now)) {
throw new ConstraintViolation('The token cannot be used yet');
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(Token $token, DateTimeInterface $now)
{
if (! $token->hasBeenIssuedBefore($now)) {
throw new ConstraintViolation('The token was issued in the future');
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class ConstraintViolation extends RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class NoConstraintsGiven extends RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,53 @@
<?php
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 $violations = [];
/**
* @param ConstraintViolation ...$violations
* @return self
*/
public static function fromViolations(ConstraintViolation ...$violations)
{
$exception = new self(self::buildMessage($violations));
$exception->violations = $violations;
return $exception;
}
/**
* @param ConstraintViolation[] $violations
*
* @return string
*/
private static function buildMessage(array $violations)
{
$violations = array_map(
static function (ConstraintViolation $violation) {
return '- ' . $violation->getMessage();
},
$violations
);
$message = "The token violates some mandatory constraints, details:\n";
$message .= implode("\n", $violations);
return $message;
}
/** @return ConstraintViolation[] */
public function violations()
{
return $this->violations;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
final class Validator implements \Lcobucci\JWT\Validator
{
public function assert(Token $token, Constraint ...$constraints)
{
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
) {
try {
$constraint->assert($token);
} catch (ConstraintViolation $e) {
$violations[] = $e;
}
}
public function validate(Token $token, Constraint ...$constraints)
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
try {
foreach ($constraints as $constraint) {
$constraint->assert($token);
}
return true;
} catch (ConstraintViolation $e) {
return false;
}
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* This file is part of Lcobucci\JWT, a simple library to handle JWT and JWS
*
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause
*/
namespace Lcobucci\JWT;
/**
* Class that wraps validation values
*
* @deprecated This component has been removed from the interface in v4.0
* @see \Lcobucci\JWT\Validation\Validator
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 2.0.0
*/
class ValidationData
{
/**
* The list of things to be validated
*
* @var array
*/
private $items;
/**
* The leeway (in seconds) to use when validating time claims
* @var int
*/
private $leeway;
/**
* Initializes the object
*
* @param int $currentTime
* @param int $leeway
*/
public function __construct($currentTime = null, $leeway = 0)
{
$currentTime = $currentTime ?: time();
$this->leeway = (int) $leeway;
$this->items = [
'jti' => null,
'iss' => null,
'aud' => null,
'sub' => null
];
$this->setCurrentTime($currentTime);
}
/**
* Configures the id
*
* @param string $id
*/
public function setId($id)
{
$this->items['jti'] = (string) $id;
}
/**
* Configures the issuer
*
* @param string $issuer
*/
public function setIssuer($issuer)
{
$this->items['iss'] = (string) $issuer;
}
/**
* Configures the audience
*
* @param string $audience
*/
public function setAudience($audience)
{
$this->items['aud'] = (string) $audience;
}
/**
* Configures the subject
*
* @param string $subject
*/
public function setSubject($subject)
{
$this->items['sub'] = (string) $subject;
}
/**
* Configures the time that "iat", "nbf" and "exp" should be based on
*
* @param int $currentTime
*/
public function setCurrentTime($currentTime)
{
$currentTime = (int) $currentTime;
$this->items['iat'] = $currentTime + $this->leeway;
$this->items['nbf'] = $currentTime + $this->leeway;
$this->items['exp'] = $currentTime - $this->leeway;
}
/**
* Returns the requested item
*
* @param string $name
*
* @return mixed
*/
public function get($name)
{
return isset($this->items[$name]) ? $this->items[$name] : null;
}
/**
* Returns if the item is present
*
* @param string $name
*
* @return boolean
*/
public function has($name)
{
return !empty($this->items[$name]);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Lcobucci\JWT;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\NoConstraintsGiven;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
interface Validator
{
/**
* @throws RequiredConstraintsViolated
* @throws NoConstraintsGiven
*/
public function assert(Token $token, Constraint ...$constraints);
/**
* @return bool
*
* @throws NoConstraintsGiven
*/
public function validate(Token $token, Constraint ...$constraints);
}