From 7ead1dc7a88abd62dbe4ca55a2a89624fb030155 Mon Sep 17 00:00:00 2001 From: Oinkling Date: Thu, 8 Aug 2024 09:07:20 +0200 Subject: [PATCH 1/4] feat: Allows Key instance to be used for encoding Allows an instance of Key to be passed as the second argument to JWT::encode and JWT::sign, making $alg optional. --- src/JWT.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/JWT.php b/src/JWT.php index e9d75639..cb3579ab 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -185,8 +185,8 @@ public static function decode( * Converts and signs a PHP array into a JWT string. * * @param array $payload PHP array - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', + * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate|Key $key The secret key. + * @param ?string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * @param string $keyId * @param array $head An array with header elements to attach @@ -199,10 +199,16 @@ public static function decode( public static function encode( array $payload, $key, - string $alg, + ?string $alg = null, string $keyId = null, array $head = null ): string { + if (is_a($key, Key::class)) { + $alg = $key->getAlgorithm(); + $key = $key->getKeyMaterial(); + } elseif ($alg === null) { + throw new InvalidArgumentException('alg cannot be null unless key is instance of Key'); + } $header = ['typ' => 'JWT']; if (isset($head) && \is_array($head)) { $header = \array_merge($header, $head); @@ -226,8 +232,8 @@ public static function encode( * Sign a string with a given key and algorithm. * * @param string $msg The message to sign - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', + * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate|Key $key The secret key. + * @param ?string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * * @return string An encrypted message @@ -237,8 +243,14 @@ public static function encode( public static function sign( string $msg, $key, - string $alg + ?string $alg = null ): string { + if (is_a($key, Key::class)) { + $alg = $key->getAlgorithm(); + $key = $key->getKeyMaterial(); + } elseif ($alg === null) { + throw new InvalidArgumentException('alg cannot be null unless key is instance of Key'); + } if (empty(static::$supported_algs[$alg])) { throw new DomainException('Algorithm not supported'); } From 6b4141780c6a382e99abe1f011b2c713b54acb98 Mon Sep 17 00:00:00 2001 From: Oinkling Date: Tue, 29 Apr 2025 08:30:17 +0200 Subject: [PATCH 2/4] Enforce null alg when key is Key object --- src/JWT.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/JWT.php b/src/JWT.php index 1130b15e..3b96320d 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -193,17 +193,22 @@ public static function decode( * * @return string A signed JWT * + * @throws InvalidArgumentException + * * @uses jsonEncode * @uses urlsafeB64Encode */ public static function encode( array $payload, $key, - ?string $alg, + ?string $alg = null, ?string $keyId = null, ?array $head = null ): string { - if (is_a($key, Key::class)) { + if ($key instanceof Key) { + if ($alg !== null) { + throw new InvalidArgumentException('If key is instance of Key alg must be null'); + } $alg = $key->getAlgorithm(); $key = $key->getKeyMaterial(); } elseif ($alg === null) { From e78315de7b5f1ad61f9bed5e5d20f352e7da66fd Mon Sep 17 00:00:00 2001 From: Oinkling Date: Tue, 29 Apr 2025 08:30:32 +0200 Subject: [PATCH 3/4] Added tests for encoding with Key object --- tests/JWTTest.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/JWTTest.php b/tests/JWTTest.php index d09d43e3..95184940 100644 --- a/tests/JWTTest.php +++ b/tests/JWTTest.php @@ -546,4 +546,27 @@ public function testAdditionalHeaderOverrides() $this->assertEquals('my_key_id', $headers->kid, 'key param not overridden'); $this->assertEquals('HS256', $headers->alg, 'alg param not overridden'); } + + public function testNullAlgFails() + { + $this->expectException(InvalidArgumentException::class); + JWT::encode(['message' => 'abc'], 'my_key'); + } + + public function testEncodingWithKeyObject() + { + $payload = [ + 'message' => 'abc' + ]; + $key = new Key('my_key', 'HS256'); + $encoded = JWT::encode($payload, $key); + $decoded = JWT::decode($encoded, $key); + $this->assertSame($decoded->message, 'abc'); + } + + public function testNotNullAlgWhenUsingKeyObjectFails() + { + $this->expectException(InvalidArgumentException::class); + JWT::encode(['message' => 'abc'], new Key('my_key', 'HS256'), 'HS256'); + } } From e9cace21685dc7bdd5657efaa862dd27305c8625 Mon Sep 17 00:00:00 2001 From: Oinkling Date: Tue, 29 Apr 2025 09:22:42 +0200 Subject: [PATCH 4/4] Enforce null alg when key is Key object in sign --- src/JWT.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/JWT.php b/src/JWT.php index 3b96320d..c44e5ca2 100644 --- a/src/JWT.php +++ b/src/JWT.php @@ -244,6 +244,7 @@ public static function encode( * @return string An encrypted message * * @throws DomainException Unsupported algorithm or bad key was specified + * @throws InvalidArgumentException */ public static function sign( string $msg, @@ -251,6 +252,9 @@ public static function sign( ?string $alg = null ): string { if (is_a($key, Key::class)) { + if ($alg !== null) { + throw new InvalidArgumentException('If key is instance of Key alg must be null'); + } $alg = $key->getAlgorithm(); $key = $key->getKeyMaterial(); } elseif ($alg === null) {