package and depencies

This commit is contained in:
RafficMohammed
2023-01-08 02:57:24 +05:30
parent d5332eb421
commit 1d54b8bc7f
4309 changed files with 193331 additions and 172289 deletions

21
vendor/lcobucci/clock/LICENSE vendored Normal file
View File

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

48
vendor/lcobucci/clock/composer.json vendored Normal file
View File

@@ -0,0 +1,48 @@
{
"name": "lcobucci/clock",
"description": "Yet another clock abstraction",
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com"
}
],
"require": {
"php": "~8.1.0 || ~8.2.0",
"psr/clock": "^1.0"
},
"require-dev": {
"infection/infection": "^0.26",
"lcobucci/coding-standard": "^9.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9.4",
"phpstan/phpstan-deprecation-rules": "^1.1.1",
"phpstan/phpstan-phpunit": "^1.3.2",
"phpstan/phpstan-strict-rules": "^1.4.4",
"phpunit/phpunit": "^9.5.27"
},
"autoload": {
"psr-4": {
"Lcobucci\\Clock\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Lcobucci\\Clock\\": "test"
}
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"infection/extension-installer": true,
"phpstan/extension-installer": true
}
},
"provide": {
"psr/clock-implementation": "1.0"
}
}

6
vendor/lcobucci/clock/renovate.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>lcobucci/.github:renovate-config"
]
}

