From: Robert <symfony@robert.meijers.dev>
Date: Fri, 3 Nov 2023 17:09:59 +0100
Subject: [Security] Fix possible session fixation when only the *token*
 changes

Origin: upstream, https://github.com/symfony/symfony/commit/dc356499d5ceb86f7cf2b4c7f032eca97061ed74
Bug: https://symfony.com/blog/cve-2023-46733-possible-session-fixation
Bug-Debian: https://bugs.debian.org/1055775
---
 .../Http/EventListener/SessionStrategyListener.php  |  2 +-
 .../EventListener/SessionStrategyListenerTest.php   | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/src/Symfony/Component/Security/Http/EventListener/SessionStrategyListener.php b/src/Symfony/Component/Security/Http/EventListener/SessionStrategyListener.php
index 311a52f..c6fcba8 100644
--- a/src/Symfony/Component/Security/Http/EventListener/SessionStrategyListener.php
+++ b/src/Symfony/Component/Security/Http/EventListener/SessionStrategyListener.php
@@ -48,7 +48,7 @@ class SessionStrategyListener implements EventSubscriberInterface
             $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername();
             $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername();
 
-            if ('' !== ($user ?? '') && $user === $previousUser) {
+            if ('' !== ($user ?? '') && $user === $previousUser && \get_class($token) === \get_class($previousToken)) {
                 return;
             }
         }
diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/SessionStrategyListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/SessionStrategyListenerTest.php
index 51b8dc1..29ef9b6 100644
--- a/src/Symfony/Component/Security/Http/Tests/EventListener/SessionStrategyListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/EventListener/SessionStrategyListenerTest.php
@@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
 use Symfony\Component\Security\Core\Authentication\Token\NullToken;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
 use Symfony\Component\Security\Core\User\InMemoryUser;
 use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
 use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
@@ -81,6 +82,26 @@ class SessionStrategyListenerTest extends TestCase
         $this->listener->onSuccessfulLogin($event);
     }
 
+    public function testRequestWithSamePreviousUserButDifferentTokenType()
+    {
+        $this->configurePreviousSession();
+
+        $token = $this->createMock(NullToken::class);
+        $token->expects($this->once())
+            ->method('getUserIdentifier')
+            ->willReturn('test');
+        $previousToken = $this->createMock(UsernamePasswordToken::class);
+        $previousToken->expects($this->once())
+            ->method('getUserIdentifier')
+            ->willReturn('test');
+
+        $this->sessionAuthenticationStrategy->expects($this->once())->method('onAuthentication')->with($this->request, $token);
+
+        $event = new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new UserBadge('test', function () {})), $token, $this->request, null, 'main_firewall', $previousToken);
+
+        $this->listener->onSuccessfulLogin($event);
+    }
+
     private function createEvent($firewallName)
     {
         return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new UserBadge('test', function ($username) { return new InMemoryUser($username, null); })), $this->token, $this->request, null, $firewallName);
