diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 702ce63b..d7e25b59 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: php: ['7.4', '8.0', '8.1', '8.2'] - symfony: ['4.4.*', '5.4.*', '6.0.*', '6.1.*', '6.2.*', '6.3.*'] + symfony: ['4.4.*', '5.4.*', '6.0.*', '6.1.*', '6.2.*', '6.3.*', '6.4.*', '7.0.*'] composer-flags: ['--prefer-stable'] can-fail: [false] extensions: ['curl, iconv, mbstring, mongodb, pdo, pdo_sqlite, sqlite, zip'] @@ -29,18 +29,28 @@ jobs: symfony: '6.2.*' - php: '7.4' symfony: '6.3.*' + - php: '7.4' + symfony: '6.4.*' + - php: '7.4' + symfony: '7.0.*' - php: '8.0' symfony: '6.1.*' - php: '8.0' symfony: '6.2.*' - php: '8.0' symfony: '6.3.*' + - php: '8.0' + symfony: '6.4.*' + - php: '8.0' + symfony: '7.0.*' + - php: '8.1' + symfony: '7.0.*' name: "PHP ${{ matrix.php }} - Symfony ${{ matrix.symfony }}${{ matrix.composer-flags != '' && format(' - Composer {0}', matrix.composer-flags) || '' }}" steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache dependencies uses: actions/cache@v3 @@ -63,8 +73,8 @@ jobs: version: '5.0' topology: server - - name: Remove Guard - if: contains(fromJSON('["6.0.*", "6.1.*", "6.2.*", "6.3.*"]'), matrix.symfony) + - name: Remove Guard (Symfony >=6.0) + if: contains(fromJSON('["6.0.*", "6.1.*", "6.2.*", "6.3.*", "6.4.*", "7.0.*"]'), matrix.symfony) run: composer remove --dev --no-update symfony/security-guard - name: Install dependencies diff --git a/Security/Exception/InvalidTokenException.php b/Security/Exception/InvalidTokenException.php index 6aa8129a..fec95dad 100644 --- a/Security/Exception/InvalidTokenException.php +++ b/Security/Exception/InvalidTokenException.php @@ -15,10 +15,7 @@ class InvalidTokenException extends AuthenticationException { - /** - * @return string - */ - public function getMessageKey() + public function getMessageKey(): string { return 'Invalid JWT Refresh Token'; } diff --git a/Security/Exception/MissingTokenException.php b/Security/Exception/MissingTokenException.php index 409385ee..2c63c4d8 100644 --- a/Security/Exception/MissingTokenException.php +++ b/Security/Exception/MissingTokenException.php @@ -15,10 +15,7 @@ class MissingTokenException extends AuthenticationException { - /** - * @return string - */ - public function getMessageKey() + public function getMessageKey(): string { return 'Missing JWT Refresh Token'; } diff --git a/Security/Exception/TokenNotFoundException.php b/Security/Exception/TokenNotFoundException.php index 519cb17b..5d4834f3 100644 --- a/Security/Exception/TokenNotFoundException.php +++ b/Security/Exception/TokenNotFoundException.php @@ -15,10 +15,7 @@ class TokenNotFoundException extends AuthenticationException { - /** - * @return string - */ - public function getMessageKey() + public function getMessageKey(): string { return 'JWT Refresh Token Not Found'; } diff --git a/Security/Provider/RefreshTokenProvider.php b/Security/Provider/RefreshTokenProvider.php index 66793e34..f6a1d106 100644 --- a/Security/Provider/RefreshTokenProvider.php +++ b/Security/Provider/RefreshTokenProvider.php @@ -21,10 +21,56 @@ trigger_deprecation('gesdinet/jwt-refresh-token-bundle', '1.0', 'The "%s" class is deprecated, configure the user provider for the `refresh_jwt` authenticator instead.', RefreshTokenProvider::class); +if ((new \ReflectionClass(UserProviderInterface::class))->getMethod('supportsClass')->hasReturnType()) { + /** + * Compatibility layer for Symfony 7.0 and later, where {@see UserProviderInterface::supportsClass()} has a return type. + * + * @internal + */ + abstract class CompatRefreshTokenProvider implements UserProviderInterface + { + /** + * @param class-string $class + */ + public function supportsClass(string $class): bool + { + return $this->doSupportsClass($class); + } + + /** + * @param class-string $class + */ + abstract protected function doSupportsClass(string $class): bool; + } +} else { + /** + * Compatibility layer for Symfony 6.4 and earlier, where {@see UserProviderInterface::supportsClass()} does not have a return type. + * + * @internal + */ + abstract class CompatRefreshTokenProvider implements UserProviderInterface + { + /** + * @param class-string $class + * + * @return bool + */ + public function supportsClass($class) + { + return $this->doSupportsClass($class); + } + + /** + * @param class-string $class + */ + abstract protected function doSupportsClass(string $class): bool; + } +} + /** * @deprecated configure the user provider for the `refresh_jwt` authenticator instead */ -class RefreshTokenProvider implements UserProviderInterface +class RefreshTokenProvider extends CompatRefreshTokenProvider { /** * @var RefreshTokenManagerInterface @@ -102,10 +148,7 @@ public function loadUserByIdentifier(string $identifier): UserInterface ); } - /** - * @return UserInterface - */ - public function refreshUser(UserInterface $user) + public function refreshUser(UserInterface $user): UserInterface { if (null !== $this->customUserProvider) { return $this->customUserProvider->refreshUser($user); @@ -116,10 +159,8 @@ public function refreshUser(UserInterface $user) /** * @param class-string $class - * - * @return bool */ - public function supportsClass($class) + protected function doSupportsClass(string $class): bool { if (null !== $this->customUserProvider) { return $this->customUserProvider->supportsClass($class); diff --git a/Tests/Unit/EventListener/AttachRefreshTokenOnSuccessListenerTest.php b/Tests/Unit/EventListener/AttachRefreshTokenOnSuccessListenerTest.php index a71e15f9..7b8e898d 100644 --- a/Tests/Unit/EventListener/AttachRefreshTokenOnSuccessListenerTest.php +++ b/Tests/Unit/EventListener/AttachRefreshTokenOnSuccessListenerTest.php @@ -298,6 +298,10 @@ public function testAttachesTheTokenToTheResponseBodyOnCredentialsAuth() public function testDoesNothingWhenThereIsNotAUser() { + if ((new \ReflectionClass(AuthenticationSuccessEvent::class))->getMethod('getUser')->hasReturnType()) { + $this->markTestSkipped(sprintf('%s::getUser() has a non-nullable return type in LexikJWTAuthenticationBundle 3.x', AuthenticationSuccessEvent::class)); + } + /** @var AuthenticationSuccessEvent|MockObject $event */ $event = $this->createMock(AuthenticationSuccessEvent::class); diff --git a/Tests/Unit/Request/Extractor/RequestCookieExtractorTest.php b/Tests/Unit/Request/Extractor/RequestCookieExtractorTest.php index 8f6b1be7..d47695e3 100644 --- a/Tests/Unit/Request/Extractor/RequestCookieExtractorTest.php +++ b/Tests/Unit/Request/Extractor/RequestCookieExtractorTest.php @@ -6,6 +6,7 @@ use Gesdinet\JWTRefreshTokenBundle\Request\Extractor\RequestCookieExtractor; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\InputBag; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; @@ -29,13 +30,9 @@ public function testGetsTheTokenFromTheRequestCookies(): void { $token = 'my-refresh-token'; - /** @var ParameterBag|MockObject $cookieBag */ - $cookieBag = $this->createMock(ParameterBag::class); - $cookieBag - ->expects($this->once()) - ->method('get') - ->with(self::PARAMETER_NAME) - ->willReturn($token); + /** @var ParameterBag|InputBag $cookieBag */ + $cookieBag = class_exists(InputBag::class) ? new InputBag() : new ParameterBag(); + $cookieBag->set(self::PARAMETER_NAME, $token); /** @var Request|MockObject $request */ $request = $this->createMock(Request::class); diff --git a/composer.json b/composer.json index 258beeba..141d9b03 100644 --- a/composer.json +++ b/composer.json @@ -34,11 +34,11 @@ "doctrine/cache": "^1.11|^2.0", "doctrine/mongodb-odm": "^2.2", "doctrine/orm": "^2.7", - "matthiasnoback/symfony-config-test": "^4.2", - "matthiasnoback/symfony-dependency-injection-test": "^4.2", + "matthiasnoback/symfony-config-test": "^4.2|^5.0", + "matthiasnoback/symfony-dependency-injection-test": "^4.2|^5.0", "phpunit/phpunit": "^9.5", "symfony/cache": "^4.4|^5.4|^6.0|^7.0", - "symfony/security-guard": "^4.4|^5.4|^6.0|^7.0" + "symfony/security-guard": "^4.4|^5.4" }, "conflict": { "doctrine/mongodb-odm": "<2.2",