Skip to content

Commit c5d7967

Browse files
committed
Add forward compat layer for changed logout listener configuration in 2.x
1 parent e63319d commit c5d7967

File tree

9 files changed

+404
-15
lines changed

9 files changed

+404
-15
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Changelog
22

3-
## Unreleased
3+
## 1.2.0
4+
5+
- [B/C Break] Removed the `Gesdinet\JWTRefreshTokenBundle\EventListener\LogoutEventListener` service definition; if needed, an abstract `gesdinet_jwt_refresh_token.security.listener.logout` definition replaces it and does not have a `kernel.event_listener` tag
6+
- [B/C Break] The `logout_firewall` config node default value is now null
7+
- Deprecated the `logout_firewall` config node, the `invalidate_token_on_logout` option should be set on the `refresh_jwt` authenticator
8+
9+
## 1.1.0
410

511
- [B/C Break] Changed the object mappings to mapped superclasses, this requires updating your app's configuration
612
- Added support for checking the request path in the `refresh_jwt` authenticator

DependencyInjection/Configuration.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ public function getConfigTreeBuilder(): TreeBuilder
109109
->end()
110110
->end()
111111
->scalarNode('logout_firewall')
112-
->defaultValue('api')
113-
->info('Name of the firewall that triggers the logout event to hook into (default: api)')
112+
->setDeprecated(...$this->getDeprecationParameters('The "%node%" node is deprecated, enable the "invalidate_token_on_logout" option on the "refresh_jwt" firewall instead.', '1.2'))
113+
->defaultNull()
114+
->info('Name of the firewall that triggers the logout event to hook into')
114115
->end()
115116
->scalarNode('return_expiration')
116117
->defaultFalse()

DependencyInjection/GesdinetJWTRefreshTokenExtension.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616
use Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken as RefreshTokenDocument;
1717
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as RefreshTokenEntity;
1818
use Gesdinet\JWTRefreshTokenBundle\Request\Extractor\ExtractorInterface;
19+
use Symfony\Component\DependencyInjection\ChildDefinition;
1920
use Symfony\Component\DependencyInjection\ContainerBuilder;
2021
use Symfony\Component\Config\FileLocator;
22+
use Symfony\Component\DependencyInjection\Parameter;
2123
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
2224
use Symfony\Component\DependencyInjection\Loader;
25+
use Symfony\Component\Security\Http\Event\LogoutEvent;
2326

