<?php
namespace App\Security\Account;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Nellapp\Bundle\SDKBundle\Auth\Entity\UserInterface as NellappUserInterface;
use Nellapp\Bundle\SDKBundle\Auth\Messenger\Message\RefreshUserMessage;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class AccountAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(
private EntityManagerInterface $em,
private UrlGeneratorInterface $urlGenerator,
private CsrfTokenManagerInterface $csrfTokenManager,
private UserPasswordHasherInterface $passwordHasher,
private MessageBusInterface $bus,
)
{
}
public function supports(Request $request): bool
{
return
self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request): array
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email'],
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider): UserInterface
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $userProvider->loadUserByIdentifier($credentials['email']);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user): bool
{
if (!$user instanceof PasswordAuthenticatedUserInterface) {
return false;
}
return $this->passwordHasher->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): Response
{
$user = $token->getUser();
if ($user instanceof NellappUserInterface) {
$this->bus->dispatch(new RefreshUserMessage($user->getId()));
}
if ($request->request->get('_target_path') !== null) {
return new RedirectResponse($request->request->get('_target_path'));
}
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse('/');
}
protected function getLoginUrl(): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}