Skip to content

Commit 77e7502

Browse files
authored
Merge branch '3.2.x' into 3.1.x-merge-up-into-3.2.x_y5PglacC
2 parents 050c8b3 + f01e77f commit 77e7502

26 files changed

+238
-101
lines changed

.github/workflows/release-on-milestone-closed.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
uses: "actions/checkout@v3"
1818

1919
- name: "Release"
20-
uses: "laminas/automatic-releases@1.15.0"
20+
uses: "laminas/automatic-releases@1.24.0"
2121
with:
2222
command-name: "laminas:automatic-releases:release"
2323
env:
@@ -28,7 +28,7 @@ jobs:
2828
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
2929

3030
- name: "Create Merge-Up Pull Request"
31-
uses: "laminas/automatic-releases@1.15.0"
31+
uses: "laminas/automatic-releases@1.24.0"
3232
with:
3333
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
3434
env:
@@ -39,7 +39,7 @@ jobs:
3939
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
4040

4141
- name: "Create and/or Switch to new Release Branch"
42-
uses: "laminas/automatic-releases@1.15.0"
42+
uses: "laminas/automatic-releases@1.24.0"
4343
with:
4444
command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
4545
env:
@@ -50,7 +50,7 @@ jobs:
5050
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
5151

5252
- name: "Bump Changelog Version On Originating Release Branch"
53-
uses: "laminas/automatic-releases@1.15.0"
53+
uses: "laminas/automatic-releases@1.24.0"
5454
with:
5555
command-name: "laminas:automatic-releases:bump-changelog"
5656
env:
@@ -61,7 +61,7 @@ jobs:
6161
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
6262

6363
- name: "Create new milestones"
64-
uses: "laminas/automatic-releases@1.15.0"
64+
uses: "laminas/automatic-releases@1.24.0"
6565
with:
6666
command-name: "laminas:automatic-releases:create-milestones"
6767
env:

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mu: vendor ## Mutation tests
2-
vendor/bin/infection -s --threads=$(nproc) --min-msi=40 --min-covered-msi=40
2+
vendor/bin/infection -s --threads=$$(nproc) --min-msi=40 --min-covered-msi=40
33

44
tests: vendor ## Run all tests
55
vendor/bin/phpunit --color
@@ -8,7 +8,7 @@ cc: vendor ## Show test coverage rates (HTML)
88
vendor/bin/phpunit --coverage-html ./build
99

1010
cs: vendor ## Fix all files using defined ECS rules
11-
vendor/bin/ecs check --fix
11+
XDEBUG_MODE=off vendor/bin/ecs check --fix
1212

1313
tu: vendor ## Run only unit tests
1414
vendor/bin/phpunit --color --group Unit
@@ -20,19 +20,19 @@ tf: vendor ## Run only functional tests
2020
vendor/bin/phpunit --color --group Functional
2121

2222
st: vendor ## Run static analyse
23-
vendor/bin/phpstan analyse
23+
XDEBUG_MODE=off vendor/bin/phpstan analyse
2424

2525

2626
################################################
2727

2828
ci-mu: vendor ## Mutation tests (for Github only)
29-
vendor/bin/infection --logger-github -s --threads=$(nproc) --min-msi=40 --min-covered-msi=40
29+
vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=40 --min-covered-msi=40
3030

3131
ci-cc: vendor ## Show test coverage rates (console)
3232
vendor/bin/phpunit --coverage-text
3333

3434
ci-cs: vendor ## Check all files using defined ECS rules
35-
vendor/bin/ecs check
35+
XDEBUG_MODE=off vendor/bin/ecs check
3636

3737
################################################
3838

@@ -42,7 +42,7 @@ vendor: composer.json composer.lock
4242
composer install
4343

4444
rector: vendor ## Check all files using Rector
45-
vendor/bin/rector process --ansi --dry-run --xdebug
45+
XDEBUG_MODE=off vendor/bin/rector process --ansi --dry-run --xdebug
4646

4747
.DEFAULT_GOAL := help
4848
help:

README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
PHP JWT Framework
22
=================
33