12
vendor/lcobucci/clock/src/Clock.php vendored Normal file
View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Lcobucci\Clock;
use DateTimeImmutable;
use Psr\Clock\ClockInterface;
interface Clock extends ClockInterface
{
public function now(): DateTimeImmutable;
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Lcobucci\Clock;
use DateTimeImmutable;
use DateTimeZone;
final class FrozenClock implements Clock
{
public function __construct(private DateTimeImmutable $now)
{
}
public static function fromUTC(): self
{
return new self(new DateTimeImmutable('now', new DateTimeZone('UTC')));
}
public function setTo(DateTimeImmutable $now): void
{
$this->now = $now;
}
public function now(): DateTimeImmutable
{
return $this->now;
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Lcobucci\Clock;
use DateTimeImmutable;
use DateTimeZone;
use function date_default_timezone_get;
final class SystemClock implements Clock
{
public function __construct(private readonly DateTimeZone $timezone)
{
}
public static function fromUTC(): self
{
return new self(new DateTimeZone('UTC'));
}
public static function fromSystemTimezone(): self
{
return new self(new DateTimeZone(date_default_timezone_get()));
}
public function now(): DateTimeImmutable
{
return new DateTimeImmutable('now', $this->timezone);
}
}

View File

@@ -1,3 +0,0 @@
/vendor
/phpunit.xml
/composer.lock

View File

@@ -1,56 +0,0 @@
build:
environment:
mysql: false
postgresql: false
redis: false
rabbitmq: false
php:
version: 5.6
tools:
php_sim: true
php_pdepend: true
php_analyzer: true
php_changetracking: true
php_code_sniffer:
config:
standard: "PSR2"
php_mess_detector: true
checks:
php:
code_rating: true
duplication: true
argument_type_checks: true
assignment_of_null_return: true
avoid_conflicting_incrementers: true
avoid_useless_overridden_methods: true
catch_class_exists: true
closure_use_modifiable: true
closure_use_not_conflicting: true
deprecated_code_usage: true
method_calls_on_non_object: true
missing_arguments: true
no_duplicate_arguments: true
no_non_implemented_abstract_methods: true
no_property_on_interface: true
parameter_non_unique: true
precedence_in_conditions: true
precedence_mistakes: true
require_php_tag_first: true
security_vulnerabilities: true
sql_injection_vulnerabilities: true
too_many_arguments: true
unreachable_code: true
unused_methods: true
unused_parameters: true
unused_properties: true
unused_variables: true
use_statement_alias_conflict: true
useless_calls: true
variable_existence: true
verify_access_scope_valid: true
verify_argument_usable_as_reference: true
verify_property_names: true
filter:
excluded_paths:
- test/*

View File

@@ -1,15 +0,0 @@
language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- nightly
matrix:
allow_failures:
- php: nightly
before_script:
- composer install --prefer-dist -o

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014-2015, Luís Otávio Cobucci Oblonczyk
Copyright (c) 2014, Luís Cobucci
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,194 +0,0 @@
# JWT
[![Gitter](https://img.shields.io/badge/GITTER-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat-square)](https://gitter.im/lcobucci/jwt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Total Downloads](https://img.shields.io/packagist/dt/lcobucci/jwt.svg?style=flat-square)](https://packagist.org/packages/lcobucci/jwt) [![Latest Stable Version](https://img.shields.io/packagist/v/lcobucci/jwt.svg?style=flat-square)](https://packagist.org/packages/lcobucci/jwt)
![Branch master](https://img.shields.io/badge/branch-master-brightgreen.svg?style=flat-square)
[![Build Status](https://img.shields.io/travis/lcobucci/jwt/master.svg?style=flat-square)](http://travis-ci.org/#!/lcobucci/jwt)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/lcobucci/jwt/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/lcobucci/jwt/?branch=master)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/lcobucci/jwt/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/lcobucci/jwt/?branch=master)
A simple library to work with JSON Web Token and JSON Web Signature (requires PHP 5.6+).
The implementation is based on the [RFC 7519](https://tools.ietf.org/html/rfc7519).
## Installation
Package is available on [Packagist](http://packagist.org/packages/lcobucci/jwt),
you can install it using [Composer](http://getcomposer.org).
```shell
composer require lcobucci/jwt
```
### Dependencies
- PHP 5.6+
- OpenSSL Extension
## Basic usage
### Creating
Just use the builder to create a new JWT/JWS tokens:
```php
use Lcobucci\JWT\Builder;
$time = time();
$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->permittedFor('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
->withClaim('uid', 1) // Configures a new claim, called "uid"
->getToken(); // Retrieves the generated token
$token->getHeaders(); // Retrieves the token headers
$token->getClaims(); // Retrieves the token claims
echo $token->getHeader('jti'); // will print "4f1g23a12aa"
echo $token->getClaim('iss'); // will print "http://example.com"
echo $token->getClaim('uid'); // will print "1"
echo $token; // The string representation of the object is a JWT string (pretty easy, right?)
```
### Parsing from strings
Use the parser to create a new token from a JWT string (using the previous token as example):
```php
use Lcobucci\JWT\Parser;
$token = (new Parser())->parse((string) $token); // Parses from a string
$token->getHeaders(); // Retrieves the token header
$token->getClaims(); // Retrieves the token claims
echo $token->getHeader('jti'); // will print "4f1g23a12aa"
echo $token->getClaim('iss'); // will print "http://example.com"
echo $token->getClaim('uid'); // will print "1"
```
### Validating
We can easily validate if the token is valid (using the previous token and time as example):
```php
use Lcobucci\JWT\ValidationData;
$data = new ValidationData(); // It will use the current time to validate (iat, nbf and exp)
$data->setIssuer('http://example.com');
$data->setAudience('http://example.org');
$data->setId('4f1g23a12aa');
var_dump($token->validate($data)); // false, because token cannot be used before now() + 60
$data->setCurrentTime($time + 61); // changing the validation time to future
var_dump($token->validate($data)); // true, because current time is between "nbf" and "exp" claims
$data->setCurrentTime($time + 4000); // changing the validation time to future
var_dump($token->validate($data)); // false, because token is expired since current time is greater than exp
// We can also use the $leeway parameter to deal with clock skew (see notes below)
// If token's claimed time is invalid but the difference between that and the validation time is less than $leeway,
// then token is still considered valid
$dataWithLeeway = new ValidationData($time, 20);
$dataWithLeeway->setIssuer('http://example.com');
$dataWithLeeway->setAudience('http://example.org');
$dataWithLeeway->setId('4f1g23a12aa');
var_dump($token->validate($dataWithLeeway)); // false, because token can't be used before now() + 60, not within leeway
$dataWithLeeway->setCurrentTime($time + 51); // changing the validation time to future
var_dump($token->validate($dataWithLeeway)); // true, because current time plus leeway is between "nbf" and "exp" claims
$dataWithLeeway->setCurrentTime($time + 3610); // changing the validation time to future but within leeway
var_dump($token->validate($dataWithLeeway)); // true, because current time - 20 seconds leeway is less than exp
$dataWithLeeway->setCurrentTime($time + 4000); // changing the validation time to future outside of leeway
var_dump($token->validate($dataWithLeeway)); // false, because token is expired since current time is greater than exp
```
#### Important
- You have to configure ```ValidationData``` informing all claims you want to validate the token.
- If ```ValidationData``` contains claims that are not being used in token or token has claims that are not
configured in ```ValidationData``` they will be ignored by ```Token::validate()```.
- ```exp```, ```nbf``` and ```iat``` claims are configured by default in ```ValidationData::__construct()```
with the current UNIX time (```time()```).
- The optional ```$leeway``` parameter of ```ValidationData``` will cause us to use that number of seconds of leeway
when validating the time-based claims, pretending we are further in the future for the "Issued At" (```iat```) and "Not
Before" (```nbf```) claims and pretending we are further in the past for the "Expiration Time" (```exp```) claim. This
allows for situations where the clock of the issuing server has a different time than the clock of the verifying server,
as mentioned in [section 4.1 of RFC 7519](https://tools.ietf.org/html/rfc7519#section-4.1).
## Token signature
We can use signatures to be able to verify if the token was not modified after its generation. This library implements Hmac, RSA and ECDSA signatures (using 256, 384 and 512).
### Important
Do not allow the string sent to the Parser to dictate which signature algorithm
to use, or else your application will be vulnerable to a [critical JWT security vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries).
The examples below are safe because the choice in `Signer` is hard-coded and
cannot be influenced by malicious users.
### Hmac
Hmac signatures are really simple to be used:
```php
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Hmac\Sha256;
$signer = new Sha256();
$time = time();
$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->permittedFor('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
->withClaim('uid', 1) // Configures a new claim, called "uid"
->getToken($signer, new Key('testing')); // Retrieves the generated token
var_dump($token->verify($signer, 'testing 1')); // false, because the key is different
var_dump($token->verify($signer, 'testing')); // true, because the key is the same
```
### RSA and ECDSA
RSA and ECDSA signatures are based on public and private keys so you have to generate using the private key and verify using the public key:
```php
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256; // you can use Lcobucci\JWT\Signer\Ecdsa\Sha256 if you're using ECDSA keys
$signer = new Sha256();
$privateKey = new Key('file://{path to your private key}');
$time = time();
$token = (new Builder())->issuedBy('http://example.com') // Configures the issuer (iss claim)
->permittedFor('http://example.org') // Configures the audience (aud claim)
->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
->issuedAt($time) // Configures the time that the token was issue (iat claim)
->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
->withClaim('uid', 1) // Configures a new claim, called "uid"
->getToken($signer, $privateKey); // Retrieves the generated token
$publicKey = new Key('file://{path to your public key}');
var_dump($token->verify($signer, $publicKey)); // true when the public key was generated by the private one =)
```
**It's important to say that if you're using RSA keys you shouldn't invoke ECDSA signers (and vice-versa), otherwise ```sign()``` and ```verify()``` will raise an exception!**

View File

@@ -1,32 +1,42 @@
{
"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"
}
"license": [
"BSD-3-Clause"
],
"type": "library",
"keywords": [
"JWT",
"JWS"
],
"license": [
"BSD-3-Clause"
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"require": {
"php": "^5.6 || ^7.0",
"php": "^7.4 || ^8.0",
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*"
"ext-openssl": "*",
"ext-sodium": "*",
"lcobucci/clock": "^2.0 || ^3.0"
},
"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"
"infection/infection": "^0.21",
"lcobucci/coding-standard": "^6.0",
"mikey179/vfsstream": "^1.6.7",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/php-invoker": "^3.1",
"phpunit/phpunit": "^9.5"
},
"autoload": {
"psr-4": {
@@ -36,14 +46,21 @@
"autoload-dev": {
"psr-4": {
"Lcobucci\\JWT\\": [
"test/_keys",
"test/unit",
"test/functional"
]
"test/performance"
],
"Lcobucci\\JWT\\FunctionalTests\\": "test/functional"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"phpstan/extension-installer": true,
"infection/extension-installer": true,
"ocramius/package-versions": true
},
"preferred-install": "dist",
"sort-packages": true
}
}

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
verbose="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
beStrictAboutChangesToGlobalState="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutResourceUsageDuringSmallTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
forceCoversAnnotation="true">
<testsuites>
<testsuite name="Unit Test Suite">
<directory>test/unit</directory>
</testsuite>
<testsuite name="Integration Test Suite">
<directory>test/functional</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -1,472 +1,77 @@
<?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
*/
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Claim\Factory as ClaimFactory;
use Lcobucci\JWT\Parsing\Encoder;
use DateTimeImmutable;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use function implode;
use Lcobucci\JWT\Token\Plain;
use Lcobucci\JWT\Token\RegisteredClaimGiven;
/**
* This class makes easier the token creation process
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Builder
interface Builder
{
/**
* The token header
*
* @var array
* Appends new items to audience
*/
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->setRegisteredClaim('aud', (string) $audience, $replicateAsHeader);
}
/**
* Configures the audience
*
* @param string $audience
* @param bool $replicateAsHeader
*
* @return Builder
*/
public function permittedFor($audience, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('aud', (string) $audience, $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->setRegisteredClaim('aud', (string) $audience, $replicateAsHeader);
}
public function permittedFor(string ...$audiences): Builder;
/**
* Configures the expiration time
*
* @param int $expiration
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function expiresAt($expiration, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('exp', (int) $expiration, $replicateAsHeader);
}
/**
* Configures the expiration time
*
* @deprecated This method will be removed on v4
* @see Builder::expiresAt()
*
* @param int $expiration
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setExpiration($expiration, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('exp', (int) $expiration, $replicateAsHeader);
}
public function expiresAt(DateTimeImmutable $expiration): Builder;
/**
* 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);
}
public function identifiedBy(string $id): Builder;
/**
* Configures the time that the token was issued
*
* @param int $issuedAt
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function issuedAt($issuedAt, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('iat', (int) $issuedAt, $replicateAsHeader);
}
/**
* Configures the time that the token was issued
*
* @deprecated This method will be removed on v4
* @see Builder::issuedAt()
*
* @param int $issuedAt
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setIssuedAt($issuedAt, $replicateAsHeader = false)
{
return $this->issuedAt($issuedAt, $replicateAsHeader);
}
public function issuedAt(DateTimeImmutable $issuedAt): Builder;
/**
* 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);
}
public function issuedBy(string $issuer): Builder;
/**
* Configures the time before which the token cannot be accepted
*
* @param int $notBefore
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function canOnlyBeUsedAfter($notBefore, $replicateAsHeader = false)
{
return $this->setRegisteredClaim('nbf', (int) $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 $notBefore
* @param boolean $replicateAsHeader
*
* @return Builder
*/
public function setNotBefore($notBefore, $replicateAsHeader = false)
{
return $this->canOnlyBeUsedAfter($notBefore, $replicateAsHeader);
}
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): Builder;
/**
* 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->withClaim($name, $value);
if ($replicate) {
$this->headers[$name] = $this->claims[$name];
}
return $this;
}
public function relatedTo(string $subject): Builder;
/**
* Configures a header item
*
* @param string $name
* @param mixed $value
*
* @return Builder
*/
public function withHeader($name, $value)
{
$this->headers[(string) $name] = $this->claimFactory->create($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);
}
public function withHeader(string $name, $value): Builder;
/**
* 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
* @throws RegisteredClaimGiven When trying to set a registered claim.
*/
public function with($name, $value)
{
return $this->withClaim($name, $value);
}
public function withClaim(string $name, $value): Builder;
/**
* Configures a claim item
* Returns a signed token to be used
*
* @param string $name
* @param mixed $value
*
* @return Builder
* @throws CannotEncodeContent When data cannot be converted to JSON.
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
public function withClaim($name, $value)
{
$this->claims[(string) $name] = $this->claimFactory->create($name, $value);
return $this;
}
/**
* 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->withClaim($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) {
$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)
{
$signer = $signer ?: $this->signer;
$key = $key ?: $this->key;
if ($signer instanceof Signer) {
$signer->modifyHeader($this->headers);
}
$payload = [
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->headers)),
$this->encoder->base64UrlEncode($this->encoder->jsonEncode($this->claims))
];
$signature = $this->createSignature($payload, $signer, $key);
if ($signature !== null) {
$payload[] = $this->encoder->base64UrlEncode($signature);
}
return new Token($this->headers, $this->claims, $signature, $payload);
}
/**
* @param string[] $payload
*
* @return Signature|null
*/
private function createSignature(array $payload, Signer $signer = null, Key $key = null)
{
if ($signer === null || $key === null) {
return null;
}
return $signer->sign(implode('.', $payload), $key);
}
public function getToken(Signer $signer, Key $key): Plain;
}

View File

@@ -1,40 +0,0 @@
<?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

@@ -1,75 +0,0 @@
<?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

@@ -1,34 +0,0 @@
<?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

@@ -1,118 +0,0 @@
<?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;
/**
* 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 (!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

@@ -1,34 +0,0 @@
<?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

@@ -1,34 +0,0 @@
<?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

@@ -1,30 +0,0 @@
<?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,14 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
interface ClaimsFormatter
{
/**
* @param array<string, mixed> $claims
*
* @return array<string, mixed>
*/
public function formatClaims(array $claims): array;
}