2427
class GesdinetJWTRefreshTokenExtension extends Extension
2528
{
@@ -48,6 +51,12 @@ public function load(array $configs, ContainerBuilder $container): void
4851
$container->setParameter('gesdinet_jwt_refresh_token.return_expiration', $config['return_expiration']);
4952
$container->setParameter('gesdinet_jwt_refresh_token.return_expiration_parameter_name', $config['return_expiration_parameter_name']);
5053

54+
if ($config['logout_firewall']) {
55+
$container->setDefinition('gesdinet_jwt_refresh_token.security.listener.logout.legacy_config', new ChildDefinition('gesdinet_jwt_refresh_token.security.listener.logout'))
56+
->addArgument(new Parameter('gesdinet_jwt_refresh_token.logout_firewall_context'))
57+
->addTag('kernel.event_listener', ['event' => LogoutEvent::class, 'method' => 'onLogout']);
58+
}
59+
5160
$refreshTokenClass = RefreshTokenEntity::class;
5261
$objectManager = 'doctrine.orm.entity_manager';
5362

DependencyInjection/Security/Factory/RefreshTokenAuthenticatorFactory.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Parameter;
1919
use Symfony\Component\DependencyInjection\Reference;
20+
use Symfony\Component\Security\Http\Event\LogoutEvent;
2021

2122
final class RefreshTokenAuthenticatorFactory implements AuthenticatorFactoryInterface
2223
{
@@ -51,6 +52,10 @@ public function addConfiguration(NodeDefinition $builder): void
5152
->scalarNode('provider')->end()
5253
->scalarNode('success_handler')->end()
5354
->scalarNode('failure_handler')->end()
55+
->booleanNode('invalidate_token_on_logout')
56+
->defaultFalse() // TODO - Enable by default in 2.0.
57+
->info('When enabled, the refresh token will be invalided on logout.')
58+
->end()
5459
/*
5560
->integerNode('ttl')
5661
->defaultNull()
@@ -87,6 +92,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
8792
->replaceArgument(5, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)))
8893
->replaceArgument(6, $options);
8994

95+
if ($config['invalidate_token_on_logout']) {
96+
$container->setDefinition('gesdinet_jwt_refresh_token.security.listener.logout.'.$firewallName, new ChildDefinition('gesdinet_jwt_refresh_token.security.listener.logout'))
97+
->addTag('kernel.event_listener', ['event' => LogoutEvent::class, 'method' => 'onLogout', 'dispatcher' => 'security.event_dispatcher.'.$firewallName]);
98+
}
99+
90100
return $authenticatorId;
91101
}
92102

EventListener/LogoutEventListener.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ class LogoutEventListener
2222
private ExtractorInterface $refreshTokenExtractor;
2323
private string $tokenParameterName;
2424
private array $cookieSettings;
25-
private string $logout_firewall_context;
25+
26+
/**
27+
* @deprecated to be removed in 2.0
28+
*/
29+
private ?string $logoutFirewallContext;
2630

2731
public function __construct(
2832
RefreshTokenManagerInterface $refreshTokenManager,
2933
ExtractorInterface $refreshTokenExtractor,
3034
string $tokenParameterName,
3135
array $cookieSettings,
32-
string $logout_firewall_context
36+
?string $logoutFirewallContext = null
3337
) {
3438
$this->refreshTokenManager = $refreshTokenManager;
3539
$this->refreshTokenExtractor = $refreshTokenExtractor;
@@ -43,15 +47,24 @@ public function __construct(
4347
'secure' => true,
4448
'remove_token_from_body' => true,
4549
], $cookieSettings);
46-
$this->logout_firewall_context = $logout_firewall_context;
50+
$this->logoutFirewallContext = $logoutFirewallContext;
51+
52+
if (null !== $logoutFirewallContext) {
53+
trigger_deprecation('gesdinet/jwt-refresh-token-bundle', '1.2', 'Passing the logout firewall context to "%s" is deprecated and will not be supported in 2.0.', self::class);
54+
}
4755
}
4856

4957
public function onLogout(LogoutEvent $event): void
5058
{
5159
$request = $event->getRequest();
52-
$current_firewall_context = $request->attributes->get('_firewall_context');
5360

54-
if ($current_firewall_context !== $this->logout_firewall_context) {
61+
/*
62+
* This listener should only act in one of two conditions:
63+
*
64+
* 1) If the firewall context is not configured (this implies the listener is registered to the firewall specific event dispatcher)
65+
* 2) (Deprecated) The request's firewall context matches the configured firewall context
66+
*/
67+
if (null !== $this->logoutFirewallContext && $request->attributes->get('_firewall_context') !== $this->logoutFirewallContext) {
5568
return;
5669
}
5770

Resources/config/services.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,13 @@
170170
])
171171
->tag('console.command');
172172

173-
$services->set(LogoutEventListener::class)
173+
$services->set('gesdinet_jwt_refresh_token.security.listener.logout')
174+
->abstract()
175+
->class(LogoutEventListener::class)
174176
->args([
175177
new Reference('gesdinet.jwtrefreshtoken.refresh_token_manager'),
176178
new Reference('gesdinet.jwtrefreshtoken.request.extractor.chain'),
177179
new Parameter('gesdinet_jwt_refresh_token.token_parameter_name'),
178180
new Parameter('gesdinet_jwt_refresh_token.cookie'),
179-
new Parameter('gesdinet_jwt_refresh_token.logout_firewall_context'),
180-
])
181-
->tag('kernel.event_listener', [
182-
'event' => 'Symfony\Component\Security\Http\Event\LogoutEvent',
183-
'method' => 'onLogout',
184181
]);
185182
};

