<?php
namespace App\Infrastructure\EventSubscriber;
use App\Database\Domain\Entity\User\UserAccessToken;
use App\Database\Domain\Repository\AccessTokenRepository;
use App\Infrastructure\Security\AuthManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class AuthByApiTokenSubscriber implements EventSubscriberInterface
{
private ContainerInterface $container;
private AuthManager $authManager;
private AccessTokenRepository $accessTokenRepository;
public function __construct(
ContainerInterface $container,
AuthManager $authManager,
AccessTokenRepository $accessTokenRepository
) {
$this->container = $container;
$this->authManager = $authManager;
$this->accessTokenRepository = $accessTokenRepository;
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
// Handle referral code
if ($request->query->has('referral_code')) {
$code = $request->query->get('referral_code');
$request->getSession()->set('referral_code', $code);
}
// Skip API routes
if (strpos($request->getPathInfo(), 'api') !== false) {
return;
}
$tokenStorage = $this->container->get('security.token_storage');
$token = $tokenStorage->getToken();
$router = $this->container->get('router');
$logoutResponse = new RedirectResponse($router->generate('user_logout'));
// Check if we have authentication cookies
$hasAuthCookies = $request->cookies->has('token') &&
$request->cookies->has('token-expires-at');
// Case 1: User is authenticated but cookies are missing - logout
if ($token && $token->getUser() && !is_string($token->getUser()) && !$hasAuthCookies) {
$event->setResponse($logoutResponse);
return;
}
// Case 2: We have auth cookies - attempt to authenticate/validate
if ($hasAuthCookies) {
$cookieToken = $request->cookies->get('token');
/** @var UserAccessToken|null $accessToken */
$accessToken = $this->accessTokenRepository->findOneBy(['token' => $cookieToken]);
// Invalid token - logout only if user was previously authenticated
if ((!$accessToken || !$accessToken->isValid())) {
if ($token && $token->getUser()) {
$event->setResponse($logoutResponse);
}
return;
}
// Valid token - authenticate the user
if ($accessToken->isValid()) {
// Only create new token if not already properly authenticated
$currentUser = $token ? $token->getUser() : null;
$needsAuthentication = !$currentUser ||
is_string($currentUser) ||
$currentUser->getId() !== $accessToken->getUser()->getId();
if ($needsAuthentication) {
$user = $accessToken->getUser();
$newToken = new UsernamePasswordToken(
$user,
null,
'main',
$user->getRoles()
);
$tokenStorage->setToken($newToken);
// Ensure the session is started and store the token
$session = $request->getSession();
$session->set('_security_main', serialize($newToken));
// Handle redirect after login
$loginUrl = $session->get('login_url');
if ($loginUrl) {
$session->remove('login_url');
$event->setResponse(new RedirectResponse($loginUrl));
}
}
}
}
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 7]
];
}
}