update packages

This commit is contained in:
2026-05-28 18:49:18 +03:30
parent 97b39bda75
commit 83c6ce655b
50 changed files with 41 additions and 2289 deletions
Generated
+1 -1
View File
@@ -81,5 +81,5 @@
"php": "^8.0"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}
+2 -5
View File
@@ -14,12 +14,9 @@ if (PHP_VERSION_ID < 50600) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit1e84672d5963a78cf9ba3e25dc66cf6f::getLoader();
return ComposerAutoloaderInit1fdc3a2c72edb2354d568e34dfeb297d::getLoader();
+19 -1
View File
@@ -26,6 +26,12 @@ use Composer\Semver\VersionParser;
*/
class InstalledVersions
{
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
@@ -322,6 +328,18 @@ class InstalledVersions
self::$installedIsLocalDir = false;
}
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
@@ -336,7 +354,7 @@ class InstalledVersions
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = strtr(__DIR__, '\\', '/');
$selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
+5 -5
View File
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit1e84672d5963a78cf9ba3e25dc66cf6f
class ComposerAutoloaderInit1fdc3a2c72edb2354d568e34dfeb297d
{
private static $loader;
@@ -24,16 +24,16 @@ class ComposerAutoloaderInit1e84672d5963a78cf9ba3e25dc66cf6f
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit1e84672d5963a78cf9ba3e25dc66cf6f', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit1fdc3a2c72edb2354d568e34dfeb297d', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit1e84672d5963a78cf9ba3e25dc66cf6f', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit1fdc3a2c72edb2354d568e34dfeb297d', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+9 -9
View File
@@ -4,18 +4,18 @@
namespace Composer\Autoload;
class ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f
class ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d
{
public static $files = array (
'd0629c9e69b7d023623cf02e243b4e2a' => __DIR__ . '/../..' . '/src/helpers.php',
);
public static $prefixLengthsPsr4 = array (
'F' =>
'F' =>
array (
'Firebase\\JWT\\' => 13,
),
'D' =>
'D' =>
array (
'Dabestaniha\\AuthenticateBridge\\App\\' => 35,
'Dabestaniha\\AuthenticateBridge\\' => 31,
@@ -23,15 +23,15 @@ class ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f
);
public static $prefixDirsPsr4 = array (
'Firebase\\JWT\\' =>
'Firebase\\JWT\\' =>
array (
0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
),
'Dabestaniha\\AuthenticateBridge\\App\\' =>
'Dabestaniha\\AuthenticateBridge\\App\\' =>
array (
0 => __DIR__ . '/../..' . '/src/app',
),
'Dabestaniha\\AuthenticateBridge\\' =>
'Dabestaniha\\AuthenticateBridge\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
@@ -44,9 +44,9 @@ class ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit1e84672d5963a78cf9ba3e25dc66cf6f::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit1fdc3a2c72edb2354d568e34dfeb297d::$classMap;
}, null, ClassLoader::class);
}
+1 -1
View File
@@ -33,7 +33,7 @@
},
"time": "2026-04-01T20:38:03+00:00",
"type": "library",
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Firebase\\JWT\\": "src"
+2 -2
View File
@@ -3,7 +3,7 @@
'name' => 'dabestaniha/authenticate-bridge',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '9b7405e64a048cceba04a129ae20961d1ac90532',
'reference' => '97b39bda75a620048b6186351b47b42a74cecb19',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'dabestaniha/authenticate-bridge' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '9b7405e64a048cceba04a129ae20961d1ac90532',
'reference' => '97b39bda75a620048b6186351b47b42a74cecb19',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
+2 -3
View File
@@ -19,8 +19,7 @@ if ($issues) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
);
}
-9
View File
@@ -1,9 +0,0 @@
* text=auto
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
/.php-cs-fixer.dist.php export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
@@ -1,20 +0,0 @@
name: release-please
on:
push:
branches:
- main
permissions:
# Needed for Release Please to create and update files
contents: write
# Needed for Release Please to create Release PRs
pull-requests: write
jobs:
release-please:
environment: Release
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
token: ${{ secrets.YOSHI_CODE_BOT_TOKEN }}
release-type: simple
-57
View File
@@ -1,57 +0,0 @@
name: Test Suite
on:
push:
branches:
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [ "8.0", "8.1", "8.2", "8.3", "8.4", "8.5" ]
name: PHP ${{matrix.php }} Unit Test
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- name: Install Dependencies
uses: nick-invision/retry@v1
with:
timeout_minutes: 10
max_attempts: 3
command: composer install
- name: Run Script
run: vendor/bin/phpunit
style:
runs-on: ubuntu-latest
name: PHP Style Check
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
- name: Run Script
run: |
composer global require friendsofphp/php-cs-fixer
~/.composer/vendor/bin/php-cs-fixer fix --diff --dry-run --allow-risky=yes .
staticanalysis:
runs-on: ubuntu-latest
name: PHPStan Static Analysis
steps:
- uses: actions/checkout@v2
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Run Script
run: |
composer install
composer global require phpstan/phpstan:~1.10.0
~/.composer/vendor/bin/phpstan analyse
-7
View File
@@ -1,7 +0,0 @@
vendor
phpunit.phar
phpunit.phar.asc
composer.phar
composer.lock
.phpunit.result.cache
.php-cs-fixer.cache
-28
View File
@@ -1,28 +0,0 @@
<?php
return (new PhpCsFixer\Config())
->setRules([
'@PSR2' => true,
'concat_space' => ['spacing' => 'one'],
'no_unused_imports' => true,
'ordered_imports' => true,
'new_with_braces' => true,
'method_argument_space' => false,
'whitespace_after_comma_in_array' => true,
'return_type_declaration' => [
'space_before' => 'none'
],
'single_quote' => true,
'native_function_invocation' => [
'strict' => false
],
'nullable_type_declaration' => [
'syntax' => 'question_mark',
],
'nullable_type_declaration_for_default_null_value' => true,
])
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__)
)
;
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
-5
View File
@@ -1,5 +0,0 @@
parameters:
level: 7
paths:
- src
treatPhpDocTypesAsCertain: false
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="PHP JSON Web Token Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
Vendored Regular → Executable
View File
View File
Vendored Regular → Executable
View File
View File
-649
View File
@@ -1,649 +0,0 @@
<?php
namespace Firebase\JWT;
use LogicException;
use OutOfBoundsException;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use RuntimeException;
class CachedKeySetTest extends TestCase
{
use ProphecyTrait;
private $testJwksUri = 'https://jwk.uri';
private $testJwksUriKey = 'jwkshttpsjwk.uri';
private $testJwks1 = '{"keys": [{"kid":"foo","kty":"RSA","alg":"foo","n":"","e":""}]}';
private $testCachedJwks1 = ['foo' => ['kid' => 'foo', 'kty' => 'RSA', 'alg' => 'foo', 'n' => '', 'e' => '']];
private $testJwks2 = '{"keys": [{"kid":"bar","kty":"RSA","alg":"bar","n":"","e":""}]}';
private $testJwks3 = '{"keys": [{"kid":"baz","kty":"RSA","n":"","e":""}]}';
private $googleRsaUri = 'https://www.googleapis.com/oauth2/v3/certs';
private $googleEcUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
public function testEmptyUriThrowsException()
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('JWKS URI is empty');
$cachedKeySet = new CachedKeySet(
'',
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$this->prophesize(CacheItemPoolInterface::class)->reveal()
);
$cachedKeySet['foo'];
}
public function testOffsetSetThrowsException()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Method not implemented');
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$this->prophesize(CacheItemPoolInterface::class)->reveal()
);
$cachedKeySet['foo'] = 'bar';
}
public function testOffsetUnsetThrowsException()
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Method not implemented');
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$this->prophesize(CacheItemPoolInterface::class)->reveal()
);
unset($cachedKeySet['foo']);
}
public function testOutOfBoundsThrowsException()
{
$this->expectException(OutOfBoundsException::class);
$this->expectExceptionMessage('Key ID not found');
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks1),
$this->getMockHttpFactory(),
$this->getMockEmptyCache()
);
// keyID doesn't exist
$cachedKeySet['bar'];
}
public function testInvalidHttpResponseThrowsException()
{
$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('HTTP Error: 404 URL not found');
$this->expectExceptionCode(404);
$response = $this->prophesize('Psr\Http\Message\ResponseInterface');
$response->getStatusCode()
->shouldBeCalled()
->willReturn(404);
$response->getReasonPhrase()
->shouldBeCalledTimes(1)
->willReturn('URL not found');
$http = $this->prophesize(ClientInterface::class);
$http->sendRequest(Argument::any())
->shouldBeCalledTimes(1)
->willReturn($response->reveal());
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$http->reveal(),
$this->getMockHttpFactory(),
$this->getMockEmptyCache()
);
isset($cachedKeySet[0]);
}
public function testWithExistingKeyId()
{
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks1),
$this->getMockHttpFactory(),
$this->getMockEmptyCache()
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
}
public function testWithDefaultAlg()
{
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks3),
$this->getMockHttpFactory(),
$this->getMockEmptyCache(),
null,
false,
'baz256'
);
$this->assertInstanceOf(Key::class, $cachedKeySet['baz']);
$this->assertSame('baz256', $cachedKeySet['baz']->getAlgorithm());
}
public function testKeyIdIsCached()
{
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->willReturn(true);
$cacheItem->get()
->willReturn($this->testCachedJwks1);
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->willReturn(true);
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$cache->reveal()
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
}
public function testCachedKeyIdRefresh()
{
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->shouldBeCalledOnce()
->willReturn(true);
$cacheItem->get()
->shouldBeCalledOnce()
->willReturn($this->testCachedJwks1);
$cacheItem->set(Argument::any())
->shouldBeCalledOnce()
->will(function () {
return $this;
});
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->shouldBeCalledOnce()
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->shouldBeCalledOnce()
->willReturn(true);
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks2), // updated JWK
$this->getMockHttpFactory(),
$cache->reveal()
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
$this->assertInstanceOf(Key::class, $cachedKeySet['bar']);
$this->assertSame('bar', $cachedKeySet['bar']->getAlgorithm());
}
public function testKeyIdIsCachedFromPreviousFormat()
{
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->willReturn(true);
$cacheItem->get()
->willReturn($this->testJwks1);
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->willReturn(true);
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$cache->reveal()
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
}
public function testCachedKeyIdRefreshFromPreviousFormat()
{
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->shouldBeCalledOnce()
->willReturn(true);
$cacheItem->get()
->shouldBeCalledOnce()
->willReturn($this->testJwks1);
$cacheItem->set(Argument::any())
->shouldBeCalledOnce()
->will(function () {
return $this;
});
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->shouldBeCalledOnce()
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->shouldBeCalledOnce()
->willReturn(true);
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks2), // updated JWK
$this->getMockHttpFactory(),
$cache->reveal()
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
$this->assertInstanceOf(Key::class, $cachedKeySet['bar']);
$this->assertSame('bar', $cachedKeySet['bar']->getAlgorithm());
}
public function testCacheItemWithExpiresAfter()
{
$expiresAfter = 10;
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->shouldBeCalledOnce()
->willReturn(false);
$cacheItem->set(Argument::any())
->shouldBeCalledOnce()
->will(function () {
return $this;
});
$cacheItem->expiresAfter($expiresAfter)
->shouldBeCalledOnce()
->will(function () {
return $this;
});
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->shouldBeCalledOnce()
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->shouldBeCalledOnce();
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks1),
$this->getMockHttpFactory(),
$cache->reveal(),
$expiresAfter
);
$this->assertInstanceOf(Key::class, $cachedKeySet['foo']);
$this->assertSame('foo', $cachedKeySet['foo']->getAlgorithm());
}
public function testJwtVerify()
{
$privKey1 = file_get_contents(__DIR__ . '/data/rsa1-private.pem');
$payload = ['sub' => 'foo', 'exp' => strtotime('+10 seconds')];
$msg = JWT::encode($payload, $privKey1, 'RS256', 'jwk1');
// format the cached value to match the expected format
$cachedJwks = [];
$rsaKeySet = file_get_contents(__DIR__ . '/data/rsa-jwkset.json');
foreach (json_decode($rsaKeySet, true)['keys'] as $k => $v) {
$cachedJwks[$v['kid']] = $v;
}
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->willReturn(true);
$cacheItem->get()
->willReturn($cachedJwks);
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->willReturn($cacheItem->reveal());
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->prophesize(ClientInterface::class)->reveal(),
$this->prophesize(RequestFactoryInterface::class)->reveal(),
$cache->reveal()
);
$result = JWT::decode($msg, $cachedKeySet);
$this->assertSame('foo', $result->sub);
}
public function testRateLimit()
{
// We request the key 11 times, HTTP should only be called 10 times
$shouldBeCalledTimes = 10;
// Instantiate the cached key set
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks1, $shouldBeCalledTimes),
$this->getMockHttpFactory($shouldBeCalledTimes),
new TestMemoryCacheItemPool(),
10, // expires after seconds
true // enable rate limiting
);
$invalidKid = 'invalidkey';
for ($i = 0; $i < 10; $i++) {
$this->assertFalse(isset($cachedKeySet[$invalidKid]));
}
// The 11th time does not call HTTP
$this->assertFalse(isset($cachedKeySet[$invalidKid]));
}
public function testRateLimitWithExpiresAfter()
{
// We request the key 17 times, HTTP should only be called 15 times
$shouldBeCalledTimes = 10;
$cachedTimes = 2;
$afterExpirationTimes = 5;
$totalHttpTimes = $shouldBeCalledTimes + $afterExpirationTimes;
$cachePool = new TestMemoryCacheItemPool();
// Instantiate the cached key set
$cachedKeySet = new CachedKeySet(
$this->testJwksUri,
$this->getMockHttpClient($this->testJwks1, $totalHttpTimes),
$this->getMockHttpFactory($totalHttpTimes),
$cachePool,
10, // expires after seconds
true // enable rate limiting
);
// Set the rate limit cache to expire after 1 second
$cacheItem = $cachePool->getItem('jwksratelimitjwkshttpsjwk.uri');
$cacheItem->set([
'expiry' => new \DateTime('+1 second', new \DateTimeZone('UTC')),
'callsPerMinute' => 0,
]);
$cacheItem->expiresAfter(1);
$cachePool->save($cacheItem);
$invalidKid = 'invalidkey';
for ($i = 0; $i < $shouldBeCalledTimes; $i++) {
$this->assertFalse(isset($cachedKeySet[$invalidKid]));
}
// The next calls do not call HTTP
for ($i = 0; $i < $cachedTimes; $i++) {
$this->assertFalse(isset($cachedKeySet[$invalidKid]));
}
sleep(1); // wait for cache to expire
// These calls DO call HTTP because the cache has expired
for ($i = 0; $i < $afterExpirationTimes; $i++) {
$this->assertFalse(isset($cachedKeySet[$invalidKid]));
}
}
/**
* @dataProvider provideFullIntegration
*/
public function testFullIntegration(string $jwkUri): void
{
if (!class_exists(\GuzzleHttp\Psr7\HttpFactory::class)) {
self::markTestSkipped('Guzzle 7 only');
}
// Create cache and http objects
$cache = new TestMemoryCacheItemPool();
$http = new \GuzzleHttp\Client();
$factory = new \GuzzleHttp\Psr7\HttpFactory();
// Determine "kid" dynamically, because these constantly change
$response = $http->get($jwkUri);
$json = (string) $response->getBody();
$keys = json_decode($json, true);
$kid = $keys['keys'][0]['kid'] ?? null;
$this->assertNotNull($kid);
// Instantiate the cached key set
$cachedKeySet = new CachedKeySet(
$jwkUri,
$http,
$factory,
$cache
);
$this->assertArrayHasKey($kid, $cachedKeySet);
$key = $cachedKeySet[$kid];
$this->assertInstanceOf(Key::class, $key);
$this->assertSame($keys['keys'][0]['alg'], $key->getAlgorithm());
}
public function provideFullIntegration()
{
return [
[$this->googleRsaUri],
[$this->googleEcUri, 'LYyP2g']
];
}
private function getMockHttpClient($testJwks, int $timesCalled = 1)
{
$body = $this->prophesize('Psr\Http\Message\StreamInterface');
$body->__toString()
->shouldBeCalledTimes($timesCalled)
->willReturn($testJwks);
$response = $this->prophesize('Psr\Http\Message\ResponseInterface');
$response->getBody()
->shouldBeCalledTimes($timesCalled)
->willReturn($body->reveal());
$response->getStatusCode()
->shouldBeCalledTimes($timesCalled)
->willReturn(200);
$http = $this->prophesize(ClientInterface::class);
$http->sendRequest(Argument::any())
->shouldBeCalledTimes($timesCalled)
->willReturn($response->reveal());
return $http->reveal();
}
private function getMockHttpFactory(int $timesCalled = 1)
{
$request = $this->prophesize('Psr\Http\Message\RequestInterface');
$factory = $this->prophesize(RequestFactoryInterface::class);
$factory->createRequest('GET', $this->testJwksUri)
->shouldBeCalledTimes($timesCalled)
->willReturn($request->reveal());
return $factory->reveal();
}
private function getMockEmptyCache()
{
$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->isHit()
->shouldBeCalledOnce()
->willReturn(false);
$cacheItem->set(Argument::any())
->will(function () {
return $this;
});
$cache = $this->prophesize(CacheItemPoolInterface::class);
$cache->getItem($this->testJwksUriKey)
->shouldBeCalledOnce()
->willReturn($cacheItem->reveal());
$cache->save(Argument::any())
->willReturn(true);
return $cache->reveal();
}
}
/**
* A cache item pool
*/
final class TestMemoryCacheItemPool implements CacheItemPoolInterface
{
private $items;
private $deferredItems;
public function getItem($key): CacheItemInterface
{
$item = current($this->getItems([$key]));
$item->expiresAt(null); // mimic symfony cache behavior
return $item;
}
public function getItems(array $keys = []): iterable
{
$items = [];
foreach ($keys as $key) {
$items[$key] = $this->hasItem($key) ? clone $this->items[$key] : new TestMemoryCacheItem($key);
}
return $items;
}
public function hasItem($key): bool
{
return isset($this->items[$key]) && $this->items[$key]->isHit();
}
public function clear(): bool
{
$this->items = [];
$this->deferredItems = [];
return true;
}
public function deleteItem($key): bool
{
return $this->deleteItems([$key]);
}
public function deleteItems(array $keys): bool
{
foreach ($keys as $key) {
unset($this->items[$key]);
}
return true;
}
public function save(CacheItemInterface $item): bool
{
$this->items[$item->getKey()] = $item;
return true;
}
public function saveDeferred(CacheItemInterface $item): bool
{
$this->deferredItems[$item->getKey()] = $item;
return true;
}
public function commit(): bool
{
foreach ($this->deferredItems as $item) {
$this->save($item);
}
$this->deferredItems = [];
return true;
}
}
/**
* A cache item.
*/
final class TestMemoryCacheItem implements CacheItemInterface
{
private $key;
private $value;
private $expiration;
private $isHit = false;
public function __construct(string $key)
{
$this->key = $key;
}
public function getKey(): string
{
return $this->key;
}
public function get(): mixed
{
return $this->isHit() ? $this->value : null;
}
public function isHit(): bool
{
if (!$this->isHit) {
return false;
}
if ($this->expiration === null) {
return true;
}
return $this->currentTime()->getTimestamp() < $this->expiration->getTimestamp();
}
public function set(mixed $value): static
{
$this->isHit = true;
$this->value = $value;
return $this;
}
public function expiresAt($expiration): static
{
$this->expiration = $expiration;
return $this;
}
public function expiresAfter($time): static
{
$this->expiration = $this->currentTime()->add(new \DateInterval("PT{$time}S"));
return $this;
}
protected function currentTime()
{
return new \DateTime('now', new \DateTimeZone('UTC'));
}
}
-232
View File
@@ -1,232 +0,0 @@
<?php
namespace Firebase\JWT;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use UnexpectedValueException;
class JWKTest extends TestCase
{
private static $keys;
private static $privKey1;
private static $privKey2;
public function testMissingKty()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('JWK must contain a "kty" parameter');
$badJwk = ['kid' => 'foo'];
$keys = JWK::parseKeySet(['keys' => [$badJwk]]);
}
public function testInvalidAlgorithm()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('No supported algorithms found in JWK Set');
$badJwk = ['kty' => 'BADTYPE', 'alg' => 'RSA256'];
$keys = JWK::parseKeySet(['keys' => [$badJwk]]);
}
public function testParsePrivateKey()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('RSA private keys are not supported');
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/rsa-jwkset.json'),
true
);
$jwkSet['keys'][0]['d'] = 'privatekeyvalue';
JWK::parseKeySet($jwkSet);
}
public function testParsePrivateKeyWithoutAlg()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('JWK must contain an "alg" parameter');
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/rsa-jwkset.json'),
true
);
unset($jwkSet['keys'][0]['alg']);
JWK::parseKeySet($jwkSet);
}
public function testParsePrivateKeyWithoutAlgWithDefaultAlgParameter()
{
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/rsa-jwkset.json'),
true
);
unset($jwkSet['keys'][0]['alg']);
$jwks = JWK::parseKeySet($jwkSet, 'foo');
$this->assertSame('foo', $jwks['jwk1']->getAlgorithm());
}
public function testParseKeyWithEmptyDValue()
{
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/rsa-jwkset.json'),
true
);
// empty or null values are ok
$jwkSet['keys'][0]['d'] = null;
$keys = JWK::parseKeySet($jwkSet);
$this->assertTrue(\is_array($keys));
}
public function testParseJwkKeySet()
{
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/rsa-jwkset.json'),
true
);
$keys = JWK::parseKeySet($jwkSet);
$this->assertTrue(\is_array($keys));
$this->assertArrayHasKey('jwk1', $keys);
self::$keys = $keys;
}
public function testParseJwkKey_empty()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('JWK must not be empty');
JWK::parseKeySet(['keys' => [[]]]);
}
public function testParseJwkKeySet_empty()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('JWK Set did not contain any keys');
JWK::parseKeySet(['keys' => []]);
}
/**
* @depends testParseJwkKeySet
*/
public function testDecodeByJwkKeySetTokenExpired()
{
$privKey1 = file_get_contents(__DIR__ . '/data/rsa1-private.pem');
$payload = ['exp' => strtotime('-1 hour')];
$msg = JWT::encode($payload, $privKey1, 'RS256', 'jwk1');
$this->expectException(ExpiredException::class);
JWT::decode($msg, self::$keys);
}
/**
* @dataProvider provideDecodeByJwkKeySet
*/
public function testDecodeByJwkKeySet($pemFile, $jwkFile, $alg, $keyId)
{
$privKey1 = file_get_contents(__DIR__ . '/data/' . $pemFile);
$payload = ['sub' => 'foo', 'exp' => strtotime('+10 seconds')];
$msg = JWT::encode($payload, $privKey1, $alg, $keyId);
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/' . $jwkFile),
true
);
$keys = JWK::parseKeySet($jwkSet);
$result = JWT::decode($msg, $keys);
$this->assertSame('foo', $result->sub);
}
public function provideDecodeByJwkKeySet()
{
return [
['rsa1-private.pem', 'rsa-jwkset.json', 'RS256', 'jwk1'],
['ecdsa256-private.pem', 'ec-jwkset.json', 'ES256', 'jwk1'],
['ecdsa384-private.pem', 'ec-jwkset.json', 'ES384', 'jwk4'],
['ed25519-1.sec', 'ed25519-jwkset.json', 'EdDSA', 'jwk1'],
];
}
/**
* @depends testParseJwkKeySet
*/
public function testDecodeByMultiJwkKeySet()
{
$privKey2 = file_get_contents(__DIR__ . '/data/rsa2-private.pem');
$payload = ['sub' => 'bar', 'exp' => strtotime('+10 seconds')];
$msg = JWT::encode($payload, $privKey2, 'RS256', 'jwk2');
$result = JWT::decode($msg, self::$keys);
$this->assertSame('bar', $result->sub);
}
public function testDecodeByOctetJwkKeySet()
{
$jwkSet = json_decode(
file_get_contents(__DIR__ . '/data/octet-jwkset.json'),
true
);
$keys = JWK::parseKeySet($jwkSet);
$payload = ['sub' => 'foo', 'exp' => strtotime('+10 seconds')];
foreach ($keys as $keyId => $key) {
$msg = JWT::encode($payload, $key->getKeyMaterial(), $key->getAlgorithm(), $keyId);
$result = JWT::decode($msg, $keys);
$this->assertSame('foo', $result->sub);
}
}
public function testOctetJwkMissingK()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('k not set');
$badJwk = ['kty' => 'oct', 'alg' => 'HS256'];
$keys = JWK::parseKeySet(['keys' => [$badJwk]]);
}
public function testParseKey()
{
// Use a known module and exponent, and ensure it parses as expected
$jwk = [
'alg' => 'RS256',
'kty' => 'RSA',
'n' => 'hsYvCPtkUV7SIxwkOkJsJfhwV_CMdXU5i0UmY2QEs-Pa7v0-0y-s4EjEDtsQ8Yow6hc670JhkGBcMzhU4DtrqNGROXebyOse5FX0m0UvWo1qXqNTf28uBKB990mY42Icr8sGjtOw8ajyT9kufbmXi3eZKagKpG0TDGK90oBEfoGzCxoFT87F95liNth_GoyU5S8-G3OqIqLlQCwxkI5s-g2qvg_aooALfh1rhvx2wt4EJVMSrdnxtPQSPAtZBiw5SwCnVglc6OnalVNvAB2JArbqC9GAzzz9pApAk28SYg5a4hPiPyqwRv-4X1CXEK8bO5VesIeRX0oDf7UoM-pVAw',
'use' => 'sig',
'e' => 'AQAB',
'kid' => '838c06c62046c2d948affe137dd5310129f4d5d1'
];
$key = JWK::parseKey($jwk);
$this->assertNotNull($key);
$openSslKey = $key->getKeyMaterial();
$pubKey = openssl_pkey_get_public($openSslKey);
$keyData = openssl_pkey_get_details($pubKey);
$expectedPublicKey = <<<EOF
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhsYvCPtkUV7SIxwkOkJs
JfhwV/CMdXU5i0UmY2QEs+Pa7v0+0y+s4EjEDtsQ8Yow6hc670JhkGBcMzhU4Dtr
qNGROXebyOse5FX0m0UvWo1qXqNTf28uBKB990mY42Icr8sGjtOw8ajyT9kufbmX
i3eZKagKpG0TDGK90oBEfoGzCxoFT87F95liNth/GoyU5S8+G3OqIqLlQCwxkI5s
+g2qvg/aooALfh1rhvx2wt4EJVMSrdnxtPQSPAtZBiw5SwCnVglc6OnalVNvAB2J
ArbqC9GAzzz9pApAk28SYg5a4hPiPyqwRv+4X1CXEK8bO5VesIeRX0oDf7UoM+pV
AwIDAQAB
-----END PUBLIC KEY-----
EOF;
$this->assertEquals($expectedPublicKey, $keyData['key']);
}
}
-778
View File
@@ -1,778 +0,0 @@
<?php
namespace Firebase\JWT;
use DomainException;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use stdClass;
use TypeError;
use UnexpectedValueException;
class JWTTest extends TestCase
{
private Key $hmacKey;
public function setUp(): void
{
$this->hmacKey = $this->generateHmac256();
}
public function testUrlSafeCharacters()
{
$encoded = JWT::encode(['message' => 'f?'], $this->hmacKey->getKeyMaterial(), 'HS256');
$expected = new stdClass();
$expected->message = 'f?';
$this->assertEquals($expected, JWT::decode($encoded, $this->hmacKey));
}
public function testMalformedUtf8StringsFail()
{
$this->expectException(DomainException::class);
JWT::encode(['message' => pack('c', 128)], $this->hmacKey->getKeyMaterial(), 'HS256');
}
public function testInvalidKeyOpensslSignFail()
{
$this->expectException(DomainException::class);
JWT::sign('message', 'invalid key', 'openssl');
}
public function testMalformedJsonThrowsException()
{
$this->expectException(DomainException::class);
JWT::jsonDecode('this is not valid JSON string');
}
public function testExpiredToken()
{
$this->expectException(ExpiredException::class);
$payload = [
'message' => 'abc',
'exp' => time() - 20, // time in the past
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey);
}
public function testBeforeValidTokenWithNbf()
{
$this->expectException(BeforeValidException::class);
$payload = [
'message' => 'abc',
'nbf' => time() + 20, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey);
}
public function testBeforeValidTokenWithIat()
{
$this->expectException(BeforeValidException::class);
$payload = [
'message' => 'abc',
'iat' => time() + 20, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey);
}
public function testValidToken()
{
$payload = [
'message' => 'abc',
'exp' => time() + JWT::$leeway + 20, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
/**
* @runInSeparateProcess
*/
public function testValidTokenWithLeeway()
{
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'exp' => time() - 20, // time in the past
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
/**
* @runInSeparateProcess
*/
public function testExpiredTokenWithLeeway()
{
$this->expectException(ExpiredException::class);
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'exp' => time() - 70, // time far in the past
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
public function testExpiredExceptionPayload()
{
$this->expectException(ExpiredException::class);
$payload = [
'message' => 'abc',
'exp' => time() - 100, // time in the past
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
try {
JWT::decode($encoded, $this->hmacKey);
} catch (ExpiredException $e) {
$exceptionPayload = (array) $e->getPayload();
$this->assertEquals($exceptionPayload, $payload);
throw $e;
}
}
/**
* @runInSeparateProcess
*/
public function testExpiredExceptionTimestamp()
{
$this->expectException(ExpiredException::class);
JWT::$timestamp = 98765;
$payload = [
'message' => 'abc',
'exp' => 1234,
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
try {
JWT::decode($encoded, $this->hmacKey);
} catch (ExpiredException $e) {
$exTimestamp = $e->getTimestamp();
$this->assertSame(98765, $exTimestamp);
throw $e;
}
}
public function testBeforeValidExceptionPayload()
{
$this->expectException(BeforeValidException::class);
$payload = [
'message' => 'abc',
'iat' => time() + 100, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
try {
JWT::decode($encoded, $this->hmacKey);
} catch (BeforeValidException $e) {
$exceptionPayload = (array) $e->getPayload();
$this->assertEquals($exceptionPayload, $payload);
throw $e;
}
}
public function testValidTokenWithNbf()
{
$payload = [
'message' => 'abc',
'iat' => time(),
'exp' => time() + 20, // time in the future
'nbf' => time() - 20
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
/**
* @runInSeparateProcess
*/
public function testValidTokenWithNbfLeeway()
{
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'nbf' => time() + 20, // not before in near (leeway) future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
/**
* @runInSeparateProcess
*/
public function testInvalidTokenWithNbfLeeway()
{
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'nbf' => time() + 65, // not before too far in future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(BeforeValidException::class);
$this->expectExceptionMessage('Cannot handle token with nbf prior to');
JWT::decode($encoded, $this->hmacKey);
}
public function testValidTokenWithNbfIgnoresIat()
{
$payload = [
'message' => 'abc',
'nbf' => time() - 20, // time in the future
'iat' => time() + 20, // time in the past
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertEquals('abc', $decoded->message);
}
public function testValidTokenWithNbfMicrotime()
{
$payload = [
'message' => 'abc',
'nbf' => microtime(true), // use microtime
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertEquals('abc', $decoded->message);
}
public function testInvalidTokenWithNbfMicrotime()
{
$this->expectException(BeforeValidException::class);
$this->expectExceptionMessage('Cannot handle token with nbf prior to');
$payload = [
'message' => 'abc',
'nbf' => microtime(true) + 20, // use microtime in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey);
}
/**
* @runInSeparateProcess
*/
public function testValidTokenWithIatLeeway()
{
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'iat' => time() + 20, // issued in near (leeway) future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertSame($decoded->message, 'abc');
}
/**
* @runInSeparateProcess
*/
public function testInvalidTokenWithIatLeeway()
{
JWT::$leeway = 60;
$payload = [
'message' => 'abc',
'iat' => time() + 65, // issued too far in future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(BeforeValidException::class);
$this->expectExceptionMessage('Cannot handle token with iat prior to');
JWT::decode($encoded, $this->hmacKey);
}
public function testValidTokenWithIatMicrotime()
{
$payload = [
'message' => 'abc',
'iat' => microtime(true), // use microtime
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$decoded = JWT::decode($encoded, $this->hmacKey);
$this->assertEquals('abc', $decoded->message);
}
public function testInvalidTokenWithIatMicrotime()
{
$this->expectException(BeforeValidException::class);
$this->expectExceptionMessage('Cannot handle token with iat prior to');
$payload = [
'message' => 'abc',
'iat' => microtime(true) + 20, // use microtime in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey);
}
public function testInvalidToken()
{
$encodeKey = $this->generateHmac256();
$decodeKey = $this->generateHmac256();
$payload = [
'message' => 'abc',
'exp' => time() + 20, // time in the future
];
$encoded = JWT::encode($payload, $encodeKey->getKeyMaterial(), $encodeKey->getAlgorithm());
$this->expectException(SignatureInvalidException::class);
JWT::decode($encoded, $decodeKey);
}
public function testNullKeyFails()
{
$payload = [
'message' => 'abc',
'exp' => time() + JWT::$leeway + 20, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(TypeError::class);
JWT::decode($encoded, new Key(null, 'HS256'));
}
public function testEmptyKeyFails()
{
$payload = [
'message' => 'abc',
'exp' => time() + JWT::$leeway + 20, // time in the future
];
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(InvalidArgumentException::class);
JWT::decode($encoded, new Key('', 'HS256'));
}
public function testKIDChooser()
{
$keys = [
'0' => $this->generateHmac256(),
'1' => $this->generateHmac256(),
'2' => $this->generateHmac256()
];
$msg = JWT::encode(['message' => 'abc'], $keys['0']->getKeyMaterial(), 'HS256', '0');
$decoded = JWT::decode($msg, $keys);
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals($decoded, $expected);
}
public function testArrayAccessKIDChooser()
{
$keys = [
'0' => $this->generateHmac256(),
'1' => $this->generateHmac256(),
'2' => $this->generateHmac256()
];
$msg = JWT::encode(['message' => 'abc'], $keys['0']->getKeyMaterial(), 'HS256', '0');
$decoded = JWT::decode($msg, $keys);
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals($decoded, $expected);
}
public function testNoneAlgorithm()
{
$msg = JWT::encode(['message' => 'abc'], $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(UnexpectedValueException::class);
JWT::decode($msg, new Key($this->hmacKey->getKeyMaterial(), 'none'));
}
public function testIncorrectAlgorithm()
{
$msg = JWT::encode(['message' => 'abc'], $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(UnexpectedValueException::class);
// TODO: Generate proper RS256 key
JWT::decode($msg, new Key($this->hmacKey->getKeyMaterial(), 'RS256'));
}
public function testEmptyAlgorithm()
{
$msg = JWT::encode(['message' => 'abc'], $this->hmacKey->getKeyMaterial(), 'HS256');
$this->expectException(InvalidArgumentException::class);
JWT::decode($msg, new Key($this->hmacKey->getKeyMaterial(), ''));
}
public function testAdditionalHeaders()
{
$msg = JWT::encode(['message' => 'abc'], $this->hmacKey->getKeyMaterial(), 'HS256', null, ['cty' => 'test-eit;v=1']);
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals(JWT::decode($msg, $this->hmacKey), $expected);
}
public function testInvalidSegmentCount()
{
$this->expectException(UnexpectedValueException::class);
JWT::decode('brokenheader.brokenbody', $this->hmacKey);
}
public function testInvalidSignatureEncoding()
{
$msg = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6ImZvbyJ9.Q4Kee9E8o0Xfo4ADXvYA8t7dN_X_bU9K5w6tXuiSjlUxx';
$this->expectException(UnexpectedValueException::class);
JWT::decode($msg, $this->hmacKey);
}
public function testHSEncodeDecode()
{
$msg = JWT::encode(['message' => 'abc'], $this->hmacKey->getKeyMaterial(), 'HS256');
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals(JWT::decode($msg, $this->hmacKey), $expected);
}
public function testRSEncodeDecode()
{
$privKey = openssl_pkey_new([
'digest_alg' => 'sha256',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
$msg = JWT::encode(['message' => 'abc'], $privKey, 'RS256');
$pubKey = openssl_pkey_get_details($privKey);
$pubKey = $pubKey['key'];
$decoded = JWT::decode($msg, new Key($pubKey, 'RS256'));
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals($decoded, $expected);
}
public function testEdDsaEncodeDecode()
{
$keyPair = sodium_crypto_sign_keypair();
$privKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
$payload = ['foo' => 'bar'];
$msg = JWT::encode($payload, $privKey, 'EdDSA');
$pubKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
$decoded = JWT::decode($msg, new Key($pubKey, 'EdDSA'));
$this->assertSame('bar', $decoded->foo);
}
public function testInvalidEdDsaEncodeDecode()
{
$keyPair = sodium_crypto_sign_keypair();
$privKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
$payload = ['foo' => 'bar'];
$msg = JWT::encode($payload, $privKey, 'EdDSA');
// Generate a different key.
$keyPair = sodium_crypto_sign_keypair();
$pubKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
$this->expectException(SignatureInvalidException::class);
JWT::decode($msg, new Key($pubKey, 'EdDSA'));
}
public function testRSEncodeDecodeWithPassphrase()
{
$privateKey = openssl_pkey_get_private(
file_get_contents(__DIR__ . '/data/rsa-with-passphrase.pem'),
'passphrase'
);
$jwt = JWT::encode(['message' => 'abc'], $privateKey, 'RS256');
$keyDetails = openssl_pkey_get_details($privateKey);
$pubKey = $keyDetails['key'];
$decoded = JWT::decode($jwt, new Key($pubKey, 'RS256'));
$expected = new stdClass();
$expected->message = 'abc';
$this->assertEquals($decoded, $expected);
}
public function testDecodesEmptyArrayAsObject()
{
$key = 'yma6Hq4XQegCVND8ef23OYgxSrC3IKqk';
$payload = [];
$jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
$this->assertEquals((object) $payload, $decoded);
}
public function testDecodesArraysInJWTAsArray()
{
$key = 'yma6Hq4XQegCVND8ef23OYgxSrC3IKqk';
$payload = ['foo' => [1, 2, 3]];
$jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
$this->assertSame($payload['foo'], $decoded->foo);
}
/**
* @runInSeparateProcess
* @dataProvider provideEncodeDecode
*/
public function testEncodeDecode($privateKeyFile, $publicKeyFile, $alg)
{
$privateKey = file_get_contents($privateKeyFile);
$payload = ['foo' => 'bar'];
$encoded = JWT::encode($payload, $privateKey, $alg);
// Verify decoding succeeds
$publicKey = file_get_contents($publicKeyFile);
$decoded = JWT::decode($encoded, new Key($publicKey, $alg));
$this->assertSame('bar', $decoded->foo);
}
public function provideEncodeDecode()
{
return [
[__DIR__ . '/data/ecdsa-private.pem', __DIR__ . '/data/ecdsa-public.pem', 'ES256'],
[__DIR__ . '/data/ecdsa384-private.pem', __DIR__ . '/data/ecdsa384-public.pem', 'ES384'],
[__DIR__ . '/data/rsa1-private.pem', __DIR__ . '/data/rsa1-public.pub', 'RS512'],
[__DIR__ . '/data/ed25519-1.sec', __DIR__ . '/data/ed25519-1.pub', 'EdDSA'],
[__DIR__ . '/data/secp256k1-private.pem', __DIR__ . '/data/secp256k1-public.pem', 'ES256K'],
];
}
public function testEncodeDecodeWithOpenSSLAsymmetricKey()
{
$pem = file_get_contents(__DIR__ . '/data/rsa1-public.pub');
$keyMaterial = openssl_pkey_get_public($pem);
$privateKey = file_get_contents(__DIR__ . '/data/rsa1-private.pem');
$payload = ['foo' => 'bar'];
$encoded = JWT::encode($payload, $privateKey, 'RS512');
// Verify decoding succeeds
$decoded = JWT::decode($encoded, new Key($keyMaterial, 'RS512'));
$this->assertSame('bar', $decoded->foo);
}
public function testGetHeaders()
{
$payload = [
'message' => 'abc',
'exp' => time() + JWT::$leeway + 20, // time in the future
];
$headers = new stdClass();
$encoded = JWT::encode($payload, $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($encoded, $this->hmacKey, $headers);
$this->assertEquals($headers->typ, 'JWT');
$this->assertEquals($headers->alg, 'HS256');
}
public function testAdditionalHeaderOverrides()
{
$msg = JWT::encode(
['message' => 'abc'],
$this->hmacKey->getKeyMaterial(),
'HS256',
'my_key_id',
[
'cty' => 'test-eit;v=1',
'typ' => 'JOSE', // override type header
'kid' => 'not_my_key_id', // should not override $key param
'alg' => 'BAD', // should not override $alg param
]
);
$headers = new stdClass();
JWT::decode($msg, $this->hmacKey, $headers);
$this->assertEquals('test-eit;v=1', $headers->cty, 'additional field works');
$this->assertEquals('JOSE', $headers->typ, 'typ override works');
$this->assertEquals('my_key_id', $headers->kid, 'key param not overridden');
$this->assertEquals('HS256', $headers->alg, 'alg param not overridden');
}
public function testDecodeExpectsIntegerIat()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Payload iat must be a number');
$payload = JWT::encode(['iat' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($payload, $this->hmacKey);
}
public function testDecodeExpectsIntegerNbf()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Payload nbf must be a number');
$payload = JWT::encode(['nbf' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($payload, $this->hmacKey);
}
public function testDecodeExpectsIntegerExp()
{
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Payload exp must be a number');
$payload = JWT::encode(['exp' => 'not-an-int'], $this->hmacKey->getKeyMaterial(), 'HS256');
JWT::decode($payload, $this->hmacKey);
}
public function testRsaKeyLengthValidationThrowsException(): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided key is too short');
// Generate an RSA key that is smaller than the 2048-bit minimum
$shortRsaKey = openssl_pkey_new([
'private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
self::assertNotFalse($shortRsaKey, 'Failed to generate a short RSA key for testing.');
$payload = ['message' => 'abc'];
JWT::encode($payload, $shortRsaKey, 'RS256');
}
/** @dataProvider provideHmac */
public function testHmacKeyLengthValidationThrowsExceptionEncode(string $alg, int $minLength): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided key is too short');
$tooShortKeyBytes = str_repeat('b', $minLength - 1);
$payload = ['message' => 'abc'];
JWT::encode($payload, $tooShortKeyBytes, $alg);
}
/** @dataProvider provideHmac */
public function testHmacKeyLengthValidationThrowsExceptionDecode(string $alg, int $minLength): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided key is too short');
$tooShortKeyBytes = str_repeat('b', $minLength - 1);
$payload = ['message' => 'abc'];
$validKeyBytes = str_repeat('b', $minLength);
$encoded = JWT::encode($payload, $validKeyBytes, $alg);
JWT::decode($encoded, new Key($tooShortKeyBytes, $alg));
}
/** @dataProvider provideHmac */
public function testHmacKeyLengthValidationPassesWithCorrectLength(string $alg, int $minLength): void
{
$payload = ['message' => 'test hmac length'];
// Test with a key that is exactly the required length
$minKeyBytes = str_repeat('b', $minLength);
$encoded48 = JWT::encode($payload, $minKeyBytes, $alg);
$decoded48 = JWT::decode($encoded48, new Key($minKeyBytes, $alg));
$this->assertEquals($payload['message'], $decoded48->message);
// Test with a key that is longer than the required length
$largeKeyBytes = str_repeat('c', $minLength * 2); // Longer than min bytes
$encoded64 = JWT::encode($payload, $largeKeyBytes, $alg);
$decoded64 = JWT::decode($encoded64, new Key($largeKeyBytes, $alg));
$this->assertEquals($payload['message'], $decoded64->message);
}
public function provideHmac()
{
return [
['HS384', 48],
['HS256', 32],
];
}
public function testEdDsaHandlesBase64UrlKeys()
{
if (!\extension_loaded('sodium')) {
$this->markTestSkipped('libsodium is not available');
}
// Generate a deterministic Ed25519 keypair using a specific seed. The byte "\xfb"
// translates to '+' and '/' in standard base64, which become '-' and '_' in Base64URL.
// This guarantees our keys will contain the URL-safe characters that get incorrectly
// stripped by base64_decode().
$seed = str_repeat("\xfb", 32);
$keyPair = sodium_crypto_sign_seed_keypair($seed);
$secretKey = sodium_crypto_sign_secretkey($keyPair);
$publicKey = sodium_crypto_sign_publickey($keyPair);
// Convert the raw keys to Base64URL encoded strings
$secretKeyB64u = JWT::urlsafeB64Encode($secretKey);
$publicKeyB64u = JWT::urlsafeB64Encode($publicKey);
// Ensure our test keys actually contain the characters that get
// incorrectly stripped by a standard base64_decode().
$this->assertTrue(strpos($secretKeyB64u, '-') !== false || strpos($secretKeyB64u, '_') !== false);
$this->assertTrue(strpos($publicKeyB64u, '-') !== false || strpos($publicKeyB64u, '_') !== false);
// Test Encoding
$token = JWT::encode(['issue' => 596], $secretKeyB64u, 'EdDSA');
$this->assertIsString($token);
// Test Decoding
$decoded = JWT::decode($token, new Key($publicKeyB64u, 'EdDSA'));
$this->assertSame(596, $decoded->issue);
}
/** @dataProvider provideEcKeyInvalidLength */
public function testEcKeyLengthValidationThrowsExceptionEncode(string $keyFile, string $alg): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided key is too short');
$tooShortEcKey = file_get_contents(__DIR__ . '/data/' . $keyFile);
$payload = ['message' => 'abc'];
JWT::encode($payload, $tooShortEcKey, $alg);
}
public function testEcKeyLengthValidationThrowsExceptionDecode(): void
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage('Provided key is too short');
$payload = ['message' => 'abc'];
$validEcKeyBytes = file_get_contents(__DIR__ . '/data/ecdsa384-private.pem');
$encoded = JWT::encode($payload, $validEcKeyBytes, 'ES256');
$tooShortEcKey = file_get_contents(__DIR__ . '/data/ecdsa192-public.pem');
JWT::decode($encoded, new Key($tooShortEcKey, 'ES256'));
}
/** @dataProvider provideEcKey */
public function testEcKeyLengthValidationPassesWithCorrectLength(
string $privateKeyFile,
string $publicKeyFile,
string $alg
): void {
$payload = ['message' => 'test hmac length'];
// Test with a key that is the required length
$privateKeyBytes = file_get_contents(__DIR__ . '/data/' . $privateKeyFile);
$encoded48 = JWT::encode($payload, $privateKeyBytes, $alg);
$publicKeyBytes = file_get_contents(__DIR__ . '/data/' . $publicKeyFile);
$decoded48 = JWT::decode($encoded48, new Key($publicKeyBytes, $alg));
$this->assertEquals($payload['message'], $decoded48->message);
}
public function provideEcKeyInvalidLength()
{
return [
['ecdsa192-private.pem', 'ES256'],
['ecdsa-private.pem', 'ES384'],
];
}
public function provideEcKey()
{
return [
['ecdsa-private.pem', 'ecdsa-public.pem', 'ES256'],
['ecdsa384-private.pem', 'ecdsa384-public.pem', 'ES384'],
];
}
private function generateHmac256(): Key
{
return new Key(random_bytes(32), 'HS256');
}
}
-201
View File
@@ -1,201 +0,0 @@
<?php
namespace Firebase\JWT;
use PHPUnit\Framework\TestCase;
class ReadmeTest extends TestCase
{
private const CODEBLOCK_REGEX = '/^(?m)(\s*)(`{3,}|~{3,})[ \t]*(.*?)\n([\s\S]*?)\1\2\s*$/m';
private array $payload = [
'iss' => 'example.org',
'aud' => 'example.com',
'iat' => 1356999524,
'nbf' => 1357000000,
];
public function testExample()
{
$codeblock = $this->extractCodeBlock('Example');
$output = $codeblock->invoke();
$header = ['typ' => 'JWT', 'alg' => 'HS256'];
$this->assertEquals(
print_r((object) $this->payload, true) . print_r((object) $header, true),
$output
);
}
public function testExampleEncodeDecodeHeaders()
{
$codeblock = $this->extractCodeBlock('Example encode/decode headers');
$output = $codeblock->invoke();
$header = [
'typ' => 'JWT',
'x-forwarded-for' => 'www.google.com',
'alg' => 'HS256',
];
$this->assertEquals(
print_r($header, true),
$output
);
}
public function testExampleWithRS256()
{
$codeblock = $this->extractCodeBlock('Example with RS256 (openssl)');
$output = $codeblock->invoke();
$this->assertStringContainsString(
"Decode:\n" . print_r($this->payload, true),
$output
);
}
public function testExampleWithPassphrase()
{
$codeblock = $this->extractCodeBlock('Example with a passphrase');
$codeblock->replace('[YOUR_PASSPHRASE]', 'passphrase');
$codeblock->replace(
'/path/to/key-with-passphrase.pem',
__DIR__ . '/data/rsa-with-passphrase.pem'
);
$output = $codeblock->invoke();
$this->assertStringContainsString(
"Decode:\n" . print_r($this->payload, true),
$output
);
}
public function testExampleWithEdDSA()
{
$codeblock = $this->extractCodeBlock('Example with EdDSA (libsodium and Ed25519 signature)');
$output = $codeblock->invoke();
$this->assertStringContainsString(
"Decode:\n" . print_r($this->payload, true),
$output
);
}
public function testExampleWithMultipleKeys()
{
$codeblock = $this->extractCodeBlock('Example with multiple keys');
$keys = [
'$privateRsKey' => 'rsa1-private.pem',
'$publicRsKey' => 'rsa1-public.pub',
'$privateEcKey' => 'ed25519-1.sec',
'$publicEcKey' => 'ed25519-1.pub',
];
foreach ($keys as $varName => $keyFile) {
$codeblock->replace(
\sprintf('// %s = \'...\'', $varName),
\sprintf('%s = file_get_contents(\'%s/data/%s\')', $varName, __DIR__, $keyFile)
);
}
$output = $codeblock->invoke();
$this->assertStringContainsString(
"Decode 1:\n" . print_r($this->payload, true),
$output
);
$this->assertStringContainsString(
"Decode 2:\n" . print_r($this->payload, true),
$output
);
}
public function testUsingJWKs()
{
$codeblock = $this->extractCodeBlock('Using JWKs');
$privateKey = file_get_contents(__DIR__ . '/data/rsa1-private.pem');
$jwt = JWT::encode($this->payload, $privateKey, 'RS256', 'jwk1');
$keysJson = file_get_contents(__DIR__ . '/data/rsa-jwkset.json');
$jwkSet = json_decode($keysJson, true);
$codeblock->replace('$jwt', \sprintf("'%s'", $jwt));
$codeblock->replace(
'[\'keys\' => []]',
var_export($jwkSet, true)
);
$output = $codeblock->invoke();
$this->assertEquals(
print_r((object) $this->payload, true),
$output
);
}
public function testUsingCachedKeySets()
{
// We must accept a failure because we are not signing the keys
// This is the farthest we can go without retreiving an actual JWT
// or hosting our own JWKs url.
$this->expectException(SignatureInvalidException::class);
$this->expectExceptionMessage('Signature verification failed');
$codeblock = $this->extractCodeBlock('Using Cached Key Sets');
$privateKey = file_get_contents(__DIR__ . '/data/ecdsa256-private.pem');
$jwt = JWT::encode($this->payload, $privateKey, 'ES256', '_xiGEQ');
$codeblock->replace('eyJhbGci...', $jwt);
$codeblock->invoke();
}
private function extractCodeBlock(string $header)
{
// Normalize line endings to \n to make regex handling consistent across platforms
$markdown = str_replace(["\r\n", "\r"], "\n", file_get_contents(__DIR__ . '/../README.md'));
// find by header
$pattern = '/^#+\s*' . preg_quote($header, '/') . '\s*\n([\s\S]*?)(?=^#+.*$|\Z)/m';
if (!preg_match($pattern, $markdown, $matches)) {
throw new \Exception('Header "' . $header . '" not found in README.md');
}
$markdown = trim($matches[1]);
// extract fenced codeblock
if (!preg_match_all(self::CODEBLOCK_REGEX, $markdown, $matches, PREG_SET_ORDER)) {
throw new \Exception('No code block found in README.md under header "' . $header . '"');
}
$codeblock = $matches[0][4];
return new class($codeblock) {
public function __construct(public string $codeblock)
{
}
public function invoke()
{
try {
ob_start();
eval($this->codeblock);
return ob_get_clean();
} catch (\Exception $e) {
ob_end_clean();
throw $e;
}
}
public function replace($old, $new)
{
$this->codeblock = str_replace($old, $new, $this->codeblock);
}
};
}
}
-40
View File
@@ -1,40 +0,0 @@
{
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "jwk1",
"x": "ALXnvdCvbBx35J2bozBkIFHPT747KiYioLK4JquMhZU",
"y": "fAt_rGPqS95Ytwdluh4TNWTmj9xkcAbKGBRpP5kuGBk",
"alg": "ES256"
},
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "jwk2",
"x": "mQa0q5FvxPRujxzFazQT1Mo2YJJzuKiXU3svOJ41jhw",
"y": "jAz7UwIl2oOFk06kj42ZFMOXmGMFUGjKASvyYtibCH0",
"alg": "ES256"
},
{
"kty": "EC",
"use": "sig",
"crv": "secp256k1",
"kid": "jwk3",
"x": "EFpwNuP322bU3WP1DtJgx67L0CUV1MxNixqPVMH2L9Q",
"y": "_fSTbijIJjpsqL16cIEvxxf3MaYMY8MbqEq066yV9ls",
"alg": "ES256K"
},
{
"kty": "EC",
"use": "sig",
"crv": "P-384",
"kid": "jwk4",
"x": "FhXXcyKmWkTkdVbWYYU3dtJqpJ0JmLGftEdNzUEFEKSU5MlnLr_FjcneszvXAqEB",
"y": "M4veJF_dO_zhFk44bh_ELXbp0_nn9QaViVtQpuTvpu29eefx6PfUMqX0K--IS4NQ",
"alg": "ES384"
}
]
}
-18
View File
@@ -1,18 +0,0 @@
-----BEGIN EC PARAMETERS-----
MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////
/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6
k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+
kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK
fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz
ucrC/GMlUQIBAQ==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIIBaAIBAQQgyP9e7yS1tjpXa0l6o+80dbSxuMcqx3lUg0n2OT9AmiuggfowgfcC
AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////
MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr
vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE
axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W
K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8
YyVRAgEBoUQDQgAE2klp6aX6y5kAir3EWQt0QAeapTW+db/9fD65KAoDzVajtThx
PVLEf1CufcfTxMQAQPM3wkZhu0NjlWFetcMdcQ==
-----END EC PRIVATE KEY-----
-9
View File
@@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA
AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////
///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd
NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5
RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA
//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABNpJaeml+suZAIq9xFkLdEAH
mqU1vnW//Xw+uSgKA81Wo7U4cT1SxH9Qrn3H08TEAEDzN8JGYbtDY5VhXrXDHXE=
-----END PUBLIC KEY-----
@@ -1,5 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MF8CAQEEGPRkK7lK/9FuZ3BE8ZX+dlHavL22Q9CN2KAKBggqhkjOPQMBAaE0AzIA
BL4pM50YcLq/I9Y8T+C+fwoOtwRW8zdV6yQmG9fD8zWaAs28+UxHeK8VD7THatbp
wg==
-----END EC PRIVATE KEY-----
@@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEvikznRhwur8j1jxP4L5/Cg63BFbz
N1XrJCYb18PzNZoCzbz5TEd4rxUPtMdq1unC
-----END PUBLIC KEY-----
@@ -1,4 +0,0 @@
-----BEGIN PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCD0KvVxLJEzRBQmcEXf
D2okKCNoUwZY8fc1/1Z4aJuJdg==
-----END PRIVATE KEY-----
@@ -1,6 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBQJuwafREZ1494Fm2MTVXuZbWXVAOwIAxGhyLdc3CChzi0FVXZq8e6
65oR0Qq9Jv2gBwYFK4EEACKhZANiAAQWFddzIqZaROR1VtZhhTd20mqknQmYsZ+0
R03NQQUQpJTkyWcuv8WNyd6zO9cCoQEzi94kX907/OEWTjhuH8QtdunT+ef1BpWJ
W1Cm5O+m7b155/Ho99QypfQr74hLg1A=
-----END EC PRIVATE KEY-----
@@ -1,5 +0,0 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEFhXXcyKmWkTkdVbWYYU3dtJqpJ0JmLGf
tEdNzUEFEKSU5MlnLr/FjcneszvXAqEBM4veJF/dO/zhFk44bh/ELXbp0/nn9QaV
iVtQpuTvpu29eefx6PfUMqX0K++IS4NQ
-----END PUBLIC KEY-----
-1
View File
@@ -1 +0,0 @@
uOSJMhbKSG4V5xUHS7B9YHmVg/1yVd+G+Io6oBFhSfY=
-1
View File
@@ -1 +0,0 @@
i4eTKkWNIISKumdk3v90cPDrY/g8WRTJWy7DmGDsdzC45IkyFspIbhXnFQdLsH1geZWD/XJV34b4ijqgEWFJ9g==
-11
View File
@@ -1,11 +0,0 @@
{
"keys": [
{
"kid": "jwk1",
"alg": "EdDSA",
"kty": "OKP",
"crv": "Ed25519",
"x": "uOSJMhbKSG4V5xUHS7B9YHmVg_1yVd-G-Io6oBFhSfY"
}
]
}
-22
View File
@@ -1,22 +0,0 @@
{
"keys": [
{
"kty": "oct",
"alg": "HS256",
"kid": "jwk1",
"k": "xUNfVvQ-WdmXB9qp6qK0SrG-yKW4AJqmcSP66Gm2TrE"
},
{
"kty": "oct",
"alg": "HS384",
"kid": "jwk2",
"k": "z7990HoD72QDX9JKqeQc3l7EtXutco72j2YulZMjeakFVDbFGXGDFG4awOF7eu9l"
},
{
"kty": "oct",
"alg": "HS512",
"kid": "jwk3",
"k": "EmYGSDG5W1UjkPIL7LelG-QMVtsXn7bz5lUxBrkqq3kdFEzkLWVGrXKpZxRe7YcApCe0d4s9lXRQtn5Nzaf49w"
}
]
}
-18
View File
@@ -1,18 +0,0 @@
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "jwk1",
"n": "0Ttga33B1yX4w77NbpKyNYDNSVCo8j-RlZaZ9tI-KfkV1d-tfsvI9ZPAheP11FoN52ceBaY5ltelHW-IKwCfyT0orLdsxLgowaXki9woF1Azvcg2JVxQLv9aVjjAvy3CZFIG_EeN7J3nsyCXGnu1yMEbnvkWxA88__Q6HQ2K9wqfApkQ0LNlsK0YHz_sfjHNvRKxnbAJk7D5fUhZunPZXOPHXFgA5SvLvMaNIXduMKJh4OMfuoLdJowXJAR9j31Mqz_is4FMhm_9Mq7vZZ-uF09htRvIR8tRY28oJuW1gKWyg7cQQpnjHgFyG3XLXWAeXclWqyh_LfjyHQjrYhyeFw",
"alg": "RS256"
},
{
"kty": "RSA",
"e": "AQAB",
"kid": "jwk2",
"n": "pXi2o6AnNhwL30MaK_nuDHi2fxZHVen7Xwk0bjLGlHYpq3mSvXm2HBA-zR41vQCbHkYGsDpsyDhIXLBDTbSa7ue7D1ZqYdv5YLIS33zdX9GtUHfFHc6zYgXAU9ziWeyTzVn7icAbjxqcgT2xKNuGK7Zf2ZJ053rr-dxjAE-SjX4SG0WWUhwPjxlr1etF7mEurhHweuSdZYl36g39o9BtTBVfS87io2MwdIRsnL3w8ulgXRVRWjv-vvcuhMS_y6zGbzOC55Yr23sb4h2PSll32bgyglEIsGgHqjOdyjuUzl0t6jh86DHzbu9h-u1iihX8EI8t7CBbizbPPyHQygp-rQ",
"alg": "RS256"
}
]
}
@@ -1,42 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,D8AA4EC8D8B5883F09ACB308FB026C94
ixqo1+NTlkiUHUa1bucqHNQ4nca4cnaosK8Lauftc0WuyqNVE+NL/zxdiUKN+Qi1
bhEkvMKgbqTMzPFUws3wNoPEI/eaoGYHTl4nAX79JWjJ8/DWY+VVp5IFSzNEM1MP
NMWaivfBGhd8W9kBmpOJpQjwePFk7hdLkEvSngGRhDmEV046cWr7I+koYKEG/oW9
53NnDNKPKLPkzM/Me4GQ6nXarqUPoIn/c3qFLgkhkzLJ/Lu21wnYx46RasXJv3oq
xT3nRIat/Q6jtlzLLwvo+lpvJW3G+rKqjEi76Av7Cm1TkHQFW9CGsnQ4ZDn427KL
FGojP6QG5RjLI6IiAHgt0lnzOwtjbF1RQBHIWedC4Rufb5u92SRKJ2PvidB/suJ7
SR/PPA2XpK22QBMccO9yjNh4ZZIV6I2cqv3BlKR2RFU0552sEQr6usxPfFhExIRR
1eiaLtIupo3uEC5e2fBKtI7D3T7WztUagTw0vSgoxhTdc8XIoT0prV91SvyEEZMw
r5LSRW4BvyCekG9FFyIS2fOWabgxmm16siNErTbS2RS3GGimX0v5O+KIN9ho1uAY
5U865amaOZshop1YYixtDJL27JhpkODhwXrB1lNQOCdi64CV2r8VlVPNg6TWZlli
vJ6agKvWmTppy07ovbBRB+llmW6eGtjwEmAvMaWNgkFNkgDF/wBnDi91tx8/8UL7
XQy0VZz128FtpJC0G0Z/5HmxqoEJAwk1+EzO5tgnfc+2wIONGCV2ISph0efVtPui
xOP6geaeSrxBxL/BUcIX5DMfN6hsvz+Pb8bE9WT2+fz/ySCJhkfraC/vHbs3wn3R
CICCvYtR803ku53GCgsEZ8vmIxMb1D0mJnfWvSQtDBqF8XwhL6m5ShbeaMLkbmZ9
0WLWj0zAcOkbX4TXLGVaRPRs9HjSEr7+jEVHO6OeKj60rG9M3NVmfig7J8ta/zvy
1Hk4MiucTsp0I+G/hx8dqoV4x1kTyn0WZMfD8PxnbPdPvbhG2tQn7xkZykgtvK5y
s1fMbvqVGDfn5PmLeSwYkyohYZGbiwV5UldhwdG/ZnagI1KPuJ10OYBOSLCcGufY
aUHmIFSvfYqbN5YfKsMCZmmrX73pDcXOWGWto8nTFS9f4RlQI0Vh25xJqqinD6Vu
ErP7+XxDZCLqKew/xfq1fcKoiCOA/9IK5meyjRV4Z5QxkgTeBmyNVt/MW+6QIJJJ
WoBWqpootxtb28YN2RuD0byEIyP8pmoyN3MOPYGNSia8PAQgIL6z71Ju2SejXADy
ybirbrS0Y/oZABqhLK5qDdCYe4O5zp/lbwWn2Gfp3G3xKUxfBWi4f/VQwUjUbYCz
XHFVLpDY1mMPaedo7Tp5ZGN4OHwIlpspcwI0U9TYac0AxZuSBPjE8YqJ2qJBhaiZ
dEE7CxwkSLLxXVEPp7+VO6CORZfYXXaRcpTAZfrDURSI5RkT8n6LElnrzFBilb0q
ejlKaLD4MLlvlc/NWl/w+TfuN/iGlQm02Ul8yysG1b0w8R+seMNHhHS4+848ZRBd
HoWUuYiYXZTJxmP5dc0f/Sul672YSFp7rGzt9+7hFV6WrkAFNxETkQ8cbA/GiGvz
Kvv1GI/Ms8YymAJWiv7skFTmGcHMbjxga2EOBtSfYF5mwV3KEMPRpYsn1nw6U99E
NuWFqT+p4VqVSgmeG11zwM7v+Vt3RZDUggZWDsNKGA9V9ciAlHY2U7CH6xihBCfh
suHNuzVC1nAwi/ZrhfJXMKk+hJ8o+5dXSTYp4eCEGh4U2l3pmmAejZenJqlGs0Ke
MYHQRCk5zaB5myRYuvwtUSbZ/BaVVFSQz758Vw4HxKFLnvudtAXktu3sTcOgYKQS
PaiolwZFr4lp3h74BlIYcYrREmBJv6Hy1lOLAd5X3iExiy+DdRJWkuNd+19Cblq3
ePHf2Mgp+AElxmyA6EHyt86v3E2mL7xNAUUVrNb3UJTi6io5KASMVmNbrGGJksC7
y3OuHaq1RM7UvR/eI38nI2YOckoKDgkhHPtaXkIpO9jX3RRYlA2uzsf44DU7etyc
c2ICApYVdKruR/pmFN45pcIPy6x3zU34fkRTMf1F3yShJzr8Ntd/C63Km8XaganW
2AVWuuvOJXjMqu4+OXzrIqObFFp6naqv1E+O8/14i8k4VW3dmWnMM7eq9FvqQdiM
y0tBbGILfAVYtjh59r+CKeqRoq7o/xlsVin1Vxn74K6uYUphjXWUhMXXStGZ8sBc
QDOPTanB+LPBeCAgQFQe1SHrGiIognXT0g2WFqW8DrxwTqr6olPoMF6LU01vqT0+
HVZtczjk0LvDLZm8bsCDGBPDdbDI/tfvXncP5PgEtFSTUiRy+zryy82AF4rJhudH
-----END RSA PRIVATE KEY-----
-27
View File
@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0Ttga33B1yX4w77NbpKyNYDNSVCo8j+RlZaZ9tI+KfkV1d+t
fsvI9ZPAheP11FoN52ceBaY5ltelHW+IKwCfyT0orLdsxLgowaXki9woF1Azvcg2
JVxQLv9aVjjAvy3CZFIG/EeN7J3nsyCXGnu1yMEbnvkWxA88//Q6HQ2K9wqfApkQ
0LNlsK0YHz/sfjHNvRKxnbAJk7D5fUhZunPZXOPHXFgA5SvLvMaNIXduMKJh4OMf
uoLdJowXJAR9j31Mqz/is4FMhm/9Mq7vZZ+uF09htRvIR8tRY28oJuW1gKWyg7cQ
QpnjHgFyG3XLXWAeXclWqyh/LfjyHQjrYhyeFwIDAQABAoIBAHMqdJsWAGEVNIVB
+792HYNXnydQr32PwemNmLeD59WglgU/9jZJoxaROjI4VLKK0wZg+uRvJ1nA3tCB
+Hh7Anh5Im9XExaAq2ZTkqXtC2AxtBktH6iW1EfaI/Y7jNRuMoaXo+Ku3A62p7cw
JBvepiOXL0Xko0RNguz7mBUvxCLPhYhzn7qCbM8uXLcjsXq/YhWQwQmtMqv0sd3W
Hy+8Jb2c18sqDeZIBne4dWD6qPClPEOsrq9gPTkl0DjbT27oVc2u1p4HMNm5BJIh
u3rMSxnZHUd7Axj1FgyLIOHl63UhaiaA1aPe/fLiVIGOA1jBZrpbnjgqDy9Uxyn6
eydbiwECgYEA9mtRydz22idyUOlBCDXk+vdGBvFAucNYaNNUAXUJ2wfPmdGgFCA7
g5eQG8JC6J/FU+2AfIuz6LGr7SxMBYcsWGjFAzGqs/sJib+zzN1dPUSRn4uJNFit
51yQzPgBqHS6S/XBi6YAODeZDl9jiPl3FxxucqLY5NstqZFXbE0SjIECgYEA2V3r
7xnRAK1krY1+zkPof4kcBmjqOXjnl/oRxlXP65lEXmyNJwm/ulOIko9mElWRs8CG
AxSWKaab9Gk6lc8MHjVRbuW52RGLGKq1mp6ENr4d3IBOfrNsTvD3gtNEN1JFLeF1
jIbSsrbi2txr7VZ06Irac0C/ytro0QDOUoXkvpcCgYA8O0EzmToRWsD7e/g0XJAK
s/Q+8CtE/LWYccc/z+7HxeH9lBqPsM07Pgmwb0xRdfQSrqPQTYl9ICiJAWHXnBG/
zmQRgstZ0MulCuGU+qq2thLuL3oq/F4NhjeykhA9r8J1nK1hSAMXuqdDtxcqPOfa
E03/4UQotFY181uuEiytgQKBgHQT+gjHqptH/XnJFCymiySAXdz2bg6fCF5aht95
t/1C7gXWxlJQnHiuX0KVHZcw5wwtBePjPIWlmaceAtE5rmj7ZC9qsqK/AZ78mtql
SEnLoTq9si1rN624dRUCKW25m4Py4MlYvm/9xovGJkSqZOhCLoJZ05JK8QWb/pKH
Oi6lAoGBAOUN6ICpMQvzMGPgIbgS0H/gvRTnpAEs59vdgrkhlCII4tzfgvBQlVae
hRcdM6GTMq5pekBPKu45eanIzwVc88P6coT4qiWYKk2jYoLBa0UV3xEAuqBMymrj
X4nLcSbZtO0tcDGMfMpWF2JGYOEJQNetPozL/ICGVFyIO8yzXm8U
-----END RSA PRIVATE KEY-----
-9
View File
@@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Ttga33B1yX4w77NbpKy
NYDNSVCo8j+RlZaZ9tI+KfkV1d+tfsvI9ZPAheP11FoN52ceBaY5ltelHW+IKwCf
yT0orLdsxLgowaXki9woF1Azvcg2JVxQLv9aVjjAvy3CZFIG/EeN7J3nsyCXGnu1
yMEbnvkWxA88//Q6HQ2K9wqfApkQ0LNlsK0YHz/sfjHNvRKxnbAJk7D5fUhZunPZ
XOPHXFgA5SvLvMaNIXduMKJh4OMfuoLdJowXJAR9j31Mqz/is4FMhm/9Mq7vZZ+u
F09htRvIR8tRY28oJuW1gKWyg7cQQpnjHgFyG3XLXWAeXclWqyh/LfjyHQjrYhye
FwIDAQAB
-----END PUBLIC KEY-----
-27
View File
@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEApXi2o6AnNhwL30MaK/nuDHi2fxZHVen7Xwk0bjLGlHYpq3mS
vXm2HBA+zR41vQCbHkYGsDpsyDhIXLBDTbSa7ue7D1ZqYdv5YLIS33zdX9GtUHfF
Hc6zYgXAU9ziWeyTzVn7icAbjxqcgT2xKNuGK7Zf2ZJ053rr+dxjAE+SjX4SG0WW
UhwPjxlr1etF7mEurhHweuSdZYl36g39o9BtTBVfS87io2MwdIRsnL3w8ulgXRVR
Wjv+vvcuhMS/y6zGbzOC55Yr23sb4h2PSll32bgyglEIsGgHqjOdyjuUzl0t6jh8
6DHzbu9h+u1iihX8EI8t7CBbizbPPyHQygp+rQIDAQABAoIBACF25kj1LLjutx/x
7CsUoqX3C8Fr+gVQCrxPmkDnF+4Sb570OU8EfGX0ix7kiy2sH7LhqpydVD6x00Cb
jSD785F5YAVcDqu31xlNKi/0irjEKO7rKfw7P2AFlb3gIA7bn5CaMBrNtUUdtqUU
mu2OZ/YTLhNMYUQnQe4IOiVn8lWW5D4Kje/RlLRRdGn8voXaD5BnOwZNXAxjdXqM
RxyXRG74tLKyfe3W8xTL8uhlKCNHjsdtUg9IZdnKT7I3DJPobpqgC3fUuC/IbfGf
MPK1aiu067/3DdgonC2ZWqFeKLJqtUa7z0pSQaZeDa1iiUuRivfqKYEBovFre6ni
1qHkp8ECgYEA089VnKc74NRGVbIs0VtQGprNhkl47eBq6jhTlG3hfaFF4VuDiZiu
wT8enlbhlbDb/gM0CDr9tkfDs7R4exNnhSVvn2PT8b1mhonOAeE466y/4YBA0d9x
gj0wF2vjH/bsVNBe6MBrIx12R2tBKTZ7tbCzgJRszSZqkrK7sljTlaUCgYEAx/54
G3Yd3ULqGIG/JA7w/QEYitgjwAUSJ+eLU+iqlIjo/njAJwJ/kixqaI3Jzcl+kYmp
yNIXNNaJUz8c0M/QsuqvQjLnHkF0FOZUrdyVseU2mSbI6DhAGsPJEtAOep/61vyz
uJSu0z34gQ6bNrKdqfkA7XIQRNJ1r0qQXrVLRmkCgYB2/UYaIDTaREZTBCp7XnHs
0ERfiUz/TZCijgweGXCQ1BXe2TtXBEhAVcZMq4BFSLr9wyzq5sD7Muu1O9BnS+pe
+T3w6/L4Hi/HqwjpM253r2+ILjW78Wvh/5/RuJE6tsvjhb+bv+UwL+/vhUhw76Ol
2WOt+zP4N/ms+e3J7m7G5QKBgQCmasN65nC3WyT8u4pX8O7rOOw5LN2ivRV8ixnO
+r5m1v46MjSCwXtyIO9yjPmt+csOQ+U6LEgPOa4PzWanAyaAmvS3OzBCZui3M2qn
OfR+kWM7UaDAS35cRyqcMvC5bUIHf0P1hhNryBdvHL5fZ4X2mDMDYnTTL+WptXwo
sucucQKBgAGHzi5+ZRwffhpZiYVR/lA6zvqyekAncJZwGe2UVDL0axTumX1NPdin
2mOnVuvKVvJkisyKTIQzFk6ClQEyiArO4+t7zhUbg5Crh8q6nObRo2R2NcP8o0Iq
BRIwPgaG/WlEvZ6zqlHQ0qH7WoL4HnRG5uyLOuzRIkjasYmZdfR8
-----END RSA PRIVATE KEY-----
@@ -1,5 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgC8ouvv1ZOmOjh5Nbwx6i
3b35wWN+OEkW2hzm3BKAQJ2hRANCAAT9nYGLVP6Unm/LXOoyWhsKpalffMSr3EHV
iUE8gVmj2/atnPkblx38Yj6bC3z1urERAB+JqgpWOAKaWcEYCUuO
-----END PRIVATE KEY-----
@@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/Z2Bi1T+lJ5vy1zqMlobCqWpX3zEq9xB
1YlBPIFZo9v2rZz5G5cd/GI+mwt89bqxEQAfiaoKVjgCmlnBGAlLjg==
-----END PUBLIC KEY-----