Tests/Functional/DependencyInjection/GesdinetJWTRefreshTokenExtensionTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken as RefreshTokenDocument;
77
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as RefreshTokenEntity;
88
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
9+
use Symfony\Component\Security\Http\Event\LogoutEvent;
910

1011
final class GesdinetJWTRefreshTokenExtensionTest extends AbstractExtensionTestCase
1112
{
@@ -40,10 +41,13 @@ public function test_container_is_loaded_with_default_configuration(): void
4041
'remove_token_from_body' => true,
4142
],
4243
);
44+
$this->assertContainerBuilderHasParameter('gesdinet_jwt_refresh_token.logout_firewall_context', 'security.firewall.map.context.');
4345

4446
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.refresh_token.class', RefreshTokenEntity::class);
4547
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.object_manager.id', 'doctrine.orm.entity_manager');
4648
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.user_checker.id', 'security.user_checker');
49+
50+
$this->assertContainerBuilderNotHasService('gesdinet_jwt_refresh_token.security.listener.logout.legacy_config');
4751
}
4852

4953
public function test_container_is_loaded_with_custom_configuration(): void
@@ -91,10 +95,13 @@ public function test_container_is_loaded_with_custom_configuration(): void
9195
'remove_token_from_body' => true,
9296
],
9397
);
98+
$this->assertContainerBuilderHasParameter('gesdinet_jwt_refresh_token.logout_firewall_context', 'security.firewall.map.context.');
9499

95100
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.refresh_token.class', RefreshTokenDocument::class);
96101
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.object_manager.id', 'doctrine_mongodb.odm.document_manager');
97102
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.user_checker.id', 'my.user_checker');
103+
104+
$this->assertContainerBuilderNotHasService('gesdinet_jwt_refresh_token.security.listener.logout.legacy_config');
98105
}
99106

100107
public function test_container_is_loaded_with_deprecated_parameters(): void
@@ -103,9 +110,13 @@ public function test_container_is_loaded_with_deprecated_parameters(): void
103110
'manager_type' => 'mongodb',
104111
'refresh_token_entity' => RefreshTokenDocument::class,
105112
'entity_manager' => 'doctrine_mongodb.odm.document_manager',
113+
'logout_firewall' => 'api',
106114
]);
107115

116+
$this->assertContainerBuilderHasParameter('gesdinet_jwt_refresh_token.logout_firewall_context', 'security.firewall.map.context.api');
108117
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.refresh_token.class', RefreshTokenDocument::class);
109118
$this->assertContainerBuilderHasParameter('gesdinet.jwtrefreshtoken.object_manager.id', 'doctrine_mongodb.odm.document_manager');
119+
120+
$this->assertContainerBuilderHasServiceDefinitionWithTag('gesdinet_jwt_refresh_token.security.listener.logout.legacy_config', 'kernel.event_listener', ['event' => LogoutEvent::class, 'method' => 'onLogout']);
110121
}
111122
}

Tests/Functional/DependencyInjection/Security/Factory/RefreshTokenAuthenticatorFactoryTest.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Symfony\Component\DependencyInjection\ChildDefinition;
88
use Symfony\Component\DependencyInjection\ContainerBuilder;
99
use Symfony\Component\DependencyInjection\Reference;
10+
use Symfony\Component\Security\Http\Event\LogoutEvent;
1011
use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface;
1112

1213
final class RefreshTokenAuthenticatorFactoryTest extends TestCase
@@ -39,13 +40,16 @@ public function test_authenticator_service_is_created_with_default_configuration
3940
$this->factory->createAuthenticator(
4041
$this->container,
4142
'test',
42-
[],
43+
[
44+
'invalidate_token_on_logout' => false,
45+
],
4346
'app.user_provider'
4447
);
4548