View File

@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
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
{
private Parser $parser;
private Signer $signer;
private Key $signingKey;
private Key $verificationKey;
private Validator $validator;
/** @var Closure(ClaimsFormatter $claimFormatter): Builder */
private Closure $builderFactory;
/** @var Constraint[] */
private array $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 Token\Parser($decoder ?? new JoseEncoder());
$this->validator = new Validation\Validator();
$this->builderFactory = static function (ClaimsFormatter $claimFormatter) use ($encoder): Builder {
return new Token\Builder($encoder ?? new JoseEncoder(), $claimFormatter);
};
}
public static function forAsymmetricSigner(
Signer $signer,
Key $signingKey,
Key $verificationKey,
?Encoder $encoder = null,
?Decoder $decoder = null
): self {
return new self(
$signer,
$signingKey,
$verificationKey,
$encoder,
$decoder
);
}
public static function forSymmetricSigner(
Signer $signer,
Key $key,
?Encoder $encoder = null,
?Decoder $decoder = null
): self {
return new self(
$signer,
$key,
$key,
$encoder,
$decoder
);
}
/** @deprecated Deprecated since v4.3 */
public static function forUnsecuredSigner(
?Encoder $encoder = null,
?Decoder $decoder = null
): self {
$key = InMemory::empty();
return new self(
new None(),
$key,
$key,
$encoder,
$decoder
);
}
/** @param callable(ClaimsFormatter): Builder $builderFactory */
public function setBuilderFactory(callable $builderFactory): void
{
$this->builderFactory = Closure::fromCallable($builderFactory);
}
public function builder(?ClaimsFormatter $claimFormatter = null): Builder
{
return ($this->builderFactory)($claimFormatter ?? ChainedFormatter::default());
}
public function parser(): Parser
{
return $this->parser;
}
public function setParser(Parser $parser): void
{
$this->parser = $parser;
}
public function signer(): Signer
{
return $this->signer;
}
public function signingKey(): Key
{
return $this->signingKey;
}
public function verificationKey(): Key
{
return $this->verificationKey;
}
public function validator(): Validator
{
return $this->validator;
}
public function setValidator(Validator $validator): void
{
$this->validator = $validator;
}
/** @return Constraint[] */
public function validationConstraints(): array
{
return $this->validationConstraints;
}
public function setValidationConstraints(Constraint ...$validationConstraints): void
{
$this->validationConstraints = $validationConstraints;
}
}

27
vendor/lcobucci/jwt/src/Decoder.php vendored Normal file
View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
interface Decoder
{
/**
* Decodes from JSON, validating the errors
*
* @return mixed
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
public function jsonDecode(string $json);
/**
* Decodes from Base64URL
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
public function base64UrlDecode(string $data): string;
}

25
vendor/lcobucci/jwt/src/Encoder.php vendored Normal file
View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
interface Encoder
{
/**
* Encodes to JSON, validating the errors
*
* @param mixed $data
*
* @throws CannotEncodeContent When something goes wrong while encoding.
*/
public function jsonEncode($data): string;
/**
* Encodes to base64url
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*/
public function base64UrlEncode(string $data): string;
}

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
final class ChainedFormatter implements ClaimsFormatter
{
/** @var list<ClaimsFormatter> */
private array $formatters;
public function __construct(ClaimsFormatter ...$formatters)
{
$this->formatters = $formatters;
}
public static function default(): self
{
return new self(new UnifyAudience(), new MicrosecondBasedDateConversion());
}
public static function withUnixTimestampDates(): self
{
return new self(new UnifyAudience(), new UnixTimestampDates());
}
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach ($this->formatters as $formatter) {
$claims = $formatter->formatClaims($claims);
}
return $claims;
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\SodiumBase64Polyfill;
use function json_decode;
use function json_encode;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
/**
* A utilitarian class that encodes and decodes data according with JOSE specifications
*/
final class JoseEncoder implements Encoder, Decoder
{
private const JSON_DEFAULT_DEPTH = 512;
/** @inheritdoc */
public function jsonEncode($data): string
{
try {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotEncodeContent::jsonIssues($exception);
}
}
/** @inheritdoc */
public function jsonDecode(string $json)
{
try {
return json_decode($json, true, self::JSON_DEFAULT_DEPTH, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotDecodeContent::jsonIssues($exception);
}
}
public function base64UrlEncode(string $data): string
{
return SodiumBase64Polyfill::bin2base64(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
);
}
public function base64UrlDecode(string $data): string
{
return SodiumBase64Polyfill::base642bin(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final class MicrosecondBasedDateConversion implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
/** @return int|float */
private function convertDate(DateTimeImmutable $date)
{
if ($date->format('u') === '000000') {
return (int) $date->format('U');
}
return (float) $date->format('U.u');
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
use function count;
use function current;
final class UnifyAudience implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
if (
! array_key_exists(RegisteredClaims::AUDIENCE, $claims)
|| count($claims[RegisteredClaims::AUDIENCE]) !== 1
) {
return $claims;
}
$claims[RegisteredClaims::AUDIENCE] = current($claims[RegisteredClaims::AUDIENCE]);
return $claims;
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final class UnixTimestampDates implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
private function convertDate(DateTimeImmutable $date): int
{
return $date->getTimestamp();
}
}

10
vendor/lcobucci/jwt/src/Exception.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Throwable;
interface Exception extends Throwable
{
}

66
vendor/lcobucci/jwt/src/JwtFacade.php vendored Normal file
View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use DateTimeImmutable;
use Lcobucci\Clock\Clock;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\SignedWith;
use Lcobucci\JWT\Validation\ValidAt;
use Lcobucci\JWT\Validation\Validator;
use function assert;
final class JwtFacade
{
private Parser $parser;
private Clock $clock;
public function __construct(?Parser $parser = null, ?Clock $clock = null)
{
$this->parser = $parser ?? new Token\Parser(new JoseEncoder());
$this->clock = $clock ?? SystemClock::fromSystemTimezone();
}
/** @param Closure(Builder, DateTimeImmutable):Builder $customiseBuilder */
public function issue(
Signer $signer,
Key $signingKey,
Closure $customiseBuilder
): UnencryptedToken {
$builder = new Token\Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates());
$now = $this->clock->now();
$builder
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($now->modify('+5 minutes'));
return $customiseBuilder($builder, $now)->getToken($signer, $signingKey);
}
public function parse(
string $jwt,
SignedWith $signedWith,
ValidAt $validAt,
Constraint ...$constraints
): UnencryptedToken {
$token = $this->parser->parse($jwt);
assert($token instanceof UnencryptedToken);
(new Validator())->assert(
$token,
$signedWith,
$validAt,
...$constraints
);
return $token;
}
}

View File

@@ -1,157 +1,20 @@
<?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
*/
declare(strict_types=1);
namespace Lcobucci\JWT;
use InvalidArgumentException;
use Lcobucci\JWT\Claim\Factory as ClaimFactory;
use Lcobucci\JWT\Parsing\Decoder;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
/**
* 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
interface Parser
{
/**
* The data decoder
*
* @var Decoder
*/
private $decoder;
/**
* The claims factory
*
* @var ClaimFactory
*/
private $claimFactory;
/**
* Initializes the object
*
* @param Decoder $decoder
* @param ClaimFactory $claimFactory
*/
public function __construct(
Decoder $decoder = null,
ClaimFactory $claimFactory = null
) {
$this->decoder = $decoder ?: new Decoder();
$this->claimFactory = $claimFactory ?: new ClaimFactory();
}
/**
* Parses the JWT and returns a token
*
* @param string $jwt
*
* @return Token
* @throws CannotDecodeContent When something goes wrong while decoding.
* @throws InvalidTokenStructure When token string structure is invalid.
* @throws UnsupportedHeaderFound When parsed token has an unsupported header.
*/
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;
}
}
if ($signature === null) {
unset($data[2]);
}
return new Token($header, $claims, $signature, $data);
}
/**
* 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 new InvalidArgumentException('The JWT string must have two dots');
}
$data = explode('.', $jwt);
if (count($data) != 3) {
throw new InvalidArgumentException('The JWT string must have two dots');
}
return $data;
}
/**
* Parses the header from a string
*
* @param string $data
*
* @return array
*
* @throws InvalidArgumentException When an invalid header is informed
*/
protected function parseHeader($data)
{
$header = (array) $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (isset($header['enc'])) {
throw new InvalidArgumentException('Encryption is not supported yet');
}
return $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));
foreach ($claims as $name => &$value) {
$value = $this->claimFactory->create($name, $value);
}
return $claims;
}
/**
* 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 null;
}
$hash = $this->decoder->base64UrlDecode($data);
return new Signature($hash);
}
public function parse(string $jwt): Token;
}

View File

@@ -1,56 +0,0 @@
<?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 RuntimeException;
/**
* 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)
{
$data = json_decode($json);
if (json_last_error() != JSON_ERROR_NONE) {
throw new RuntimeException('Error while decoding to JSON: ' . json_last_error_msg());
}
return $data;
}
/**
* 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

@@ -1,51 +0,0 @@
<?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 RuntimeException;
/**
* 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)
{
$json = json_encode($data);
if (json_last_error() != JSON_ERROR_NONE) {
throw new RuntimeException('Error while encoding to JSON: ' . json_last_error_msg());
}
return $json;
}
/**
* Encodes to base64url
*
* @param string $data
* @return string
*/
public function base64UrlEncode($data)
{
return str_replace('=', '', strtr(base64_encode($data), '+/', '-_'));
}
}

