From 492d1da53dfaf88a9aa2164bcbe00c27773f5ce7 Mon Sep 17 00:00:00 2001 From: Frederic Marchal Date: Sat, 2 Nov 2024 09:49:48 +0100 Subject: [PATCH 1/2] Ignore token not having the getCredentials() method during decode The TokenInterface doesn't have a getCredentials() method anymore since Symfony 6. Beside, some tokens, such as TestBrowserToken returns null. Decoding a jwt token is only meaningful when the token is a JWTPostAuthenticationToken. Any other token can be ignored. --- Services/JWTManager.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Services/JWTManager.php b/Services/JWTManager.php index 0266b584..28ecdc98 100644 --- a/Services/JWTManager.php +++ b/Services/JWTManager.php @@ -10,6 +10,7 @@ use Lexik\Bundle\JWTAuthenticationBundle\Events; use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException; use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTEncodeFailureException; +use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\Token\JWTPostAuthenticationToken; use Lexik\Bundle\JWTAuthenticationBundle\Services\PayloadEnrichment\NullEnrichment; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -98,6 +99,16 @@ private function generateJwtStringAndDispatchEvents(UserInterface $user, array $ */ public function decode(TokenInterface $token): array|bool { + if (!$token instanceof JWTPostAuthenticationToken) { + /* + * TokenInterface::getCredentials() was deprecated in Symfony 5.4 and removed in Symfony 6. + * Because tokens should no longer contain credentials (as they represent authenticated sessions). + * https://github.com/symfony/symfony/commit/922c1314bd73f14fb8aa80e62b27b3bca66ee7b0#diff-24aa6aa94d3f4a8aed52d78f81f217b60fd69dccb575357844c1250a61153a34 + */ + @trigger_deprecation("lexik/jwt-authentication-bundle", "3.2", "Not passing a JWTPostAuthenticationToken to JWTManager::decode() is deprecated"); + + return false; + } if (!($payload = $this->jwtEncoder->decode($token->getCredentials()))) { return false; } From 1a09a468bd7028becbce2c8c5726be56a8cdc173 Mon Sep 17 00:00:00 2001 From: Frederic Marchal Date: Sat, 2 Nov 2024 10:12:38 +0100 Subject: [PATCH 2/2] Add unit tests. --- Tests/Services/JWTManagerTest.php | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Tests/Services/JWTManagerTest.php b/Tests/Services/JWTManagerTest.php index d14766e4..9cb8883d 100644 --- a/Tests/Services/JWTManagerTest.php +++ b/Tests/Services/JWTManagerTest.php @@ -12,6 +12,8 @@ use Lexik\Bundle\JWTAuthenticationBundle\Services\PayloadEnrichmentInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\TestBrowserToken; +use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; @@ -137,6 +139,56 @@ public function testDecode() $this->assertSame(['foo' => 'bar'], $manager->decode($this->getJWTUserTokenMock())); } + /** + * test decode a TokenInterface without getCredentials. + */ + public function testDecode_noGetCredentials() + { + $dispatcher = $this->getEventDispatcherMock(); + $dispatcher + ->expects($this->never()) + ->method('dispatch') + ->with( + $this->isInstanceOf(JWTDecodedEvent::class), + $this->equalTo(Events::JWT_DECODED) + ); + + $encoder = $this->getJWTEncoderMock(); + $encoder + ->expects($this->never()) + ->method('decode') + ->willReturn(['foo' => 'bar']); + + $token = new NullToken(); + $manager = new JWTManager($encoder, $dispatcher, 'username'); + $this->assertFalse($manager->decode($token)); + } + + /** + * test decode a TokenInterface with getCredentials returning null. + */ + public function testDecode_nullGetCredentials() + { + $dispatcher = $this->getEventDispatcherMock(); + $dispatcher + ->expects($this->never()) + ->method('dispatch') + ->with( + $this->isInstanceOf(JWTDecodedEvent::class), + $this->equalTo(Events::JWT_DECODED) + ); + + $encoder = $this->getJWTEncoderMock(); + $encoder + ->expects($this->never()) + ->method('decode') + ->willReturn(['foo' => 'bar']); + + $token = new TestBrowserToken(); + $manager = new JWTManager($encoder, $dispatcher, 'username'); + $this->assertFalse($manager->decode($token)); + } + public function testParse() { $dispatcher = $this->getEventDispatcherMock();