Skip to content

Commit e83ad8b

Browse files
committed
feature #38550 [Security] Added check_post_only to the login link authenticator (wouterj)
This PR was squashed before being merged into the 5.x branch. Discussion ---------- [Security] Added check_post_only to the login link authenticator | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - This is useful when adding a page that requires a user action in order to validate the check link. That is required when using a single-use login link, to workaround browser and email client previews (which trigger a request). See also the short docs discussion about this: symfony/symfony-docs#14389 (comment) For reference, I choose this option name as it relates to the `post_only` option in the `FormLoginAuthenticator`, which is about exactly the same thing. I didn't think `post_only` was a 100% clear name, but I'm happy to change this option to that for complete consistency. cc @weaverryan Commits ------- 5093e0d [Security] Added check_post_only to the login link authenticator
2 parents 3553ac5 + 5093e0d commit e83ad8b

File tree

4 files changed

+28
-4
lines changed

4 files changed

+28
-4
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ public function addConfiguration(NodeDefinition $node)
3636
$builder
3737
->scalarNode('check_route')
3838
->isRequired()
39-
->info('Route that will validate the login link - e.g. app_login_link_verify.')
39+
->info('Route that will validate the login link - e.g. "app_login_link_verify".')
40+
->end()
41+
->scalarNode('check_post_only')
42+
->defaultFalse()
43+
->info('If true, only HTTP POST requests to "check_route" will be handled by the authenticator.')
4044
->end()
4145
->arrayNode('signature_properties')
4246
->isRequired()
@@ -128,6 +132,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
128132
->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)))
129133
->replaceArgument(4, [
130134
'check_route' => $config['check_route'],
135+
'check_post_only' => $config['check_post_only'],
131136
]);
132137

133138
return $authenticatorId;

src/Symfony/Component/Security/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ CHANGELOG
1313
* Added translator to `\Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator` and `\Symfony\Component\Security\Http\Firewall\UsernamePasswordJsonAuthenticationListener` to translate authentication failure messages
1414
* Added a CurrentUser attribute to force the UserValueResolver to resolve an argument to the current user.
1515
* Added `LoginThrottlingListener`.
16+
* Added `LoginLinkAuthenticator`.
1617

1718
5.1.0
1819
-----

src/Symfony/Component/Security/Http/Authenticator/LoginLinkAuthenticator.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,18 @@ public function __construct(LoginLinkHandlerInterface $loginLinkHandler, HttpUti
4343
$this->httpUtils = $httpUtils;
4444
$this->successHandler = $successHandler;
4545
$this->failureHandler = $failureHandler;
46-
$this->options = $options;
46+
$this->options = $options + ['check_post_only' => false];
4747
}
4848

4949
public function supports(Request $request): ?bool
5050
{
51-
return $this->httpUtils->checkRequestPath($request, $this->options['check_route']);
51+
return ($this->options['check_post_only'] ? $request->isMethod('POST') : true)
52+
&& $this->httpUtils->checkRequestPath($request, $this->options['check_route']);
5253
}
5354

5455
public function authenticate(Request $request): PassportInterface
5556
{
5657
$username = $request->get('user');
57-
5858
if (!$username) {
5959
throw new InvalidLoginLinkAuthenticationException('Missing user from link.');
6060
}

src/Symfony/Component/Security/Http/Tests/Authenticator/LoginLinkAuthenticatorTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ protected function setUp(): void
3939
$this->failureHandler = $this->createMock(AuthenticationFailureHandlerInterface::class);
4040
}
4141

42+
/**
43+
* @dataProvider provideSupportData
44+
*/
45+
public function testSupport(array $options, $request, bool $supported)
46+
{
47+
$this->setUpAuthenticator($options);
48+
49+
$this->assertEquals($supported, $this->authenticator->supports($request));
50+
}
51+
52+
public function provideSupportData()
53+
{
54+
yield [['check_route' => '/validate_link'], Request::create('/validate_link?hash=abc123'), true];
55+
yield [['check_route' => '/validate_link'], Request::create('/login?hash=abc123'), false];
56+
yield [['check_route' => '/validate_link', 'check_post_only' => true], Request::create('/validate_link?hash=abc123'), false];
57+
yield [['check_route' => '/validate_link', 'check_post_only' => true], Request::create('/validate_link?hash=abc123', 'POST'), true];
58+
}
59+
4260
public function testSuccessfulAuthenticate()
4361
{
4462
$this->setUpAuthenticator();

0 commit comments

Comments
 (0)