mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts: # composer.json # composer.lock # lib/composer/autoload_classmap.php # lib/composer/autoload_static.php # lib/composer/installed.php # lib/composer/platform_check.php # setup/setuputils.class.inc.php
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"ext-mysqli": "*",
|
"ext-mysqli": "*",
|
||||||
"ext-soap": "*",
|
"ext-soap": "*",
|
||||||
"combodo/tcpdf": "~6.4.4",
|
"combodo/tcpdf": "~6.4.4",
|
||||||
|
"firebase/php-jwt": "~6.4.0",
|
||||||
"guzzlehttp/guzzle": "^6.5.8",
|
"guzzlehttp/guzzle": "^6.5.8",
|
||||||
"laminas/laminas-mail": "^2.11",
|
"laminas/laminas-mail": "^2.11",
|
||||||
"laminas/laminas-servicemanager": "^3.5",
|
"laminas/laminas-servicemanager": "^3.5",
|
||||||
|
|||||||
26
composer.lock
generated
26
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "86ca84263f7f271dfc10e5e63bd02385",
|
"content-hash": "bad4899b1df95e6ab4ab2e42e4213acd",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "combodo/tcpdf",
|
"name": "combodo/tcpdf",
|
||||||
@@ -266,25 +266,31 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
"version": "v5.5.1",
|
"version": "v6.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/firebase/php-jwt.git",
|
"url": "https://github.com/firebase/php-jwt.git",
|
||||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6"
|
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6",
|
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
||||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6",
|
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0"
|
"php": "^7.1||^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": ">=4.8 <=9"
|
"guzzlehttp/guzzle": "^6.5||^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^1.1",
|
||||||
|
"phpunit/phpunit": "^7.5||^9.5",
|
||||||
|
"psr/cache": "^1.0||^2.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@@ -317,9 +323,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||||
"source": "https://github.com/firebase/php-jwt/tree/v5.5.1"
|
"source": "https://github.com/firebase/php-jwt/tree/v6.4.0"
|
||||||
},
|
},
|
||||||
"time": "2021-11-08T20:18:51+00:00"
|
"time": "2023-02-09T21:01:23+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
@@ -4752,7 +4758,7 @@
|
|||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": ">=7.1.3 <8.0.0",
|
"php": ">=7.1.3 <8.1.0",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
// autoload.php @generated by Composer
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
if (PHP_VERSION_ID < 50600) {
|
|
||||||
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
return ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239::getLoader();
|
return ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239::getLoader();
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ class ClassLoader
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[] Array of classname => path
|
* @return string[] Array of classname => path
|
||||||
* @psalm-return array<string, string>
|
* @psalm-var array<string, string>
|
||||||
*/
|
*/
|
||||||
public function getClassMap()
|
public function getClassMap()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
@@ -550,6 +550,7 @@ return array(
|
|||||||
'FilterPrivateKey' => $baseDir . '/core/filterdef.class.inc.php',
|
'FilterPrivateKey' => $baseDir . '/core/filterdef.class.inc.php',
|
||||||
'FindStylesheetObject' => $baseDir . '/application/findstylesheetobject.class.inc.php',
|
'FindStylesheetObject' => $baseDir . '/application/findstylesheetobject.class.inc.php',
|
||||||
'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
|
'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
|
||||||
|
'Firebase\\JWT\\CachedKeySet' => $vendorDir . '/firebase/php-jwt/src/CachedKeySet.php',
|
||||||
'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
|
'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
|
||||||
'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php',
|
'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php',
|
||||||
'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
|
'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
|
||||||
|
|||||||
@@ -2,25 +2,25 @@
|
|||||||
|
|
||||||
// autoload_files.php @generated by Composer
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
|
||||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||||
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
||||||
|
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
|
||||||
|
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
|
||||||
|
'7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
|
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
|
|
||||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
|
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
|
||||||
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_namespaces.php @generated by Composer
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
@@ -25,20 +25,33 @@ class ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239
|
|||||||
require __DIR__ . '/platform_check.php';
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', 'loadClassLoader'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', 'loadClassLoader'));
|
||||||
|
|
||||||
$includePaths = require __DIR__ . '/include_paths.php';
|
$includePaths = require __DIR__ . '/include_paths.php';
|
||||||
$includePaths[] = get_include_path();
|
$includePaths[] = get_include_path();
|
||||||
set_include_path(implode(PATH_SEPARATOR, $includePaths));
|
set_include_path(implode(PATH_SEPARATOR, $includePaths));
|
||||||
|
|
||||||
require __DIR__ . '/autoload_static.php';
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::getInitializer($loader));
|
if ($useStaticLoader) {
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::getInitializer($loader));
|
||||||
|
} else {
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$loader->setClassMapAuthoritative(true);
|
$loader->setClassMapAuthoritative(true);
|
||||||
$loader->register(true);
|
$loader->register(true);
|
||||||
|
|
||||||
$includeFiles = \Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::$files;
|
if ($useStaticLoader) {
|
||||||
|
$includeFiles = Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::$files;
|
||||||
|
} else {
|
||||||
|
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||||
|
}
|
||||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||||
composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file);
|
composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file);
|
||||||
}
|
}
|
||||||
@@ -47,16 +60,11 @@ class ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $fileIdentifier
|
|
||||||
* @param string $file
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file)
|
function composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file)
|
||||||
{
|
{
|
||||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
|
||||||
|
|
||||||
require $file;
|
require $file;
|
||||||
|
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,20 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239
|
|||||||
public static $files = array (
|
public static $files = array (
|
||||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
|
||||||
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
||||||
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
||||||
|
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
|
||||||
|
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||||
|
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
|
||||||
|
'7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
|
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
|
|
||||||
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
|
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
|
||||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
|
||||||
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -918,6 +918,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239
|
|||||||
'FilterPrivateKey' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php',
|
'FilterPrivateKey' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php',
|
||||||
'FindStylesheetObject' => __DIR__ . '/../..' . '/application/findstylesheetobject.class.inc.php',
|
'FindStylesheetObject' => __DIR__ . '/../..' . '/application/findstylesheetobject.class.inc.php',
|
||||||
'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
|
'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
|
||||||
|
'Firebase\\JWT\\CachedKeySet' => __DIR__ . '/..' . '/firebase/php-jwt/src/CachedKeySet.php',
|
||||||
'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
|
'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
|
||||||
'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php',
|
'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php',
|
||||||
'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
|
'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// include_paths.php @generated by Composer
|
// include_paths.php @generated by Composer
|
||||||
|
|
||||||
$vendorDir = dirname(__DIR__);
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|||||||
@@ -272,29 +272,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
"version": "v5.5.1",
|
"version": "v6.4.0",
|
||||||
"version_normalized": "5.5.1.0",
|
"version_normalized": "6.4.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/firebase/php-jwt.git",
|
"url": "https://github.com/firebase/php-jwt.git",
|
||||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6"
|
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6",
|
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
||||||
"reference": "83b609028194aa042ea33b5af2d41a7427de80e6",
|
"reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0"
|
"php": "^7.1||^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": ">=4.8 <=9"
|
"guzzlehttp/guzzle": "^6.5||^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^1.1",
|
||||||
|
"phpunit/phpunit": "^7.5||^9.5",
|
||||||
|
"psr/cache": "^1.0||^2.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||||
},
|
},
|
||||||
"time": "2021-11-08T20:18:51+00:00",
|
"time": "2023-02-09T21:01:23+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -326,7 +332,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/firebase/php-jwt/issues",
|
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||||
"source": "https://github.com/firebase/php-jwt/tree/v5.5.1"
|
"source": "https://github.com/firebase/php-jwt/tree/v6.4.0"
|
||||||
},
|
},
|
||||||
"install-path": "../firebase/php-jwt"
|
"install-path": "../firebase/php-jwt"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<?php return array(
|
<?php return array(
|
||||||
'root' => array(
|
'root' => array(
|
||||||
'pretty_version' => '3.0.2-rc1',
|
'pretty_version' => 'dev-develop',
|
||||||
'version' => '3.0.2.0-RC1',
|
'version' => 'dev-develop',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c5d5379c492059a80eb72cc1dab148dd6e82705f',
|
'reference' => '05efe40a2a415dba06d4dba615df89f455904377',
|
||||||
'name' => 'combodo/itop',
|
'name' => 'combodo/itop',
|
||||||
'dev' => true,
|
'dev' => true,
|
||||||
),
|
),
|
||||||
'versions' => array(
|
'versions' => array(
|
||||||
'combodo/itop' => array(
|
'combodo/itop' => array(
|
||||||
'pretty_version' => '3.0.2-rc1',
|
'pretty_version' => 'dev-develop',
|
||||||
'version' => '3.0.2.0-RC1',
|
'version' => 'dev-develop',
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'c5d5379c492059a80eb72cc1dab148dd6e82705f',
|
'reference' => '05efe40a2a415dba06d4dba615df89f455904377',
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'combodo/tcpdf' => array(
|
'combodo/tcpdf' => array(
|
||||||
@@ -62,12 +62,12 @@
|
|||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'firebase/php-jwt' => array(
|
'firebase/php-jwt' => array(
|
||||||
'pretty_version' => 'v5.5.1',
|
'pretty_version' => 'v6.4.0',
|
||||||
'version' => '5.5.1.0',
|
'version' => '6.4.0.0',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../firebase/php-jwt',
|
'install_path' => __DIR__ . '/../firebase/php-jwt',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6',
|
'reference' => '4dd1e007f22a927ac77da5a3fbb067b42d3bc224',
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/guzzle' => array(
|
'guzzlehttp/guzzle' => array(
|
||||||
|
|||||||
105
lib/firebase/php-jwt/CHANGELOG.md
Normal file
105
lib/firebase/php-jwt/CHANGELOG.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [6.4.0](https://github.com/firebase/php-jwt/compare/v6.3.2...v6.4.0) (2023-02-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support for W3C ES256K ([#462](https://github.com/firebase/php-jwt/issues/462)) ([213924f](https://github.com/firebase/php-jwt/commit/213924f51936291fbbca99158b11bd4ae56c2c95))
|
||||||
|
* improve caching by only decoding jwks when necessary ([#486](https://github.com/firebase/php-jwt/issues/486)) ([78d3ed1](https://github.com/firebase/php-jwt/commit/78d3ed1073553f7d0bbffa6c2010009a0d483d5c))
|
||||||
|
|
||||||
|
## [6.3.2](https://github.com/firebase/php-jwt/compare/v6.3.1...v6.3.2) (2022-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check kid before using as array index ([bad1b04](https://github.com/firebase/php-jwt/commit/bad1b040d0c736bbf86814c6b5ae614f517cf7bd))
|
||||||
|
|
||||||
|
## [6.3.1](https://github.com/firebase/php-jwt/compare/v6.3.0...v6.3.1) (2022-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* casing of GET for PSR compat ([#451](https://github.com/firebase/php-jwt/issues/451)) ([60b52b7](https://github.com/firebase/php-jwt/commit/60b52b71978790eafcf3b95cfbd83db0439e8d22))
|
||||||
|
* string interpolation format for php 8.2 ([#446](https://github.com/firebase/php-jwt/issues/446)) ([2e07d8a](https://github.com/firebase/php-jwt/commit/2e07d8a1524d12b69b110ad649f17461d068b8f2))
|
||||||
|
|
||||||
|
## 6.3.0 / 2022-07-15
|
||||||
|
|
||||||
|
- Added ES256 support to JWK parsing ([#399](https://github.com/firebase/php-jwt/pull/399))
|
||||||
|
- Fixed potential caching error in `CachedKeySet` by caching jwks as strings ([#435](https://github.com/firebase/php-jwt/pull/435))
|
||||||
|
|
||||||
|
## 6.2.0 / 2022-05-14
|
||||||
|
|
||||||
|
- Added `CachedKeySet` ([#397](https://github.com/firebase/php-jwt/pull/397))
|
||||||
|
- Added `$defaultAlg` parameter to `JWT::parseKey` and `JWT::parseKeySet` ([#426](https://github.com/firebase/php-jwt/pull/426)).
|
||||||
|
|
||||||
|
## 6.1.0 / 2022-03-23
|
||||||
|
|
||||||
|
- Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0
|
||||||
|
- Add parameter typing and return types where possible
|
||||||
|
|
||||||
|
## 6.0.0 / 2022-01-24
|
||||||
|
|
||||||
|
- **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information.
|
||||||
|
- New Key object to prevent key/algorithm type confusion (#365)
|
||||||
|
- Add JWK support (#273)
|
||||||
|
- Add ES256 support (#256)
|
||||||
|
- Add ES384 support (#324)
|
||||||
|
- Add Ed25519 support (#343)
|
||||||
|
|
||||||
|
## 5.0.0 / 2017-06-26
|
||||||
|
- Support RS384 and RS512.
|
||||||
|
See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
|
||||||
|
- Add an example for RS256 openssl.
|
||||||
|
See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
|
||||||
|
- Detect invalid Base64 encoding in signature.
|
||||||
|
See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
|
||||||
|
- Update `JWT::verify` to handle OpenSSL errors.
|
||||||
|
See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
|
||||||
|
- Add `array` type hinting to `decode` method
|
||||||
|
See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
|
||||||
|
- Add all JSON error types.
|
||||||
|
See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
|
||||||
|
- Bugfix 'kid' not in given key list.
|
||||||
|
See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
|
||||||
|
- Miscellaneous cleanup, documentation and test fixes.
|
||||||
|
See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
|
||||||
|
[#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
|
||||||
|
[#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
|
||||||
|
[@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
|
||||||
|
|
||||||
|
## 4.0.0 / 2016-07-17
|
||||||
|
- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
|
||||||
|
- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
|
||||||
|
- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
|
||||||
|
- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
|
||||||
|
|
||||||
|
## 3.0.0 / 2015-07-22
|
||||||
|
- Minimum PHP version updated from `5.2.0` to `5.3.0`.
|
||||||
|
- Add `\Firebase\JWT` namespace. See
|
||||||
|
[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
|
||||||
|
[@Dashron](https://github.com/Dashron)!
|
||||||
|
- Require a non-empty key to decode and verify a JWT. See
|
||||||
|
[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
|
||||||
|
[@sjones608](https://github.com/sjones608)!
|
||||||
|
- Cleaner documentation blocks in the code. See
|
||||||
|
[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
|
||||||
|
[@johanderuijter](https://github.com/johanderuijter)!
|
||||||
|
|
||||||
|
## 2.2.0 / 2015-06-22
|
||||||
|
- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
|
||||||
|
[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
|
||||||
|
[@mcocaro](https://github.com/mcocaro)!
|
||||||
|
|
||||||
|
## 2.1.0 / 2015-05-20
|
||||||
|
- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
|
||||||
|
between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
|
||||||
|
- Add support for passing an object implementing the `ArrayAccess` interface for
|
||||||
|
`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
|
||||||
|
|
||||||
|
## 2.0.0 / 2015-04-01
|
||||||
|
- **Note**: It is strongly recommended that you update to > v2.0.0 to address
|
||||||
|
known security vulnerabilities in prior versions when both symmetric and
|
||||||
|
asymmetric keys are used together.
|
||||||
|
- Update signature for `JWT::decode(...)` to require an array of supported
|
||||||
|
algorithms to use when verifying token signatures.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://travis-ci.org/firebase/php-jwt)
|

|
||||||
[](https://packagist.org/packages/firebase/php-jwt)
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
[](https://packagist.org/packages/firebase/php-jwt)
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
[](https://packagist.org/packages/firebase/php-jwt)
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
@@ -29,13 +29,13 @@ Example
|
|||||||
use Firebase\JWT\JWT;
|
use Firebase\JWT\JWT;
|
||||||
use Firebase\JWT\Key;
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
$key = "example_key";
|
$key = 'example_key';
|
||||||
$payload = array(
|
$payload = [
|
||||||
"iss" => "http://example.org",
|
'iss' => 'http://example.org',
|
||||||
"aud" => "http://example.com",
|
'aud' => 'http://example.com',
|
||||||
"iat" => 1356999524,
|
'iat' => 1356999524,
|
||||||
"nbf" => 1357000000
|
'nbf' => 1357000000
|
||||||
);
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IMPORTANT:
|
* IMPORTANT:
|
||||||
@@ -98,12 +98,12 @@ ehde/zUxo6UvS7UrBQIDAQAB
|
|||||||
-----END PUBLIC KEY-----
|
-----END PUBLIC KEY-----
|
||||||
EOD;
|
EOD;
|
||||||
|
|
||||||
$payload = array(
|
$payload = [
|
||||||
"iss" => "example.org",
|
'iss' => 'example.org',
|
||||||
"aud" => "example.com",
|
'aud' => 'example.com',
|
||||||
"iat" => 1356999524,
|
'iat' => 1356999524,
|
||||||
"nbf" => 1357000000
|
'nbf' => 1357000000
|
||||||
);
|
];
|
||||||
|
|
||||||
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
@@ -139,12 +139,12 @@ $privateKey = openssl_pkey_get_private(
|
|||||||
$passphrase
|
$passphrase
|
||||||
);
|
);
|
||||||
|
|
||||||
$payload = array(
|
$payload = [
|
||||||
"iss" => "example.org",
|
'iss' => 'example.org',
|
||||||
"aud" => "example.com",
|
'aud' => 'example.com',
|
||||||
"iat" => 1356999524,
|
'iat' => 1356999524,
|
||||||
"nbf" => 1357000000
|
'nbf' => 1357000000
|
||||||
);
|
];
|
||||||
|
|
||||||
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
@@ -173,12 +173,12 @@ $privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
|
|||||||
|
|
||||||
$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
|
$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
|
||||||
|
|
||||||
$payload = array(
|
$payload = [
|
||||||
"iss" => "example.org",
|
'iss' => 'example.org',
|
||||||
"aud" => "example.com",
|
'aud' => 'example.com',
|
||||||
"iat" => 1356999524,
|
'iat' => 1356999524,
|
||||||
"nbf" => 1357000000
|
'nbf' => 1357000000
|
||||||
);
|
];
|
||||||
|
|
||||||
$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
|
$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
|
||||||
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
@@ -198,72 +198,115 @@ use Firebase\JWT\JWT;
|
|||||||
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
|
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
|
||||||
$jwks = ['keys' => []];
|
$jwks = ['keys' => []];
|
||||||
|
|
||||||
// JWK::parseKeySet($jwks) returns an associative array of **kid** to private
|
// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
|
||||||
// key. Pass this as the second parameter to JWT::decode.
|
// objects. Pass this as the second parameter to JWT::decode.
|
||||||
// NOTE: The deprecated $supportedAlgorithm must be supplied when parsing from JWK.
|
JWT::decode($payload, JWK::parseKeySet($jwks));
|
||||||
JWT::decode($payload, JWK::parseKeySet($jwks), $supportedAlgorithm);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Changelog
|
Using Cached Key Sets
|
||||||
---------
|
---------------------
|
||||||
|
|
||||||
#### 5.0.0 / 2017-06-26
|
The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
|
||||||
- Support RS384 and RS512.
|
This has the following advantages:
|
||||||
See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
|
|
||||||
- Add an example for RS256 openssl.
|
|
||||||
See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
|
|
||||||
- Detect invalid Base64 encoding in signature.
|
|
||||||
See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
|
|
||||||
- Update `JWT::verify` to handle OpenSSL errors.
|
|
||||||
See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
|
|
||||||
- Add `array` type hinting to `decode` method
|
|
||||||
See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
|
|
||||||
- Add all JSON error types.
|
|
||||||
See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
|
|
||||||
- Bugfix 'kid' not in given key list.
|
|
||||||
See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
|
|
||||||
- Miscellaneous cleanup, documentation and test fixes.
|
|
||||||
See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
|
|
||||||
[#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
|
|
||||||
[#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
|
|
||||||
[@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
|
|
||||||
|
|
||||||
#### 4.0.0 / 2016-07-17
|
1. The results are cached for performance.
|
||||||
- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
|
2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation.
|
||||||
- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
|
3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second.
|
||||||
- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
|
|
||||||
- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
|
|
||||||
|
|
||||||
#### 3.0.0 / 2015-07-22
|
```php
|
||||||
- Minimum PHP version updated from `5.2.0` to `5.3.0`.
|
use Firebase\JWT\CachedKeySet;
|
||||||
- Add `\Firebase\JWT` namespace. See
|
use Firebase\JWT\JWT;
|
||||||
[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
|
|
||||||
[@Dashron](https://github.com/Dashron)!
|
|
||||||
- Require a non-empty key to decode and verify a JWT. See
|
|
||||||
[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
|
|
||||||
[@sjones608](https://github.com/sjones608)!
|
|
||||||
- Cleaner documentation blocks in the code. See
|
|
||||||
[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
|
|
||||||
[@johanderuijter](https://github.com/johanderuijter)!
|
|
||||||
|
|
||||||
#### 2.2.0 / 2015-06-22
|
// The URI for the JWKS you wish to cache the results from
|
||||||
- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
|
$jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
|
||||||
[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
|
|
||||||
[@mcocaro](https://github.com/mcocaro)!
|
|
||||||
|
|
||||||
#### 2.1.0 / 2015-05-20
|
// Create an HTTP client (can be any PSR-7 compatible HTTP client)
|
||||||
- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
|
$httpClient = new GuzzleHttp\Client();
|
||||||
between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
|
|
||||||
- Add support for passing an object implementing the `ArrayAccess` interface for
|
|
||||||
`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
|
|
||||||
|
|
||||||
#### 2.0.0 / 2015-04-01
|
// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
|
||||||
- **Note**: It is strongly recommended that you update to > v2.0.0 to address
|
$httpFactory = new GuzzleHttp\Psr\HttpFactory();
|
||||||
known security vulnerabilities in prior versions when both symmetric and
|
|
||||||
asymmetric keys are used together.
|
|
||||||
- Update signature for `JWT::decode(...)` to require an array of supported
|
|
||||||
algorithms to use when verifying token signatures.
|
|
||||||
|
|
||||||
|
// Create a cache item pool (can be any PSR-6 compatible cache item pool)
|
||||||
|
$cacheItemPool = Phpfastcache\CacheManager::getInstance('files');
|
||||||
|
|
||||||
|
$keySet = new CachedKeySet(
|
||||||
|
$jwksUri,
|
||||||
|
$httpClient,
|
||||||
|
$httpFactory,
|
||||||
|
$cacheItemPool,
|
||||||
|
null, // $expiresAfter int seconds to set the JWKS to expire
|
||||||
|
true // $rateLimit true to enable rate limit of 10 RPS on lookup of invalid keys
|
||||||
|
);
|
||||||
|
|
||||||
|
$jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above
|
||||||
|
$decoded = JWT::decode($jwt, $keySet);
|
||||||
|
```
|
||||||
|
|
||||||
|
Miscellaneous
|
||||||
|
-------------
|
||||||
|
|
||||||
|
#### Exception Handling
|
||||||
|
|
||||||
|
When a call to `JWT::decode` is invalid, it will throw one of the following exceptions:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\SignatureInvalidException;
|
||||||
|
use Firebase\JWT\BeforeValidException;
|
||||||
|
use Firebase\JWT\ExpiredException;
|
||||||
|
use DomainException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$decoded = JWT::decode($payload, $keys);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
// provided key/key-array is empty or malformed.
|
||||||
|
} catch (DomainException $e) {
|
||||||
|
// provided algorithm is unsupported OR
|
||||||
|
// provided key is invalid OR
|
||||||
|
// unknown error thrown in openSSL or libsodium OR
|
||||||
|
// libsodium is required but not available.
|
||||||
|
} catch (SignatureInvalidException $e) {
|
||||||
|
// provided JWT signature verification failed.
|
||||||
|
} catch (BeforeValidException $e) {
|
||||||
|
// provided JWT is trying to be used before "nbf" claim OR
|
||||||
|
// provided JWT is trying to be used before "iat" claim.
|
||||||
|
} catch (ExpiredException $e) {
|
||||||
|
// provided JWT is trying to be used after "exp" claim.
|
||||||
|
} catch (UnexpectedValueException $e) {
|
||||||
|
// provided JWT is malformed OR
|
||||||
|
// provided JWT is missing an algorithm / using an unsupported algorithm OR
|
||||||
|
// provided JWT algorithm does not match provided key OR
|
||||||
|
// provided key ID in key/key-array is empty or invalid.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
try {
|
||||||
|
$decoded = JWT::decode($payload, $keys);
|
||||||
|
} catch (LogicException $e) {
|
||||||
|
// errors having to do with environmental setup or malformed JWT Keys
|
||||||
|
} catch (UnexpectedValueException $e) {
|
||||||
|
// errors having to do with JWT signature and claims
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Casting to array
|
||||||
|
|
||||||
|
The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays
|
||||||
|
instead, you can do the following:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// return type is stdClass
|
||||||
|
$decoded = JWT::decode($payload, $keys);
|
||||||
|
|
||||||
|
// cast to array
|
||||||
|
$decoded = json_decode(json_encode($decoded), true);
|
||||||
|
```
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
-----
|
-----
|
||||||
|
|||||||
@@ -20,10 +20,11 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0"
|
"php": "^7.1||^8.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present",
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -31,6 +32,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": ">=4.8 <=9"
|
"guzzlehttp/guzzle": "^6.5||^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^1.1",
|
||||||
|
"phpunit/phpunit": "^7.5||^9.5",
|
||||||
|
"psr/cache": "^1.0||^2.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
258
lib/firebase/php-jwt/src/CachedKeySet.php
Normal file
258
lib/firebase/php-jwt/src/CachedKeySet.php
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use LogicException;
|
||||||
|
use OutOfBoundsException;
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Psr\Http\Client\ClientInterface;
|
||||||
|
use Psr\Http\Message\RequestFactoryInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @implements ArrayAccess<string, Key>
|
||||||
|
*/
|
||||||
|
class CachedKeySet implements ArrayAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $jwksUri;
|
||||||
|
/**
|
||||||
|
* @var ClientInterface
|
||||||
|
*/
|
||||||
|
private $httpClient;
|
||||||
|
/**
|
||||||
|
* @var RequestFactoryInterface
|
||||||
|
*/
|
||||||
|
private $httpFactory;
|
||||||
|
/**
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
/**
|
||||||
|
* @var ?int
|
||||||
|
*/
|
||||||
|
private $expiresAfter;
|
||||||
|
/**
|
||||||
|
* @var ?CacheItemInterface
|
||||||
|
*/
|
||||||
|
private $cacheItem;
|
||||||
|
/**
|
||||||
|
* @var array<string, array<mixed>>
|
||||||
|
*/
|
||||||
|
private $keySet;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cacheKey;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cacheKeyPrefix = 'jwks';
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $maxKeyLength = 64;
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $rateLimit;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $rateLimitCacheKey;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $maxCallsPerMinute = 10;
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $defaultAlg;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $jwksUri,
|
||||||
|
ClientInterface $httpClient,
|
||||||
|
RequestFactoryInterface $httpFactory,
|
||||||
|
CacheItemPoolInterface $cache,
|
||||||
|
int $expiresAfter = null,
|
||||||
|
bool $rateLimit = false,
|
||||||
|
string $defaultAlg = null
|
||||||
|
) {
|
||||||
|
$this->jwksUri = $jwksUri;
|
||||||
|
$this->httpClient = $httpClient;
|
||||||
|
$this->httpFactory = $httpFactory;
|
||||||
|
$this->cache = $cache;
|
||||||
|
$this->expiresAfter = $expiresAfter;
|
||||||
|
$this->rateLimit = $rateLimit;
|
||||||
|
$this->defaultAlg = $defaultAlg;
|
||||||
|
$this->setCacheKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyId
|
||||||
|
* @return Key
|
||||||
|
*/
|
||||||
|
public function offsetGet($keyId): Key
|
||||||
|
{
|
||||||
|
if (!$this->keyIdExists($keyId)) {
|
||||||
|
throw new OutOfBoundsException('Key ID not found');
|
||||||
|
}
|
||||||
|
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists($keyId): bool
|
||||||
|
{
|
||||||
|
return $this->keyIdExists($keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $offset
|
||||||
|
* @param Key $value
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value): void
|
||||||
|
{
|
||||||
|
throw new LogicException('Method not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset): void
|
||||||
|
{
|
||||||
|
throw new LogicException('Method not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<mixed>
|
||||||
|
*/
|
||||||
|
private function formatJwksForCache(string $jwks): array
|
||||||
|
{
|
||||||
|
$jwks = json_decode($jwks, true);
|
||||||
|
|
||||||
|
if (!isset($jwks['keys'])) {
|
||||||
|
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwks['keys'])) {
|
||||||
|
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = [];
|
||||||
|
foreach ($jwks['keys'] as $k => $v) {
|
||||||
|
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||||
|
$keys[(string) $kid] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function keyIdExists(string $keyId): bool
|
||||||
|
{
|
||||||
|
if (null === $this->keySet) {
|
||||||
|
$item = $this->getCacheItem();
|
||||||
|
// Try to load keys from cache
|
||||||
|
if ($item->isHit()) {
|
||||||
|
// item found! retrieve it
|
||||||
|
$this->keySet = $item->get();
|
||||||
|
// If the cached item is a string, the JWKS response was cached (previous behavior).
|
||||||
|
// Parse this into expected format array<kid, jwk> instead.
|
||||||
|
if (\is_string($this->keySet)) {
|
||||||
|
$this->keySet = $this->formatJwksForCache($this->keySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->keySet[$keyId])) {
|
||||||
|
if ($this->rateLimitExceeded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
|
||||||
|
$jwksResponse = $this->httpClient->sendRequest($request);
|
||||||
|
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
|
||||||
|
|
||||||
|
if (!isset($this->keySet[$keyId])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->getCacheItem();
|
||||||
|
$item->set($this->keySet);
|
||||||
|
if ($this->expiresAfter) {
|
||||||
|
$item->expiresAfter($this->expiresAfter);
|
||||||
|
}
|
||||||
|
$this->cache->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rateLimitExceeded(): bool
|
||||||
|
{
|
||||||
|
if (!$this->rateLimit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
|
||||||
|
if (!$cacheItem->isHit()) {
|
||||||
|
$cacheItem->expiresAfter(1); // # of calls are cached each minute
|
||||||
|
}
|
||||||
|
|
||||||
|
$callsPerMinute = (int) $cacheItem->get();
|
||||||
|
if (++$callsPerMinute > $this->maxCallsPerMinute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$cacheItem->set($callsPerMinute);
|
||||||
|
$this->cache->save($cacheItem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCacheItem(): CacheItemInterface
|
||||||
|
{
|
||||||
|
if (\is_null($this->cacheItem)) {
|
||||||
|
$this->cacheItem = $this->cache->getItem($this->cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cacheItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCacheKeys(): void
|
||||||
|
{
|
||||||
|
if (empty($this->jwksUri)) {
|
||||||
|
throw new RuntimeException('JWKS URI is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure we do not have illegal characters
|
||||||
|
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
|
||||||
|
|
||||||
|
// add prefix
|
||||||
|
$key = $this->cacheKeyPrefix . $key;
|
||||||
|
|
||||||
|
// Hash keys if they exceed $maxKeyLength of 64
|
||||||
|
if (\strlen($key) > $this->maxKeyLength) {
|
||||||
|
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cacheKey = $key;
|
||||||
|
|
||||||
|
if ($this->rateLimit) {
|
||||||
|
// add prefix
|
||||||
|
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
|
||||||
|
|
||||||
|
// Hash keys if they exceed $maxKeyLength of 64
|
||||||
|
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
|
||||||
|
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->rateLimitCacheKey = $rateLimitKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,12 +20,25 @@ use UnexpectedValueException;
|
|||||||
*/
|
*/
|
||||||
class JWK
|
class JWK
|
||||||
{
|
{
|
||||||
|
private const OID = '1.2.840.10045.2.1';
|
||||||
|
private const ASN1_OBJECT_IDENTIFIER = 0x06;
|
||||||
|
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
|
||||||
|
private const ASN1_BIT_STRING = 0x03;
|
||||||
|
private const EC_CURVES = [
|
||||||
|
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
|
||||||
|
'secp256k1' => '1.3.132.0.10', // Len: 64
|
||||||
|
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
|
||||||
|
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a set of JWK keys
|
* Parse a set of JWK keys
|
||||||
*
|
*
|
||||||
* @param array $jwks The JSON Web Key Set as an associative array
|
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
|
||||||
|
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||||
|
* JSON Web Key Set
|
||||||
*
|
*
|
||||||
* @return array An associative array that represents the set of keys
|
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException Provided JWK Set is empty
|
* @throws InvalidArgumentException Provided JWK Set is empty
|
||||||
* @throws UnexpectedValueException Provided JWK Set was invalid
|
* @throws UnexpectedValueException Provided JWK Set was invalid
|
||||||
@@ -33,21 +46,22 @@ class JWK
|
|||||||
*
|
*
|
||||||
* @uses parseKey
|
* @uses parseKey
|
||||||
*/
|
*/
|
||||||
public static function parseKeySet(array $jwks)
|
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
|
||||||
{
|
{
|
||||||
$keys = array();
|
$keys = [];
|
||||||
|
|
||||||
if (!isset($jwks['keys'])) {
|
if (!isset($jwks['keys'])) {
|
||||||
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($jwks['keys'])) {
|
if (empty($jwks['keys'])) {
|
||||||
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($jwks['keys'] as $k => $v) {
|
foreach ($jwks['keys'] as $k => $v) {
|
||||||
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||||
if ($key = self::parseKey($v)) {
|
if ($key = self::parseKey($v, $defaultAlg)) {
|
||||||
$keys[$kid] = $key;
|
$keys[(string) $kid] = $key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,9 +75,11 @@ class JWK
|
|||||||
/**
|
/**
|
||||||
* Parse a JWK key
|
* Parse a JWK key
|
||||||
*
|
*
|
||||||
* @param array $jwk An individual JWK
|
* @param array<mixed> $jwk An individual JWK
|
||||||
|
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||||
|
* JSON Web Key Set
|
||||||
*
|
*
|
||||||
* @return resource|array An associative array that represents the key
|
* @return Key The key object for the JWK
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException Provided JWK is empty
|
* @throws InvalidArgumentException Provided JWK is empty
|
||||||
* @throws UnexpectedValueException Provided JWK was invalid
|
* @throws UnexpectedValueException Provided JWK was invalid
|
||||||
@@ -71,15 +87,27 @@ class JWK
|
|||||||
*
|
*
|
||||||
* @uses createPemFromModulusAndExponent
|
* @uses createPemFromModulusAndExponent
|
||||||
*/
|
*/
|
||||||
public static function parseKey(array $jwk)
|
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
|
||||||
{
|
{
|
||||||
if (empty($jwk)) {
|
if (empty($jwk)) {
|
||||||
throw new InvalidArgumentException('JWK must not be empty');
|
throw new InvalidArgumentException('JWK must not be empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($jwk['kty'])) {
|
if (!isset($jwk['kty'])) {
|
||||||
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($jwk['alg'])) {
|
||||||
|
if (\is_null($defaultAlg)) {
|
||||||
|
// The "alg" parameter is optional in a KTY, but an algorithm is required
|
||||||
|
// for parsing in this library. Use the $defaultAlg parameter when parsing the
|
||||||
|
// key set in order to prevent this error.
|
||||||
|
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
|
||||||
|
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
|
||||||
|
}
|
||||||
|
$jwk['alg'] = $defaultAlg;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($jwk['kty']) {
|
switch ($jwk['kty']) {
|
||||||
case 'RSA':
|
case 'RSA':
|
||||||
if (!empty($jwk['d'])) {
|
if (!empty($jwk['d'])) {
|
||||||
@@ -96,11 +124,72 @@ class JWK
|
|||||||
'OpenSSL error: ' . \openssl_error_string()
|
'OpenSSL error: ' . \openssl_error_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return $publicKey;
|
return new Key($publicKey, $jwk['alg']);
|
||||||
|
case 'EC':
|
||||||
|
if (isset($jwk['d'])) {
|
||||||
|
// The key is actually a private key
|
||||||
|
throw new UnexpectedValueException('Key data must be for a public key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwk['crv'])) {
|
||||||
|
throw new UnexpectedValueException('crv not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset(self::EC_CURVES[$jwk['crv']])) {
|
||||||
|
throw new DomainException('Unrecognised or unsupported EC curve');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwk['x']) || empty($jwk['y'])) {
|
||||||
|
throw new UnexpectedValueException('x and y not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
|
||||||
|
return new Key($publicKey, $jwk['alg']);
|
||||||
default:
|
default:
|
||||||
// Currently only RSA is supported
|
// Currently only RSA is supported
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the EC JWK values to pem format.
|
||||||
|
*
|
||||||
|
* @param string $crv The EC curve (only P-256 is supported)
|
||||||
|
* @param string $x The EC x-coordinate
|
||||||
|
* @param string $y The EC y-coordinate
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
|
||||||
|
{
|
||||||
|
$pem =
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_OBJECT_IDENTIFIER,
|
||||||
|
self::encodeOID(self::OID)
|
||||||
|
)
|
||||||
|
. self::encodeDER(
|
||||||
|
self::ASN1_OBJECT_IDENTIFIER,
|
||||||
|
self::encodeOID(self::EC_CURVES[$crv])
|
||||||
|
)
|
||||||
|
) .
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_BIT_STRING,
|
||||||
|
\chr(0x00) . \chr(0x04)
|
||||||
|
. JWT::urlsafeB64Decode($x)
|
||||||
|
. JWT::urlsafeB64Decode($y)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
|
||||||
|
wordwrap(base64_encode($pem), 64, "\n", true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,22 +202,22 @@ class JWK
|
|||||||
*
|
*
|
||||||
* @uses encodeLength
|
* @uses encodeLength
|
||||||
*/
|
*/
|
||||||
private static function createPemFromModulusAndExponent($n, $e)
|
private static function createPemFromModulusAndExponent(
|
||||||
{
|
string $n,
|
||||||
$modulus = JWT::urlsafeB64Decode($n);
|
string $e
|
||||||
$publicExponent = JWT::urlsafeB64Decode($e);
|
): string {
|
||||||
|
$mod = JWT::urlsafeB64Decode($n);
|
||||||
|
$exp = JWT::urlsafeB64Decode($e);
|
||||||
|
|
||||||
$components = array(
|
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
|
||||||
'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
|
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
|
||||||
'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
|
|
||||||
);
|
|
||||||
|
|
||||||
$rsaPublicKey = \pack(
|
$rsaPublicKey = \pack(
|
||||||
'Ca*a*a*',
|
'Ca*a*a*',
|
||||||
48,
|
48,
|
||||||
self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
|
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
|
||||||
$components['modulus'],
|
$modulus,
|
||||||
$components['publicExponent']
|
$publicExponent
|
||||||
);
|
);
|
||||||
|
|
||||||
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
||||||
@@ -143,11 +232,9 @@ class JWK
|
|||||||
$rsaOID . $rsaPublicKey
|
$rsaOID . $rsaPublicKey
|
||||||
);
|
);
|
||||||
|
|
||||||
$rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
|
return "-----BEGIN PUBLIC KEY-----\r\n" .
|
||||||
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
||||||
'-----END PUBLIC KEY-----';
|
'-----END PUBLIC KEY-----';
|
||||||
|
|
||||||
return $rsaPublicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +246,7 @@ class JWK
|
|||||||
* @param int $length
|
* @param int $length
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function encodeLength($length)
|
private static function encodeLength(int $length): string
|
||||||
{
|
{
|
||||||
if ($length <= 0x7F) {
|
if ($length <= 0x7F) {
|
||||||
return \chr($length);
|
return \chr($length);
|
||||||
@@ -169,4 +256,68 @@ class JWK
|
|||||||
|
|
||||||
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a value into a DER object.
|
||||||
|
* Also defined in Firebase\JWT\JWT
|
||||||
|
*
|
||||||
|
* @param int $type DER tag
|
||||||
|
* @param string $value the value to encode
|
||||||
|
* @return string the encoded object
|
||||||
|
*/
|
||||||
|
private static function encodeDER(int $type, string $value): string
|
||||||
|
{
|
||||||
|
$tag_header = 0;
|
||||||
|
if ($type === self::ASN1_SEQUENCE) {
|
||||||
|
$tag_header |= 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
$der = \chr($tag_header | $type);
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$der .= \chr(\strlen($value));
|
||||||
|
|
||||||
|
return $der . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string into a DER-encoded OID.
|
||||||
|
*
|
||||||
|
* @param string $oid the OID string
|
||||||
|
* @return string the binary DER-encoded OID
|
||||||
|
*/
|
||||||
|
private static function encodeOID(string $oid): string
|
||||||
|
{
|
||||||
|
$octets = explode('.', $oid);
|
||||||
|
|
||||||
|
// Get the first octet
|
||||||
|
$first = (int) array_shift($octets);
|
||||||
|
$second = (int) array_shift($octets);
|
||||||
|
$oid = \chr($first * 40 + $second);
|
||||||
|
|
||||||
|
// Iterate over subsequent octets
|
||||||
|
foreach ($octets as $octet) {
|
||||||
|
if ($octet == 0) {
|
||||||
|
$oid .= \chr(0x00);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$bin = '';
|
||||||
|
|
||||||
|
while ($octet) {
|
||||||
|
$bin .= \chr(0x80 | ($octet & 0x7f));
|
||||||
|
$octet >>= 7;
|
||||||
|
}
|
||||||
|
$bin[0] = $bin[0] & \chr(0x7f);
|
||||||
|
|
||||||
|
// Convert to big endian if necessary
|
||||||
|
if (pack('V', 65534) == pack('L', 65534)) {
|
||||||
|
$oid .= strrev($bin);
|
||||||
|
} else {
|
||||||
|
$oid .= $bin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
namespace Firebase\JWT;
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
|
use DateTime;
|
||||||
use DomainException;
|
use DomainException;
|
||||||
use Exception;
|
use Exception;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use OpenSSLAsymmetricKey;
|
use OpenSSLAsymmetricKey;
|
||||||
|
use OpenSSLCertificate;
|
||||||
|
use stdClass;
|
||||||
use UnexpectedValueException;
|
use UnexpectedValueException;
|
||||||
use DateTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON Web Token implementation, based on this spec:
|
* JSON Web Token implementation, based on this spec:
|
||||||
@@ -25,52 +27,58 @@ use DateTime;
|
|||||||
*/
|
*/
|
||||||
class JWT
|
class JWT
|
||||||
{
|
{
|
||||||
const ASN1_INTEGER = 0x02;
|
private const ASN1_INTEGER = 0x02;
|
||||||
const ASN1_SEQUENCE = 0x10;
|
private const ASN1_SEQUENCE = 0x10;
|
||||||
const ASN1_BIT_STRING = 0x03;
|
private const ASN1_BIT_STRING = 0x03;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When checking nbf, iat or expiration times,
|
* When checking nbf, iat or expiration times,
|
||||||
* we want to provide some extra leeway time to
|
* we want to provide some extra leeway time to
|
||||||
* account for clock skew.
|
* account for clock skew.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
*/
|
*/
|
||||||
public static $leeway = 0;
|
public static $leeway = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow the current timestamp to be specified.
|
* Allow the current timestamp to be specified.
|
||||||
* Useful for fixing a value within unit testing.
|
* Useful for fixing a value within unit testing.
|
||||||
*
|
|
||||||
* Will default to PHP time() value if null.
|
* Will default to PHP time() value if null.
|
||||||
|
*
|
||||||
|
* @var ?int
|
||||||
*/
|
*/
|
||||||
public static $timestamp = null;
|
public static $timestamp = null;
|
||||||
|
|
||||||
public static $supported_algs = array(
|
/**
|
||||||
'ES384' => array('openssl', 'SHA384'),
|
* @var array<string, string[]>
|
||||||
'ES256' => array('openssl', 'SHA256'),
|
*/
|
||||||
'HS256' => array('hash_hmac', 'SHA256'),
|
public static $supported_algs = [
|
||||||
'HS384' => array('hash_hmac', 'SHA384'),
|
'ES384' => ['openssl', 'SHA384'],
|
||||||
'HS512' => array('hash_hmac', 'SHA512'),
|
'ES256' => ['openssl', 'SHA256'],
|
||||||
'RS256' => array('openssl', 'SHA256'),
|
'ES256K' => ['openssl', 'SHA256'],
|
||||||
'RS384' => array('openssl', 'SHA384'),
|
'HS256' => ['hash_hmac', 'SHA256'],
|
||||||
'RS512' => array('openssl', 'SHA512'),
|
'HS384' => ['hash_hmac', 'SHA384'],
|
||||||
'EdDSA' => array('sodium_crypto', 'EdDSA'),
|
'HS512' => ['hash_hmac', 'SHA512'],
|
||||||
);
|
'RS256' => ['openssl', 'SHA256'],
|
||||||
|
'RS384' => ['openssl', 'SHA384'],
|
||||||
|
'RS512' => ['openssl', 'SHA512'],
|
||||||
|
'EdDSA' => ['sodium_crypto', 'EdDSA'],
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a JWT string into a PHP object.
|
* Decodes a JWT string into a PHP object.
|
||||||
*
|
*
|
||||||
* @param string $jwt The JWT
|
* @param string $jwt The JWT
|
||||||
* @param Key|array<Key>|mixed $keyOrKeyArray The Key or array of Key objects.
|
* @param Key|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
|
||||||
* If the algorithm used is asymmetric, this is the public key
|
* If the algorithm used is asymmetric, this is the public key
|
||||||
* Each Key object contains an algorithm and matching key.
|
* Each Key object contains an algorithm and matching key.
|
||||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
||||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
* @param array $allowed_algs [DEPRECATED] List of supported verification algorithms. Only
|
|
||||||
* should be used for backwards compatibility.
|
|
||||||
*
|
*
|
||||||
* @return object The JWT's payload as a PHP object
|
* @return stdClass The JWT's payload as a PHP object
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException Provided JWT was empty
|
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
|
||||||
|
* @throws DomainException Provided JWT is malformed
|
||||||
* @throws UnexpectedValueException Provided JWT was invalid
|
* @throws UnexpectedValueException Provided JWT was invalid
|
||||||
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||||
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||||
@@ -80,27 +88,37 @@ class JWT
|
|||||||
* @uses jsonDecode
|
* @uses jsonDecode
|
||||||
* @uses urlsafeB64Decode
|
* @uses urlsafeB64Decode
|
||||||
*/
|
*/
|
||||||
public static function decode($jwt, $keyOrKeyArray, array $allowed_algs = array())
|
public static function decode(
|
||||||
{
|
string $jwt,
|
||||||
|
$keyOrKeyArray
|
||||||
|
): stdClass {
|
||||||
|
// Validate JWT
|
||||||
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||||
|
|
||||||
if (empty($keyOrKeyArray)) {
|
if (empty($keyOrKeyArray)) {
|
||||||
throw new InvalidArgumentException('Key may not be empty');
|
throw new InvalidArgumentException('Key may not be empty');
|
||||||
}
|
}
|
||||||
$tks = \explode('.', $jwt);
|
$tks = \explode('.', $jwt);
|
||||||
if (\count($tks) != 3) {
|
if (\count($tks) !== 3) {
|
||||||
throw new UnexpectedValueException('Wrong number of segments');
|
throw new UnexpectedValueException('Wrong number of segments');
|
||||||
}
|
}
|
||||||
list($headb64, $bodyb64, $cryptob64) = $tks;
|
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||||
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
|
$headerRaw = static::urlsafeB64Decode($headb64);
|
||||||
|
if (null === ($header = static::jsonDecode($headerRaw))) {
|
||||||
throw new UnexpectedValueException('Invalid header encoding');
|
throw new UnexpectedValueException('Invalid header encoding');
|
||||||
}
|
}
|
||||||
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
|
$payloadRaw = static::urlsafeB64Decode($bodyb64);
|
||||||
|
if (null === ($payload = static::jsonDecode($payloadRaw))) {
|
||||||
throw new UnexpectedValueException('Invalid claims encoding');
|
throw new UnexpectedValueException('Invalid claims encoding');
|
||||||
}
|
}
|
||||||
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
|
if (\is_array($payload)) {
|
||||||
throw new UnexpectedValueException('Invalid signature encoding');
|
// prevent PHP Fatal Error in edge-cases when payload is empty array
|
||||||
|
$payload = (object) $payload;
|
||||||
}
|
}
|
||||||
|
if (!$payload instanceof stdClass) {
|
||||||
|
throw new UnexpectedValueException('Payload must be a JSON object');
|
||||||
|
}
|
||||||
|
$sig = static::urlsafeB64Decode($cryptob64);
|
||||||
if (empty($header->alg)) {
|
if (empty($header->alg)) {
|
||||||
throw new UnexpectedValueException('Empty algorithm');
|
throw new UnexpectedValueException('Empty algorithm');
|
||||||
}
|
}
|
||||||
@@ -108,31 +126,18 @@ class JWT
|
|||||||
throw new UnexpectedValueException('Algorithm not supported');
|
throw new UnexpectedValueException('Algorithm not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
list($keyMaterial, $algorithm) = self::getKeyMaterialAndAlgorithm(
|
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
|
||||||
$keyOrKeyArray,
|
|
||||||
empty($header->kid) ? null : $header->kid
|
|
||||||
);
|
|
||||||
|
|
||||||
if (empty($algorithm)) {
|
// Check the algorithm
|
||||||
// Use deprecated "allowed_algs" to determine if the algorithm is supported.
|
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
|
||||||
// This opens up the possibility of an attack in some implementations.
|
// See issue #351
|
||||||
// @see https://github.com/firebase/php-jwt/issues/351
|
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
||||||
if (!\in_array($header->alg, $allowed_algs)) {
|
|
||||||
throw new UnexpectedValueException('Algorithm not allowed');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check the algorithm
|
|
||||||
if (!self::constantTimeEquals($algorithm, $header->alg)) {
|
|
||||||
// See issue #351
|
|
||||||
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ($header->alg === 'ES256' || $header->alg === 'ES384') {
|
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
|
||||||
// OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
|
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
|
||||||
$sig = self::signatureToDER($sig);
|
$sig = self::signatureToDER($sig);
|
||||||
}
|
}
|
||||||
|
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
|
||||||
if (!static::verify("$headb64.$bodyb64", $sig, $keyMaterial, $header->alg)) {
|
|
||||||
throw new SignatureInvalidException('Signature verification failed');
|
throw new SignatureInvalidException('Signature verification failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,34 +167,37 @@ class JWT
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts and signs a PHP object or array into a JWT string.
|
* Converts and signs a PHP array into a JWT string.
|
||||||
*
|
*
|
||||||
* @param object|array $payload PHP object or array
|
* @param array<mixed> $payload PHP array
|
||||||
* @param string|resource $key The secret key.
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||||
* If the algorithm used is asymmetric, this is the private key
|
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||||
* @param string $alg The signing algorithm.
|
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
* @param string $keyId
|
||||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
* @param array<string, string> $head An array with header elements to attach
|
||||||
* @param mixed $keyId
|
|
||||||
* @param array $head An array with header elements to attach
|
|
||||||
*
|
*
|
||||||
* @return string A signed JWT
|
* @return string A signed JWT
|
||||||
*
|
*
|
||||||
* @uses jsonEncode
|
* @uses jsonEncode
|
||||||
* @uses urlsafeB64Encode
|
* @uses urlsafeB64Encode
|
||||||
*/
|
*/
|
||||||
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
|
public static function encode(
|
||||||
{
|
array $payload,
|
||||||
$header = array('typ' => 'JWT', 'alg' => $alg);
|
$key,
|
||||||
|
string $alg,
|
||||||
|
string $keyId = null,
|
||||||
|
array $head = null
|
||||||
|
): string {
|
||||||
|
$header = ['typ' => 'JWT', 'alg' => $alg];
|
||||||
if ($keyId !== null) {
|
if ($keyId !== null) {
|
||||||
$header['kid'] = $keyId;
|
$header['kid'] = $keyId;
|
||||||
}
|
}
|
||||||
if (isset($head) && \is_array($head)) {
|
if (isset($head) && \is_array($head)) {
|
||||||
$header = \array_merge($head, $header);
|
$header = \array_merge($head, $header);
|
||||||
}
|
}
|
||||||
$segments = array();
|
$segments = [];
|
||||||
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
|
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
|
||||||
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
|
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
|
||||||
$signing_input = \implode('.', $segments);
|
$signing_input = \implode('.', $segments);
|
||||||
|
|
||||||
$signature = static::sign($signing_input, $key, $alg);
|
$signature = static::sign($signing_input, $key, $alg);
|
||||||
@@ -201,67 +209,84 @@ class JWT
|
|||||||
/**
|
/**
|
||||||
* Sign a string with a given key and algorithm.
|
* Sign a string with a given key and algorithm.
|
||||||
*
|
*
|
||||||
* @param string $msg The message to sign
|
* @param string $msg The message to sign
|
||||||
* @param string|resource $key The secret key
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||||
* @param string $alg The signing algorithm.
|
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||||
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
|
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
* 'HS512', 'RS256', 'RS384', and 'RS512'
|
|
||||||
*
|
*
|
||||||
* @return string An encrypted message
|
* @return string An encrypted message
|
||||||
*
|
*
|
||||||
* @throws DomainException Unsupported algorithm or bad key was specified
|
* @throws DomainException Unsupported algorithm or bad key was specified
|
||||||
*/
|
*/
|
||||||
public static function sign($msg, $key, $alg = 'HS256')
|
public static function sign(
|
||||||
{
|
string $msg,
|
||||||
|
$key,
|
||||||
|
string $alg
|
||||||
|
): string {
|
||||||
if (empty(static::$supported_algs[$alg])) {
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
throw new DomainException('Algorithm not supported');
|
throw new DomainException('Algorithm not supported');
|
||||||
}
|
}
|
||||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
switch ($function) {
|
switch ($function) {
|
||||||
case 'hash_hmac':
|
case 'hash_hmac':
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||||
|
}
|
||||||
return \hash_hmac($algorithm, $msg, $key, true);
|
return \hash_hmac($algorithm, $msg, $key, true);
|
||||||
case 'openssl':
|
case 'openssl':
|
||||||
$signature = '';
|
$signature = '';
|
||||||
$success = \openssl_sign($msg, $signature, $key, $algorithm);
|
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
|
||||||
if (!$success) {
|
if (!$success) {
|
||||||
throw new DomainException("OpenSSL unable to sign data");
|
throw new DomainException('OpenSSL unable to sign data');
|
||||||
}
|
}
|
||||||
if ($alg === 'ES256') {
|
if ($alg === 'ES256' || $alg === 'ES256K') {
|
||||||
$signature = self::signatureFromDER($signature, 256);
|
$signature = self::signatureFromDER($signature, 256);
|
||||||
} elseif ($alg === 'ES384') {
|
} elseif ($alg === 'ES384') {
|
||||||
$signature = self::signatureFromDER($signature, 384);
|
$signature = self::signatureFromDER($signature, 384);
|
||||||
}
|
}
|
||||||
return $signature;
|
return $signature;
|
||||||
case 'sodium_crypto':
|
case 'sodium_crypto':
|
||||||
if (!function_exists('sodium_crypto_sign_detached')) {
|
if (!\function_exists('sodium_crypto_sign_detached')) {
|
||||||
throw new DomainException('libsodium is not available');
|
throw new DomainException('libsodium is not available');
|
||||||
}
|
}
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// The last non-empty line is used as the key.
|
// The last non-empty line is used as the key.
|
||||||
$lines = array_filter(explode("\n", $key));
|
$lines = array_filter(explode("\n", $key));
|
||||||
$key = base64_decode(end($lines));
|
$key = base64_decode((string) end($lines));
|
||||||
|
if (\strlen($key) === 0) {
|
||||||
|
throw new DomainException('Key cannot be empty string');
|
||||||
|
}
|
||||||
return sodium_crypto_sign_detached($msg, $key);
|
return sodium_crypto_sign_detached($msg, $key);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new DomainException($e->getMessage(), 0, $e);
|
throw new DomainException($e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify a signature with the message, key and method. Not all methods
|
* Verify a signature with the message, key and method. Not all methods
|
||||||
* are symmetric, so we must have a separate verify and sign method.
|
* are symmetric, so we must have a separate verify and sign method.
|
||||||
*
|
*
|
||||||
* @param string $msg The original message (header and body)
|
* @param string $msg The original message (header and body)
|
||||||
* @param string $signature The original signature
|
* @param string $signature The original signature
|
||||||
* @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||||
* @param string $alg The algorithm
|
* @param string $alg The algorithm
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*
|
*
|
||||||
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
||||||
*/
|
*/
|
||||||
private static function verify($msg, $signature, $key, $alg)
|
private static function verify(
|
||||||
{
|
string $msg,
|
||||||
|
string $signature,
|
||||||
|
$keyMaterial,
|
||||||
|
string $alg
|
||||||
|
): bool {
|
||||||
if (empty(static::$supported_algs[$alg])) {
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
throw new DomainException('Algorithm not supported');
|
throw new DomainException('Algorithm not supported');
|
||||||
}
|
}
|
||||||
@@ -269,10 +294,11 @@ class JWT
|
|||||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
switch ($function) {
|
switch ($function) {
|
||||||
case 'openssl':
|
case 'openssl':
|
||||||
$success = \openssl_verify($msg, $signature, $key, $algorithm);
|
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
|
||||||
if ($success === 1) {
|
if ($success === 1) {
|
||||||
return true;
|
return true;
|
||||||
} elseif ($success === 0) {
|
}
|
||||||
|
if ($success === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// returns 1 on success, 0 on failure, -1 on error.
|
// returns 1 on success, 0 on failure, -1 on error.
|
||||||
@@ -280,21 +306,33 @@ class JWT
|
|||||||
'OpenSSL error: ' . \openssl_error_string()
|
'OpenSSL error: ' . \openssl_error_string()
|
||||||
);
|
);
|
||||||
case 'sodium_crypto':
|
case 'sodium_crypto':
|
||||||
if (!function_exists('sodium_crypto_sign_verify_detached')) {
|
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
|
||||||
throw new DomainException('libsodium is not available');
|
throw new DomainException('libsodium is not available');
|
||||||
}
|
}
|
||||||
try {
|
if (!\is_string($keyMaterial)) {
|
||||||
// The last non-empty line is used as the key.
|
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||||
$lines = array_filter(explode("\n", $key));
|
}
|
||||||
$key = base64_decode(end($lines));
|
try {
|
||||||
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
// The last non-empty line is used as the key.
|
||||||
} catch (Exception $e) {
|
$lines = array_filter(explode("\n", $keyMaterial));
|
||||||
throw new DomainException($e->getMessage(), 0, $e);
|
$key = base64_decode((string) end($lines));
|
||||||
}
|
if (\strlen($key) === 0) {
|
||||||
|
throw new DomainException('Key cannot be empty string');
|
||||||
|
}
|
||||||
|
if (\strlen($signature) === 0) {
|
||||||
|
throw new DomainException('Signature cannot be empty string');
|
||||||
|
}
|
||||||
|
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new DomainException($e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
case 'hash_hmac':
|
case 'hash_hmac':
|
||||||
default:
|
default:
|
||||||
$hash = \hash_hmac($algorithm, $msg, $key, true);
|
if (!\is_string($keyMaterial)) {
|
||||||
return self::constantTimeEquals($signature, $hash);
|
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||||
|
}
|
||||||
|
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
|
||||||
|
return self::constantTimeEquals($hash, $signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,30 +341,16 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @param string $input JSON string
|
* @param string $input JSON string
|
||||||
*
|
*
|
||||||
* @return object Object representation of JSON string
|
* @return mixed The decoded JSON string
|
||||||
*
|
*
|
||||||
* @throws DomainException Provided string was invalid JSON
|
* @throws DomainException Provided string was invalid JSON
|
||||||
*/
|
*/
|
||||||
public static function jsonDecode($input)
|
public static function jsonDecode(string $input)
|
||||||
{
|
{
|
||||||
if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
|
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||||
/** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
|
|
||||||
* to specify that large ints (like Steam Transaction IDs) should be treated as
|
|
||||||
* strings, rather than the PHP default behaviour of converting them to floats.
|
|
||||||
*/
|
|
||||||
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
|
||||||
} else {
|
|
||||||
/** Not all servers will support that, however, so for older versions we must
|
|
||||||
* manually detect large ints in the JSON string and quote them (thus converting
|
|
||||||
*them to strings) before decoding, hence the preg_replace() call.
|
|
||||||
*/
|
|
||||||
$max_int_length = \strlen((string) PHP_INT_MAX) - 1;
|
|
||||||
$json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
|
|
||||||
$obj = \json_decode($json_without_bigints);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($errno = \json_last_error()) {
|
if ($errno = \json_last_error()) {
|
||||||
static::handleJsonError($errno);
|
self::handleJsonError($errno);
|
||||||
} elseif ($obj === null && $input !== 'null') {
|
} elseif ($obj === null && $input !== 'null') {
|
||||||
throw new DomainException('Null result with non-null input');
|
throw new DomainException('Null result with non-null input');
|
||||||
}
|
}
|
||||||
@@ -334,22 +358,30 @@ class JWT
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode a PHP object into a JSON string.
|
* Encode a PHP array into a JSON string.
|
||||||
*
|
*
|
||||||
* @param object|array $input A PHP object or array
|
* @param array<mixed> $input A PHP array
|
||||||
*
|
*
|
||||||
* @return string JSON representation of the PHP object or array
|
* @return string JSON representation of the PHP array
|
||||||
*
|
*
|
||||||
* @throws DomainException Provided object could not be encoded to valid JSON
|
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||||
*/
|
*/
|
||||||
public static function jsonEncode($input)
|
public static function jsonEncode(array $input): string
|
||||||
{
|
{
|
||||||
$json = \json_encode($input);
|
if (PHP_VERSION_ID >= 50400) {
|
||||||
|
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
|
||||||
|
} else {
|
||||||
|
// PHP 5.3 only
|
||||||
|
$json = \json_encode($input);
|
||||||
|
}
|
||||||
if ($errno = \json_last_error()) {
|
if ($errno = \json_last_error()) {
|
||||||
static::handleJsonError($errno);
|
self::handleJsonError($errno);
|
||||||
} elseif ($json === 'null' && $input !== null) {
|
} elseif ($json === 'null' && $input !== null) {
|
||||||
throw new DomainException('Null result with non-null input');
|
throw new DomainException('Null result with non-null input');
|
||||||
}
|
}
|
||||||
|
if ($json === false) {
|
||||||
|
throw new DomainException('Provided object could not be encoded to valid JSON');
|
||||||
|
}
|
||||||
return $json;
|
return $json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +391,10 @@ class JWT
|
|||||||
* @param string $input A Base64 encoded string
|
* @param string $input A Base64 encoded string
|
||||||
*
|
*
|
||||||
* @return string A decoded string
|
* @return string A decoded string
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException invalid base64 characters
|
||||||
*/
|
*/
|
||||||
public static function urlsafeB64Decode($input)
|
public static function urlsafeB64Decode(string $input): string
|
||||||
{
|
{
|
||||||
$remainder = \strlen($input) % 4;
|
$remainder = \strlen($input) % 4;
|
||||||
if ($remainder) {
|
if ($remainder) {
|
||||||
@@ -377,7 +411,7 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @return string The base64 encode of what you passed in
|
* @return string The base64 encode of what you passed in
|
||||||
*/
|
*/
|
||||||
public static function urlsafeB64Encode($input)
|
public static function urlsafeB64Encode(string $input): string
|
||||||
{
|
{
|
||||||
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||||
}
|
}
|
||||||
@@ -386,67 +420,54 @@ class JWT
|
|||||||
/**
|
/**
|
||||||
* Determine if an algorithm has been provided for each Key
|
* Determine if an algorithm has been provided for each Key
|
||||||
*
|
*
|
||||||
* @param Key|array<Key>|mixed $keyOrKeyArray
|
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
|
||||||
* @param string|null $kid
|
* @param string|null $kid
|
||||||
*
|
*
|
||||||
* @throws UnexpectedValueException
|
* @throws UnexpectedValueException
|
||||||
*
|
*
|
||||||
* @return array containing the keyMaterial and algorithm
|
* @return Key
|
||||||
*/
|
*/
|
||||||
private static function getKeyMaterialAndAlgorithm($keyOrKeyArray, $kid = null)
|
private static function getKey(
|
||||||
{
|
$keyOrKeyArray,
|
||||||
if (
|
?string $kid
|
||||||
is_string($keyOrKeyArray)
|
): Key {
|
||||||
|| is_resource($keyOrKeyArray)
|
|
||||||
|| $keyOrKeyArray instanceof OpenSSLAsymmetricKey
|
|
||||||
) {
|
|
||||||
return array($keyOrKeyArray, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($keyOrKeyArray instanceof Key) {
|
if ($keyOrKeyArray instanceof Key) {
|
||||||
return array($keyOrKeyArray->getKeyMaterial(), $keyOrKeyArray->getAlgorithm());
|
return $keyOrKeyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
|
if (empty($kid)) {
|
||||||
if (!isset($kid)) {
|
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||||
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
|
||||||
}
|
|
||||||
if (!isset($keyOrKeyArray[$kid])) {
|
|
||||||
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
|
||||||
}
|
|
||||||
|
|
||||||
$key = $keyOrKeyArray[$kid];
|
|
||||||
|
|
||||||
if ($key instanceof Key) {
|
|
||||||
return array($key->getKeyMaterial(), $key->getAlgorithm());
|
|
||||||
}
|
|
||||||
|
|
||||||
return array($key, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnexpectedValueException(
|
if ($keyOrKeyArray instanceof CachedKeySet) {
|
||||||
'$keyOrKeyArray must be a string|resource key, an array of string|resource keys, '
|
// Skip "isset" check, as this will automatically refresh if not set
|
||||||
. 'an instance of Firebase\JWT\Key key or an array of Firebase\JWT\Key keys'
|
return $keyOrKeyArray[$kid];
|
||||||
);
|
}
|
||||||
|
|
||||||
|
if (!isset($keyOrKeyArray[$kid])) {
|
||||||
|
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $keyOrKeyArray[$kid];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $left
|
* @param string $left The string of known length to compare against
|
||||||
* @param string $right
|
* @param string $right The user-supplied string
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function constantTimeEquals($left, $right)
|
public static function constantTimeEquals(string $left, string $right): bool
|
||||||
{
|
{
|
||||||
if (\function_exists('hash_equals')) {
|
if (\function_exists('hash_equals')) {
|
||||||
return \hash_equals($left, $right);
|
return \hash_equals($left, $right);
|
||||||
}
|
}
|
||||||
$len = \min(static::safeStrlen($left), static::safeStrlen($right));
|
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
|
||||||
|
|
||||||
$status = 0;
|
$status = 0;
|
||||||
for ($i = 0; $i < $len; $i++) {
|
for ($i = 0; $i < $len; $i++) {
|
||||||
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
||||||
}
|
}
|
||||||
$status |= (static::safeStrlen($left) ^ static::safeStrlen($right));
|
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
|
||||||
|
|
||||||
return ($status === 0);
|
return ($status === 0);
|
||||||
}
|
}
|
||||||
@@ -456,17 +477,19 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @param int $errno An error number from json_last_error()
|
* @param int $errno An error number from json_last_error()
|
||||||
*
|
*
|
||||||
|
* @throws DomainException
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function handleJsonError($errno)
|
private static function handleJsonError(int $errno): void
|
||||||
{
|
{
|
||||||
$messages = array(
|
$messages = [
|
||||||
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||||
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||||
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||||
);
|
];
|
||||||
throw new DomainException(
|
throw new DomainException(
|
||||||
isset($messages[$errno])
|
isset($messages[$errno])
|
||||||
? $messages[$errno]
|
? $messages[$errno]
|
||||||
@@ -481,7 +504,7 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
private static function safeStrlen($str)
|
private static function safeStrlen(string $str): int
|
||||||
{
|
{
|
||||||
if (\function_exists('mb_strlen')) {
|
if (\function_exists('mb_strlen')) {
|
||||||
return \mb_strlen($str, '8bit');
|
return \mb_strlen($str, '8bit');
|
||||||
@@ -495,10 +518,11 @@ class JWT
|
|||||||
* @param string $sig The ECDSA signature to convert
|
* @param string $sig The ECDSA signature to convert
|
||||||
* @return string The encoded DER object
|
* @return string The encoded DER object
|
||||||
*/
|
*/
|
||||||
private static function signatureToDER($sig)
|
private static function signatureToDER(string $sig): string
|
||||||
{
|
{
|
||||||
// Separate the signature into r-value and s-value
|
// Separate the signature into r-value and s-value
|
||||||
list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
|
$length = max(1, (int) (\strlen($sig) / 2));
|
||||||
|
list($r, $s) = \str_split($sig, $length);
|
||||||
|
|
||||||
// Trim leading zeros
|
// Trim leading zeros
|
||||||
$r = \ltrim($r, "\x00");
|
$r = \ltrim($r, "\x00");
|
||||||
@@ -525,9 +549,10 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @param int $type DER tag
|
* @param int $type DER tag
|
||||||
* @param string $value the value to encode
|
* @param string $value the value to encode
|
||||||
|
*
|
||||||
* @return string the encoded object
|
* @return string the encoded object
|
||||||
*/
|
*/
|
||||||
private static function encodeDER($type, $value)
|
private static function encodeDER(int $type, string $value): string
|
||||||
{
|
{
|
||||||
$tag_header = 0;
|
$tag_header = 0;
|
||||||
if ($type === self::ASN1_SEQUENCE) {
|
if ($type === self::ASN1_SEQUENCE) {
|
||||||
@@ -548,9 +573,10 @@ class JWT
|
|||||||
*
|
*
|
||||||
* @param string $der binary signature in DER format
|
* @param string $der binary signature in DER format
|
||||||
* @param int $keySize the number of bits in the key
|
* @param int $keySize the number of bits in the key
|
||||||
|
*
|
||||||
* @return string the signature
|
* @return string the signature
|
||||||
*/
|
*/
|
||||||
private static function signatureFromDER($der, $keySize)
|
private static function signatureFromDER(string $der, int $keySize): string
|
||||||
{
|
{
|
||||||
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||||
list($offset, $_) = self::readDER($der);
|
list($offset, $_) = self::readDER($der);
|
||||||
@@ -575,9 +601,10 @@ class JWT
|
|||||||
* @param string $der the binary data in DER format
|
* @param string $der the binary data in DER format
|
||||||
* @param int $offset the offset of the data stream containing the object
|
* @param int $offset the offset of the data stream containing the object
|
||||||
* to decode
|
* to decode
|
||||||
* @return array [$offset, $data] the new offset and the decoded object
|
*
|
||||||
|
* @return array{int, string|null} the new offset and the decoded object
|
||||||
*/
|
*/
|
||||||
private static function readDER($der, $offset = 0)
|
private static function readDER(string $der, int $offset = 0): array
|
||||||
{
|
{
|
||||||
$pos = $offset;
|
$pos = $offset;
|
||||||
$size = \strlen($der);
|
$size = \strlen($der);
|
||||||
@@ -595,7 +622,7 @@ class JWT
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
if ($type == self::ASN1_BIT_STRING) {
|
if ($type === self::ASN1_BIT_STRING) {
|
||||||
$pos++; // Skip the first contents octet (padding indicator)
|
$pos++; // Skip the first contents octet (padding indicator)
|
||||||
$data = \substr($der, $pos, $len - 1);
|
$data = \substr($der, $pos, $len - 1);
|
||||||
$pos += $len - 1;
|
$pos += $len - 1;
|
||||||
@@ -606,6 +633,6 @@ class JWT
|
|||||||
$data = null;
|
$data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array($pos, $data);
|
return [$pos, $data];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,37 +4,42 @@ namespace Firebase\JWT;
|
|||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use OpenSSLAsymmetricKey;
|
use OpenSSLAsymmetricKey;
|
||||||
|
use OpenSSLCertificate;
|
||||||
|
use TypeError;
|
||||||
|
|
||||||
class Key
|
class Key
|
||||||
{
|
{
|
||||||
/** @var string $algorithm */
|
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
|
||||||
|
private $keyMaterial;
|
||||||
|
/** @var string */
|
||||||
private $algorithm;
|
private $algorithm;
|
||||||
|
|
||||||
/** @var string|resource|OpenSSLAsymmetricKey $keyMaterial */
|
|
||||||
private $keyMaterial;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|resource|OpenSSLAsymmetricKey $keyMaterial
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||||
* @param string $algorithm
|
* @param string $algorithm
|
||||||
*/
|
*/
|
||||||
public function __construct($keyMaterial, $algorithm)
|
public function __construct(
|
||||||
{
|
$keyMaterial,
|
||||||
|
string $algorithm
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
!is_string($keyMaterial)
|
!\is_string($keyMaterial)
|
||||||
&& !is_resource($keyMaterial)
|
|
||||||
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||||
|
&& !$keyMaterial instanceof OpenSSLCertificate
|
||||||
|
&& !\is_resource($keyMaterial)
|
||||||
) {
|
) {
|
||||||
throw new InvalidArgumentException('Type error: $keyMaterial must be a string, resource, or OpenSSLAsymmetricKey');
|
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($keyMaterial)) {
|
if (empty($keyMaterial)) {
|
||||||
throw new InvalidArgumentException('Type error: $keyMaterial must not be empty');
|
throw new InvalidArgumentException('Key material must not be empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_string($algorithm)|| empty($keyMaterial)) {
|
if (empty($algorithm)) {
|
||||||
throw new InvalidArgumentException('Type error: $algorithm must be a string');
|
throw new InvalidArgumentException('Algorithm must not be empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
|
||||||
$this->keyMaterial = $keyMaterial;
|
$this->keyMaterial = $keyMaterial;
|
||||||
$this->algorithm = $algorithm;
|
$this->algorithm = $algorithm;
|
||||||
}
|
}
|
||||||
@@ -44,13 +49,13 @@ class Key
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getAlgorithm()
|
public function getAlgorithm(): string
|
||||||
{
|
{
|
||||||
return $this->algorithm;
|
return $this->algorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string|resource|OpenSSLAsymmetricKey
|
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||||
*/
|
*/
|
||||||
public function getKeyMaterial()
|
public function getKeyMaterial()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user