View File

@@ -1,61 +0,0 @@
<?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;
/**
* Initializes the object
*
* @param string $hash
*/
public function __construct($hash)
{
$this->hash = $hash;
}
/**
* 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
*
* @return string
*/
public function __toString()
{
return $this->hash;
}
}

View File

@@ -1,59 +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
*/
declare(strict_types=1);
namespace Lcobucci\JWT;
use InvalidArgumentException;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
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();
public function algorithmId(): string;
/**
* Apply changes on headers according with algorithm
* Creates a hash for the given payload
*
* @param array $headers
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
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);
public function sign(string $payload, Key $key): string;
/**
* 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
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
public function verify($expected, $payload, $key);
public function verify(string $expected, string $payload, Key $key): bool;
}

View File

@@ -1,85 +0,0 @@
<?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;
/**
* 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)) {
$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,36 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use function hash_equals;
use function sodium_crypto_generichash;
use function strlen;
final class Blake2b implements Signer
{
private const MINIMUM_KEY_LENGTH_IN_BITS = 256;
public function algorithmId(): string
{
return 'BLAKE2B';
}
public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());
if ($actualKeyLength < self::MINIMUM_KEY_LENGTH_IN_BITS) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH_IN_BITS, $actualKeyLength);
}
return sodium_crypto_generichash($payload, $key->contents());
}
public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
}

View File

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

View File

@@ -1,69 +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
*/
declare(strict_types=1);
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;
private SignatureConverter $converter;
public function __construct(SignatureConverter $converter = null)
public function __construct(?SignatureConverter $converter = null)
{
$this->converter = $converter ?: new MultibyteStringConverter();
$this->converter = $converter ?? new MultibyteStringConverter();
}
/**
* {@inheritdoc}
*/
public function createHash($payload, Key $key)
/** @deprecated */
public static function create(): Ecdsa
{
return new static(); // @phpstan-ignore-line
}
final public function sign(string $payload, Key $key): string
{
return $this->converter->fromAsn1(
parent::createHash($payload, $key),
$this->getKeyLength()
$this->createSignature($key->contents(), $key->passphrase(), $payload),
$this->pointLength()
);
}
/**
* {@inheritdoc}
*/
public function doVerify($expected, $payload, Key $key)
final public function verify(string $expected, string $payload, Key $key): bool
{
return parent::doVerify(
$this->converter->toAsn1($expected, $this->getKeyLength()),
return $this->verifySignature(
$this->converter->toAsn1($expected, $this->pointLength()),
$payload,
$key
$key->contents()
);
}
/** {@inheritdoc} */
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_EC) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_EC],
self::KEY_TYPE_MAP[$type],
);
}
$expectedKeyLength = $this->expectedKeyLength();
if ($lengthInBits !== $expectedKeyLength) {
throw InvalidKeyProvided::incompatibleKeyLength($expectedKeyLength, $lengthInBits);
}
}
/** @internal */
abstract public function expectedKeyLength(): int;
/**
* 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;
}
abstract public function pointLength(): int;
}

View File

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

View File

@@ -1,4 +1,6 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
@@ -9,16 +11,19 @@
*
* @link https://github.com/web-token/jwt-framework/blob/v1.2/src/Component/Core/Util/ECSignature.php
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use InvalidArgumentException;
use function assert;
use function bin2hex;
use function dechex;
use function hex2bin;
use function hexdec;
use function is_string;
use function mb_strlen;
use function mb_substr;
use function str_pad;
use const STR_PAD_LEFT;
/**
@@ -28,24 +33,24 @@ use const STR_PAD_LEFT;
*/
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;
private const ASN1_SEQUENCE = '30';
private const ASN1_INTEGER = '02';
private const ASN1_MAX_SINGLE_BYTE = 128;
private const ASN1_LENGTH_2BYTES = '81';
private const ASN1_BIG_INTEGER_LIMIT = '7f';
private const ASN1_NEGATIVE_INTEGER = '00';
private const BYTE_SIZE = 2;
public function toAsn1($signature, $length)
public function toAsn1(string $points, int $length): string
{
$signature = bin2hex($signature);
$points = bin2hex($points);
if (self::octetLength($signature) !== $length) {
throw new InvalidArgumentException('Invalid signature length.');
if (self::octetLength($points) !== $length) {
throw ConversionFailed::invalidLength();
}
$pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
$pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
$pointR = self::preparePositiveInteger(mb_substr($points, 0, $length, '8bit'));
$pointS = self::preparePositiveInteger(mb_substr($points, $length, null, '8bit'));
$lengthR = self::octetLength($pointR);
$lengthS = self::octetLength($pointS);
@@ -59,38 +64,42 @@ final class MultibyteStringConverter implements SignatureConverter
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
. self::ASN1_INTEGER . dechex($lengthS) . $pointS
);
assert(is_string($asn1));
return $asn1;
}
private static function octetLength($data)
private static function octetLength(string $data): int
{
return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE);
}
private static function preparePositiveInteger($data)
private static function preparePositiveInteger(string $data): string
{
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) {
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)
public function fromAsn1(string $signature, int $length): string
{
$message = bin2hex($signature);
$position = 0;
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_SEQUENCE) {
throw new InvalidArgumentException('Invalid data. Should start with a sequence.');
throw ConversionFailed::incorrectStartSequence();
}
// @phpstan-ignore-next-line
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
$position += self::BYTE_SIZE;
}
@@ -99,11 +108,12 @@ final class MultibyteStringConverter implements SignatureConverter
$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));
assert(is_string($points));
return $points;
}
private static function readAsn1Content($message, &$position, $length)
private static function readAsn1Content(string $message, int &$position, int $length): string
{
$content = mb_substr($message, $position, $length, '8bit');
$position += $length;
@@ -111,10 +121,10 @@ final class MultibyteStringConverter implements SignatureConverter
return $content;
}
private static function readAsn1Integer($message, &$position)
private static function readAsn1Integer(string $message, int &$position): string
{
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
throw new InvalidArgumentException('Invalid data. Should contain an integer.');
throw ConversionFailed::integerExpected();
}
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
@@ -122,10 +132,12 @@ final class MultibyteStringConverter implements SignatureConverter
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
}
private static function retrievePositiveInteger($data)
private static function retrievePositiveInteger(string $data): string
{
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) {
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');
}

View File

@@ -1,43 +1,31 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA256;
final class Sha256 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'ES256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return 'sha256';
return OPENSSL_ALGO_SHA256;
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
public function pointLength(): int
{
return 64;
}
public function expectedKeyLength(): int
{
return 256;
}
}

View File

