friendship ended with social-app. php is my new best friend

helpo

Changed files
+3763 -7
vendor
+2 -1
composer.json
···
"jerome/fetch-php": "^3.2",
"react/http": "^1.11",
"react/promise": "^3.3",
-
"react/dns": "^1.13"
},
"require-dev": {
"flightphp/tracy-extensions": "^0.2.7"
···
"jerome/fetch-php": "^3.2",
"react/http": "^1.11",
"react/promise": "^3.3",
+
"react/dns": "^1.13",
+
"lcobucci/jwt": "^5.6"
},
"require-dev": {
"flightphp/tracy-extensions": "^0.2.7"
+122 -1
composer.lock
···
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
-
"content-hash": "c6002ed16d9e230ae38c8eebfa96ea6f",
"packages": [
{
"name": "chillerlan/php-http-message-utils",
···
"time": "2025-10-31T00:53:04+00:00"
},
{
"name": "league/commonmark",
"version": "2.7.1",
"source": {
···
"source": "https://github.com/nette/utils/tree/v4.0.8"
},
"time": "2025-08-06T21:43:34+00:00"
},
{
"name": "psr/event-dispatcher",
···
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
+
"content-hash": "b58d32e8c5e8307c1994ae27ab44347d",
"packages": [
{
"name": "chillerlan/php-http-message-utils",
···
"time": "2025-10-31T00:53:04+00:00"
},
{
+
"name": "lcobucci/jwt",
+
"version": "5.6.0",
+
"source": {
+
"type": "git",
+
"url": "https://github.com/lcobucci/jwt.git",
+
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e"
+
},
+
"dist": {
+
"type": "zip",
+
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e",
+
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e",
+
"shasum": ""
+
},
+
"require": {
+
"ext-openssl": "*",
+
"ext-sodium": "*",
+
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+
"psr/clock": "^1.0"
+
},
+
"require-dev": {
+
"infection/infection": "^0.29",
+
"lcobucci/clock": "^3.2",
+
"lcobucci/coding-standard": "^11.0",
+
"phpbench/phpbench": "^1.2",
+
"phpstan/extension-installer": "^1.2",
+
"phpstan/phpstan": "^1.10.7",
+
"phpstan/phpstan-deprecation-rules": "^1.1.3",
+
"phpstan/phpstan-phpunit": "^1.3.10",
+
"phpstan/phpstan-strict-rules": "^1.5.0",
+
"phpunit/phpunit": "^11.1"
+
},
+
"suggest": {
+
"lcobucci/clock": ">= 3.2"
+
},
+
"type": "library",
+
"autoload": {
+
"psr-4": {
+
"Lcobucci\\JWT\\": "src"
+
}
+
},
+
"notification-url": "https://packagist.org/downloads/",
+
"license": [
+
"BSD-3-Clause"
+
],
+
"authors": [
+
{
+
"name": "Luís Cobucci",
+
"email": "lcobucci@gmail.com",
+
"role": "Developer"
+
}
+
],
+
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
+
"keywords": [
+
"JWS",
+
"jwt"
+
],
+
"support": {
+
"issues": "https://github.com/lcobucci/jwt/issues",
+
"source": "https://github.com/lcobucci/jwt/tree/5.6.0"
+
},
+
"funding": [
+
{
+
"url": "https://github.com/lcobucci",
+
"type": "github"
+
},
+
{
+
"url": "https://www.patreon.com/lcobucci",
+
"type": "patreon"
+
}
+
],
+
"time": "2025-10-17T11:30:53+00:00"
+
},
+
{
"name": "league/commonmark",
"version": "2.7.1",
"source": {
···
"source": "https://github.com/nette/utils/tree/v4.0.8"
},
"time": "2025-08-06T21:43:34+00:00"
+
},
+
{
+
"name": "psr/clock",
+
"version": "1.0.0",
+
"source": {
+
"type": "git",
+
"url": "https://github.com/php-fig/clock.git",
+
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+
},
+
"dist": {
+
"type": "zip",
+
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+
"shasum": ""
+
},
+
"require": {
+
"php": "^7.0 || ^8.0"
+
},
+
"type": "library",
+
"autoload": {
+
"psr-4": {
+
"Psr\\Clock\\": "src/"
+
}
+
},
+
"notification-url": "https://packagist.org/downloads/",
+
"license": [
+
"MIT"
+
],
+
"authors": [
+
{
+
"name": "PHP-FIG",
+
"homepage": "https://www.php-fig.org/"
+
}
+
],
+
"description": "Common interface for reading the clock.",
+
"homepage": "https://github.com/php-fig/clock",
+
"keywords": [
+
"clock",
+
"now",
+
"psr",
+
"psr-20",
+
"time"
+
],
+
"support": {
+
"issues": "https://github.com/php-fig/clock/issues",
+
"source": "https://github.com/php-fig/clock/tree/1.0.0"
+
},
+
"time": "2022-11-25T14:36:26+00:00"
},
{
"name": "psr/event-dispatcher",
+21 -3
index.php
···
use Matrix\Async;
use GuzzleHttp\Psr7\HttpFactory;
use chillerlan\OAuth\OAuthOptions;
use Smallnest\Bsky\BskyProvider;
$bskyToucher = new BskyToucher();
···
if (!$userInfo) die(1);
$pds = $userInfo->pds;
$provider->setPds($pds);
-
$jwt_header = base64_encode(json_encode([
'alg' => 'ES256',
'typ' => 'JWT'
]));
···
'jti' => hash('sha512', bin2hex(random_bytes(256 / 2))),
'iat' => strtotime('now')
]));
-
$jwt = $jwt_header.$jwt_body.base64_encode(CERT);
$client->setDefaultOption('headers', [
'User-Agent' => USER_AGENT_STR,
-
'Authorization' => 'Bearer: '.$jwt
]);
if (isset($_GET['login']) && $_GET['login'] === $name) {
$auth_url = $provider->getAuthorizationUrl();
···
use Matrix\Async;
use GuzzleHttp\Psr7\HttpFactory;
use chillerlan\OAuth\OAuthOptions;
+
use DateTimeImmutable;
+
use Lcobucci\JWT\Builder;
+
use Lcobucci\JWT\JwtFacade;
+
use Lcobucci\JWT\Signer\Hmac\Sha256;
+
use Lcobucci\JWT\Signer\Key\InMemory;
use Smallnest\Bsky\BskyProvider;
$bskyToucher = new BskyToucher();
···
if (!$userInfo) die(1);
$pds = $userInfo->pds;
$provider->setPds($pds);
+
$token_builder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
+
$algorithm = new Sha256();
+
$signing_key = InMemory::plainText(random_bytes(32));
+
$now = new DateTimeImmutable();
+
$token = $token_builder
+
->withHeader('alg', 'ES256')
+
->withHeader('typ', 'JWT')
+
->withClaim('iss', $userInfo->did)
+
->withClaim('sub', 'https://'.SITE_DOMAIN.CLIENT_ID)
+
->withClaim('aud', 'did:web:'.str_replace("/", str_replace("https://", $pds)))
+
->withClaim('iat', strtotime('now'))
+
->getToken($algorithm, $signing_key);
+
+
/*$jwt_header = base64_encode(json_encode([
'alg' => 'ES256',
'typ' => 'JWT'
]));
···
'jti' => hash('sha512', bin2hex(random_bytes(256 / 2))),
'iat' => strtotime('now')
]));
+
$jwt = $jwt_header.$jwt_body.base64_encode(CERT);*/
$client->setDefaultOption('headers', [
'User-Agent' => USER_AGENT_STR,
+
'Authorization' => 'Bearer: '.$token->toString()
]);
if (isset($_GET['login']) && $_GET['login'] === $name) {
$auth_url = $provider->getAuthorizationUrl();
+2
vendor/composer/autoload_psr4.php
···
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'),
'Matrix\\' => array($vendorDir . '/jerome/matrix/src/Matrix'),
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'League\\Config\\' => array($vendorDir . '/league/config/src'),
'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
'Latte\\' => array($vendorDir . '/latte/latte/src/Latte'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
···
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
+
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'),
'Matrix\\' => array($vendorDir . '/jerome/matrix/src/Matrix'),
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'League\\Config\\' => array($vendorDir . '/league/config/src'),
'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
+
'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
'Latte\\' => array($vendorDir . '/latte/latte/src/Latte'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
+10
vendor/composer/autoload_static.php
···
'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16,
'Psr\\EventDispatcher\\' => 20,
),
'N' =>
array (
···
array (
'League\\Config\\' => 14,
'League\\CommonMark\\' => 18,
'Latte\\' => 6,
),
'G' =>
···
array (
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
),
'Nette\\' =>
array (
0 => __DIR__ . '/..' . '/nette/schema/src',
···
'League\\CommonMark\\' =>
array (
0 => __DIR__ . '/..' . '/league/commonmark/src',
),
'Latte\\' =>
array (
···
'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16,
'Psr\\EventDispatcher\\' => 20,
+
'Psr\\Clock\\' => 10,
),
'N' =>
array (
···
array (
'League\\Config\\' => 14,
'League\\CommonMark\\' => 18,
+
'Lcobucci\\JWT\\' => 13,
'Latte\\' => 6,
),
'G' =>
···
array (
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
),
+
'Psr\\Clock\\' =>
+
array (
+
0 => __DIR__ . '/..' . '/psr/clock/src',
+
),
'Nette\\' =>
array (
0 => __DIR__ . '/..' . '/nette/schema/src',
···
'League\\CommonMark\\' =>
array (
0 => __DIR__ . '/..' . '/league/commonmark/src',
+
),
+
'Lcobucci\\JWT\\' =>
+
array (
+
0 => __DIR__ . '/..' . '/lcobucci/jwt/src',
),
'Latte\\' =>
array (
+127
vendor/composer/installed.json
···
"install-path": "../latte/latte"
},
{
"name": "league/commonmark",
"version": "2.7.1",
"version_normalized": "2.7.1.0",
···
"source": "https://github.com/nette/utils/tree/v4.0.8"
},
"install-path": "../nette/utils"
},
{
"name": "psr/event-dispatcher",
···
"install-path": "../latte/latte"
},
{
+
"name": "lcobucci/jwt",
+
"version": "5.6.0",
+
"version_normalized": "5.6.0.0",
+
"source": {
+
"type": "git",
+
"url": "https://github.com/lcobucci/jwt.git",
+
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e"
+
},
+
"dist": {
+
"type": "zip",
+
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e",
+
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e",
+
"shasum": ""
+
},
+
"require": {
+
"ext-openssl": "*",
+
"ext-sodium": "*",
+
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+
"psr/clock": "^1.0"
+
},
+
"require-dev": {
+
"infection/infection": "^0.29",
+
"lcobucci/clock": "^3.2",
+
"lcobucci/coding-standard": "^11.0",
+
"phpbench/phpbench": "^1.2",
+
"phpstan/extension-installer": "^1.2",
+
"phpstan/phpstan": "^1.10.7",
+
"phpstan/phpstan-deprecation-rules": "^1.1.3",
+
"phpstan/phpstan-phpunit": "^1.3.10",
+
"phpstan/phpstan-strict-rules": "^1.5.0",
+
"phpunit/phpunit": "^11.1"
+
},
+
"suggest": {
+
"lcobucci/clock": ">= 3.2"
+
},
+
"time": "2025-10-17T11:30:53+00:00",
+
"type": "library",
+
"installation-source": "dist",
+
"autoload": {
+
"psr-4": {
+
"Lcobucci\\JWT\\": "src"
+
}
+
},
+
"notification-url": "https://packagist.org/downloads/",
+
"license": [
+
"BSD-3-Clause"
+
],
+
"authors": [
+
{
+
"name": "Luís Cobucci",
+
"email": "lcobucci@gmail.com",
+
"role": "Developer"
+
}
+
],
+
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
+
"keywords": [
+
"JWS",
+
"jwt"
+
],
+
"support": {
+
"issues": "https://github.com/lcobucci/jwt/issues",
+
"source": "https://github.com/lcobucci/jwt/tree/5.6.0"
+
},
+
"funding": [
+
{
+
"url": "https://github.com/lcobucci",
+
"type": "github"
+
},
+
{
+
"url": "https://www.patreon.com/lcobucci",
+
"type": "patreon"
+
}
+
],
+
"install-path": "../lcobucci/jwt"
+
},
+
{
"name": "league/commonmark",
"version": "2.7.1",
"version_normalized": "2.7.1.0",
···
"source": "https://github.com/nette/utils/tree/v4.0.8"
},
"install-path": "../nette/utils"
+
},
+
{
+
"name": "psr/clock",
+
"version": "1.0.0",
+
"version_normalized": "1.0.0.0",
+
"source": {
+
"type": "git",
+
"url": "https://github.com/php-fig/clock.git",
+
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+
},
+
"dist": {
+
"type": "zip",
+
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+
"shasum": ""
+
},
+
"require": {
+
"php": "^7.0 || ^8.0"
+
},
+
"time": "2022-11-25T14:36:26+00:00",
+
"type": "library",
+
"installation-source": "dist",
+
"autoload": {
+
"psr-4": {
+
"Psr\\Clock\\": "src/"
+
}
+
},
+
"notification-url": "https://packagist.org/downloads/",
+
"license": [
+
"MIT"
+
],
+
"authors": [
+
{
+
"name": "PHP-FIG",
+
"homepage": "https://www.php-fig.org/"
+
}
+
],
+
"description": "Common interface for reading the clock.",
+
"homepage": "https://github.com/php-fig/clock",
+
"keywords": [
+
"clock",
+
"now",
+
"psr",
+
"psr-20",
+
"time"
+
],
+
"support": {
+
"issues": "https://github.com/php-fig/clock/issues",
+
"source": "https://github.com/php-fig/clock/tree/1.0.0"
+
},
+
"install-path": "../psr/clock"
},
{
"name": "psr/event-dispatcher",
+20 -2
vendor/composer/installed.php
···
'name' => '__root__',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
-
'reference' => '257124f543ff1eb7215a3a52a494881277141421',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
···
'__root__' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
-
'reference' => '257124f543ff1eb7215a3a52a494881277141421',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
···
'aliases' => array(),
'dev_requirement' => false,
),
'league/commonmark' => array(
'pretty_version' => '2.7.1',
'version' => '2.7.1.0',
···
'reference' => 'c930ca4e3cf4f17dcfb03037703679d2396d2ede',
'type' => 'library',
'install_path' => __DIR__ . '/../nette/utils',
'aliases' => array(),
'dev_requirement' => false,
),
···
'name' => '__root__',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
+
'reference' => '988c87ce046f0ee346497af8fb5c755d3ae75b7b',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
···
'__root__' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
+
'reference' => '988c87ce046f0ee346497af8fb5c755d3ae75b7b',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
···
'aliases' => array(),
'dev_requirement' => false,
),
+
'lcobucci/jwt' => array(
+
'pretty_version' => '5.6.0',
+
'version' => '5.6.0.0',
+
'reference' => 'bb3e9f21e4196e8afc41def81ef649c164bca25e',
+
'type' => 'library',
+
'install_path' => __DIR__ . '/../lcobucci/jwt',
+
'aliases' => array(),
+
'dev_requirement' => false,
+
),
'league/commonmark' => array(
'pretty_version' => '2.7.1',
'version' => '2.7.1.0',
···
'reference' => 'c930ca4e3cf4f17dcfb03037703679d2396d2ede',
'type' => 'library',
'install_path' => __DIR__ . '/../nette/utils',
+
'aliases' => array(),
+
'dev_requirement' => false,
+
),
+
'psr/clock' => array(
+
'pretty_version' => '1.0.0',
+
'version' => '1.0.0.0',
+
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
+
'type' => 'library',
+
'install_path' => __DIR__ . '/../psr/clock',
'aliases' => array(),
'dev_requirement' => false,
),
+27
vendor/lcobucci/jwt/LICENSE
···
···
+
Copyright (c) 2014, Luís Cobucci
+
All rights reserved.
+
+
Redistribution and use in source and binary forms, with or without
+
modification, are permitted provided that the following conditions are met:
+
+
* Redistributions of source code must retain the above copyright notice, this
+
list of conditions and the following disclaimer.
+
+
* Redistributions in binary form must reproduce the above copyright notice,
+
this list of conditions and the following disclaimer in the documentation
+
and/or other materials provided with the distribution.
+
+
* Neither the name of the {organization} nor the names of its
+
contributors may be used to endorse or promote products derived from
+
this software without specific prior written permission.
+
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+63
vendor/lcobucci/jwt/composer.json
···
···
+
{
+
"name": "lcobucci/jwt",
+
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
+
"license": [
+
"BSD-3-Clause"
+
],
+
"type": "library",
+
"keywords": [
+
"JWT",
+
"JWS"
+
],
+
"authors": [
+
{
+
"name": "Luís Cobucci",
+
"email": "lcobucci@gmail.com",
+
"role": "Developer"
+
}
+
],
+
"require": {
+
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
+
"ext-openssl": "*",
+
"ext-sodium": "*",
+
"psr/clock": "^1.0"
+
},
+
"require-dev": {
+
"infection/infection": "^0.29",
+
"lcobucci/clock": "^3.2",
+
"lcobucci/coding-standard": "^11.0",
+
"phpbench/phpbench": "^1.2",
+
"phpstan/extension-installer": "^1.2",
+
"phpstan/phpstan": "^1.10.7",
+
"phpstan/phpstan-deprecation-rules": "^1.1.3",
+
"phpstan/phpstan-phpunit": "^1.3.10",
+
"phpstan/phpstan-strict-rules": "^1.5.0",
+
"phpunit/phpunit": "^11.1"
+
},
+
"suggest": {
+
"lcobucci/clock": ">= 3.2"
+
},
+
"autoload": {
+
"psr-4": {
+
"Lcobucci\\JWT\\": "src"
+
}
+
},
+
"autoload-dev": {
+
"psr-4": {
+
"Lcobucci\\JWT\\Tests\\": "tests"
+
}
+
},
+
"config": {
+
"allow-plugins": {
+
"dealerdirect/phpcodesniffer-composer-installer": true,
+
"infection/extension-installer": true,
+
"ocramius/package-versions": true,
+
"phpstan/extension-installer": true
+
},
+
"platform": {
+
"php": "8.2.99"
+
},
+
"preferred-install": "dist",
+
"sort-packages": true
+
}
+
}
+85
vendor/lcobucci/jwt/src/Builder.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
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 Lcobucci\JWT\Token\RegisteredClaimGiven;
+
+
/** @immutable */
+
interface Builder
+
{
+
/**
+
* Appends new items to audience
+
*
+
* @param non-empty-string ...$audiences
+
*/
+
public function permittedFor(string ...$audiences): Builder;
+
+
/**
+
* Configures the expiration time
+
*/
+
public function expiresAt(DateTimeImmutable $expiration): Builder;
+
+
/**
+
* Configures the token id
+
*
+
* @param non-empty-string $id
+
*/
+
public function identifiedBy(string $id): Builder;
+
+
/**
+
* Configures the time that the token was issued
+
*/
+
public function issuedAt(DateTimeImmutable $issuedAt): Builder;
+
+
/**
+
* Configures the issuer
+
*
+
* @param non-empty-string $issuer
+
*/
+
public function issuedBy(string $issuer): Builder;
+
+
/**
+
* Configures the time before which the token cannot be accepted
+
*/
+
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): Builder;
+
+
/**
+
* Configures the subject
+
*
+
* @param non-empty-string $subject
+
*/
+
public function relatedTo(string $subject): Builder;
+
+
/**
+
* Configures a header item
+
*
+
* @param non-empty-string $name
+
*/
+
public function withHeader(string $name, mixed $value): Builder;
+
+
/**
+
* Configures a claim item
+
*
+
* @param non-empty-string $name
+
*
+
* @throws RegisteredClaimGiven When trying to set a registered claim.
+
*/
+
public function withClaim(string $name, mixed $value): Builder;
+
+
/**
+
* Returns a signed token to be used
+
*
+
* @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 getToken(Signer $signer, Key $key): UnencryptedToken;
+
}
+14
vendor/lcobucci/jwt/src/ClaimsFormatter.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
interface ClaimsFormatter
+
{
+
/**
+
* @param array<non-empty-string, mixed> $claims
+
*
+
* @return array<non-empty-string, mixed>
+
*/
+
public function formatClaims(array $claims): array;
+
}
+213
vendor/lcobucci/jwt/src/Configuration.php
···
···
+
<?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\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 Validator $validator;
+
+
/** @var Closure(ClaimsFormatter $claimFormatter): Builder */
+
private Closure $builderFactory;
+
+
/** @var Constraint[] */
+
private array $validationConstraints;
+
+
/** @param Closure(ClaimsFormatter $claimFormatter): Builder|null $builderFactory */
+
private function __construct(
+
private readonly Signer $signer,
+
private readonly Key $signingKey,
+
private readonly Key $verificationKey,
+
private readonly Encoder $encoder,
+
private readonly Decoder $decoder,
+
?Parser $parser,
+
?Validator $validator,
+
?Closure $builderFactory,
+
Constraint ...$validationConstraints,
+
) {
+
$this->parser = $parser ?? new Token\Parser($decoder);
+
$this->validator = $validator ?? new Validation\Validator();
+
+
$this->builderFactory = $builderFactory
+
?? static function (ClaimsFormatter $claimFormatter) use ($encoder): Builder {
+
return Token\Builder::new($encoder, $claimFormatter);
+
};
+
+
$this->validationConstraints = $validationConstraints;
+
}
+
+
public static function forAsymmetricSigner(
+
Signer $signer,
+
Key $signingKey,
+
Key $verificationKey,
+
Encoder $encoder = new JoseEncoder(),
+
Decoder $decoder = new JoseEncoder(),
+
): self {
+
return new self(
+
$signer,
+
$signingKey,
+
$verificationKey,
+
$encoder,
+
$decoder,
+
null,
+
null,
+
null,
+
);
+
}
+
+
public static function forSymmetricSigner(
+
Signer $signer,
+
Key $key,
+
Encoder $encoder = new JoseEncoder(),
+
Decoder $decoder = new JoseEncoder(),
+
): self {
+
return new self(
+
$signer,
+
$key,
+
$key,
+
$encoder,
+
$decoder,
+
null,
+
null,
+
null,
+
);
+
}
+
+
/**
+
* @deprecated Deprecated since v5.5, please use {@see self::withBuilderFactory()} instead
+
*
+
* @param callable(ClaimsFormatter): Builder $builderFactory
+
*/
+
public function setBuilderFactory(callable $builderFactory): void
+
{
+
$this->builderFactory = $builderFactory(...);
+
}
+
+
/** @param callable(ClaimsFormatter): Builder $builderFactory */
+
public function withBuilderFactory(callable $builderFactory): self
+
{
+
return new self(
+
$this->signer,
+
$this->signingKey,
+
$this->verificationKey,
+
$this->encoder,
+
$this->decoder,
+
$this->parser,
+
$this->validator,
+
$builderFactory(...),
+
...$this->validationConstraints,
+
);
+
}
+
+
public function builder(?ClaimsFormatter $claimFormatter = null): Builder
+
{
+
return ($this->builderFactory)($claimFormatter ?? ChainedFormatter::default());
+
}
+
+
public function parser(): Parser
+
{
+
return $this->parser;
+
}
+
+
/** @deprecated Deprecated since v5.5, please use {@see self::withParser()} instead */
+
public function setParser(Parser $parser): void
+
{
+
$this->parser = $parser;
+
}
+
+
public function withParser(Parser $parser): self
+
{
+
return new self(
+
$this->signer,
+
$this->signingKey,
+
$this->verificationKey,
+
$this->encoder,
+
$this->decoder,
+
$parser,
+
$this->validator,
+
$this->builderFactory,
+
...$this->validationConstraints,
+
);
+
}
+
+
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;
+
}
+
+
/** @deprecated Deprecated since v5.5, please use {@see self::withValidator()} instead */
+
public function setValidator(Validator $validator): void
+
{
+
$this->validator = $validator;
+
}
+
+
public function withValidator(Validator $validator): self
+
{
+
return new self(
+
$this->signer,
+
$this->signingKey,
+
$this->verificationKey,
+
$this->encoder,
+
$this->decoder,
+
$this->parser,
+
$validator,
+
$this->builderFactory,
+
...$this->validationConstraints,
+
);
+
}
+
+
/** @return Constraint[] */
+
public function validationConstraints(): array
+
{
+
return $this->validationConstraints;
+
}
+
+
/** @deprecated Deprecated since v5.5, please use {@see self::withValidationConstraints()} instead */
+
public function setValidationConstraints(Constraint ...$validationConstraints): void
+
{
+
$this->validationConstraints = $validationConstraints;
+
}
+
+
public function withValidationConstraints(Constraint ...$validationConstraints): self
+
{
+
return new self(
+
$this->signer,
+
$this->signingKey,
+
$this->verificationKey,
+
$this->encoder,
+
$this->decoder,
+
$this->parser,
+
$this->validator,
+
$this->builderFactory,
+
...$validationConstraints,
+
);
+
}
+
}
+29
vendor/lcobucci/jwt/src/Decoder.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Lcobucci\JWT\Encoding\CannotDecodeContent;
+
+
interface Decoder
+
{
+
/**
+
* Decodes from JSON, validating the errors
+
*
+
* @param non-empty-string $json
+
*
+
* @throws CannotDecodeContent When something goes wrong while decoding.
+
*/
+
public function jsonDecode(string $json): mixed;
+
+
/**
+
* Decodes from Base64URL
+
*
+
* @link http://tools.ietf.org/html/rfc4648#section-5
+
*
+
* @return ($data is non-empty-string ? non-empty-string : string)
+
*
+
* @throws CannotDecodeContent When something goes wrong while decoding.
+
*/
+
public function base64UrlDecode(string $data): string;
+
}
+27
vendor/lcobucci/jwt/src/Encoder.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Lcobucci\JWT\Encoding\CannotEncodeContent;
+
+
interface Encoder
+
{
+
/**
+
* Encodes to JSON, validating the errors
+
*
+
* @return non-empty-string
+
*
+
* @throws CannotEncodeContent When something goes wrong while encoding.
+
*/
+
public function jsonEncode(mixed $data): string;
+
+
/**
+
* Encodes to base64url
+
*
+
* @link http://tools.ietf.org/html/rfc4648#section-5
+
*
+
* @return ($data is non-empty-string ? non-empty-string : string)
+
*/
+
public function base64UrlEncode(string $data): string;
+
}
+21
vendor/lcobucci/jwt/src/Encoding/CannotDecodeContent.php
···
···
+
<?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(message: 'Error while decoding from JSON', previous: $previous);
+
}
+
+
public static function invalidBase64String(): self
+
{
+
return new self('Error while decoding from Base64Url, invalid base64 characters detected');
+
}
+
}
+16
vendor/lcobucci/jwt/src/Encoding/CannotEncodeContent.php
···
···
+
<?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(message: 'Error while encoding to JSON', previous: $previous);
+
}
+
}
+37
vendor/lcobucci/jwt/src/Encoding/ChainedFormatter.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Encoding;
+
+
use Lcobucci\JWT\ClaimsFormatter;
+
+
final class ChainedFormatter implements ClaimsFormatter
+
{
+
/** @var array<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;
+
}
+
}
+56
vendor/lcobucci/jwt/src/Encoding/JoseEncoder.php
···
···
+
<?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 to JOSE specifications
+
*/
+
final class JoseEncoder implements Encoder, Decoder
+
{
+
public function jsonEncode(mixed $data): string
+
{
+
try {
+
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
+
} catch (JsonException $exception) {
+
throw CannotEncodeContent::jsonIssues($exception);
+
}
+
}
+
+
public function jsonDecode(string $json): mixed
+
{
+
try {
+
return json_decode(json: $json, associative: true, flags: 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,
+
);
+
}
+
}
+36
vendor/lcobucci/jwt/src/Encoding/MicrosecondBasedDateConversion.php
···
···
+
<?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;
+
}
+
+
private function convertDate(DateTimeImmutable $date): int|float
+
{
+
if ($date->format('u') === '000000') {
+
return (int) $date->format('U');
+
}
+
+
return (float) $date->format('U.u');
+
}
+
}
+29
vendor/lcobucci/jwt/src/Encoding/UnifyAudience.php
···
···
+
<?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;
+
}
+
}
+32
vendor/lcobucci/jwt/src/Encoding/UnixTimestampDates.php
···
···
+
<?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
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Throwable;
+
+
interface Exception extends Throwable
+
{
+
}
+71
vendor/lcobucci/jwt/src/JwtFacade.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Closure;
+
use DateTimeImmutable;
+
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 Psr\Clock\ClockInterface as Clock;
+
+
use function assert;
+
+
final class JwtFacade
+
{
+
private readonly Clock $clock;
+
+
public function __construct(
+
private readonly Parser $parser = new Token\Parser(new JoseEncoder()),
+
?Clock $clock = null,
+
) {
+
$this->clock = $clock ?? new class implements Clock {
+
public function now(): DateTimeImmutable
+
{
+
return new DateTimeImmutable();
+
}
+
};
+
}
+
+
/** @param Closure(Builder, DateTimeImmutable):Builder $customiseBuilder */
+
public function issue(
+
Signer $signer,
+
Key $signingKey,
+
Closure $customiseBuilder,
+
): UnencryptedToken {
+
$builder = Token\Builder::new(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates());
+
+
$now = $this->clock->now();
+
$builder = $builder
+
->issuedAt($now)
+
->canOnlyBeUsedAfter($now)
+
->expiresAt($now->modify('+5 minutes'));
+
+
return $customiseBuilder($builder, $now)->getToken($signer, $signingKey);
+
}
+
+
/** @param non-empty-string $jwt */
+
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;
+
}
+
}
+22
vendor/lcobucci/jwt/src/Parser.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Lcobucci\JWT\Encoding\CannotDecodeContent;
+
use Lcobucci\JWT\Token\InvalidTokenStructure;
+
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
+
+
interface Parser
+
{
+
/**
+
* Parses the JWT and returns a token
+
*
+
* @param non-empty-string $jwt
+
*
+
* @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(string $jwt): Token;
+
}
+43
vendor/lcobucci/jwt/src/Signer.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Lcobucci\JWT\Signer\CannotSignPayload;
+
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
+
use Lcobucci\JWT\Signer\InvalidKeyProvided;
+
use Lcobucci\JWT\Signer\Key;
+
+
interface Signer
+
{
+
/**
+
* Returns the algorithm id
+
*
+
* @return non-empty-string
+
*/
+
public function algorithmId(): string;
+
+
/**
+
* Creates a hash for the given payload
+
*
+
* @param non-empty-string $payload
+
*
+
* @return non-empty-string
+
*
+
* @throws CannotSignPayload When payload signing fails.
+
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
+
* @throws ConversionFailed When signature could not be converted.
+
*/
+
public function sign(string $payload, Key $key): string;
+
+
/**
+
* Returns if the expected hash matches with the data and key
+
*
+
* @param non-empty-string $expected
+
* @param non-empty-string $payload
+
*
+
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
+
* @throws ConversionFailed When signature could not be converted.
+
*/
+
public function verify(string $expected, string $payload, Key $key): bool;
+
}
+36
vendor/lcobucci/jwt/src/Signer/Blake2b.php
···
···
+
<?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));
+
}
+
}
+15
vendor/lcobucci/jwt/src/Signer/CannotSignPayload.php
···
···
+
<?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);
+
}
+
}
+67
vendor/lcobucci/jwt/src/Signer/Ecdsa.php
···
···
+
<?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;
+
+
abstract class Ecdsa extends OpenSSL
+
{
+
public function __construct(
+
private readonly SignatureConverter $converter = new MultibyteStringConverter(),
+
) {
+
}
+
+
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(),
+
);
+
}
+
+
/** {@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
+
*
+
* @return positive-int
+
*/
+
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
+
*
+
* @return positive-int
+
*/
+
abstract public function pointLength(): int;
+
}
+25
vendor/lcobucci/jwt/src/Signer/Ecdsa/ConversionFailed.php
···
···
+
<?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.');
+
}
+
}
+148
vendor/lcobucci/jwt/src/Signer/Ecdsa/MultibyteStringConverter.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
/*
+
* The MIT License (MIT)
+
*
+
* Copyright (c) 2014-2018 Spomky-Labs
+
*
+
* This software may be modified and distributed under the terms
+
* of the MIT license. See the LICENSE file for details.
+
*
+
* @link https://github.com/web-token/jwt-framework/blob/v1.2/src/Component/Core/Util/ECSignature.php
+
*/
+
+
namespace Lcobucci\JWT\Signer\Ecdsa;
+
+
use function assert;
+
use function bin2hex;
+
use function dechex;
+
use function hex2bin;
+
use function hexdec;
+
use function is_string;
+
use function str_pad;
+
use function strlen;
+
use function substr;
+
+
use const STR_PAD_LEFT;
+
+
/**
+
* ECDSA signature converter using ext-mbstring
+
*
+
* @internal
+
*/
+
final class MultibyteStringConverter implements SignatureConverter
+
{
+
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(string $points, int $length): string
+
{
+
$points = bin2hex($points);
+
+
if (self::octetLength($points) !== $length) {
+
throw ConversionFailed::invalidLength();
+
}
+
+
$pointR = self::preparePositiveInteger(substr($points, 0, $length));
+
$pointS = self::preparePositiveInteger(substr($points, $length, null));
+
+
$lengthR = self::octetLength($pointR);
+
$lengthS = self::octetLength($pointS);
+
+
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
+
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
+
+
$asn1 = hex2bin(
+
self::ASN1_SEQUENCE
+
. $lengthPrefix . dechex($totalLength)
+
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
+
. self::ASN1_INTEGER . dechex($lengthS) . $pointS,
+
);
+
assert(is_string($asn1));
+
assert($asn1 !== '');
+
+
return $asn1;
+
}
+
+
private static function octetLength(string $data): int
+
{
+
return (int) (strlen($data) / self::BYTE_SIZE);
+
}
+
+
private static function preparePositiveInteger(string $data): string
+
{
+
if (substr($data, 0, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT) {
+
return self::ASN1_NEGATIVE_INTEGER . $data;
+
}
+
+
while (
+
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
+
&& substr($data, 2, self::BYTE_SIZE) <= self::ASN1_BIG_INTEGER_LIMIT
+
) {
+
$data = substr($data, 2, null);
+
}
+
+
return $data;
+
}
+
+
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 ConversionFailed::incorrectStartSequence();
+
}
+
+
// @phpstan-ignore-next-line
+
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
+
$position += self::BYTE_SIZE;
+
}
+
+
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
+
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
+
+
$points = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT) . str_pad($pointS, $length, '0', STR_PAD_LEFT));
+
assert(is_string($points));
+
assert($points !== '');
+
+
return $points;
+
}
+
+
private static function readAsn1Content(string $message, int &$position, int $length): string
+
{
+
$content = substr($message, $position, $length);
+
$position += $length;
+
+
return $content;
+
}
+
+
private static function readAsn1Integer(string $message, int &$position): string
+
{
+
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
+
throw ConversionFailed::integerExpected();
+
}
+
+
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
+
+
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
+
}
+
+
private static function retrievePositiveInteger(string $data): string
+
{
+
while (
+
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
+
&& substr($data, 2, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT
+
) {
+
$data = substr($data, 2, null);
+
}
+
+
return $data;
+
}
+
}
+31
vendor/lcobucci/jwt/src/Signer/Ecdsa/Sha256.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Ecdsa;
+
+
use Lcobucci\JWT\Signer\Ecdsa;
+
+
use const OPENSSL_ALGO_SHA256;
+
+
final class Sha256 extends Ecdsa
+
{
+
public function algorithmId(): string
+
{
+
return 'ES256';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA256;
+
}
+
+
public function pointLength(): int
+
{
+
return 64;
+
}
+
+
public function expectedKeyLength(): int
+
{
+
return 256;
+
}
+
}
+31
vendor/lcobucci/jwt/src/Signer/Ecdsa/Sha384.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Ecdsa;
+
+
use Lcobucci\JWT\Signer\Ecdsa;
+
+
use const OPENSSL_ALGO_SHA384;
+
+
final class Sha384 extends Ecdsa
+
{
+
public function algorithmId(): string
+
{
+
return 'ES384';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA384;
+
}
+
+
public function pointLength(): int
+
{
+
return 96;
+
}
+
+
public function expectedKeyLength(): int
+
{
+
return 384;
+
}
+
}
+33
vendor/lcobucci/jwt/src/Signer/Ecdsa/Sha512.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Ecdsa;
+
+
use Lcobucci\JWT\Signer\Ecdsa;
+
+
use const OPENSSL_ALGO_SHA512;
+
+
final class Sha512 extends Ecdsa
+
{
+
public function algorithmId(): string
+
{
+
return 'ES512';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA512;
+
}
+
+
public function pointLength(): int
+
{
+
return 132;
+
}
+
+
public function expectedKeyLength(): int
+
{
+
// ES512 means ECDSA using P-521 and SHA-512.
+
// The key size is indeed 521 bits.
+
return 521;
+
}
+
}
+40
vendor/lcobucci/jwt/src/Signer/Ecdsa/SignatureConverter.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Ecdsa;
+
+
/**
+
* Manipulates the result of a ECDSA signature (points R and S) according to the
+
* JWA specs.
+
*
+
* OpenSSL creates a signature using the ASN.1 format and, according the JWA specs,
+
* the signature for JWTs must be the concatenated values of points R and S (in
+
* big-endian octet order).
+
*
+
* @internal
+
*
+
* @see https://tools.ietf.org/html/rfc7518#page-9
+
* @see https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
+
*/
+
interface SignatureConverter
+
{
+
/**
+
* Converts the signature generated by OpenSSL into what JWA defines
+
*
+
* @return non-empty-string
+
*
+
* @throws ConversionFailed When there was an issue during the format conversion.
+
*/
+
public function fromAsn1(string $signature, int $length): string;
+
+
/**
+
* Converts the JWA signature into something OpenSSL understands
+
*
+
* @param non-empty-string $points
+
*
+
* @return non-empty-string
+
*
+
* @throws ConversionFailed When there was an issue during the format conversion.
+
*/
+
public function toAsn1(string $points, int $length): string;
+
}
+36
vendor/lcobucci/jwt/src/Signer/Eddsa.php
···
···
+
<?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);
+
}
+
}
+
}
+44
vendor/lcobucci/jwt/src/Signer/Hmac.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer;
+
+
use Lcobucci\JWT\Signer;
+
+
use function hash_equals;
+
use function hash_hmac;
+
use function strlen;
+
+
abstract class Hmac implements Signer
+
{
+
final public function sign(string $payload, Key $key): string
+
{
+
$actualKeyLength = 8 * strlen($key->contents());
+
$expectedKeyLength = $this->minimumBitsLengthForKey();
+
+
if ($actualKeyLength < $expectedKeyLength) {
+
throw InvalidKeyProvided::tooShort($expectedKeyLength, $actualKeyLength);
+
}
+
+
return hash_hmac($this->algorithm(), $payload, $key->contents(), true);
+
}
+
+
final public function verify(string $expected, string $payload, Key $key): bool
+
{
+
return hash_equals($expected, $this->sign($payload, $key));
+
}
+
+
/**
+
* @internal
+
*
+
* @return non-empty-string
+
*/
+
abstract public function algorithm(): string;
+
+
/**
+
* @internal
+
*
+
* @return positive-int
+
*/
+
abstract public function minimumBitsLengthForKey(): int;
+
}
+24
vendor/lcobucci/jwt/src/Signer/Hmac/Sha256.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Hmac;
+
+
use Lcobucci\JWT\Signer\Hmac;
+
+
final class Sha256 extends Hmac
+
{
+
public function algorithmId(): string
+
{
+
return 'HS256';
+
}
+
+
public function algorithm(): string
+
{
+
return 'sha256';
+
}
+
+
public function minimumBitsLengthForKey(): int
+
{
+
return 256;
+
}
+
}
+24
vendor/lcobucci/jwt/src/Signer/Hmac/Sha384.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Hmac;
+
+
use Lcobucci\JWT\Signer\Hmac;
+
+
final class Sha384 extends Hmac
+
{
+
public function algorithmId(): string
+
{
+
return 'HS384';
+
}
+
+
public function algorithm(): string
+
{
+
return 'sha384';
+
}
+
+
public function minimumBitsLengthForKey(): int
+
{
+
return 384;
+
}
+
}
+24
vendor/lcobucci/jwt/src/Signer/Hmac/Sha512.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Hmac;
+
+
use Lcobucci\JWT\Signer\Hmac;
+
+
final class Sha512 extends Hmac
+
{
+
public function algorithmId(): string
+
{
+
return 'HS512';
+
}
+
+
public function algorithm(): string
+
{
+
return 'sha512';
+
}
+
+
public function minimumBitsLengthForKey(): int
+
{
+
return 512;
+
}
+
}
+47
vendor/lcobucci/jwt/src/Signer/InvalidKeyProvided.php
···
···
+
<?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);
+
}
+
+
/**
+
* @param non-empty-string $expectedType
+
* @param non-empty-string $actualType
+
*/
+
public static function incompatibleKeyType(string $expectedType, string $actualType): self
+
{
+
return new self(
+
'The type of the provided key is not "' . $expectedType
+
. '", "' . $actualType . '" provided',
+
);
+
}
+
+
/** @param positive-int $expectedLength */
+
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');
+
}
+
}
+12
vendor/lcobucci/jwt/src/Signer/Key.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer;
+
+
interface Key
+
{
+
/** @return non-empty-string */
+
public function contents(): string;
+
+
public function passphrase(): string;
+
}
+20
vendor/lcobucci/jwt/src/Signer/Key/FileCouldNotBeRead.php
···
···
+
<?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
+
{
+
/** @param non-empty-string $path */
+
public static function onPath(string $path, ?Throwable $cause = null): self
+
{
+
return new self(
+
message: 'The path "' . $path . '" does not contain a valid key file',
+
previous: $cause,
+
);
+
}
+
}
+98
vendor/lcobucci/jwt/src/Signer/Key/InMemory.php
···
···
+
<?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 SensitiveParameter;
+
use SplFileObject;
+
use Throwable;
+
+
use function assert;
+
use function is_string;
+
+
final class InMemory implements Key
+
{
+
/** @param non-empty-string $contents */
+
private function __construct(
+
#[SensitiveParameter]
+
public readonly string $contents,
+
#[SensitiveParameter]
+
public readonly string $passphrase,
+
) {
+
}
+
+
/** @param non-empty-string $contents */
+
public static function plainText(
+
#[SensitiveParameter]
+
string $contents,
+
#[SensitiveParameter]
+
string $passphrase = '',
+
): self {
+
self::guardAgainstEmptyKey($contents);
+
+
return new self($contents, $passphrase);
+
}
+
+
/** @param non-empty-string $contents */
+
public static function base64Encoded(
+
#[SensitiveParameter]
+
string $contents,
+
#[SensitiveParameter]
+
string $passphrase = '',
+
): self {
+
$decoded = SodiumBase64Polyfill::base642bin(
+
$contents,
+
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL,
+
);
+
+
self::guardAgainstEmptyKey($decoded);
+
+
return new self($decoded, $passphrase);
+
}
+
+
/**
+
* @param non-empty-string $path
+
*
+
* @throws FileCouldNotBeRead
+
*/
+
public static function file(
+
string $path,
+
#[SensitiveParameter]
+
string $passphrase = '',
+
): self {
+
try {
+
$file = new SplFileObject($path);
+
} catch (Throwable $exception) {
+
throw FileCouldNotBeRead::onPath($path, $exception);
+
}
+
+
$fileSize = $file->getSize();
+
$contents = $fileSize > 0 ? $file->fread($file->getSize()) : '';
+
assert(is_string($contents));
+
+
self::guardAgainstEmptyKey($contents);
+
+
return new self($contents, $passphrase);
+
}
+
+
/** @phpstan-assert non-empty-string $contents */
+
private static function guardAgainstEmptyKey(string $contents): void
+
{
+
if ($contents === '') {
+
throw InvalidKeyProvided::cannotBeEmpty();
+
}
+
}
+
+
public function contents(): string
+
{
+
return $this->contents;
+
}
+
+
public function passphrase(): string
+
{
+
return $this->passphrase;
+
}
+
}
+133
vendor/lcobucci/jwt/src/Signer/OpenSSL.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer;
+
+
use Lcobucci\JWT\Signer;
+
use OpenSSLAsymmetricKey;
+
use SensitiveParameter;
+
+
use function array_key_exists;
+
use function assert;
+
use function is_array;
+
use function is_bool;
+
use function is_int;
+
use function openssl_error_string;
+
use function openssl_pkey_get_details;
+
use function openssl_pkey_get_private;
+
use function openssl_pkey_get_public;
+
use function openssl_sign;
+
use function openssl_verify;
+
+
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
+
{
+
protected const KEY_TYPE_MAP = [
+
OPENSSL_KEYTYPE_RSA => 'RSA',
+
OPENSSL_KEYTYPE_DSA => 'DSA',
+
OPENSSL_KEYTYPE_DH => 'DH',
+
OPENSSL_KEYTYPE_EC => 'EC',
+
];
+
+
/**
+
* @return non-empty-string
+
*
+
* @throws CannotSignPayload
+
* @throws InvalidKeyProvided
+
*/
+
final protected function createSignature(
+
#[SensitiveParameter]
+
string $pem,
+
#[SensitiveParameter]
+
string $passphrase,
+
string $payload,
+
): string {
+
$key = $this->getPrivateKey($pem, $passphrase);
+
+
$signature = '';
+
+
if (! openssl_sign($payload, $signature, $key, $this->algorithm())) {
+
throw CannotSignPayload::errorHappened($this->fullOpenSSLErrorString());
+
}
+
+
return $signature;
+
}
+
+
/** @throws CannotSignPayload */
+
private function getPrivateKey(
+
#[SensitiveParameter]
+
string $pem,
+
#[SensitiveParameter]
+
string $passphrase,
+
): OpenSSLAsymmetricKey {
+
return $this->validateKey(openssl_pkey_get_private($pem, $passphrase));
+
}
+
+
/** @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());
+
+
return $result === 1;
+
}
+
+
/** @throws InvalidKeyProvided */
+
private function getPublicKey(string $pem): OpenSSLAsymmetricKey
+
{
+
return $this->validateKey(openssl_pkey_get_public($pem));
+
}
+
+
/**
+
* Raises an exception when the key type is not the expected type
+
*
+
* @throws InvalidKeyProvided
+
*/
+
private function validateKey(OpenSSLAsymmetricKey|bool $key): OpenSSLAsymmetricKey
+
{
+
if (is_bool($key)) {
+
throw InvalidKeyProvided::cannotBeParsed($this->fullOpenSSLErrorString());
+
}
+
+
$details = openssl_pkey_get_details($key);
+
assert(is_array($details));
+
+
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']);
+
+
return $key;
+
}
+
+
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;
+
+
/**
+
* Returns which algorithm to be used to create/verify the signature (using OpenSSL constants)
+
*
+
* @internal
+
*/
+
abstract public function algorithm(): int;
+
}
+35
vendor/lcobucci/jwt/src/Signer/Rsa.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer;
+
+
use const OPENSSL_KEYTYPE_RSA;
+
+
abstract class Rsa extends OpenSSL
+
{
+
private const MINIMUM_KEY_LENGTH = 2048;
+
+
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());
+
}
+
+
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);
+
}
+
}
+
}
+21
vendor/lcobucci/jwt/src/Signer/Rsa/Sha256.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Rsa;
+
+
use Lcobucci\JWT\Signer\Rsa;
+
+
use const OPENSSL_ALGO_SHA256;
+
+
final class Sha256 extends Rsa
+
{
+
public function algorithmId(): string
+
{
+
return 'RS256';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA256;
+
}
+
}
+21
vendor/lcobucci/jwt/src/Signer/Rsa/Sha384.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Rsa;
+
+
use Lcobucci\JWT\Signer\Rsa;
+
+
use const OPENSSL_ALGO_SHA384;
+
+
final class Sha384 extends Rsa
+
{
+
public function algorithmId(): string
+
{
+
return 'RS384';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA384;
+
}
+
}
+21
vendor/lcobucci/jwt/src/Signer/Rsa/Sha512.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Signer\Rsa;
+
+
use Lcobucci\JWT\Signer\Rsa;
+
+
use const OPENSSL_ALGO_SHA512;
+
+
final class Sha512 extends Rsa
+
{
+
public function algorithmId(): string
+
{
+
return 'RS512';
+
}
+
+
public function algorithm(): int
+
{
+
return OPENSSL_ALGO_SHA512;
+
}
+
}
+98
vendor/lcobucci/jwt/src/SodiumBase64Polyfill.php
···
···
+
<?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;
+
+
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
+
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);
+
}
+
+
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
+
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;
+
}
+
+
/**
+
* @return ($encoded is non-empty-string ? non-empty-string : string)
+
*
+
* @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) {
+
throw CannotDecodeContent::invalidBase64String();
+
}
+
}
+
+
/**
+
* @return ($encoded is non-empty-string ? non-empty-string : string)
+
*
+
* @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;
+
}
+
}
+65
vendor/lcobucci/jwt/src/Token.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use DateTimeInterface;
+
use Lcobucci\JWT\Token\DataSet;
+
+
interface Token
+
{
+
/**
+
* Returns the token headers
+
*/
+
public function headers(): DataSet;
+
+
/**
+
* Returns if the token is allowed to be used by the audience
+
*
+
* @param non-empty-string $audience
+
*/
+
public function isPermittedFor(string $audience): bool;
+
+
/**
+
* Returns if the token has the given id
+
*
+
* @param non-empty-string $id
+
*/
+
public function isIdentifiedBy(string $id): bool;
+
+
/**
+
* Returns if the token has the given subject
+
*
+
* @param non-empty-string $subject
+
*/
+
public function isRelatedTo(string $subject): bool;
+
+
/**
+
* Returns if the token was issued by any of given issuers
+
*
+
* @param non-empty-string ...$issuers
+
*/
+
public function hasBeenIssuedBy(string ...$issuers): bool;
+
+
/**
+
* Returns if the token was issued before of given time
+
*/
+
public function hasBeenIssuedBefore(DateTimeInterface $now): bool;
+
+
/**
+
* Returns if the token minimum time is before than given time
+
*/
+
public function isMinimumTimeBefore(DateTimeInterface $now): bool;
+
+
/**
+
* Returns if the token is expired
+
*/
+
public function isExpired(DateTimeInterface $now): bool;
+
+
/**
+
* Returns an encoded representation of the token
+
*
+
* @return non-empty-string
+
*/
+
public function toString(): string;
+
}
+167
vendor/lcobucci/jwt/src/Token/Builder.php
···
···
+
<?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 Lcobucci\JWT\UnencryptedToken;
+
+
use function array_diff;
+
use function array_merge;
+
use function in_array;
+
+
/** @immutable */
+
final class Builder implements BuilderInterface
+
{
+
/** @var array<non-empty-string, mixed> */
+
private array $headers = ['typ' => 'JWT', 'alg' => null];
+
+
/** @var array<non-empty-string, mixed> */
+
private array $claims = [];
+
+
/** @deprecated Deprecated since v5.5, please use {@see self::new()} instead */
+
public function __construct(private readonly Encoder $encoder, private readonly ClaimsFormatter $claimFormatter)
+
{
+
}
+
+
public static function new(Encoder $encoder, ClaimsFormatter $claimFormatter): self
+
{
+
return new self($encoder, $claimFormatter);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
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));
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function expiresAt(DateTimeImmutable $expiration): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::EXPIRATION_TIME, $expiration);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function identifiedBy(string $id): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::ID, $id);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function issuedAt(DateTimeImmutable $issuedAt): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::ISSUED_AT, $issuedAt);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function issuedBy(string $issuer): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::ISSUER, $issuer);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::NOT_BEFORE, $notBefore);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function relatedTo(string $subject): BuilderInterface
+
{
+
return $this->setClaim(RegisteredClaims::SUBJECT, $subject);
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function withHeader(string $name, mixed $value): BuilderInterface
+
{
+
$new = clone $this;
+
$new->headers[$name] = $value;
+
+
return $new;
+
}
+
+
/**
+
* @inheritDoc
+
* @pure
+
*/
+
public function withClaim(string $name, mixed $value): BuilderInterface
+
{
+
if (in_array($name, RegisteredClaims::ALL, true)) {
+
throw RegisteredClaimGiven::forClaim($name);
+
}
+
+
return $this->setClaim($name, $value);
+
}
+
+
/** @param non-empty-string $name */
+
private function setClaim(string $name, mixed $value): BuilderInterface
+
{
+
$new = clone $this;
+
$new->claims[$name] = $value;
+
+
return $new;
+
}
+
+
/**
+
* @param array<non-empty-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): UnencryptedToken
+
{
+
$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),
+
);
+
}
+
}
+37
vendor/lcobucci/jwt/src/Token/DataSet.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Token;
+
+
use function array_key_exists;
+
+
final class DataSet
+
{
+
/** @param array<non-empty-string, mixed> $data */
+
public function __construct(private readonly array $data, private readonly string $encoded)
+
{
+
}
+
+
/** @param non-empty-string $name */
+
public function get(string $name, mixed $default = null): mixed
+
{
+
return $this->data[$name] ?? $default;
+
}
+
+
/** @param non-empty-string $name */
+
public function has(string $name): bool
+
{
+
return array_key_exists($name, $this->data);
+
}
+
+
/** @return array<non-empty-string, mixed> */
+
public function all(): array
+
{
+
return $this->data;
+
}
+
+
public function toString(): string
+
{
+
return $this->encoded;
+
}
+
}
+41
vendor/lcobucci/jwt/src/Token/InvalidTokenStructure.php
···
···
+
<?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 missingHeaderPart(): self
+
{
+
return new self('The JWT string is missing the Header part');
+
}
+
+
public static function missingClaimsPart(): self
+
{
+
return new self('The JWT string is missing the Claim part');
+
}
+
+
public static function missingSignaturePart(): self
+
{
+
return new self('The JWT string is missing the Signature part');
+
}
+
+
/** @param non-empty-string $part */
+
public static function arrayExpected(string $part): self
+
{
+
return new self($part . ' must be an array with non-empty-string keys');
+
}
+
+
public static function dateIsNotParseable(string $value): self
+
{
+
return new self('Value is not in the allowed date format: ' . $value);
+
}
+
}
+180
vendor/lcobucci/jwt/src/Token/Parser.php
···
···
+
<?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;
+
+
public function __construct(private readonly Decoder $decoder)
+
{
+
}
+
+
public function parse(string $jwt): TokenInterface
+
{
+
[$encodedHeaders, $encodedClaims, $encodedSignature] = $this->splitJwt($jwt);
+
+
if ($encodedHeaders === '') {
+
throw InvalidTokenStructure::missingHeaderPart();
+
}
+
+
if ($encodedClaims === '') {
+
throw InvalidTokenStructure::missingClaimsPart();
+
}
+
+
if ($encodedSignature === '') {
+
throw InvalidTokenStructure::missingSignaturePart();
+
}
+
+
$header = $this->parseHeader($encodedHeaders);
+
+
return new Plain(
+
new DataSet($header, $encodedHeaders),
+
new DataSet($this->parseClaims($encodedClaims), $encodedClaims),
+
$this->parseSignature($encodedSignature),
+
);
+
}
+
+
/**
+
* Splits the JWT string into an array
+
*
+
* @param non-empty-string $jwt
+
*
+
* @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
+
*
+
* @param non-empty-string $data
+
*
+
* @return array<non-empty-string, 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');
+
}
+
+
$this->guardAgainstEmptyStringKeys($header, '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
+
*
+
* @param non-empty-string $data
+
*
+
* @return array<non-empty-string, 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');
+
}
+
+
$this->guardAgainstEmptyStringKeys($claims, '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 array<string, mixed> $array
+
* @param non-empty-string $part
+
*
+
* @phpstan-assert array<non-empty-string, mixed> $array
+
*/
+
private function guardAgainstEmptyStringKeys(array $array, string $part): void
+
{
+
foreach ($array as $key => $value) {
+
if ($key === '') {
+
throw InvalidTokenStructure::arrayExpected($part);
+
}
+
}
+
}
+
+
/** @throws InvalidTokenStructure */
+
private function convertDate(int|float|string $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 non-empty-string $data
+
*/
+
private function parseSignature(string $data): Signature
+
{
+
$hash = $this->decoder->base64UrlDecode($data);
+
+
return new Signature($hash, $data);
+
}
+
}
+85
vendor/lcobucci/jwt/src/Token/Plain.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Token;
+
+
use DateTimeInterface;
+
use Lcobucci\JWT\UnencryptedToken;
+
+
use function in_array;
+
+
final class Plain implements UnencryptedToken
+
{
+
public function __construct(
+
private readonly DataSet $headers,
+
private readonly DataSet $claims,
+
private readonly 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();
+
}
+
}
+21
vendor/lcobucci/jwt/src/Token/RegisteredClaimGiven.php
···
···
+
<?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"';
+
+
/** @param non-empty-string $name */
+
public static function forClaim(string $name): self
+
{
+
return new self(sprintf(self::DEFAULT_MESSAGE, $name));
+
}
+
}
+77
vendor/lcobucci/jwt/src/Token/RegisteredClaims.php
···
···
+
<?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';
+
}
+31
vendor/lcobucci/jwt/src/Token/Signature.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Token;
+
+
final class Signature
+
{
+
/**
+
* @param non-empty-string $hash
+
* @param non-empty-string $encoded
+
*/
+
public function __construct(private readonly string $hash, private readonly string $encoded)
+
{
+
}
+
+
/** @return non-empty-string */
+
public function hash(): string
+
{
+
return $this->hash;
+
}
+
+
/**
+
* Returns the encoded version of the signature
+
*
+
* @return non-empty-string
+
*/
+
public function toString(): string
+
{
+
return $this->encoded;
+
}
+
}
+15
vendor/lcobucci/jwt/src/Token/UnsupportedHeaderFound.php
···
···
+
<?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');
+
}
+
}
+27
vendor/lcobucci/jwt/src/UnencryptedToken.php
···
···
+
<?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
+
*
+
* @return non-empty-string
+
*/
+
public function payload(): string;
+
}
+12
vendor/lcobucci/jwt/src/Validation/Constraint.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
use Lcobucci\JWT\Token;
+
+
interface Constraint
+
{
+
/** @throws ConstraintViolation */
+
public function assert(Token $token): void;
+
}
+18
vendor/lcobucci/jwt/src/Validation/Constraint/CannotValidateARegisteredClaim.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation\Constraint;
+
+
use InvalidArgumentException;
+
use Lcobucci\JWT\Exception;
+
+
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
+
{
+
/** @param non-empty-string $claim */
+
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',
+
);
+
}
+
}
+35
vendor/lcobucci/jwt/src/Validation/Constraint/HasClaim.php
···
···
+
<?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 HasClaim implements Constraint
+
{
+
/** @param non-empty-string $claim */
+
public function __construct(private readonly string $claim)
+
{
+
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
+
throw CannotValidateARegisteredClaim::create($claim);
+
}
+
}
+
+
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);
+
}
+
}
+
}
+42
vendor/lcobucci/jwt/src/Validation/Constraint/HasClaimWithValue.php
···
···
+
<?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
+
{
+
/** @param non-empty-string $claim */
+
public function __construct(private readonly string $claim, private readonly mixed $expectedValue)
+
{
+
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
+
throw CannotValidateARegisteredClaim::create($claim);
+
}
+
}
+
+
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,
+
);
+
}
+
}
+
}
+26
vendor/lcobucci/jwt/src/Validation/Constraint/IdentifiedBy.php
···
···
+
<?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
+
{
+
/** @param non-empty-string $id */
+
public function __construct(private readonly string $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,
+
);
+
}
+
}
+
}
+30
vendor/lcobucci/jwt/src/Validation/Constraint/IssuedBy.php
···
···
+
<?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 non-empty-string[] */
+
private readonly array $issuers;
+
+
/** @param non-empty-string ...$issuers */
+
public function __construct(string ...$issuers)
+
{
+
$this->issuers = $issuers;
+
}
+
+
public function assert(Token $token): void
+
{
+
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
+
throw ConstraintViolation::error(
+
'The token was not issued by the given issuers',
+
$this,
+
);
+
}
+
}
+
}
+15
vendor/lcobucci/jwt/src/Validation/Constraint/LeewayCannotBeNegative.php
···
···
+
<?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');
+
}
+
}
+67
vendor/lcobucci/jwt/src/Validation/Constraint/LooseValidAt.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation\Constraint;
+
+
use DateInterval;
+
use DateTimeInterface;
+
use Lcobucci\JWT\Token;
+
use Lcobucci\JWT\Validation\ConstraintViolation;
+
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
+
use Psr\Clock\ClockInterface as Clock;
+
+
final class LooseValidAt implements ValidAtInterface
+
{
+
private readonly DateInterval $leeway;
+
+
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
+
{
+
$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);
+
}
+
}
+
}
+26
vendor/lcobucci/jwt/src/Validation/Constraint/PermittedFor.php
···
···
+
<?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
+
{
+
/** @param non-empty-string $audience */
+
public function __construct(private readonly string $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,
+
);
+
}
+
}
+
}
+26
vendor/lcobucci/jwt/src/Validation/Constraint/RelatedTo.php
···
···
+
<?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
+
{
+
/** @param non-empty-string $subject */
+
public function __construct(private readonly string $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,
+
);
+
}
+
}
+
}
+32
vendor/lcobucci/jwt/src/Validation/Constraint/SignedWith.php
···
···
+
<?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
+
{
+
public function __construct(private readonly Signer $signer, private readonly Signer\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);
+
}
+
}
+
}
+38
vendor/lcobucci/jwt/src/Validation/Constraint/SignedWithOneInSet.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation\Constraint;
+
+
use Lcobucci\JWT\Token;
+
use Lcobucci\JWT\Validation\ConstraintViolation;
+
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
+
+
use const PHP_EOL;
+
+
final class SignedWithOneInSet implements SignedWithInterface
+
{
+
/** @var array<SignedWithUntilDate> */
+
private readonly array $constraints;
+
+
public function __construct(SignedWithUntilDate ...$constraints)
+
{
+
$this->constraints = $constraints;
+
}
+
+
public function assert(Token $token): void
+
{
+
$errorMessage = 'It was not possible to verify the signature of the token, reasons:';
+
+
foreach ($this->constraints as $constraint) {
+
try {
+
$constraint->assert($token);
+
+
return;
+
} catch (ConstraintViolation $violation) {
+
$errorMessage .= PHP_EOL . '- ' . $violation->getMessage();
+
}
+
}
+
+
throw ConstraintViolation::error($errorMessage, $this);
+
}
+
}
+47
vendor/lcobucci/jwt/src/Validation/Constraint/SignedWithUntilDate.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation\Constraint;
+
+
use DateTimeImmutable;
+
use DateTimeInterface;
+
use Lcobucci\JWT\Signer;
+
use Lcobucci\JWT\Token;
+
use Lcobucci\JWT\Validation\ConstraintViolation;
+
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
+
use Psr\Clock\ClockInterface;
+
+
final class SignedWithUntilDate implements SignedWithInterface
+
{
+
private readonly SignedWith $verifySignature;
+
private readonly ClockInterface $clock;
+
+
public function __construct(
+
Signer $signer,
+
Signer\Key $key,
+
private readonly DateTimeImmutable $validUntil,
+
?ClockInterface $clock = null,
+
) {
+
$this->verifySignature = new SignedWith($signer, $key);
+
+
$this->clock = $clock ?? new class () implements ClockInterface {
+
public function now(): DateTimeImmutable
+
{
+
return new DateTimeImmutable();
+
}
+
};
+
}
+
+
public function assert(Token $token): void
+
{
+
if ($this->validUntil < $this->clock->now()) {
+
throw ConstraintViolation::error(
+
'This constraint was only usable until '
+
. $this->validUntil->format(DateTimeInterface::RFC3339),
+
$this,
+
);
+
}
+
+
$this->verifySignature->assert($token);
+
}
+
}
+84
vendor/lcobucci/jwt/src/Validation/Constraint/StrictValidAt.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation\Constraint;
+
+
use DateInterval;
+
use DateTimeInterface;
+
use Lcobucci\JWT\Token;
+
use Lcobucci\JWT\UnencryptedToken;
+
use Lcobucci\JWT\Validation\ConstraintViolation;
+
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
+
use Psr\Clock\ClockInterface as Clock;
+
+
final class StrictValidAt implements ValidAtInterface
+
{
+
private readonly DateInterval $leeway;
+
+
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
+
{
+
$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);
+
}
+
}
+
}
+24
vendor/lcobucci/jwt/src/Validation/ConstraintViolation.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
use Lcobucci\JWT\Exception;
+
use RuntimeException;
+
+
final class ConstraintViolation extends RuntimeException implements Exception
+
{
+
/** @param class-string<Constraint>|null $constraint */
+
public function __construct(
+
string $message = '',
+
public readonly ?string $constraint = null,
+
) {
+
parent::__construct($message);
+
}
+
+
/** @param non-empty-string $message */
+
public static function error(string $message, Constraint $constraint): self
+
{
+
return new self(message: $message, constraint: $constraint::class);
+
}
+
}
+11
vendor/lcobucci/jwt/src/Validation/NoConstraintsGiven.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
use Lcobucci\JWT\Exception;
+
use RuntimeException;
+
+
final class NoConstraintsGiven extends RuntimeException implements Exception
+
{
+
}
+48
vendor/lcobucci/jwt/src/Validation/RequiredConstraintsViolated.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
use Lcobucci\JWT\Exception;
+
use RuntimeException;
+
+
use function array_map;
+
use function implode;
+
+
final class RequiredConstraintsViolated extends RuntimeException implements Exception
+
{
+
/** @param ConstraintViolation[] $violations */
+
public function __construct(
+
string $message = '',
+
public readonly array $violations = [],
+
) {
+
parent::__construct($message);
+
}
+
+
public static function fromViolations(ConstraintViolation ...$violations): self
+
{
+
return new self(message: self::buildMessage($violations), violations: $violations);
+
}
+
+
/** @param ConstraintViolation[] $violations */
+
private static function buildMessage(array $violations): string
+
{
+
$violations = array_map(
+
static function (ConstraintViolation $violation): string {
+
return '- ' . $violation->getMessage();
+
},
+
$violations,
+
);
+
+
$message = "The token violates some mandatory constraints, details:\n";
+
$message .= implode("\n", $violations);
+
+
return $message;
+
}
+
+
/** @return ConstraintViolation[] */
+
public function violations(): array
+
{
+
return $this->violations;
+
}
+
}
+8
vendor/lcobucci/jwt/src/Validation/SignedWith.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
interface SignedWith extends Constraint
+
{
+
}
+8
vendor/lcobucci/jwt/src/Validation/ValidAt.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
interface ValidAt extends Constraint
+
{
+
}
+56
vendor/lcobucci/jwt/src/Validation/Validator.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT\Validation;
+
+
use Lcobucci\JWT\Token;
+
+
final class Validator implements \Lcobucci\JWT\Validator
+
{
+
public function assert(Token $token, Constraint ...$constraints): void
+
{
+
if ($constraints === []) {
+
throw new NoConstraintsGiven('No constraint given.');
+
}
+
+
$violations = [];
+
+
foreach ($constraints as $constraint) {
+
$this->checkConstraint($constraint, $token, $violations);
+
}
+
+
if ($violations) {
+
throw RequiredConstraintsViolated::fromViolations(...$violations);
+
}
+
}
+
+
/** @param ConstraintViolation[] $violations */
+
private function checkConstraint(
+
Constraint $constraint,
+
Token $token,
+
array &$violations,
+
): void {
+
try {
+
$constraint->assert($token);
+
} catch (ConstraintViolation $e) {
+
$violations[] = $e;
+
}
+
}
+
+
public function validate(Token $token, Constraint ...$constraints): bool
+
{
+
if ($constraints === []) {
+
throw new NoConstraintsGiven('No constraint given.');
+
}
+
+
try {
+
foreach ($constraints as $constraint) {
+
$constraint->assert($token);
+
}
+
+
return true;
+
} catch (ConstraintViolation) {
+
return false;
+
}
+
}
+
}
+20
vendor/lcobucci/jwt/src/Validator.php
···
···
+
<?php
+
declare(strict_types=1);
+
+
namespace Lcobucci\JWT;
+
+
use Lcobucci\JWT\Validation\Constraint;
+
use Lcobucci\JWT\Validation\NoConstraintsGiven;
+
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
+
+
interface Validator
+
{
+
/**
+
* @throws RequiredConstraintsViolated
+
* @throws NoConstraintsGiven
+
*/
+
public function assert(Token $token, Constraint ...$constraints): void;
+
+
/** @throws NoConstraintsGiven */
+
public function validate(Token $token, Constraint ...$constraints): bool;
+
}
+11
vendor/psr/clock/CHANGELOG.md
···
···
+
# Changelog
+
+
All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+
## 1.0.0
+
+
First stable release after PSR-20 acceptance
+
+
## 0.1.0
+
+
First release
+19
vendor/psr/clock/LICENSE
···
···
+
Copyright (c) 2017 PHP Framework Interoperability Group
+
+
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.
+61
vendor/psr/clock/README.md
···
···
+
# PSR Clock
+
+
This repository holds the interface for [PSR-20][psr-url].
+
+
Note that this is not a clock of its own. It is merely an interface that
+
describes a clock. See the specification for more details.
+
+
## Installation
+
+
```bash
+
composer require psr/clock
+
```
+
+
## Usage
+
+
If you need a clock, you can use the interface like this:
+
+
```php
+
<?php
+
+
use Psr\Clock\ClockInterface;
+
+
class Foo
+
{
+
private ClockInterface $clock;
+
+
public function __construct(ClockInterface $clock)
+
{
+
$this->clock = $clock;
+
}
+
+
public function doSomething()
+
{
+
/** @var DateTimeImmutable $currentDateAndTime */
+
$currentDateAndTime = $this->clock->now();
+
// do something useful with that information
+
}
+
}
+
```
+
+
You can then pick one of the [implementations][implementation-url] of the interface to get a clock.
+
+
If you want to implement the interface, you can require this package and
+
implement `Psr\Clock\ClockInterface` in your code.
+
+
Don't forget to add `psr/clock-implementation` to your `composer.json`s `provides`-section like this:
+
+
```json
+
{
+
"provides": {
+
"psr/clock-implementation": "1.0"
+
}
+
}
+
```
+
+
And please read the [specification text][specification-url] for details on the interface.
+
+
[psr-url]: https://www.php-fig.org/psr/psr-20
+
[package-url]: https://packagist.org/packages/psr/clock
+
[implementation-url]: https://packagist.org/providers/psr/clock-implementation
+
[specification-url]: https://github.com/php-fig/fig-standards/blob/master/proposed/clock.md
+21
vendor/psr/clock/composer.json
···
···
+
{
+
"name": "psr/clock",
+
"description": "Common interface for reading the clock.",
+
"keywords": ["psr", "psr-20", "time", "clock", "now"],
+
"homepage": "https://github.com/php-fig/clock",
+
"license": "MIT",
+
"authors": [
+
{
+
"name": "PHP-FIG",
+
"homepage": "https://www.php-fig.org/"
+
}
+
],
+
"require": {
+
"php": "^7.0 || ^8.0"
+
},
+
"autoload": {
+
"psr-4": {
+
"Psr\\Clock\\": "src/"
+
}
+
}
+
}
+13
vendor/psr/clock/src/ClockInterface.php
···
···
+
<?php
+
+
namespace Psr\Clock;
+
+
use DateTimeImmutable;
+
+
interface ClockInterface
+
{
+
/**
+
* Returns the current time as a DateTimeImmutable Object
+
*/
+
public function now(): DateTimeImmutable;
+
}