diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 8a0ef0f8..3cfa1385 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -27,6 +27,11 @@ jobs: dependency-versions: "lowest" composer-stable: "1" can-fail: false + - php: "8.4" + symfony: "8.0.*" + doctrine-orm: "^3.0" + composer-flags: '' + can-fail: true exclude: - php: "8.1" symfony: "7.1.*" diff --git a/composer.json b/composer.json index e27ca9d2..5589c0c6 100644 --- a/composer.json +++ b/composer.json @@ -22,11 +22,11 @@ "nyholm/psr7": "^1.4", "psr/http-factory": "^1.0", "symfony/deprecation-contracts": "^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/psr-http-message-bridge": "^6.4|^7", - "symfony/security-bundle": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/psr-http-message-bridge": "^6.4|^7.0|^8.0", + "symfony/security-bundle": "^6.4|^7.0|^8.0" }, "require-dev": { "ext-pdo": "*", @@ -36,8 +36,8 @@ "php-cs-fixer/shim": "^3.38", "phpstan/phpstan": "^2.1", "phpstan/phpstan-symfony": "2.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/phpunit-bridge": "^7.2" + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/phpunit-bridge": "^7.3" }, "conflict": { "doctrine/doctrine-bundle": "<2.8.0", diff --git a/src/DependencyInjection/LeagueOAuth2ServerExtension.php b/src/DependencyInjection/LeagueOAuth2ServerExtension.php index 03750dd8..9b77bbff 100644 --- a/src/DependencyInjection/LeagueOAuth2ServerExtension.php +++ b/src/DependencyInjection/LeagueOAuth2ServerExtension.php @@ -49,11 +49,9 @@ final class LeagueOAuth2ServerExtension extends Extension implements PrependExtensionInterface, CompilerPassInterface { /** - * @return void - * * @throws \Exception */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config')); $loader->load('services.php'); @@ -90,10 +88,7 @@ public function getAlias(): string return 'league_oauth2_server'; } - /** - * @return void - */ - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { // If no doctrine connection is configured, the DBAL connection should // be left alone as adding any configuration setting with no connection @@ -114,10 +109,7 @@ public function prepend(ContainerBuilder $container) ]); } - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $this->assertRequiredBundlesAreEnabled($container); } diff --git a/src/LeagueOAuth2ServerBundle.php b/src/LeagueOAuth2ServerBundle.php index 050b9069..6d714f1c 100644 --- a/src/LeagueOAuth2ServerBundle.php +++ b/src/LeagueOAuth2ServerBundle.php @@ -17,10 +17,7 @@ final class LeagueOAuth2ServerBundle extends Bundle { - /** - * @return void - */ - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); diff --git a/src/Security/Authenticator/OAuth2Authenticator.php b/src/Security/Authenticator/OAuth2Authenticator.php index d986633f..270f049b 100644 --- a/src/Security/Authenticator/OAuth2Authenticator.php +++ b/src/Security/Authenticator/OAuth2Authenticator.php @@ -16,6 +16,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\AttributesBasedUserProviderInterface; +use Symfony\Component\Security\Core\User\ChainUserProvider; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; @@ -70,6 +72,14 @@ public function authenticate(Request $request): Passport /** @var string $userIdentifier */ $userIdentifier = $psr7Request->getAttribute('oauth_user_id', ''); + if ('' === $userIdentifier) { + /** + * BC layer for Symfony < 8.0 + */ + if (is_a(ChainUserProvider::class, AttributesBasedUserProviderInterface::class, true)) { + throw OAuth2AuthenticationFailedException::create('The access token has either an empty or missing "oauth_user_id" attribute.'); + } + } /** @var string $accessTokenId */ $accessTokenId = $psr7Request->getAttribute('oauth_access_token_id'); @@ -81,7 +91,10 @@ public function authenticate(Request $request): Passport $oauthClientId = $psr7Request->getAttribute('oauth_client_id', ''); $userLoader = function (string $userIdentifier) use ($oauthClientId): UserInterface { - if ('' === $userIdentifier || $oauthClientId === $userIdentifier) { + if ( + $oauthClientId === $userIdentifier + || ('' === $userIdentifier && is_a(ChainUserProvider::class, AttributesBasedUserProviderInterface::class, true)) // BC layer for Symfony < 8.0 + ) { return new ClientCredentialsUser($oauthClientId); } diff --git a/tests/Unit/OAuth2AuthenticatorTest.php b/tests/Unit/OAuth2AuthenticatorTest.php index 80e30d9f..f8f94b9c 100644 --- a/tests/Unit/OAuth2AuthenticatorTest.php +++ b/tests/Unit/OAuth2AuthenticatorTest.php @@ -97,6 +97,8 @@ public function testAuthenticateCreatePassportWithClientCredentialsUser(): void { $serverRequest = (new ServerRequest('GET', '/foo')) ->withAttribute('oauth_access_token_id', 'accessTokenId') + ->withAttribute('oauth_user_id', 'clientId') + ->withAttribute('oauth_client_id', 'clientId') ; $httpMessageFactory = $this->createMock(HttpMessageFactoryInterface::class);