8000 [Security] Add clock dependency to OidcTokenHandler · symfony/symfony@5394130 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5394130

Browse files
[Security] Add clock dependency to OidcTokenHandler
From "web-token/jwt-checker": The parameter "$clock" will become mandatory in 4.0.0. Please set a valid PSR Clock implementation instead of "null".
1 parent dec93e9 commit 5394130

File tree

10 files changed

+85
-65
lines changed

10 files changed

+85
-65
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
use Jose\Component\Core\Algorithm;
1515
use Jose\Component\Core\JWK;
16-
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory;
1716
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
1817
use Symfony\Component\DependencyInjection\ChildDefinition;
1918
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -28,26 +27,28 @@ class OidcTokenHandlerFactory implements TokenHandlerFactoryInterface
2827
{
2928
public function create(ContainerBuilder $container, string $id, array|string $config): void
3029
{
31-
$tokenHandlerDefinition = $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc'));
32-
$tokenHandlerDefinition->replaceArgument(3, $config['claim']);
33-
$tokenHandlerDefinition->replaceArgument(4, $config['audience']);
30+
$tokenHandlerDefinition = $container->setDefinition($id, (new ChildDefinition('security.access_token_handler.oidc'))
31+
->replaceArgument(4, $config['claim'])
32+
->replaceArgument(5, $config['audience'])
33+
);
3434

35-
// Create the signature algorithm and the JWK
3635
if (!ContainerBuilder::willBeAvailable('web-token/jwt-core', Algorithm::class, ['symfony/security-bundle'])) {
37-
$container->register('security.access_token_handler.oidc.signature', 'stdClass')
36+
$container->register('security.access_token_handler.oidc.signature', Algorithm::class)
3837
->addError('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "web-token/jwt-core".');
39-
$container->register('security.access_token_handler.oidc.jwk', 'stdClass')
38+
$container->register('security.access_token_handler.oidc.jwk', JWK::class)
4039
->addError('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "web-token/jwt-core".');
40+
}
41+
42+
if (\in_array($config['algorithm'], ['ES256', 'ES384', 'ES512'], true)) {
43+
$tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature.'.$config['algorithm']));
4144
} else {
42-
$container->register('security.access_token_handler.oidc.signature', Algorithm::class)
43-
->setFactory([SignatureAlgorithmFactory::class, 'create'])
44-
->setArguments([$config['signature']['algorithm']]);
45-
$container->register('security.access_token_handler.oidc.jwk', JWK::class)
46-
->setFactory([JWK::class, 'createFromJson'])
47-
->setArguments([$config['signature']['key']]);
45+
$tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature')))
46+
->replaceArgument(0, $config['algorithm']);
4847
}
49-
$tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature'));
50-
$tokenHandlerDefinition->replaceArgument(1, new Reference('security.access_token_handler.oidc.jwk'));
48+
49+
$tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwk'))
50+
->replaceArgument(0, $config['key'])
51+
);
5152
}
5253

5354
public function getKey(): string
@@ -69,18 +70,13 @@ public function addConfiguration(NodeBuilder $node): void
6970
->info('Audience set in the token, for validation purpose.')
7071
->defaultNull()
7172
->end()
72-
->arrayNode('signature')
73+
->scalarNode('algorithm')
74+
->info('Algorithm used to sign the token.')
75+
->isRequired()
76+
->end()
77+
->scalarNode('key')
78+
->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
7379
->isRequired()
74-
->children()
75-
->scalarNode('algorithm')
76-
->info('Algorithm used to sign the token.')
77-
->isRequired()
78-
->end()
79-
->scalarNode('key')
80-
->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
81-
->isRequired()
82-
->end()
83-
->end()
8480
->end()
8581
->end()
8682
->end()

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SignatureAlgorithmFactory.php

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
1313