@@ -1,43 +1,31 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA384;
final class Sha384 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'ES384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return 'sha384';
return OPENSSL_ALGO_SHA384;
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
public function pointLength(): int
{
return 96;
}
public function expectedKeyLength(): int
{
return 384;
}
}

View File

@@ -1,43 +1,31 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA512;
final class Sha512 extends Ecdsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'ES512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return 'sha512';
return OPENSSL_ALGO_SHA512;
}
/**
* {@inheritdoc}
*/
public function getKeyLength()
public function pointLength(): int
{
return 132;
}
public function expectedKeyLength(): int
{
return 521;
}
}

View File

@@ -1,4 +1,6 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
/**
@@ -19,20 +21,14 @@ interface SignatureConverter
/**
* Converts the signature generated by OpenSSL into what JWA defines
*
* @param string $signature
* @param int $length
*
* @return string
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function fromAsn1($signature, $length);
public function fromAsn1(string $signature, int $length): string;
/**
* Converts the JWA signature into something OpenSSL understands
*
* @param string $points
* @param int $length
*
* @return string
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function toAsn1($points, $length);
public function toAsn1(string $points, int $length): string;
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\UnsafeEcdsa;
use const OPENSSL_ALGO_SHA256;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha256 extends UnsafeEcdsa
{
public function algorithmId(): string
{
return 'ES256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
public function pointLength(): int
{
return 64;
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\UnsafeEcdsa;
use const OPENSSL_ALGO_SHA384;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha384 extends UnsafeEcdsa
{
public function algorithmId(): string
{
return 'ES384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
public function pointLength(): int
{
return 96;
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\UnsafeEcdsa;
use const OPENSSL_ALGO_SHA512;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha512 extends UnsafeEcdsa
{
public function algorithmId(): string
{
return 'ES512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
public function pointLength(): int
{
return 132;
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use SodiumException;
use function sodium_crypto_sign_detached;
use function sodium_crypto_sign_verify_detached;
final class Eddsa implements Signer
{
public function algorithmId(): string
{
return 'EdDSA';
}
public function sign(string $payload, Key $key): string
{
try {
return sodium_crypto_sign_detached($payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
public function verify(string $expected, string $payload, Key $key): bool
{
try {
return sodium_crypto_sign_verify_detached($expected, $payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
}

View File

@@ -1,75 +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
*/
declare(strict_types=1);
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
use Lcobucci\JWT\Signer;
use function hash_equals;
use function hash_hmac;
use function strlen;
abstract class Hmac implements Signer
{
/**
* {@inheritdoc}
*/
public function createHash($payload, Key $key)
final public function sign(string $payload, Key $key): string
{
return hash_hmac($this->getAlgorithm(), $payload, $key->getContent(), true);
}
/**
* {@inheritdoc}
*/
public function doVerify($expected, $payload, Key $key)
{
if (!is_string($expected)) {
return false;
$actualKeyLength = 8 * strlen($key->contents());
$expectedKeyLength = $this->minimumBitsLengthForKey();
if ($actualKeyLength < $expectedKeyLength) {
throw InvalidKeyProvided::tooShort($expectedKeyLength, $actualKeyLength);
}
$callback = function_exists('hash_equals') ? 'hash_equals' : [$this, 'hashEquals'];
return call_user_func($callback, $expected, $this->createHash($payload, $key));
return hash_hmac($this->algorithm(), $payload, $key->contents(), true);
}
/**
* PHP < 5.6 timing attack safe hash comparison
*
* @internal
*
* @param string $expected
* @param string $generated
*
* @return boolean
*/
public function hashEquals($expected, $generated)
final public function verify(string $expected, string $payload, Key $key): bool
{
$expectedLength = strlen($expected);
if ($expectedLength !== strlen($generated)) {
return false;
}
$res = 0;
for ($i = 0; $i < $expectedLength; ++$i) {
$res |= ord($expected[$i]) ^ ord($generated[$i]);
}
return $res === 0;
return hash_equals($expected, $this->sign($payload, $key));
}
/**
* Returns the algorithm name
*
* @internal
*
* @return string
*/
abstract public function getAlgorithm();
abstract public function algorithm(): string;
/** @return positive-int */
abstract public function minimumBitsLengthForKey(): int;
}

View File

@@ -1,35 +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
*/
declare(strict_types=1);
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
final class Sha256 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'HS256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): string
{
return 'sha256';
}
public function minimumBitsLengthForKey(): int
{
return 256;
}
}

View File

@@ -1,35 +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
*/
declare(strict_types=1);
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
final class Sha384 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'HS384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): string
{
return 'sha384';
}
public function minimumBitsLengthForKey(): int
{
return 384;
}
}

View File

