diff --git a/docs/index.md b/docs/index.md index 4843d2b4..c90f5798 100644 --- a/docs/index.md +++ b/docs/index.md @@ -75,6 +75,9 @@ For implementation into Symfony projects, please see [bundle documentation](basi # Whether to enable access token saving to persistence layer (default to true) persist_access_token: true + # Whether to revoke refresh tokens after they were used for all grant types (default to true) + revoke_refresh_tokens: true + resource_server: # Required # Full path to the public key file diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index c3566b79..063c9191 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -111,6 +111,10 @@ private function createAuthorizationServerNode(): NodeDefinition ->info('Define a custom ResponseType') ->defaultValue(null) ->end() + ->booleanNode('revoke_refresh_tokens') + ->info('Whether to revoke refresh tokens after they were used for all grant types') + ->defaultTrue() + ->end() ->end() ; diff --git a/src/DependencyInjection/LeagueOAuth2ServerExtension.php b/src/DependencyInjection/LeagueOAuth2ServerExtension.php index 1abf8809..e7b8c764 100644 --- a/src/DependencyInjection/LeagueOAuth2ServerExtension.php +++ b/src/DependencyInjection/LeagueOAuth2ServerExtension.php @@ -148,6 +148,10 @@ private function configureAuthorizationServer(ContainerBuilder $container, array $authorizationServer->replaceArgument(5, new Reference($config['response_type_class'])); } + $authorizationServer->addMethodCall('revokeRefreshTokens', [ + $config['revoke_refresh_tokens'], + ]); + if ($config['enable_client_credentials_grant']) { $authorizationServer->addMethodCall('enableGrantType', [ new Reference(ClientCredentialsGrant::class), diff --git a/tests/Unit/ExtensionTest.php b/tests/Unit/ExtensionTest.php index 15b0df9d..4fb34602 100644 --- a/tests/Unit/ExtensionTest.php +++ b/tests/Unit/ExtensionTest.php @@ -130,6 +130,30 @@ public function testDefaultScopeValidation(array $available, array $default, boo $this->addToAssertionCount(1); } + /** + * @dataProvider revokeRefreshTokensProvider + */ + public function testEnablingAndDisablingRevocationOfRefreshTokens(bool $shouldRevokeRefreshTokens): void + { + $container = new ContainerBuilder(); + $extension = new LeagueOAuth2ServerExtension(); + + $extension->load($this->getValidConfiguration(['revoke_refresh_tokens' => $shouldRevokeRefreshTokens]), $container); + + $authorizationServer = $container->findDefinition(AuthorizationServer::class); + $methodCalls = $authorizationServer->getMethodCalls(); + $revokeRefreshTokens = null; + + foreach ($methodCalls as $methodCall) { + if ('revokeRefreshTokens' === $methodCall[0]) { + $revokeRefreshTokens = $methodCall[1][0]; + break; + } + } + + $this->assertSame($shouldRevokeRefreshTokens, $revokeRefreshTokens); + } + public function scopeProvider(): iterable { yield 'when a default scope is part of available scopes' => [ @@ -155,6 +179,7 @@ private function getValidConfiguration(array $options = []): array 'enable_client_credentials_grant' => $options['enable_client_credentials_grant'] ?? true, 'enable_password_grant' => $options['enable_password_grant'] ?? true, 'enable_refresh_token_grant' => $options['enable_refresh_token_grant'] ?? true, + 'revoke_refresh_tokens' => $options['revoke_refresh_tokens'] ?? true, ], 'resource_server' => [ 'public_key' => 'foo', @@ -175,6 +200,12 @@ private function getValidConfiguration(array $options = []): array ]; } + public function revokeRefreshTokensProvider(): iterable + { + yield 'do revoke refresh tokens' => [true]; + yield 'do not revoke refresh tokens' => [false]; + } + private function setupContainer(ContainerBuilder $container): void { $container->register(ScopeManager::class);