<?php
namespace App\Infrastructure\Security\Auth;
use App\Application\EventBus\Event\User\UserAuthenticatedEvent;
use App\Database\Domain\Entity\User\User;
use App\Database\Domain\Entity\User\UserAccessToken;
use App\Database\Domain\Repository\UserRepository;
use App\Infrastructure\Security\Factory\SecurityCookieFactory;
use App\Infrastructure\Messenger\EventBus\EventBusInterface;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
class SiteAuthenticator extends AbstractAuthenticator
{
private UserRepository $userRepository;
private RouterInterface $router;
private ManagerRegistry $managerRegistry;
private UrlGeneratorInterface $urlGenerator;
private EventBusInterface $eventBus;
public function __construct(
UserRepository $userRepository,
RouterInterface $router,
ManagerRegistry $managerRegistry,
UrlGeneratorInterface $urlGenerator,
EventBusInterface $eventBus
) {
$this->userRepository = $userRepository;
$this->router = $router;
$this->managerRegistry = $managerRegistry;
$this->urlGenerator = $urlGenerator;
$this->eventBus = $eventBus;
}
public function supports(Request $request): ?bool
{
return 'user_login' === $request->get('_route') && $request->isMethod('POST');
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get('login');
$password = $request->request->get('password');
return new Passport(
new UserBadge($username, function($userIdentifier) {
$user = $this->userRepository->findOneBy(['username' => $userIdentifier]);
if (!$user) {
throw new UserNotFoundException();
}
return $user;
}),
new PasswordCredentials($password),
[
new CsrfTokenBadge(
'authenticate',
$request->request->get('_csrf_token')
),
(new RememberMeBadge())->enable(),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$accessToken = UserAccessToken::twoMonths($token->getUser());
$entityManager = $this->managerRegistry->getManager();
$entityManager->persist($accessToken);
$entityManager->flush();
$requestSession = $request->getSession();
$redirectUrl = $requestSession->get('is_safari', false)
? $this->urlGenerator->generate('user_login_succeed')
: $request->getPathInfo();
if ($requestSession->get('redirect-uri') == 'ios-login') {
$redirectUrl = $this->urlGenerator->generate('user_ios_login_index');
} elseif (null !== $requestSession->get('redirect-uri')) {
$redirectUrl = $requestSession->get('redirect-uri');
}
if (null !== $requestSession->get('login_url')) {
$redirectUrl = $requestSession->get('login_url');
$requestSession->remove('login_url');
}
$response = new RedirectResponse($redirectUrl);
$response->headers->setCookie(SecurityCookieFactory::token($accessToken));
$response->headers->setCookie(SecurityCookieFactory::expiresAt($accessToken));
$this->eventBus->fire(new UserAuthenticatedEvent($token->getUser()));
return $response;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$requestSession = $request->getSession();
$requestSession->set(Security::AUTHENTICATION_ERROR, $exception);
$params = [
'safari' => $requestSession->get('is_safari') ? 'true' : 'false',
'redirect-uri' => $requestSession->get('redirect-uri'),
];
return new RedirectResponse(
$this->urlGenerator->generate('user_login', $params)
);
}
public function start(Request $request, ?AuthenticationException $authException = null): ?RedirectResponse
{
$route = $request->get('_route');
$routeParams = $request->get('_route_params');
$query = $request->query->all();
if ($specificRouteAfterLogin = $query['specific_route_after_login'] ?? null) {
$request->getSession()->set('login_url', $specificRouteAfterLogin);
}
if ($route === 'user_take_gift' || $route === 'user_stripe_invoke_subscription') {
$loginUrl = $this->urlGenerator->generate($route, array_merge($routeParams, $query));
$request->getSession()->set('login_url', $loginUrl);
}
return new RedirectResponse($this->getLoginUrl());
}
protected function getLoginUrl()
{
$session = $this->requestStack->getCurrentRequest()->getSession();
$params = [
'safari' => $session->get('is_safari') ? 'true' : 'false',
'redirect-uri' => $session->get('redirect-uri'),
];
return $this->urlGenerator->generate('user_login', $params);
}
}