Skip to content

Commit 45f43fb

Browse files
Spomkychalasr
authored andcommitted
[Security] Support multiple signature algorithms and JWK/JWKSet for OIDC tokens
1 parent fd3ae6d commit 45f43fb

File tree

3 files changed

+37
-13
lines changed

3 files changed

+37
-13
lines changed

AccessToken/Oidc/OidcTokenHandler.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Jose\Component\Core\Algorithm;
1717
use Jose\Component\Core\AlgorithmManager;
1818
use Jose\Component\Core\JWK;
19+
use Jose\Component\Core\JWKSet;
1920
use Jose\Component\Signature\JWSTokenSupport;
2021
use Jose\Component\Signature\JWSVerifier;
2122
use Jose\Component\Signature\Serializer\CompactSerializer;
@@ -38,14 +39,22 @@ final class OidcTokenHandler implements AccessTokenHandlerInterface
3839
use OidcTrait;
3940

4041
public function __construct(
41-
private Algorithm $signatureAlgorithm,
42-
private JWK $jwk,
42+
private Algorithm|AlgorithmManager $signatureAlgorithm,
43+
private JWK|JWKSet $jwkset,
4344
private string $audience,
4445
private array $issuers,
4546
private string $claim = 'sub',
4647
private ?LoggerInterface $logger = null,
4748
private ClockInterface $clock = new Clock(),
4849
) {
50+
if ($signatureAlgorithm instanceof Algorithm) {
51+
trigger_deprecation('symfony/security-http', '7.1', 'First argument must be instance of %s, %s given.', AlgorithmManager::class, Algorithm::class);
52+
$this->signatureAlgorithm = new AlgorithmManager([$signatureAlgorithm]);
53+
}
54+
if ($jwkset instanceof JWK) {
55+
trigger_deprecation('symfony/security-http', '7.1', 'Second argument must be instance of %s, %s given.', JWKSet::class, JWK::class);
56+
$this->jwkset = new JWKSet([$jwkset]);
57+
}
4958
}
5059

5160
public function getUserBadgeFrom(string $accessToken): UserBadge
@@ -56,19 +65,19 @@ public function getUserBadgeFrom(string $accessToken): UserBadge
5665

5766
try {
5867
// Decode the token
59-
$jwsVerifier = new JWSVerifier(new AlgorithmManager([$this->signatureAlgorithm]));
68+
$jwsVerifier = new JWSVerifier($this->signatureAlgorithm);
6069
$serializerManager = new JWSSerializerManager([new CompactSerializer()]);
6170
$jws = $serializerManager->unserialize($accessToken);
6271
$claims = json_decode($jws->getPayload(), true);
6372

6473
// Verify the signature
65-
if (!$jwsVerifier->verifyWithKey($jws, $this->jwk, 0)) {
74+
if (!$jwsVerifier->verifyWithKeySet($jws, $this->jwkset, 0)) {
6675
throw new InvalidSignatureException();
6776
}
6877

6978
// Verify the headers
7079
$headerCheckerManager = new Checker\HeaderCheckerManager([
71-
new Checker\AlgorithmChecker([$this->signatureAlgorithm->name()]),
80+
new Checker\AlgorithmChecker($this->signatureAlgorithm->list()),
7281
], [
7382
new JWSTokenSupport(),
7483
]);

Tests/AccessToken/Oidc/OidcTokenHandlerTest.php

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

1414
use Jose\Component\Core\AlgorithmManager;
1515
use Jose\Component\Core\JWK;
16+
use Jose\Component\Core\JWKSet;
1617
use Jose\Component\Signature\Algorithm\ES256;
1718
use Jose\Component\Signature\JWSBuilder;
1819
use Jose\Component\Signature\Serializer\CompactSerializer;
@@ -53,8 +54,8 @@ public function testGetsUserIdentifierFromSignedToken(string $claim, string $exp
5354
$loggerMock->expects($this->never())->method('error');
5455

5556
$userBadge = (new OidcTokenHandler(
56-
new ES256(),
57-
$this->getJWK(),
57+
new AlgorithmManager([new ES256()]),
58+
$this->getJWKSet(),
5859
self::AUDIENCE,
5960
['https://www.example.com'],
6061
$claim,
@@ -87,8 +88,8 @@ public function testThrowsAnErrorIfTokenIsInvalid(string $token)
8788
$this->expectExceptionMessage('Invalid credentials.');
8889

8990
(new OidcTokenHandler(
90-
new ES256(),
91-
$this->getJWK(),
91+
new AlgorithmManager([new ES256()]),
92+
$this->getJWKSet(),
9293
self::AUDIENCE,
9394
['https://www.example.com'],
9495
'sub',
@@ -146,8 +147,8 @@ public function testThrowsAnErrorIfUserPropertyIsMissing()
146147
$this->expectExceptionMessage('Invalid credentials.');
147148

148149
(new OidcTokenHandler(
149-
new ES256(),
150-
self::getJWK(),
150+
new AlgorithmManager([new ES256()]),
151+
self::getJWKSet(),
151152
self::AUDIENCE,
152153
['https://www.example.com'],
153154
'email',
@@ -177,4 +178,18 @@ private static function getJWK(): JWK
177178
'd' => 'iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220',
178179
]);
179180
}
181+
182+
private static function getJWKSet(): JWKSet
183+
{
184+
return new JWKSet([
185+
new JWK([
186+
'kty' => 'EC',
187+
'crv' => 'P-256',
188+
'x' => 'FtgMtrsKDboRO-Zo0XC7tDJTATHVmwuf9GK409kkars',
189+
'y' => 'rWDE0ERU2SfwGYCo1DWWdgFEbZ0MiAXLRBBOzBgs_jY',
190+
'd' => '4G7bRIiKih0qrFxc0dtvkHUll19tTyctoCR3eIbOrO0',
191+
]),
192+
self::getJWK(),
193+
]);
194+
}
180195
}

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=8.2",
20+
"symfony/deprecation-contracts": "^2.5|^3",
2021
"symfony/http-foundation": "^6.4|^7.0",
2122
"symfony/http-kernel": "^6.4|^7.0",
2223
"symfony/polyfill-mbstring": "~1.0",
@@ -35,8 +36,7 @@
3536
"symfony/security-csrf": "^6.4|^7.0",
3637
"symfony/translation": "^6.4|^7.0",
3738
"psr/log": "^1|^2|^3",
38-
"web-token/jwt-checker": "^3.1",
39-
"web-token/jwt-signature-algorithm-ecdsa": "^3.1"
39+
"web-token/jwt-library": "^3.3.2"
4040
},
4141
"conflict": {
4242
"symfony/clock": "<6.4",

0 commit comments

Comments
 (0)