<?php
namespace App\Security;
use App\Helper\UnaccentedHelper;
use App\Infrastructure\RequestDto\LoginDto;
use App\Infrastructure\Service\Impl\LoginServiceImpl;
use App\Infrastructure\Traits\CookiesHelper;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
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\Credentials\CustomCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
class ApiAuthenticator extends AbstractAuthenticator
{
use CookiesHelper;
public const LOGIN_ROUTE = 'app_login';
private LoginServiceImpl $loginService;
private UrlGeneratorInterface $urlGenerator;
protected LoginDto $authenticatedUser;
protected UnaccentedHelper $unaccentedHelper;
protected SessionInterface $session;
/**
* @param LoginServiceImpl $loginService
* @param UrlGeneratorInterface $urlGenerator
* @param UnaccentedHelper $unaccentedHelper
* @param RequestStack $requestStack
*/
public function __construct(
LoginServiceImpl $loginService,
UrlGeneratorInterface $urlGenerator,
UnaccentedHelper $unaccentedHelper,
RequestStack $requestStack
) {
$this->loginService = $loginService;
$this->urlGenerator = $urlGenerator;
$this->unaccentedHelper = $unaccentedHelper;
$this->session = $requestStack->getSession();
}
public function supports(Request $request): ?bool
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function authenticate(Request $request): Passport
{
$loginDto = new LoginDto();
$loginDto->setUsername($request->get('login_form')['username']);
$loginDto->setPassword($request->get('login_form')['password']);
$apiUser = $this->loginService->login($loginDto);
if (empty($apiUser)) {
throw new CustomUserMessageAuthenticationException('No API token provided');
}
$this->setAuthenticatedUser(LoginDto::createFromArray($apiUser));
return new Passport(
new UserBadge($loginDto->getUsername()),
new CustomCredentials(
function ($credentials, LoginDto $user) {
if (!$this->getAuthenticatedUser()) {
throw new UserNotFoundException();
}
LoginDto::map($user, $this->getAuthenticatedUser());
return $user->getUserIdentifier() === $credentials;
},
$loginDto->getUsername()
)
);
}
public function onAuthenticationSuccess(
Request $request,
TokenInterface $token,
string $firewallName
): ?RedirectResponse {
/** @var LoginDto $user */
$user = $token->getUser();
$this->updateSecurityCookies($user->toArray());
return new RedirectResponse($this->urlGenerator->generate('app_redirect'));
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
return new RedirectResponse($this->urlGenerator->generate('app_login'));
}
/**
* @return LoginDto
*/
public function getAuthenticatedUser(): LoginDto
{
return $this->authenticatedUser;
}
/**
* @param LoginDto $authenticatedUser
*/
public function setAuthenticatedUser(LoginDto $authenticatedUser): void
{
$this->authenticatedUser = $authenticatedUser;
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}