4649
$this->assertTrue($this->container->hasDefinition('security.authenticator.refresh_jwt.test'));
4750
$this->assertTrue($this->container->hasDefinition('security.authentication.success_handler.test.refresh_jwt'));
4851
$this->assertTrue($this->container->hasDefinition('security.authentication.failure_handler.test.refresh_jwt'));
52+
$this->assertFalse($this->container->hasDefinition('gesdinet_jwt_refresh_token.security.listener.logout.test'));
4953

5054
/** @var ChildDefinition $successHandler */
5155
$successHandler = $this->container->getDefinition('security.authentication.success_handler.test.refresh_jwt');
@@ -56,6 +60,35 @@ public function test_authenticator_service_is_created_with_default_configuration
5660
$this->assertSame('gesdinet.jwtrefreshtoken.security.authentication.failure_handler', $failureHandler->getParent());
5761
}
5862

63+
public function test_authenticator_service_is_created_and_logout_listener_registered_to_firewall_dispatcher(): void
64+
{
65+
$this->factory->createAuthenticator(
66+
$this->container,
67+
'test',
68+
[
69+
'invalidate_token_on_logout' => true,
70+
],
71+
'app.user_provider'
72+
);
73+
74+
$this->assertTrue($this->container->hasDefinition('security.authenticator.refresh_jwt.test'));
75+
$this->assertTrue($this->container->hasDefinition('security.authentication.success_handler.test.refresh_jwt'));
76+
$this->assertTrue($this->container->hasDefinition('security.authentication.failure_handler.test.refresh_jwt'));
77+
$this->assertTrue($this->container->hasDefinition('gesdinet_jwt_refresh_token.security.listener.logout.test'));
78+
79+
/** @var ChildDefinition $successHandler */
80+
$successHandler = $this->container->getDefinition('security.authentication.success_handler.test.refresh_jwt');
81+
$this->assertSame('gesdinet.jwtrefreshtoken.security.authentication.success_handler', $successHandler->getParent());
82+
83+
/** @var ChildDefinition $failureHandler */
84+
$failureHandler = $this->container->getDefinition('security.authentication.failure_handler.test.refresh_jwt');
85+
$this->assertSame('gesdinet.jwtrefreshtoken.security.authentication.failure_handler', $failureHandler->getParent());
86+
87+
/** @var ChildDefinition $logoutListener */
88+
$logoutListener = $this->container->getDefinition('gesdinet_jwt_refresh_token.security.listener.logout.test');
89+
$this->assertSame(['event' => LogoutEvent::class, 'method' => 'onLogout', 'dispatcher' => 'security.event_dispatcher.test'], $logoutListener->getTags()['kernel.event_listener'][0]);
90+
}
91+
5992
public function test_authenticator_service_is_created_with_custom_handlers(): void
6093
{
6194
$this->factory->createAuthenticator(
@@ -64,13 +97,15 @@ public function test_authenticator_service_is_created_with_custom_handlers(): vo
6497
[
6598
'success_handler' => 'app.security.authentication.success_handler',
6699
'failure_handler' => 'app.security.authentication.failure_handler',
100+
'invalidate_token_on_logout' => false,
67101
],
68102
'app.user_provider'
69103
);
70104

71105
$this->assertTrue($this->container->hasDefinition('security.authenticator.refresh_jwt.test'));
72106
$this->assertTrue($this->container->hasDefinition('security.authentication.success_handler.test.refresh_jwt'));
73107
$this->assertTrue($this->container->hasDefinition('security.authentication.failure_handler.test.refresh_jwt'));
108+
$this->assertFalse($this->container->hasDefinition('gesdinet_jwt_refresh_token.security.listener.logout.test'));
74109

75110
/** @var ChildDefinition $successHandler */
76111
$successHandler = $this->container->getDefinition('security.authentication.success_handler.test.refresh_jwt');

0 commit comments

Comments
 (0)