Skip to content

Commit 5c07c91

Browse files
[Security] Deprecate PersistentToken::getClass() and RememberMeDetails::getUserFqcn() in order to remove the user FQCN from the remember-me cookie in 8.0
1 parent 639b911 commit 5c07c91

File tree

11 files changed

+50
-24
lines changed

11 files changed

+50
-24
lines changed

UPGRADE-7.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Security
8888
* Deprecate callable firewall listeners, extend `AbstractListener` or implement `FirewallListenerInterface` instead
8989
* Deprecate `AbstractListener::__invoke`
9090
* Deprecate `LazyFirewallContext::__invoke()`
91+
* Deprecate `PersistentTokenInterface::getClass()` and `RememberMeDetails::getUserFqcn()`, the user FQCN will be removed from the remember-me cookie in 8.0
9192

9293
Serializer
9394
----------

src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
* `series` char(88) UNIQUE PRIMARY KEY NOT NULL,
3939
* `value` char(88) NOT NULL,
4040
* `lastUsed` datetime NOT NULL,
41-
* `class` varchar(100) NOT NULL,
41+
* `class` varchar(100) DEFAULT '' NOT NULL,
4242
* `username` varchar(200) NOT NULL
4343
* );
44+
*
45+
* (the `class` column is for BC with tables created with before Symfony 8)
4446
*/
4547
final class DoctrineTokenProvider implements TokenProviderInterface, TokenVerifierInterface
4648
{
@@ -95,7 +97,7 @@ public function createNewToken(PersistentTokenInterface $token): void
9597
{
9698
$sql = 'INSERT INTO rememberme_token (class, username, series, value, lastUsed) VALUES (:class, :username, :series, :value, :lastUsed)';
9799
$paramValues = [
98-
'class' => $token->getClass(),
100+
'class' => method_exists($token, 'getClass') ? $token->getClass(false) : '',
99101
'username' => $token->getUserIdentifier(),
100102
'series' => $token->getSeries(),
101103
'value' => $token->getTokenValue(),
@@ -164,7 +166,7 @@ public function updateExistingToken(PersistentTokenInterface $token, #[\Sensitiv
164166
try {
165167
$this->deleteTokenBySeries($tmpSeries);
166168
$lastUsed = \DateTime::createFromInterface($lastUsed);
167-
$this->createNewToken(new PersistentToken($token->getClass(), $token->getUserIdentifier(), $tmpSeries, $token->getTokenValue(), $lastUsed));
169+
$this->createNewToken(new PersistentToken(method_exists($token, 'getClass') ? $token->getClass(false) : '', $token->getUserIdentifier(), $tmpSeries, $token->getTokenValue(), $lastUsed));
168170

169171
$this->conn->commit();
170172
} catch (\Exception $e) {
@@ -195,7 +197,7 @@ private function addTableToSchema(Schema $schema): void
195197
$table->addColumn('series', Types::STRING, ['length' => 88]);
196198
$table->addColumn('value', Types::STRING, ['length' => 88]);
197199
$table->addColumn('lastUsed', Types::DATETIME_IMMUTABLE);
198-
$table->addColumn('class', Types::STRING, ['length' => 100]);
200+
$table->addColumn('class', Types::STRING, ['length' => 100, 'default' => '']);
199201
$table->addColumn('username', Types::STRING, ['length' => 200]);
200202

201203
if (class_exists(PrimaryKeyConstraint::class)) {

src/Symfony/Component/Security/Core/Authentication/RememberMe/InMemoryTokenProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token
3838
}
3939

4040
$token = new PersistentToken(
41-
$this->tokens[$series]->getClass(),
41+
$this->tokens[$series]->getClass(false),
4242
$this->tokens[$series]->getUserIdentifier(),
4343
$series,
4444
$tokenValue,

src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentToken.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ public function __construct(
4343
$this->lastUsed = \DateTimeImmutable::createFromInterface($lastUsed);
4444
}
4545

46-
public function getClass(): string
46+
/**
47+
* @deprecated since Symfony 7.4
48+
*/
49+
public function getClass(bool $triggerDeprecation = true): string
4750
{
51+
if ($triggerDeprecation) {
52+
trigger_deprecation('symfony/security-core', '7.4', 'The "%s()" method is deprecated: the user class will be removed from the remember-me cookie in 8.0.', __METHOD__);
53+
}
54+
4855
return $this->class;
4956
}
5057

src/Symfony/Component/Security/Core/Authentication/RememberMe/PersistentTokenInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ interface PersistentTokenInterface
2121
{
2222
/**
2323
* Returns the class of the user.
24+
*
25+
* @deprecated since Symfony 7.4, the user class will be removed from the remember-me cookie in 8.0
2426
*/
2527
public function getClass(): string;
2628

src/Symfony/Component/Security/Core/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ CHANGELOG
44
7.4
55
---
66

7-
* Add `MermaidDumper` to dump Role Hierarchy graphs in the Mermaid.js flowchart format
7+
* Add `MermaidDumper` to dump Role Hierarchy graphs in the Mermaid.js flowchart format
8+
* Deprecate `PersistentTokenInterface::getClass()`, the user class will be removed from the remember-me cookie in 8.0
89

910
7.3
1011
---

src/Symfony/Component/Security/Core/Tests/Authentication/RememberMe/PersistentTokenTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Security\Core\Tests\Authentication\RememberMe;
1313

14+
use PHPUnit\Framework\Attributes\Group;
15+
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
1416
use PHPUnit\Framework\TestCase;
1517
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
1618

@@ -21,7 +23,6 @@ public function testConstructor()
2123
$lastUsed = new \DateTimeImmutable();
2224
$token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', $lastUsed);
2325

24-
$this->assertEquals('fooclass', $token->getClass());
2526
$this->assertEquals('fooname', $token->getUserIdentifier());
2627
$this->assertEquals('fooseries', $token->getSeries());
2728
$this->assertEquals('footokenvalue', $token->getTokenValue());
@@ -35,4 +36,12 @@ public function testDateTime()
3536

3637
$this->assertEquals($lastUsed, $token->getLastUsed());
3738
}
39+
40+
#[IgnoreDeprecations]
41+
#[Group('legacy')]
42+
public function testClassDeprecation()
43+
{
44+
$token = new PersistentToken('fooclass', 'fooname', 'fooseries', 'footokenvalue', new \DateTimeImmutable());
45+
$this->assertSame('fooclass', $token->getClass());
46+
}
3847
}

src/Symfony/Component/Security/Http/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Deprecate `AbstractListener::__invoke`
1010
* Add `$methods` argument to `#[IsGranted]` to restrict validation to specific HTTP methods
1111
* Allow subclassing `#[IsGranted]`
12+
* Deprecate `RememberMeDetails::getUserFqcn()`, the user FQCN will be removed from the remember-me cookie in 8.0
1213

1314
7.3
1415
---

src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,51 +65,51 @@ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): U
6565
}
6666

6767
[$series, $tokenValue] = explode(':', $rememberMeDetails->getValue(), 2);
68-
$persistentToken = $this->tokenProvider->loadTokenBySeries($series);
68+
$token = $this->tokenProvider->loadTokenBySeries($series);
6969

70-
if ($persistentToken->getUserIdentifier() !== $rememberMeDetails->getUserIdentifier() || $persistentToken->getClass() !== $rememberMeDetails->getUserFqcn()) {
70+
if ($token->getUserIdentifier() !== $rememberMeDetails->getUserIdentifier()) {
7171
throw new AuthenticationException('The cookie\'s hash is invalid.');
7272
}
7373

7474
// content of $rememberMeDetails is not trustable. this prevents use of this class
7575
unset($rememberMeDetails);
7676

7777
if ($this->tokenVerifier) {
78-
$isTokenValid = $this->tokenVerifier->verifyToken($persistentToken, $tokenValue);
78+
$isTokenValid = $this->tokenVerifier->verifyToken($token, $tokenValue);
7979
} else {
80-
$isTokenValid = hash_equals($persistentToken->getTokenValue(), $tokenValue);
80+
$isTokenValid = hash_equals($token->getTokenValue(), $tokenValue);
8181
}
8282
if (!$isTokenValid) {
8383
throw new CookieTheftException('This token was already used. The account is possibly compromised.');
8484
}
8585

86-
$expires = $persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'];
86+
$expires = $token->getLastUsed()->getTimestamp() + $this->options['lifetime'];
8787
if ($expires < time()) {
8888
throw new AuthenticationException('The cookie has expired.');
8989
}
9090

9191
return parent::consumeRememberMeCookie(new RememberMeDetails(
92-
$persistentToken->getClass(),
93-
$persistentToken->getUserIdentifier(),
92+
method_exists($token, 'getClass') ? $token->getClass(false) : '',
93+
$token->getUserIdentifier(),
9494
$expires,
95-
$persistentToken->getLastUsed()->getTimestamp().':'.$series.':'.$tokenValue.':'.$persistentToken->getClass()
95+
$token->getLastUsed()->getTimestamp().':'.$series.':'.$tokenValue.':'.(method_exists($token, 'getClass') ? $token->getClass(false) : '')
9696
));
9797
}
9898

9999
public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void
100100
{
101101
[$lastUsed, $series, $tokenValue, $class] = explode(':', $rememberMeDetails->getValue(), 4);
102-
$persistentToken = new PersistentToken($class, $rememberMeDetails->getUserIdentifier(), $series, $tokenValue, new \DateTimeImmutable('@'.$lastUsed));
102+
$token = new PersistentToken($class, $rememberMeDetails->getUserIdentifier(), $series, $tokenValue, new \DateTimeImmutable('@'.$lastUsed));
103103

104104
// if a token was regenerated less than a minute ago, there is no need to regenerate it
105105
// if multiple concurrent requests reauthenticate a user we do not want to update the token several times
106-
if ($persistentToken->getLastUsed()->getTimestamp() + 60 >= time()) {
106+
if ($token->getLastUsed()->getTimestamp() + 60 >= time()) {
107107
return;
108108
}
109109

110110
$tokenValue = strtr(base64_encode(random_bytes(33)), '+/=', '-_~');
111111
$tokenLastUsed = new \DateTime();
112-
$this->tokenVerifier?->updateExistingToken($persistentToken, $tokenValue, $tokenLastUsed);
112+
$this->tokenVerifier?->updateExistingToken($token, $tokenValue, $tokenLastUsed);
113113
$this->tokenProvider->updateToken($series, $tokenValue, $tokenLastUsed);
114114

115115
$this->createCookie($rememberMeDetails->withValue($series.':'.$tokenValue));

src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public static function fromRawCookie(string $rawCookie): self
4646
return new static(...$cookieParts);
4747
}
4848

49-
public static function fromPersistentToken(PersistentToken $persistentToken, int $expires): self
49+
public static function fromPersistentToken(PersistentToken $token, int $expires): self
5050
{
51-
return new static($persistentToken->getClass(), $persistentToken->getUserIdentifier(), $expires, $persistentToken->getSeries().':'.$persistentToken->getTokenValue());
51+
return new static(method_exists($token, 'getClass') ? $token->getClass(false) : '', $token->getUserIdentifier(), $expires, $token->getSeries().':'.$token->getTokenValue());
5252
}
5353

5454
public function withValue(string $value): self
@@ -59,8 +59,13 @@ public function withValue(string $value): self
5959
return $details;
6060
}
6161

62+
/**
63+
* @deprecated since Symfony 7.4, the user FQCN will be removed from the remember-me cookie in 8.0
64+
*/
6265
public function getUserFqcn(): string
6366
{
67+
trigger_deprecation('symfony/security-http', '7.4', 'The "%s()" method is deprecated: the user FQCN will be removed from the remember-me cookie in 8.0.', __METHOD__);
68+
6469
return $this->userFqcn;
6570
}
6671

0 commit comments

Comments
 (0)