src/Bidcoz/Bundle/CoreBundle/Security/Csrf/ResilientCsrfTokenManager.php line 22

Open in your IDE?
  1. <?php
  2. namespace Bidcoz\Bundle\CoreBundle\Security\Csrf;
  3. use Bidcoz\Bundle\CoreBundle\Entity\User;
  4. use Psr\Log\LoggerInterface;
  5. use Symfony\Component\HttpFoundation\RequestStack;
  6. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  7. use Symfony\Component\Security\Csrf\CsrfToken;
  8. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  9. /**
  10.  * Decorator that accepts failed CSRF tokens for authenticated ManageBundle routes.
  11.  *
  12.  * SameSite=Lax cookies already prevent cross-site form submissions, so CSRF
  13.  * validation in the management area is defense-in-depth. When session issues
  14.  * (deploy-triggered GC, save_path problems) cause token mismatches, this
  15.  * decorator prevents authenticated users from being blocked.
  16.  *
  17.  * Public forms (login, registration, donations) remain strictly validated.
  18.  */
  19. class ResilientCsrfTokenManager implements CsrfTokenManagerInterface
  20. {
  21.     private CsrfTokenManagerInterface $inner;
  22.     private TokenStorageInterface $tokenStorage;
  23.     private RequestStack $requestStack;
  24.     private LoggerInterface $logger;
  25.     public function __construct(
  26.         CsrfTokenManagerInterface $inner,
  27.         TokenStorageInterface $tokenStorage,
  28.         RequestStack $requestStack,
  29.         LoggerInterface $logger
  30.     ) {
  31.         $this->inner $inner;
  32.         $this->tokenStorage $tokenStorage;
  33.         $this->requestStack $requestStack;
  34.         $this->logger $logger;
  35.     }
  36.     public function getToken(string $tokenId): CsrfToken
  37.     {
  38.         return $this->inner->getToken($tokenId);
  39.     }
  40.     public function refreshToken(string $tokenId): CsrfToken
  41.     {
  42.         return $this->inner->refreshToken($tokenId);
  43.     }
  44.     public function removeToken(string $tokenId): ?string
  45.     {
  46.         return $this->inner->removeToken($tokenId);
  47.     }
  48.     public function isTokenValid(CsrfToken $token): bool
  49.     {
  50.         if ($this->inner->isTokenValid($token)) {
  51.             return true;
  52.         }
  53.         if ($this->shouldBypassValidation()) {
  54.             $request $this->requestStack->getCurrentRequest();
  55.             $this->logger->warning('CSRF bypass: accepting failed token for authenticated management route', [
  56.                 'route' => $request $request->attributes->get('_route') : 'unknown',
  57.                 'controller' => $request $request->attributes->get('_controller''') : 'unknown',
  58.                 'token_id' => $token->getId(),
  59.             ]);
  60.             return true;
  61.         }
  62.         return false;
  63.     }
  64.     private function shouldBypassValidation(): bool
  65.     {
  66.         $request $this->requestStack->getCurrentRequest();
  67.         if (!$request) {
  68.             return false;
  69.         }
  70.         // Check controller belongs to ManageBundle
  71.         $controller $request->attributes->get('_controller''');
  72.         if (strpos($controller'ManageBundle\\Controller') === false) {
  73.             return false;
  74.         }
  75.         // Check user is authenticated
  76.         $token $this->tokenStorage->getToken();
  77.         return $token && $token->getUser() instanceof User;
  78.     }
  79. }