@@ -1,35 +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
*/
declare(strict_types=1);
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
final class Sha512 extends Hmac
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'HS512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): string
{
return 'sha512';
}
public function minimumBitsLengthForKey(): int
{
return 512;
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha256 extends Hmac
{
public function algorithmId(): string
{
return 'HS256';
}
public function algorithm(): string
{
return 'sha256';
}
public function minimumBitsLengthForKey(): int
{
return 1;
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha384 extends Hmac
{
public function algorithmId(): string
{
return 'HS384';
}
public function algorithm(): string
{
return 'sha384';
}
public function minimumBitsLengthForKey(): int
{
return 1;
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha512 extends Hmac
{
public function algorithmId(): string
{
return 'HS512';
}
public function algorithm(): string
{
return 'sha512';
}
public function minimumBitsLengthForKey(): int
{
return 1;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidKeyProvided extends InvalidArgumentException implements Exception
{
public static function cannotBeParsed(string $details): self
{
return new self('It was not possible to parse your key, reason:' . $details);
}
public static function incompatibleKeyType(string $expectedType, string $actualType): self
{
return new self(
'The type of the provided key is not "' . $expectedType
. '", "' . $actualType . '" provided'
);
}
public static function incompatibleKeyLength(int $expectedLength, int $actualLength): self
{
return new self(
'The length of the provided key is different than ' . $expectedLength . ' bits, '
. $actualLength . ' bits provided'
);
}
public static function cannotBeEmpty(): self
{
return new self('Key cannot be empty');
}
public static function tooShort(int $expectedLength, int $actualLength): self
{
return new self('Key provided is shorter than ' . $expectedLength . ' bits,'
. ' only ' . $actualLength . ' bits provided');
}
}

View File

@@ -1,92 +1,11 @@
<?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
*/
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Exception;
use InvalidArgumentException;
use SplFileObject;
/**
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 3.0.4
*/
final class Key
interface Key
{
/**
* @var string
*/
private $content;
public function contents(): string;
/**
* @var string
*/
private $passphrase;
/**
* @param string $content
* @param string $passphrase
*/
public function __construct($content, $passphrase = null)
{
$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)
{
try {
$file = new SplFileObject(substr($content, 7));
$content = '';
while (! $file->eof()) {
$content .= $file->fgets();
}
return $content;
} catch (Exception $exception) {
throw new InvalidArgumentException('You must provide a valid key file', 0, $exception);
}
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @return string
*/
public function getPassphrase()
{
return $this->passphrase;
}
public function passphrase(): string;
}

View File

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

View File

@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\SodiumBase64Polyfill;
use SplFileObject;
use Throwable;
use function assert;
use function is_string;
final class InMemory implements Key
{
private string $contents;
private string $passphrase;
/** @param non-empty-string $contents */
private function __construct(string $contents, string $passphrase)
{
// @phpstan-ignore-next-line
if ($contents === '') {
throw InvalidKeyProvided::cannotBeEmpty();
}
$this->contents = $contents;
$this->passphrase = $passphrase;
}
/** @deprecated Deprecated since v4.3 */
public static function empty(): self
{
$emptyKey = new self('empty', 'empty');
$emptyKey->contents = '';
$emptyKey->passphrase = '';
return $emptyKey;
}
/** @param non-empty-string $contents */
public static function plainText(string $contents, string $passphrase = ''): self
{
return new self($contents, $passphrase);
}
/** @param non-empty-string $contents */
public static function base64Encoded(string $contents, string $passphrase = ''): self
{
$decoded = SodiumBase64Polyfill::base642bin(
$contents,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL
);
// @phpstan-ignore-next-line
return new self($decoded, $passphrase);
}
/** @throws FileCouldNotBeRead */
public static function file(string $path, string $passphrase = ''): self
{
try {
$file = new SplFileObject($path);
} catch (Throwable $exception) {
throw FileCouldNotBeRead::onPath($path, $exception);
}
$contents = $file->fread($file->getSize());
assert(is_string($contents));
assert($contents !== '');
return new self($contents, $passphrase);
}
public function contents(): string
{
return $this->contents;
}
public function passphrase(): string
{
return $this->passphrase;
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key;
use function file_exists;
use function strpos;
use function substr;
/** @deprecated please use {@see InMemory} instead */
final class LocalFileReference implements Key
{
private const PATH_PREFIX = 'file://';
private string $path;
private string $passphrase;
private string $contents;
private function __construct(string $path, string $passphrase)
{
$this->path = $path;
$this->passphrase = $passphrase;
}
/** @throws FileCouldNotBeRead */
public static function file(string $path, string $passphrase = ''): self
{
if (strpos($path, self::PATH_PREFIX) === 0) {
$path = substr($path, 7);
}
if (! file_exists($path)) {
throw FileCouldNotBeRead::onPath($path);
}
return new self($path, $passphrase);
}
public function contents(): string
{
if (! isset($this->contents)) {
$this->contents = InMemory::file($this->path)->contents();
}
return $this->contents;
}
public function passphrase(): string
{
return $this->passphrase;
}
}

View File

@@ -1,44 +0,0 @@
<?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);
}
}

27
vendor/lcobucci/jwt/src/Signer/None.php vendored Normal file
View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
/** @deprecated Deprecated since v4.3 */
final class None implements Signer
{
public function algorithmId(): string
{
return 'none';
}
// @phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
public function sign(string $payload, Key $key): string
{
return '';
}
// @phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
public function verify(string $expected, string $payload, Key $key): bool
{
return $expected === '';
}
}

View File

@@ -1,11 +1,16 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Signer;
use OpenSSLAsymmetricKey;
use function array_key_exists;
use function assert;
use function is_array;
use function is_resource;
use function is_bool;
use function is_int;
use function openssl_error_string;
use function openssl_free_key;
use function openssl_pkey_get_details;
@@ -14,34 +19,51 @@ use function openssl_pkey_get_public;
use function openssl_sign;
use function openssl_verify;
abstract class OpenSSL extends BaseSigner
use const OPENSSL_KEYTYPE_DH;
use const OPENSSL_KEYTYPE_DSA;
use const OPENSSL_KEYTYPE_EC;
use const OPENSSL_KEYTYPE_RSA;
use const PHP_EOL;
abstract class OpenSSL implements Signer
{
public function createHash($payload, Key $key)
{
$privateKey = $this->getPrivateKey($key->getContent(), $key->getPassphrase());
protected const KEY_TYPE_MAP = [
OPENSSL_KEYTYPE_RSA => 'RSA',
OPENSSL_KEYTYPE_DSA => 'DSA',
OPENSSL_KEYTYPE_DH => 'DH',
OPENSSL_KEYTYPE_EC => 'EC',
];
/**
* @throws CannotSignPayload
* @throws InvalidKeyProvided
*/
final protected function createSignature(
string $pem,
string $passphrase,
string $payload
): string {
$key = $this->getPrivateKey($pem, $passphrase);
try {
$signature = '';
if (! openssl_sign($payload, $signature, $privateKey, $this->getAlgorithm())) {
throw new InvalidArgumentException(
'There was an error while creating the signature: ' . openssl_error_string()
);
if (! openssl_sign($payload, $signature, $key, $this->algorithm())) {
throw CannotSignPayload::errorHappened($this->fullOpenSSLErrorString());
}
return $signature;
} finally {
openssl_free_key($privateKey);
$this->freeKey($key);
}
}
/**
* @param string $pem
* @param string $passphrase
* @return resource|OpenSSLAsymmetricKey
*
* @return resource
* @throws CannotSignPayload
*/
private function getPrivateKey($pem, $passphrase)
private function getPrivateKey(string $pem, string $passphrase)
{
$privateKey = openssl_pkey_get_private($pem, $passphrase);
$this->validateKey($privateKey);
@@ -49,27 +71,25 @@ abstract class OpenSSL extends BaseSigner
return $privateKey;
}
/**
* @param $expected
* @param $payload
* @param $pem
* @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);
/** @throws InvalidKeyProvided */
final protected function verifySignature(
string $expected,
string $payload,
string $pem
): bool {
$key = $this->getPublicKey($pem);
$result = openssl_verify($payload, $expected, $key, $this->algorithm());
$this->freeKey($key);
return $result === 1;
}
/**
* @param string $pem
* @return resource|OpenSSLAsymmetricKey
*
* @return resource
* @throws InvalidKeyProvided
*/
private function getPublicKey($pem)
private function getPublicKey(string $pem)
{
$publicKey = openssl_pkey_get_public($pem);
$this->validateKey($publicKey);
@@ -80,36 +100,55 @@ abstract class OpenSSL extends BaseSigner
/**
* Raises an exception when the key type is not the expected type
*
* @param resource|bool $key
* @param resource|OpenSSLAsymmetricKey|bool $key
*
* @throws InvalidArgumentException
* @throws InvalidKeyProvided
*/
private function validateKey($key)
private function validateKey($key): void
{
if (! is_resource($key)) {
throw new InvalidArgumentException(
'It was not possible to parse your key, reason: ' . openssl_error_string()
);
if (is_bool($key)) {
throw InvalidKeyProvided::cannotBeParsed($this->fullOpenSSLErrorString());
}
$details = openssl_pkey_get_details($key);
assert(is_array($details));
if (! isset($details['key']) || $details['type'] !== $this->getKeyType()) {
throw new InvalidArgumentException('This key is not compatible with this signer');
}
assert(array_key_exists('bits', $details));
assert(is_int($details['bits']));
assert(array_key_exists('type', $details));
assert(is_int($details['type']));
$this->guardAgainstIncompatibleKey($details['type'], $details['bits']);
}
/**
* Returns the type of key to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function getKeyType();
private function fullOpenSSLErrorString(): string
{
$error = '';
while ($msg = openssl_error_string()) {
$error .= PHP_EOL . '* ' . $msg;
}
return $error;
}
/** @throws InvalidKeyProvided */
abstract protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void;
/** @param resource|OpenSSLAsymmetricKey $key */
private function freeKey($key): void
{
if ($key instanceof OpenSSLAsymmetricKey) {
return;
}
openssl_free_key($key); // Deprecated and no longer necessary as of PHP >= 8.0
}
/**
* Returns which algorithm to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function getAlgorithm();
abstract public function algorithm(): int;
}

View File

@@ -1,24 +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
*/
declare(strict_types=1);
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()
private const MINIMUM_KEY_LENGTH = 2048;
final public function sign(string $payload, Key $key): string
{
return OPENSSL_KEYTYPE_RSA;
return $this->createSignature($key->contents(), $key->passphrase(), $payload);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature($expected, $payload, $key->contents());
}
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_RSA) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_RSA],
self::KEY_TYPE_MAP[$type],
);
}
if ($lengthInBits < self::MINIMUM_KEY_LENGTH) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH, $lengthInBits);
}
}
}

View File

@@ -1,34 +1,20 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA256;
final class Sha256 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'RS256';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}

View File

@@ -1,34 +1,20 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA384;
final class Sha384 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'RS384';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}

View File

@@ -1,34 +1,20 @@
<?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
*/
declare(strict_types=1);
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
use const OPENSSL_ALGO_SHA512;
final class Sha512 extends Rsa
{
/**
* {@inheritdoc}
*/
public function getAlgorithmId()
public function algorithmId(): string
{
return 'RS512';
}
/**
* {@inheritdoc}
*/
public function getAlgorithm()
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\UnsafeRsa;
use const OPENSSL_ALGO_SHA256;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha256 extends UnsafeRsa
{
public function algorithmId(): string
{
return 'RS256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\UnsafeRsa;
use const OPENSSL_ALGO_SHA384;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha384 extends UnsafeRsa
{
public function algorithmId(): string
{
return 'RS384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\UnsafeRsa;
use const OPENSSL_ALGO_SHA512;
/** @deprecated Deprecated since v4.2 */
final class UnsafeSha512 extends UnsafeRsa
{
public function algorithmId(): string
{
return 'RS512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter;
use Lcobucci\JWT\Signer\Ecdsa\SignatureConverter;
use const OPENSSL_KEYTYPE_EC;
/** @deprecated Deprecated since v4.2 */
abstract class UnsafeEcdsa extends OpenSSL
{
private SignatureConverter $converter;
public function __construct(SignatureConverter $converter)
{
$this->converter = $converter;
}
public static function create(): UnsafeEcdsa
{
return new static(new MultibyteStringConverter()); // @phpstan-ignore-line
}
final public function sign(string $payload, Key $key): string
{
return $this->converter->fromAsn1(
$this->createSignature($key->contents(), $key->passphrase(), $payload),
$this->pointLength()
);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature(
$this->converter->toAsn1($expected, $this->pointLength()),
$payload,
$key->contents()
);
}
// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_EC) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_EC],
self::KEY_TYPE_MAP[$type],
);
}
}
/**
* 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 pointLength(): int;
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use const OPENSSL_KEYTYPE_RSA;
/** @deprecated Deprecated since v4.2 */
abstract class UnsafeRsa extends OpenSSL
{
final public function sign(string $payload, Key $key): string
{
return $this->createSignature($key->contents(), $key->passphrase(), $payload);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature($expected, $payload, $key->contents());
}
// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_RSA) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_RSA],
self::KEY_TYPE_MAP[$type],
);
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use SodiumException;
use function base64_decode;
use function base64_encode;
use function function_exists;
use function is_string;
use function rtrim;
use function sodium_base642bin;
use function sodium_bin2base64;
use function strtr;
/** @internal */
final class SodiumBase64Polyfill
{
public const SODIUM_BASE64_VARIANT_ORIGINAL = 1;
public const SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
public const SODIUM_BASE64_VARIANT_URLSAFE = 5;
public const SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
public static function bin2base64(string $decoded, int $variant): string
{
if (! function_exists('sodium_bin2base64')) {
return self::bin2base64Fallback($decoded, $variant); // @codeCoverageIgnore
}
return sodium_bin2base64($decoded, $variant);
}
public static function bin2base64Fallback(string $decoded, int $variant): string
{
$encoded = base64_encode($decoded);
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '+/', '-_');
}
if (
$variant === self::SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = rtrim($encoded, '=');
}
return $encoded;
}
/** @throws CannotDecodeContent */
public static function base642bin(string $encoded, int $variant): string
{
if (! function_exists('sodium_base642bin')) {
return self::base642binFallback($encoded, $variant); // @codeCoverageIgnore
}
try {
return sodium_base642bin($encoded, $variant, '');
} catch (SodiumException $sodiumException) {
throw CannotDecodeContent::invalidBase64String();
}
}
/** @throws CannotDecodeContent */
public static function base642binFallback(string $encoded, int $variant): string
{
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '-_', '+/');
}
$decoded = base64_decode($encoded, true);
if (! is_string($decoded)) {
throw CannotDecodeContent::invalidBase64String();
}
return $decoded;
}
}

View File

@@ -1,285 +1,55 @@
<?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
*/
declare(strict_types=1);
namespace Lcobucci\JWT;
use BadMethodCallException;
use DateTime;
use DateTimeInterface;
use Generator;
use Lcobucci\JWT\Claim\Validatable;
use Lcobucci\JWT\Signer\Key;
use OutOfBoundsException;
use Lcobucci\JWT\Token\DataSet;
/**
* Basic structure of the JWT
*
* @author Luís Otávio Cobucci Oblonczyk <lcobucci@gmail.com>
* @since 0.1.0
*/
class Token
interface Token
{
/**
* The token headers
*
* @var array
*/
private $headers;
/**
* The token claim set
*
* @var array
*/
private $claims;
/**
* The token signature
*
* @var Signature
*/
private $signature;
/**
* The encoded data
*
* @var array
*/
private $payload;
/**
* Initializes the object
*
* @param array $headers
* @param array $claims
* @param array $payload
* @param Signature $signature
*/
public function __construct(
array $headers = ['alg' => 'none'],
array $claims = [],
Signature $signature = null,
array $payload = ['', '']
) {
$this->headers = $headers;
$this->claims = $claims;
$this->signature = $signature;
$this->payload = $payload;
}
/**
* Returns the token headers
*
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
public function headers(): DataSet;
/**
* Returns if the header is configured
*
* @param string $name
*
* @return boolean
* Returns if the token is allowed to be used by the audience
*/
public function hasHeader($name)
{
return array_key_exists($name, $this->headers);
}
public function isPermittedFor(string $audience): bool;
/**
* Returns the value of a token header
*
* @param string $name
* @param mixed $default
*
* @return mixed
*
* @throws OutOfBoundsException
* Returns if the token has the given id
*/
public function getHeader($name, $default = null)
{
if ($this->hasHeader($name)) {
return $this->getHeaderValue($name);
}
if ($default === null) {
throw new OutOfBoundsException('Requested header is not configured');
}
return $default;
}
public function isIdentifiedBy(string $id): bool;
/**
* Returns the value stored in header
*
* @param string $name
*
* @return mixed
* Returns if the token has the given subject
*/
private function getHeaderValue($name)
{
$header = $this->headers[$name];
if ($header instanceof Claim) {
return $header->getValue();
}
return $header;
}
public function isRelatedTo(string $subject): bool;
/**
* Returns the token claim set
*
* @return array
* Returns if the token was issued by any of given issuers
*/
public function getClaims()
{
return $this->claims;
}
public function hasBeenIssuedBy(string ...$issuers): bool;
/**
* Returns if the claim is configured
*
* @param string $name
*
* @return boolean
* Returns if the token was issued before of given time
*/
public function hasClaim($name)
{
return array_key_exists($name, $this->claims);
}
public function hasBeenIssuedBefore(DateTimeInterface $now): bool;
/**
* Returns the value of a token claim
*
* @param string $name
* @param mixed $default
*
* @return mixed
*
* @throws OutOfBoundsException
* Returns if the token minimum time is before than given time
*/
public function getClaim($name, $default = null)
{
if ($this->hasClaim($name)) {
return $this->claims[$name]->getValue();
}
if ($default === null) {
throw new OutOfBoundsException('Requested claim is not configured');
}
return $default;
}
public function isMinimumTimeBefore(DateTimeInterface $now): bool;
/**
* Verify if the key matches with the one that created the signature
*
* @param Signer $signer
* @param Key|string $key
*
* @return boolean
*
* @throws BadMethodCallException When token is not signed
* Returns if the token is expired
*/
public function verify(Signer $signer, $key)
{
if ($this->signature === null) {
throw new BadMethodCallException('This token is not signed');
}
if ($this->headers['alg'] !== $signer->getAlgorithmId()) {
return false;
}
return $this->signature->verify($signer, $this->getPayload(), $key);
}
/**
* Validates if the token is valid
*
* @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 $now Defaults to the current time.
*
* @return bool
*/
public function isExpired(DateTimeInterface $now = null)
{
$exp = $this->getClaim('exp', false);
if ($exp === false) {
return false;
}
$now = $now ?: new DateTime();
$expiresAt = new DateTime();
$expiresAt->setTimestamp($exp);
return $now > $expiresAt;
}
/**
* Yields the validatable claims
*
* @return Generator
*/
private function getValidatableClaims()
{
foreach ($this->claims as $claim) {
if ($claim instanceof Validatable) {
yield $claim;
}
}
}
/**
* Returns the token payload
*
* @return string
*/
public function getPayload()
{
return $this->payload[0] . '.' . $this->payload[1];
}
public function isExpired(DateTimeInterface $now): bool;
/**
* Returns an encoded representation of the token
*
* @return string
*/
public function __toString()
{
$data = implode('.', $this->payload);
if ($this->signature === null) {
$data .= '.';
}
return $data;
}
public function toString(): string;
}

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Builder as BuilderInterface;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use function array_diff;
use function array_merge;
use function in_array;
final class Builder implements BuilderInterface
{
/** @var array<string, mixed> */
private array $headers = ['typ' => 'JWT', 'alg' => null];
/** @var array<string, mixed> */
private array $claims = [];
private Encoder $encoder;
private ClaimsFormatter $claimFormatter;
public function __construct(Encoder $encoder, ClaimsFormatter $claimFormatter)
{
$this->encoder = $encoder;
$this->claimFormatter = $claimFormatter;
}
public function permittedFor(string ...$audiences): BuilderInterface
{
$configured = $this->claims[RegisteredClaims::AUDIENCE] ?? [];
$toAppend = array_diff($audiences, $configured);
return $this->setClaim(RegisteredClaims::AUDIENCE, array_merge($configured, $toAppend));
}
public function expiresAt(DateTimeImmutable $expiration): BuilderInterface
{
return $this->setClaim(RegisteredClaims::EXPIRATION_TIME, $expiration);
}
public function identifiedBy(string $id): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ID, $id);
}
public function issuedAt(DateTimeImmutable $issuedAt): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ISSUED_AT, $issuedAt);
}
public function issuedBy(string $issuer): BuilderInterface
{
return $this->setClaim(RegisteredClaims::ISSUER, $issuer);
}
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): BuilderInterface
{
return $this->setClaim(RegisteredClaims::NOT_BEFORE, $notBefore);
}
public function relatedTo(string $subject): BuilderInterface
{
return $this->setClaim(RegisteredClaims::SUBJECT, $subject);
}
/** @inheritdoc */
public function withHeader(string $name, $value): BuilderInterface
{
$this->headers[$name] = $value;
return $this;
}
/** @inheritdoc */
public function withClaim(string $name, $value): BuilderInterface
{
if (in_array($name, RegisteredClaims::ALL, true)) {
throw RegisteredClaimGiven::forClaim($name);
}
return $this->setClaim($name, $value);
}
/** @param mixed $value */
private function setClaim(string $name, $value): BuilderInterface
{
$this->claims[$name] = $value;
return $this;
}
/**
* @param array<string, mixed> $items
*
* @throws CannotEncodeContent When data cannot be converted to JSON.
*/
private function encode(array $items): string
{
return $this->encoder->base64UrlEncode(
$this->encoder->jsonEncode($items)
);
}
public function getToken(Signer $signer, Key $key): Plain
{
$headers = $this->headers;
$headers['alg'] = $signer->algorithmId();
$encodedHeaders = $this->encode($headers);
$encodedClaims = $this->encode($this->claimFormatter->formatClaims($this->claims));
$signature = $signer->sign($encodedHeaders . '.' . $encodedClaims, $key);
$encodedSignature = $this->encoder->base64UrlEncode($signature);
return new Plain(
new DataSet($headers, $encodedHeaders),
new DataSet($this->claims, $encodedClaims),
new Signature($signature, $encodedSignature)
);
}
}