14-
use Jose\Component\Core\Algorithm as SignatureAlgorithm;
14+
use Jose\Component\Core\Algorithm as AlgorithmInterface;
1515
use Jose\Component\Signature\Algorithm;
1616
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
1717
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcTokenHandler;
@@ -23,29 +23,21 @@
2323
*/
2424
final class SignatureAlgorithmFactory
2525
{
26-
public static function create(string $algorithm): SignatureAlgorithm
26+
public static function create(string $algorithm): AlgorithmInterface
2727
{
2828
switch ($algorithm) {
2929
case 'ES256':
30-
if (!class_exists(Algorithm\ES256::class)) {
31-
throw new \LogicException('You cannot use the "ES256" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
32-
}
33-
34-
return new Algorithm\ES256();
3530
case 'ES384':
36-
if (!class_exists(Algorithm\ES384::class)) {
37-
throw new \LogicException('You cannot use the "ES384" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
38-
}
39-
40-
return new Algorithm\ES384();
4131
case 'ES512':
42-
if (!class_exists(Algorithm\ES512::class)) {
43-
throw new \LogicException('You cannot use the "ES512" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".');
32+
if (!class_exists(Algorithm::class.'\\'.$algorithm)) {
33+
throw new \LogicException(sprintf('You cannot use the "%s" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".', $algorithm));
4434
}
4535

46-
return new Algorithm\ES512();
47-
default:
48-
throw new InvalidArgumentException(sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
36+
$algorithm = Algorithm::class.'\\'.$algorithm;
37+
38+
return new $algorithm();
4939
}
40+
41+
throw new InvalidArgumentException(sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
5042
}
5143
}

src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -335,16 +335,8 @@
335335
</xsd:complexType>
336336

337337
<xsd:complexType name="oidc">
338-
<xsd:sequence>
339-
<xsd:choice minOccurs="1" maxOccurs="1">
340-
<xsd:element name="signature" type="oidc_signature" />
341-
</xsd:choice>
342-
</xsd:sequence>
343338
<xsd:attribute name="claim" type="xsd:string" />
344339
<xsd:attribute name="audience" type="xsd:string" />
345-
</xsd:complexType>
346-
347-
<xsd:complexType name="oidc_signature">
348340
<xsd:attribute name="algorithm" type="xsd:string" use="required" />
349341
<xsd:attribute name="key" type="xsd:string" use="required" />
350342
</xsd:complexType>

src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Jose\Component\Core\Algorithm;
15+
use Jose\Component\Core\JWK;
16+
use Jose\Component\Signature\Algorithm\ES256;
17+
use Jose\Component\Signature\Algorithm\ES384;
18+
use Jose\Component\Signature\Algorithm\ES512;
19+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory;
1420
use Symfony\Component\Security\Http\AccessToken\ChainAccessTokenExtractor;
1521
use Symfony\Component\Security\Http\AccessToken\FormEncodedBodyExtractor;
1622
use Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor;
@@ -62,10 +68,37 @@
6268
->abstract()
6369
->args([
6470
abstract_arg('signature algorithm'),
65-
abstract_arg('jwk'),
71+
abstract_arg('signature key'),
6672
service('logger')->nullOnInvalid(),
73+
service('clock'),
6774
'sub',
6875
null,
6976
])
77+
78+
-> F438 ;set('security.access_token_handler.oidc.jwk', JWK::class)
79+
->abstract()
80+
->factory([JWK::class, 'createFromJson'])
81+
->args([
82+
abstract_arg('signature key'),
83+
])
84+
85+
->set('security.access_token_handler.oidc.signature', Algorithm::class)
86+
->abstract()
87+
->factory([SignatureAlgorithmFactory::class, 'create'])
88+
->args([
89+
abstract_arg('signature algorithm'),
90+
])
91+
92+
->set('security.access_token_handler.oidc.signature.ES256', ES256::class)
93+
->parent('security.access_token_handler.oidc.signature')
94+
->args(['index_0' => 'ES256'])
95+
96+
->set('security.access_token_handler.oidc.signature.ES384', ES384::class)
97+
->parent('security.access_token_handler.oidc.signature')
98+
->args(['index_0' => 'ES384'])
99+
100+
->set('security.access_token_handler.oidc.signature.ES512', ES512::class)
101+
->parent('security.access_token_handler.oidc.signature')
102+
->args(['index_0' => 'ES512'])
70103
;
71104
};

src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ security:
2323
oidc:
2424
claim: 'username'
2525
audience: 'Symfony OIDC'
26-
signature:
27-
algorithm: 'ES256'
28-
# tip: use https://mkjwk.org/ to generate a JWK
29-
key: '{"kty":"EC","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo"}'
26+
algorithm: 'ES256'
27+
# tip: use https://mkjwk.org/ to generate a JWK
28+
key: '{"kty":"EC","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo"}'
3029
token_extractors: 'header'
3130
realm: 'My API'
3231

src/Symfony/Bundle/SecurityBundle/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"php": ">=8.1",
2020
"composer-runtime-api": ">=2.1",
2121
"ext-xml": "*",
22+
"symfony/clock": "^6.3",
2223
"symfony/config": "^6.1",
2324
"symfony/dependency-injection": "^6.2",
2425
"symfony/event-dispatcher": "^5.4|^6.0",
@@ -38,7 +39,7 @@
3839
"symfony/dom-crawler": "^5.4|^6.0",
3940
"symfony/expression-language": "^5.4|^6.0",
4041
"symfony/form": "^5.4|^6.0",
41-
"symfony/framework-bundle": "^5.4|^6.0",
42+
"symfony/framework-bundle": "^6.3",
4243
"symfony/ldap": "^5.4|^6.0",
4344
"symfony/process": "^5.4|^6.0",
4445
"symfony/rate-limiter": "^5.4|^6.0",
@@ -59,7 +60,7 @@
5960
"conflict": {
6061
"symfony/browser-kit": "<5.4",
6162
"symfony/console": "<5.4",
62-
"symfony/framework-bundle": "<5.4",
63+
"symfony/framework-bundle": "<6.3",
6364
"symfony/http-client": "<5.4",
6465
"symfony/ldap": "<5.4",
6566
"symfony/twig-bundle": "<5.4"

src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
use Jose\Component\Signature\JWSVerifier;
2121
use Jose\Component\Signature\Serializer\CompactSerializer;
2222
use Jose\Component\Signature\Serializer\JWSSerializerManager;
23+
use Psr\Clock\ClockInterface;
2324
use Psr\Log\LoggerInterface;
24-
use Symfony\Component\Clock\NativeClock;
25+
use Symfony\Component\Clock\Clock;
2526
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
2627
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
2728
use Symfony\Component\Security\Http\AccessToken\Oidc\Exception\InvalidSignatureException;
@@ -41,6 +42,7 @@ public function __construct(
4142
private Algorithm $signatureAlgorithm,
4243
private JWK $jwk,
4344
private ?LoggerInterface $logger = null,
45+
private ClockInterface $clock = new Clock(),
4446
private string $claim = 'sub',
4547
private ?string $audience = null
4648
) {
@@ -74,11 +76,10 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
7476
$headerCheckerManager->check($jws, 0);
7577

7678
// Verify the claims
77-
$clock = class_exists(NativeClock::class) ? new NativeClock() : null;
7879
$checkers = [
79-
new Checker\IssuedAtChecker(0, false, $clock),
80-
new Checker\NotBeforeChecker(0, false, $clock),
81-
new Checker\ExpirationTimeChecker(0, false, $clock),
80+
new Checker\IssuedAtChecker(0, false, $this->clock),
81+
new Checker\NotBeforeChecker(0, false, $this->clock),
82+
new Checker\ExpirationTimeChecker(0, false, $this->clock),
8283
];
8384
if ($this->audience) {
8485
$checkers[] = new Checker\AudienceChecker($this->audience);
@@ -93,7 +94,7 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
9394

9495
// UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
9596
return new UserBadge($claims[$this->claim], fn () => $this->createUser($claims), $claims);
96-
} catch (\Throwable $e) {
97+
} catch (\Exception $e) {
9798
$this->logger?->error('An error while decoding and validating the token.', [
9899
'error' => $e->getMessage(),
99100
'trace' => $e->getTraceAsString(),

src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
494 10000 9

5050
// UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
5151
return new UserBadge($claims[$this->claim], fn () => $this->createUser($claims), $claims);
52-
} catch (\Throwable $e) {
52+
} catch (\Exception $e) {
5353
$this->logger?->error('An error occurred on OIDC server.', [
5454
'error' => $e->getMessage(),
5555
'trace' => $e->getTraceAsString(),

src/Symfony/Component/Security/Http/Tests/AccessToken/Oidc/OidcTokenHandlerTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Jose\Component\Signature\Serializer\CompactSerializer;
1919
use PHPUnit\Framework\TestCase;
2020
use Psr\Log\LoggerInterface;
21+
use Symfony\Component\Clock\Clock;
2122
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
2223
use Symfony\Component\Security\Core\User\OidcUser;
2324
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcTokenHandler;
@@ -55,6 +56,7 @@ public function testGetsUserIdentifierFromSignedToken(string $claim, string $exp
5556
new ES256(),
5657
$this->getJWK(),
5758
$loggerMock,
59+
new Clock(),
5860
$claim,
5961
self::AUDIENCE
6062
))->getUserBadgeFrom($token);
@@ -88,6 +90,7 @@ public function testThrowsAnErrorIfTokenIsInvalid(string $token)
8890
new ES256(),
8991
$this->getJWK(),
9092
$loggerMock,
93+
new Clock(),
9194
'sub',
9295
self::AUDIENCE
9396
))->getUserBadgeFrom($token);
@@ -146,6 +149,7 @@ public function testThrowsAnErrorIfUserPropertyIsMissing()
146149
new ES256(),
147150
self::getJWK(),
148151
$loggerMock,
152+
new Clock(),
149153
'email',
150154
self::AUDIENCE
151155
))->getUserBadgeFrom($token);

src/Symfony/Component/Security/Http/composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
},
2727
"require-dev": {
2828
"symfony/cache": "^5.4|^6.0",
29+
"symfony/clock": "^6.3",
2930
"symfony/expression-language": "^5.4|^6.0",
3031
"symfony/http-client-contracts": "^3.0",
3132
"symfony/rate-limiter": "^5.4|^6.0",
@@ -37,6 +38,7 @@
3738
"web-token/jwt-signature-algorithm-ecdsa": "^3.1"
3839
},
3940
"conflict": {
41+
"symfony/clock": "<6.3",
4042
"symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9",
4143
"symfony/http-client-contracts": "<3.0",
4244
"symfony/security-bundle": "<5.4",

0 commit comments

Comments
 (0)
0