Skip to content

Commit 5909d74

Browse files
[Security/Http] Remove CSRF tokens from storage on successful login
1 parent fa1827c commit 5909d74

File tree

6 files changed

+33
-7
lines changed

6 files changed

+33
-7
lines changed

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363

6464
<service id="security.authentication.session_strategy" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy">
6565
<argument>%security.authentication.session_strategy.strategy%</argument>
66+
<argument type="service" id="security.csrf.token_storage" on-invalid="ignore" />
6667
</service>
6768
<service id="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface" alias="security.authentication.session_strategy" />
6869

src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ class CsrfFormLoginTest extends AbstractWebTestCase
1919
public function testFormLoginAndLogoutWithCsrfTokens($config)
2020
{
2121
$client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
22+
static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
2223

2324
$form = $client->request('GET', '/login')->selectButton('login')->form();
2425
$form['user_login[username]'] = 'johannes';
2526
$form['user_login[password]'] = 'test';
2627
$client->submit($form);
2728

29+
$this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
30+
2831
$this->assertRedirect($client->getResponse(), '/profile');
2932

3033
$crawler = $client->followRedirect();
@@ -48,11 +51,14 @@ public function testFormLoginAndLogoutWithCsrfTokens($config)
4851
public function testFormLoginWithInvalidCsrfToken($config)
4952
{
5053
$client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]);
54+
static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
5155

5256
$form = $client->request('GET', '/login')->selectButton('login')->form();
5357
$form['user_login[_token]'] = '';
5458
$client->submit($form);
5559

60+
$this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
61+
5662
$this->assertRedirect($client->getResponse(), '/login');
5763

5864
$text = $client->followRedirect()->text(null, true);

src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,13 @@ public function testSessionLessRememberMeLogout()
3636
public function testCsrfTokensAreClearedOnLogout()
3737
{
3838
$client = $this->createClient(['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']);
39-
static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
4039

4140
$client->request('POST', '/login', [
4241
'_username' => 'johannes',
4342
'_password' => 'test',
4443
]);
4544

46-
$this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
47-
$this->assertSame('bar', static::$container->get('security.csrf.token_storage')->getToken('foo'));
45+
static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
4846

4947
$client->request('GET', '/logout');
5048

src/Symfony/Bundle/SecurityBundle/composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"symfony/security-core": "^4.4",
2626
"symfony/security-csrf": "^4.2|^5.0",
2727
"symfony/security-guard": "^4.2|^5.0",
28-
"symfony/security-http": "^4.4.5"
28+
"symfony/security-http": "^4.4.50"
2929
},
3030
"require-dev": {
3131
"doctrine/annotations": "^1.10.4",

src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php

+11-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\HttpFoundation\Request;
1515
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
16+
use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface;
1617

1718
/**
1819
* The default session strategy implementation.
@@ -31,10 +32,15 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte
3132
public const INVALIDATE = 'invalidate';
3233

3334
private $strategy;
35+
private $csrfTokenStorage = null;
3436

35-
public function __construct(string $strategy)
37+
public function __construct(string $strategy, ClearableTokenStorageInterface $csrfTokenStorage = null)
3638
{
3739
$this->strategy = $strategy;
40+
41+
if (self::MIGRATE === $strategy) {
42+
$this->csrfTokenStorage = $csrfTokenStorage;
43+
}
3844
}
3945

4046
/**
@@ -47,10 +53,12 @@ public function onAuthentication(Request $request, TokenInterface $token)
4753
return;
4854

4955
case self::MIGRATE:
50-
// Note: this logic is duplicated in several authentication listeners
51-
// until Symfony 5.0 due to a security fix with BC compat
5256
$request->getSession()->migrate(true);
5357

58+
if ($this->csrfTokenStorage) {
59+
$this->csrfTokenStorage->clear();
60+
}
61+
5462
return;
5563

5664
case self::INVALIDATE:

src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php

+13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\HttpFoundation\Request;
1616
use Symfony\Component\HttpFoundation\Session\SessionInterface;
17+
use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface;
1718
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
1819
use Symfony\Component\Security\Http\Tests\Fixtures\TokenInterface;
1920

@@ -57,6 +58,18 @@ public function testSessionIsInvalidated()
5758
$strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class));
5859
}
5960

61+
public function testCsrfTokensAreCleared()
62+
{
63+
$session = $this->createMock(SessionInterface::class);
64+
$session->expects($this->once())->method('migrate')->with($this->equalTo(true));
65+
66+
$csrfStorage = $this->createMock(ClearableTokenStorageInterface::class);
67+
$csrfStorage->expects($this->once())->method('clear');
68+
69+
$strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE, $csrfStorage);
70+
$strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class));
71+
}
72+
6073
private function getRequest($session = null)
6174
{
6275
$request = $this->createMock(Request::class);

0 commit comments

Comments
 (0)