4-
![Build Status](https://github.com/web-token/jwt-framework/workflows/Coding%20Standards/badge.svg)
5-
![Build Status](https://github.com/web-token/jwt-framework/workflows/Static%20Analyze/badge.svg)
6-
![Build Status](https://github.com/web-token/jwt-framework/workflows/Rector%20Checkstyle/badge.svg)
7-
8-
![Build Status](https://github.com/web-token/jwt-framework/workflows/Unit%20and%20Functional%20Tests/badge.svg)
9-
10-
![Build Status](https://github.com/web-token/jwt-framework/workflows/Mutation%20Testing/badge.svg)
4+
![Build Status](https://github.com/web-token/jwt-framework/workflows/Integrate/badge.svg)
115

126
[![Latest Stable Version](https://poser.pugx.org/web-token/jwt-framework/v/stable.png)](https://packagist.org/packages/web-token/jwt-framework)
137
[![Total Downloads](https://poser.pugx.org/web-token/jwt-framework/downloads.png)](https://packagist.org/packages/web-token/jwt-framework)

composer.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"ext-sodium": "*",
6060
"brick/math": "^0.9|^0.10|^0.11",
6161
"paragonie/constant_time_encoding": "^2.4",
62+
"psr/clock": "^1.0",
6263
"psr/event-dispatcher": "^1.0",
6364
"psr/http-client": "^1.0",
6465
"psr/http-factory": "^1.0",
@@ -83,14 +84,13 @@
8384
"php-http/mock-client": "^1.5",
8485
"php-parallel-lint/php-parallel-lint": "^1.3",
8586
"phpbench/phpbench": "^1.2",
86-
"phpstan/extension-installer": "^1.1",
8787
"phpstan/phpstan": "^1.8",
8888
"phpstan/phpstan-deprecation-rules": "^1.0",
8989
"phpstan/phpstan-phpunit": "^1.1",
9090
"phpstan/phpstan-strict-rules": "^1.4",
9191
"phpunit/phpunit": "^9.5.23",
9292
"qossmic/deptrac-shim": "^1.0",
93-
"rector/rector": "^0.14",
93+
"rector/rector": "^0.15",
9494
"roave/security-advisories": "dev-latest",
9595
"symfony/browser-kit": "^6.1.3",
9696
"symfony/finder": "^5.4|^6.0",
@@ -145,9 +145,10 @@
145145
"config": {
146146
"sort-packages": true,
147147
"allow-plugins": {
148-
"phpstan/extension-installer": true,
149148
"infection/extension-installer": true,
150-
"composer/package-versions-deprecated": true
149+
"composer/package-versions-deprecated": true,
150+
"phpstan/extension-installer": false,
151+
"php-http/discovery": false
151152
}
152153
}
153154
}

rector.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
$config->parallel();
3232
$config->paths([__DIR__ . '/src', __DIR__ . '/performance', __DIR__ . '/tests']);
3333
$config->skip([
34+
\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class => __DIR__ . '/tests',
35+
\Rector\PHPUnit\Rector\Class_\AnnotationWithValueToAttributeRector::class => __DIR__ . '/tests',
3436
__DIR__ . '/src/Component/Core/JWKSet.php',
3537
__DIR__ . '/src/Bundle/JoseFramework/DependencyInjection/Source/KeyManagement/JWKSource.php',
3638
__DIR__ . '/src/Bundle/JoseFramework/DependencyInjection/Source/KeyManagement/JWKSetSource.php',

src/Bundle/JoseFramework/DependencyInjection/Source/Checker/CheckerSource.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public function load(array $configs, ContainerBuilder $container): void
4545
$loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../../Resources/config'));
4646
$loader->load('checkers.php');
4747

48+
$container->setAlias('jose.clock', $configs['clock']);
4849
if (array_key_exists('checkers', $configs)) {
4950
foreach ($this->sources as $source) {
5051
$source->load($configs['checkers'], $container);
@@ -57,6 +58,13 @@ public function getNodeDefinition(NodeDefinition $node): void
5758
if (! $this->isEnabled()) {
5859
return;
5960
}
61+
$node->children()
62+
->scalarNode('clock')
63+
->defaultValue('jose.internal_clock')
64+
->cannotBeEmpty()
65+
->info('PSR-20 clock')
66+
->end()
67+
->end();
6068
$childNode = $node
6169
->children()
6270
->arrayNode($this->name())

src/Bundle/JoseFramework/Resources/config/checkers.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
use Jose\Bundle\JoseFramework\Services\ClaimCheckerManagerFactory;
66
use Jose\Bundle\JoseFramework\Services\HeaderCheckerManagerFactory;
77
use Jose\Component\Checker\ExpirationTimeChecker;
8+
use Jose\Component\Checker\InternalClock;
89
use Jose\Component\Checker\IssuedAtChecker;
910
use Jose\Component\Checker\NotBeforeChecker;
11+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
12+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
1013

1114
/*
1215
* The MIT License (MIT)
@@ -17,7 +20,6 @@
1720
* of the MIT license. See the LICENSE file for details.
1821
*/
1922

20-
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
2123

2224
return function (ContainerConfigurator $container): void {
2325
$container = $container->services()
@@ -33,6 +35,7 @@
3335
->public();
3436

3537
$container->set(ExpirationTimeChecker::class)
38+
->arg('$clock', service('jose.internal_clock'))
3639
->tag('jose.checker.claim', [
3740
'alias' => 'exp',
3841
])
@@ -41,6 +44,7 @@
4144
]);
4245

4346
$container->set(IssuedAtChecker::class)
47+
->arg('$clock', service('jose.internal_clock'))
4448
->tag('jose.checker.claim', [
4549
'alias' => 'iat',
4650
])
@@ -49,10 +53,20 @@
4953
]);
5054

5155
$container->set(NotBeforeChecker::class)
56+
->arg('$clock', service('jose.internal_clock'))
5257
->tag('jose.checker.claim', [
5358
'alias' => 'nbf',
5459
])
5560
->tag('jose.checker.header', [
5661
'alias' => 'nbf',
5762
]);
63+
64+
$container->set('jose.internal_clock')
65+
->class(InternalClock::class)
66+
->deprecate(
67+
'web-token/jwt-bundle',
68+
'3.2.0',
69+
'The service "%service_id%" is an internal service that will be removed in 4.0.0. Please use a PSR-20 compatible service as clock.'
70+
)
71+
->private();
5872
};

src/Component/Checker/ExpirationTimeChecker.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use function is_float;
88
use function is_int;
9+
use Psr\Clock\ClockInterface;
910

1011
/**
1112
* This class is a claim checker. When the "exp" is present, it will compare the value with the current timestamp.
@@ -14,10 +15,22 @@ final class ExpirationTimeChecker implements ClaimChecker, HeaderChecker
1415
{
1516
private const NAME = 'exp';
1617

18+
private readonly ClockInterface $clock;
19+
1720
public function __construct(
1821
private readonly int $allowedTimeDrift = 0,
19-
private readonly bool $protectedHeaderOnly = false
22+
private readonly bool $protectedHeaderOnly = false,
23+
?ClockInterface $clock = null,
2024
) {
25+
if ($clock === null) {
26+
trigger_deprecation(
27+
'web-token/jwt-checker',
28+
'3.2.0',
29+
'The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".'
30+
);
31+
$clock = new InternalClock();
32+
}
33+
$this->clock = $clock;
2134
}
2235

2336
/**
@@ -28,7 +41,10 @@ public function checkClaim(mixed $value): void
2841
if (! is_float($value) && ! is_int($value)) {
2942
throw new InvalidClaimException('"exp" must be an integer.', self::NAME, $value);
3043
}
31-
if (time() > $value + $this->allowedTimeDrift) {
44+
45+
$now = $this->clock->now()
46+
->getTimestamp();
47+
if ($now > $value + $this->allowedTimeDrift) {
3248
throw new InvalidClaimException('The token expired.', self::NAME, $value);
3349
}
3450
}
@@ -43,7 +59,10 @@ public function checkHeader(mixed $value): void
4359
if (! is_float($value) && ! is_int($value)) {
4460
throw new InvalidHeaderException('"exp" must be an integer.', self::NAME, $value);
4561
}
46-
if (time() > $value + $this->allowedTimeDrift) {
62+
63+
$now = $this->clock->now()
64+
->getTimestamp();
65+
if ($now > $value + $this->allowedTimeDrift) {
4766
throw new InvalidHeaderException('The token expired.', self::NAME, $value);
4867
}
4968
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Jose\Component\Checker;
6+
7+
use DateTimeImmutable;
8+
use Psr\Clock\ClockInterface;
9+
10+
/**
11+
* @internal
12+
*/
13+
final class InternalClock implements ClockInterface
14+
{
15+
public function now(): DateTimeImmutable
16+
{
17+
return new DateTimeImmutable();
18+
}
19+
}

src/Component/Checker/IssuedAtChecker.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use function is_float;
88
use function is_int;
9+
use Psr\Clock\ClockInterface;
910

1011
/**
1112
* This class is a claim checker. When the "iat" is present, it will compare the value with the current timestamp.
@@ -14,10 +15,22 @@ final class IssuedAtChecker implements ClaimChecker, HeaderChecker
1415
{
1516
private const NAME = 'iat';
1617

18+
private readonly ClockInterface $clock;
19+
1720
public function __construct(
1821
private readonly int $allowedTimeDrift = 0,
19-
private readonly bool $protectedHeaderOnly = false
22+
private readonly bool $protectedHeaderOnly = false,
23+
?ClockInterface $clock = null,
2024
) {
25+
if ($clock === null) {
26+
trigger_deprecation(
27+
'web-token/jwt-checker',
28+
'3.2.0',
29+
'The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".'
30+
);
31+
$clock = new InternalClock();
32+
}
33+
$this->clock = $clock;
2134
}
2235

2336
/**
@@ -28,7 +41,10 @@ public function checkClaim(mixed $value): void
2841
if (! is_float($value) && ! is_int($value)) {
2942
throw new InvalidClaimException('"iat" must be an integer.', self::NAME, $value);
3043
}
31-
if (time() < $value - $this->allowedTimeDrift) {
44+
45+
$now = $this->clock->now()
46+
->getTimestamp();
47+
if ($now < $value - $this->allowedTimeDrift) {
3248
throw new InvalidClaimException('The JWT is issued in the future.', self::NAME, $value);
3349
}
3450
}
@@ -43,7 +59,10 @@ public function checkHeader(mixed $value): void
4359
if (! is_float($value) && ! is_int($value)) {
4460
throw new InvalidHeaderException('The header "iat" must be an integer.', self::NAME, $value);
4561
}
46-
if (time() < $value - $this->allowedTimeDrift) {
62+
63+
$now = $this->clock->now()
64+
->getTimestamp();
65+
if ($now < $value - $this->allowedTimeDrift) {
4766
throw new InvalidHeaderException('The JWT is issued in the future.', self::NAME, $value);
4867
}
4968
}

0 commit comments

Comments
 (0)