Skip to content

Commit 2dfe2cc

Browse files
[Security] Allow passing remember-me parameters via RememberMeBadge
1 parent fda3f97 commit 2dfe2cc

File tree

5 files changed

+30
-38
lines changed

5 files changed

+30
-38
lines changed

Authenticator/JsonLoginAuthenticator.php

+9-8
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,20 @@ public function supports(Request $request): ?bool
8181
public function authenticate(Request $request): Passport
8282
{
8383
try {
84-
$credentials = $this->getCredentials($request);
84+
$data = json_decode($request->getContent());
85+
if (!$data instanceof \stdClass) {
86+
throw new BadRequestHttpException('Invalid JSON.');
87+
}
88+
89+
$credentials = $this->getCredentials($data);
8590
} catch (BadRequestHttpException $e) {
8691
$request->setRequestFormat('json');
8792

8893
throw $e;
8994
}
9095

9196
$userBadge = new UserBadge($credentials['username'], $this->userProvider->loadUserByIdentifier(...));
92-
$passport = new Passport($userBadge, new PasswordCredentials($credentials['password']), [new RememberMeBadge()]);
97+
$passport = new Passport($userBadge, new PasswordCredentials($credentials['password']), [new RememberMeBadge((array) $data)]);
9398

9499
if ($this->userProvider instanceof PasswordUpgraderInterface) {
95100
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
@@ -137,13 +142,8 @@ public function setTranslator(TranslatorInterface $translator)
137142
$this->translator = $translator;
138143
}
139144

140-
private function getCredentials(Request $request): array
145+
private function getCredentials(\stdClass $data): array
141146
{
142-
$data = json_decode($request->getContent());
143-
if (!$data instanceof \stdClass) {
144-
throw new BadRequestHttpException('Invalid JSON.');
145-
}
146-
147147
$credentials = [];
148148
try {
149149
$credentials['username'] = $this->propertyAccessor->getValue($data, $this->options['username_path']);
@@ -157,6 +157,7 @@ private function getCredentials(Request $request): array
157157

158158
try {
159159
$credentials['password'] = $this->propertyAccessor->getValue($data, $this->options['password_path']);
160+
$this->propertyAccessor->setValue($data, $this->options['password_path'], null);
160161

161162
if (!\is_string($credentials['password'])) {
162163
throw new BadRequestHttpException(sprintf('The key "%s" must be a string.', $this->options['password_path']));

Authenticator/Passport/Badge/RememberMeBadge.php

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class RememberMeBadge implements BadgeInterface
2828
{
2929
private bool $enabled = false;
3030

31+
public function __construct(
32+
public readonly array $parameters = [],
33+
) {
34+
}
35+
3136
/**
3237
* Enables remember-me cookie creation.
3338
*

EventListener/CheckRememberMeConditionsListener.php

+2-22
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
use Psr\Log\LoggerInterface;
1515
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16-
use Symfony\Component\HttpFoundation\Request;
1716
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
1817
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
1918
use Symfony\Component\Security\Http\ParameterBagUtils;
@@ -55,8 +54,8 @@ public function onSuccessfulLogin(LoginSuccessEvent $event): void
5554
/** @var RememberMeBadge $badge */
5655
$badge = $passport->getBadge(RememberMeBadge::class);
5756
if (!$this->options['always_remember_me']) {
58-
$parameter = $this->getParameter($event->getRequest(), $this->options['remember_me_parameter']);
59-
if (!('true' === $parameter || 'on' === $parameter || '1' === $parameter || 'yes' === $parameter || true === $parameter)) {
57+
$parameter = ParameterBagUtils::getRequestParameterValue($event->getRequest(), $this->options['remember_me_parameter'], $badge->parameters);
58+
if (!filter_var($parameter, \FILTER_VALIDATE_BOOL)) {
6059
$this->logger?->debug('Remember me disabled; request does not contain remember me parameter ("{parameter}").', ['parameter' => $this->options['remember_me_parameter']]);
6160

6261
return;
@@ -70,23 +69,4 @@ public static function getSubscribedEvents(): array
7069
{
7170
return [LoginSuccessEvent::class => ['onSuccessfulLogin', -32]];
7271
}
73-
74-
private function getParameter(Request $request, string $parameterName): mixed
75-
{
76-
$parameter = ParameterBagUtils::getRequestParameterValue($request, $parameterName);
77-
if (null !== $parameter) {
78-
return $parameter;
79-
}
80-
81-
if ('application/json' === $request->headers->get('Content-Type')) {
82-
$data = json_decode($request->getContent());
83-
if (!$data instanceof \stdClass) {
84-
return null;
85-
}
86-
87-
return $data->{$parameterName} ?? null;
88-
}
89-
90-
return null;
91-
}
9272
}

ParameterBagUtils.php

+10-4
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,28 @@ public static function getParameterBagValue(ParameterBag $parameters, string $pa
6060
*
6161
* @throws InvalidArgumentException when the given path is malformed
6262
*/
63-
public static function getRequestParameterValue(Request $request, string $path): mixed
63+
public static function getRequestParameterValue(Request $request, string $path, array $parameters = []): mixed
6464
{
6565
if (false === $pos = strpos($path, '[')) {
66-
return $request->get($path);
66+
return $parameters[$path] ?? $request->get($path);
6767
}
6868

6969
$root = substr($path, 0, $pos);
7070

71-
if (null === $value = $request->get($root)) {
71+
if (null === $value = $parameters[$root] ?? $request->get($root)) {
7272
return null;
7373
}
7474

7575
self::$propertyAccessor ??= PropertyAccess::createPropertyAccessor();
7676

7777
try {
78-
return self::$propertyAccessor->getValue($value, substr($path, $pos));
78+
$value = self::$propertyAccessor->getValue($value, substr($path, $pos));
79+
80+
if (null === $value && isset($parameters[$root]) && null !== $value = $request->get($root)) {
81+
$value = self::$propertyAccessor->getValue($value, substr($path, $pos));
82+
}
83+
84+
return $value;
7985
} catch (AccessException) {
8086
return null;
8187
}

Tests/EventListener/CheckRememberMeConditionsListenerTest.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testSuccessfulJsonLoginWithoutSupportingAuthenticator()
5959
public function testSuccessfulLoginWithoutRequestParameter()
6060
{
6161
$this->request = Request::create('/login');
62-
$passport = $this->createPassport();
62+
$passport = $this->createPassport([new RememberMeBadge()]);
6363

6464
$this->listener->onSuccessfulLogin($this->createLoginSuccessfulEvent($passport));
6565

@@ -110,7 +110,7 @@ public function testSuccessfulJsonLoginWithOptInRequestParameter($optInValue)
110110
{
111111
$this->createJsonRequest(['_remember_me' => $optInValue]);
112112

113-
$passport = $this->createPassport();
113+
$passport = $this->createPassport([new RememberMeBadge(['_remember_me' => $optInValue])]);
114114

115115
$this->listener->onSuccessfulLogin($this->createLoginSuccessfulEvent($passport));
116116

@@ -133,7 +133,7 @@ private function createHttpRequest(): void
133133
$this->response = new Response();
134134
}
135135

136-
private function createJsonRequest(mixed $content = ['_remember_me' => true]): void
136+
private function createJsonRequest(array $content = ['_remember_me' => true]): void
137137
{
138138
$this->request = Request::create('/login', 'POST', [], [], [], [], json_encode($content));
139139
$this->request->headers->add(['Content-Type' => 'application/json']);
@@ -147,6 +147,6 @@ private function createLoginSuccessfulEvent(Passport $passport)
147147

148148
private function createPassport(array $badges = null)
149149
{
150-
return new SelfValidatingPassport(new UserBadge('test', fn ($username) => new InMemoryUser($username, null)), $badges ?? [new RememberMeBadge()]);
150+
return new SelfValidatingPassport(new UserBadge('test', fn ($username) => new InMemoryUser($username, null)), $badges ?? [new RememberMeBadge(['_remember_me' => true])]);
151151
}
152152
}

0 commit comments

Comments
 (0)