View File

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

View File

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

154
vendor/lcobucci/jwt/src/Token/Parser.php vendored Normal file
View File

@@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Parser as ParserInterface;
use Lcobucci\JWT\Token as TokenInterface;
use function array_key_exists;
use function count;
use function explode;
use function is_array;
use function is_numeric;
use function number_format;
final class Parser implements ParserInterface
{
private const MICROSECOND_PRECISION = 6;
private Decoder $decoder;
public function __construct(Decoder $decoder)
{
$this->decoder = $decoder;
}
public function parse(string $jwt): TokenInterface
{
[$encodedHeaders, $encodedClaims, $encodedSignature] = $this->splitJwt($jwt);
$header = $this->parseHeader($encodedHeaders);
return new Plain(
new DataSet($header, $encodedHeaders),
new DataSet($this->parseClaims($encodedClaims), $encodedClaims),
$this->parseSignature($header, $encodedSignature)
);
}
/**
* Splits the JWT string into an array
*
* @return string[]
*
* @throws InvalidTokenStructure When JWT doesn't have all parts.
*/
private function splitJwt(string $jwt): array
{
$data = explode('.', $jwt);
if (count($data) !== 3) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
return $data;
}
/**
* Parses the header from a string
*
* @return mixed[]
*
* @throws UnsupportedHeaderFound When an invalid header is informed.
* @throws InvalidTokenStructure When parsed content isn't an array.
*/
private function parseHeader(string $data): array
{
$header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($header)) {
throw InvalidTokenStructure::arrayExpected('headers');
}
if (array_key_exists('enc', $header)) {
throw UnsupportedHeaderFound::encryption();
}
if (! array_key_exists('typ', $header)) {
$header['typ'] = 'JWT';
}
return $header;
}
/**
* Parses the claim set from a string
*
* @return mixed[]
*
* @throws InvalidTokenStructure When parsed content isn't an array or contains non-parseable dates.
*/
private function parseClaims(string $data): array
{
$claims = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($claims)) {
throw InvalidTokenStructure::arrayExpected('claims');
}
if (array_key_exists(RegisteredClaims::AUDIENCE, $claims)) {
$claims[RegisteredClaims::AUDIENCE] = (array) $claims[RegisteredClaims::AUDIENCE];
}
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
/**
* @param int|float|string $timestamp
*
* @throws InvalidTokenStructure
*/
private function convertDate($timestamp): DateTimeImmutable
{
if (! is_numeric($timestamp)) {
throw InvalidTokenStructure::dateIsNotParseable($timestamp);
}
$normalizedTimestamp = number_format((float) $timestamp, self::MICROSECOND_PRECISION, '.', '');
$date = DateTimeImmutable::createFromFormat('U.u', $normalizedTimestamp);
if ($date === false) {
throw InvalidTokenStructure::dateIsNotParseable($normalizedTimestamp);
}
return $date;
}
/**
* Returns the signature from given data
*
* @param mixed[] $header
*/
private function parseSignature(array $header, string $data): Signature
{
if ($data === '' || ! array_key_exists('alg', $header) || $header['alg'] === 'none') {
return Signature::fromEmptyData();
}
$hash = $this->decoder->base64UrlDecode($data);
return new Signature($hash, $data);
}
}

