Skip to content

[Security] Deprecate argument $secret of RememberMeToken and RememberMeAuthenticator #56838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions UPGRADE-7.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
UPGRADE FROM 7.1 to 7.2
=======================

Symfony 7.2 is a minor release. According to the Symfony release process, there should be no significant
backward compatibility breaks. Minor backward compatibility breaks are prefixed in this document with
`[BC BREAK]`, make sure your code is compatible with these entries before upgrading.
Read more about this in the [Symfony documentation](https://symfony.com/doc/7.2/setup/upgrade_minor.html).

If you're upgrading from a version below 7.1, follow the [7.1 upgrade guide](UPGRADE-7.1.md) first.

Security
--------

* Deprecate argument `$secret` of `RememberMeToken` and `RememberMeAuthenticator`
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
$container
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.remember_me'))
->replaceArgument(0, new Reference($rememberMeHandlerId))
->replaceArgument(3, $config['name'] ?? $this->options['name'])
->replaceArgument(2, $config['name'] ?? $this->options['name'])
;

return $authenticatorId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
->abstract()
->args([
abstract_arg('remember me handler'),
param('kernel.secret'),
service('security.token_storage'),
abstract_arg('options'),
service('logger')->nullOnInvalid(),
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/SecurityBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"symfony/http-kernel": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/password-hasher": "^6.4|^7.0",
"symfony/security-core": "^6.4|^7.0",
"symfony/security-core": "^7.2",
"symfony/security-csrf": "^6.4|^7.0",
"symfony/security-http": "^7.1",
"symfony/security-http": "^7.2",
"symfony/service-contracts": "^2.5|^3"
},
"require-dev": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,26 @@
*/
class RememberMeToken extends AbstractToken
{
private string $secret;
private ?string $secret = null;
private string $firewallName;

/**
* @param string $secret A secret used to make sure the token is created by the app and not by a malicious client
*
* @throws \InvalidArgumentException
*/
public function __construct(UserInterface $user, string $firewallName, #[\SensitiveParameter] string $secret)
public function __construct(UserInterface $user, string $firewallName)
{
parent::__construct($user->getRoles());

if (!$secret) {
throw new InvalidArgumentException('A non-empty secret is required.');
if (\func_num_args() > 2) {
trigger_deprecation('symfony/security-core', '7.2', 'The "$secret" argument of "%s()" is deprecated.', __METHOD__);
$this->secret = func_get_arg(2);
}

if (!$firewallName) {
throw new InvalidArgumentException('$firewallName must not be empty.');
}

$this->firewallName = $firewallName;
$this->secret = $secret;

$this->setUser($user);
}
Expand All @@ -52,13 +50,19 @@ public function getFirewallName(): string
return $this->firewallName;
}

/**
* @deprecated since Symfony 7.2
*/
public function getSecret(): string
{
return $this->secret;
trigger_deprecation('symfony/security-core', '7.2', 'The "%s()" method is deprecated.', __METHOD__);

return $this->secret ??= base64_encode(random_bytes(8));
}

public function __serialize(): array
{
// $this->firewallName should be kept at index 1 for compatibility with payloads generated before Symfony 8
return [$this->secret, $this->firewallName, parent::__serialize()];
}

Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/Security/Core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
CHANGELOG
=========

7.2
---

* Deprecate argument `$secret` of `RememberMeToken`

7.0
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected function getRememberMeToken()
{
$user = new InMemoryUser('wouter', '', ['ROLE_USER']);

return new RememberMeToken($user, 'main', 'secret');
return new RememberMeToken($user, 'main');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ class RememberMeTokenTest extends TestCase
public function testConstructor()
{
$user = $this->getUser();
$token = new RememberMeToken($user, 'fookey', 'foo');
$token = new RememberMeToken($user, 'fookey');

$this->assertEquals('fookey', $token->getFirewallName());
$this->assertEquals('foo', $token->getSecret());
$this->assertEquals(['ROLE_FOO'], $token->getRoleNames());
$this->assertSame($user, $token->getUser());
}

public function testConstructorSecretCannotBeEmptyString()
/**
* @group legacy
*/
public function testSecret()
{
$this->expectException(\InvalidArgumentException::class);
new RememberMeToken(
$this->getUser(),
'',
''
);
$user = $this->getUser();
$token = new RememberMeToken($user, 'fookey', 'foo');

$this->assertEquals('foo', $token->getSecret());
}

protected function getUser($roles = ['ROLE_FOO'])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public static function provider()
$user = new InMemoryUser('username', 'password', $roles);

$noToken = null;
$rememberMeToken = new RememberMeToken($user, 'firewall-name', 'firewall');
$rememberMeToken = new RememberMeToken($user, 'firewall-name');
$usernamePasswordToken = new UsernamePasswordToken($user, 'firewall-name', $roles);

return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function getCredentials()
}

if ('remembered' === $authenticated) {
return new RememberMeToken($user, 'foo', 'bar');
return new RememberMeToken($user, 'foo');
}

if ('impersonated' === $authenticated) {
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Security/Core/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/event-dispatcher-contracts": "^2.5|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/password-hasher": "^6.4|^7.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CookieTheftException;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
Expand Down Expand Up @@ -50,14 +49,23 @@
private string $cookieName;
private ?LoggerInterface $logger;

public function __construct(RememberMeHandlerInterface $rememberMeHandler, #[\SensitiveParameter] string $secret, TokenStorageInterface $tokenStorage, string $cookieName, ?LoggerInterface $logger = null)
/**
* @param TokenStorageInterface $tokenStorage
* @param string $cookieName
* @param ?LoggerInterface $logger
*/
public function __construct(RememberMeHandlerInterface $rememberMeHandler, #[\SensitiveParameter] TokenStorageInterface|string $tokenStorage, string|TokenStorageInterface $cookieName, LoggerInterface|string|null $logger = null)
{
if (!$secret) {
throw new InvalidArgumentException('A non-empty secret is required.');
if (\is_string($tokenStorage)) {
trigger_deprecation('symfony/security-core', '7.2', 'The "$secret" argument of "%s()" is deprecated.', __METHOD__);

$this->secret = $tokenStorage;

Check failure on line 62 in src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php

View workflow job for this annotation

GitHub Actions / Psalm

NoValue

src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php:62:13: NoValue: All possible types for this assignment were invalidated - This may be dead code (see https://psalm.dev/179)

Check failure on line 62 in src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php

View workflow job for this annotation

GitHub Actions / Psalm

NoValue

src/Symfony/Component/Security/Http/Authenticator/RememberMeAuthenticator.php:62:13: NoValue: All possible types for this assignment were invalidated - This may be dead code (see https://psalm.dev/179)
$tokenStorage = $cookieName;
$cookieName = $logger;
$logger = \func_num_args() > 4 ? func_get_arg(4) : null;
}

$this->rememberMeHandler = $rememberMeHandler;
$this->secret = $secret;
$this->tokenStorage = $tokenStorage;
$this->cookieName = $cookieName;
$this->logger = $logger;
Expand Down Expand Up @@ -99,7 +107,11 @@

public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new RememberMeToken($passport->getUser(), $firewallName, $this->secret);
if (isset($this->secret)) {
return new RememberMeToken($passport->getUser(), $firewallName, $this->secret);
}

return new RememberMeToken($passport->getUser(), $firewallName);
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
Expand Down
5 changes: 5 additions & 0 deletions src/Symfony/Component/Security/Http/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

7.2
---

* Deprecate argument `$secret` of `RememberMeAuthenticator`

7.1
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected function setUp(): void
{
$this->rememberMeHandler = $this->createMock(RememberMeHandlerInterface::class);
$this->tokenStorage = new TokenStorage();
$this->authenticator = new RememberMeAuthenticator($this->rememberMeHandler, 's3cr3t', $this->tokenStorage, '_remember_me_cookie');
$this->authenticator = new RememberMeAuthenticator($this->rememberMeHandler, $this->tokenStorage, '_remember_me_cookie');
}

public function testSupportsTokenStorageWithToken()
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Security/Http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"symfony/http-kernel": "^6.4|^7.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/property-access": "^6.4|^7.0",
"symfony/security-core": "^6.4|^7.0",
"symfony/security-core": "^7.2",
"symfony/service-contracts": "^2.5|^3"
},
"require-dev": {
Expand Down
Loading