92
vendor/lcobucci/jwt/src/Token/Plain.php vendored Normal file
View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeInterface;
use Lcobucci\JWT\UnencryptedToken;
use function in_array;
final class Plain implements UnencryptedToken
{
private DataSet $headers;
private DataSet $claims;
private Signature $signature;
public function __construct(
DataSet $headers,
DataSet $claims,
Signature $signature
) {
$this->headers = $headers;
$this->claims = $claims;
$this->signature = $signature;
}
public function headers(): DataSet
{
return $this->headers;
}
public function claims(): DataSet
{
return $this->claims;
}
public function signature(): Signature
{
return $this->signature;
}
public function payload(): string
{
return $this->headers->toString() . '.' . $this->claims->toString();
}
public function isPermittedFor(string $audience): bool
{
return in_array($audience, $this->claims->get(RegisteredClaims::AUDIENCE, []), true);
}
public function isIdentifiedBy(string $id): bool
{
return $this->claims->get(RegisteredClaims::ID) === $id;
}
public function isRelatedTo(string $subject): bool
{
return $this->claims->get(RegisteredClaims::SUBJECT) === $subject;
}
public function hasBeenIssuedBy(string ...$issuers): bool
{
return in_array($this->claims->get(RegisteredClaims::ISSUER), $issuers, true);
}
public function hasBeenIssuedBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::ISSUED_AT);
}
public function isMinimumTimeBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::NOT_BEFORE);
}
public function isExpired(DateTimeInterface $now): bool
{
if (! $this->claims->has(RegisteredClaims::EXPIRATION_TIME)) {
return false;
}
return $now >= $this->claims->get(RegisteredClaims::EXPIRATION_TIME);
}
public function toString(): string
{
return $this->headers->toString() . '.'
. $this->claims->toString() . '.'
. $this->signature->toString();
}
}

View File

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

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
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
{
public const ALL = [
self::AUDIENCE,
self::EXPIRATION_TIME,
self::ID,
self::ISSUED_AT,
self::ISSUER,
self::NOT_BEFORE,
self::SUBJECT,
];
public 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
*/
public 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
*/
public const EXPIRATION_TIME = 'exp';
/**
* Provides a unique identifier for the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
public const ID = 'jti';
/**
* Identifies the time at which the JWT was issued
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.6
*/
public const ISSUED_AT = 'iat';
/**
* Identifies the principal that issued the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
public 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
*/
public const NOT_BEFORE = 'nbf';
/**
* Identifies the principal that is the subject of the JWT.
*
* https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
public const SUBJECT = 'sub';
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
final class Signature
{
private string $hash;
private string $encoded;
public function __construct(string $hash, string $encoded)
{
$this->hash = $hash;
$this->encoded = $encoded;
}
/** @deprecated Deprecated since v4.3 */
public static function fromEmptyData(): self
{
return new self('', '');
}
public function hash(): string
{
return $this->hash;
}
/**
* Returns the encoded version of the signature
*/
public function toString(): string
{
return $this->encoded;
}
}

View File

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

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\Signature;
interface UnencryptedToken extends Token
{
/**
* Returns the token claims
*/
public function claims(): DataSet;
/**
* Returns the token signature
*/
public function signature(): Signature;
/**
* Returns the token payload
*/
public function payload(): string;
}

View File

@@ -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;
}

View File

@@ -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'
);
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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');
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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
);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More