<?php
declare(strict_types=1);
namespace Slivki\Security;
use Slivki\Entity\User;
use Slivki\Security\Provider\UserProvider;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\HttpUtils;
final class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
private HttpUtils $httpUtils;
private UserProvider $userProvider;
private string $baseDomain;
public function __construct(HttpUtils $httpUtils, UserProvider $userProvider, string $baseDomain)
{
$this->httpUtils = $httpUtils;
$this->userProvider = $userProvider;
$this->baseDomain = $baseDomain;
}
protected function getLoginUrl(Request $request): string
{
return $this->httpUtils->generateUri($request, 'login');
}
public function supports(Request $request): bool
{
return 'login' === $request->attributes->get('_route') && $request->isMethod('POST');
}
public function authenticate(Request $request): PassportInterface
{
$credentials = $this->getCredentials($request);
return new Passport(
new UserBadge($credentials['email'], fn (): ?User => $this->userProvider->loadUserByUsername($credentials['email'])),
new PasswordCredentials($credentials['password']),
[new RememberMeBadge()],
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$cookie = null;
if ($token->getUser() instanceof User) {
$cookie = Cookie::create(
SlivkiAuthenticator::USER_COOKIE,
$token->getUser()->getToken(),
time() + 315360000,
'/',
$this->baseDomain,
);
}
if ($request->isXmlHttpRequest()) {
$response = new JsonResponse();
if (null !== $cookie) {
$response->headers->setCookie($cookie);
}
$response->setData(['result' => true]);
return $response;
}
$response = $this->httpUtils->createRedirectResponse($request, 'homepage', Response::HTTP_FOUND);
if (null !== $cookie) {
$response->headers->setCookie($cookie);
}
return $response;
}
private function getCredentials(Request $request): array
{
$credentials = [
'email' => $request->request->get('email'),
'password' => (string) $request->request->get('password'),
];
if (!\is_string($credentials['email'])) {
throw new BadRequestHttpException(sprintf('The key "email" must be a string, "%s" given.', \gettype($credentials['email'])));
}
$credentials['email'] = \trim($credentials['email']);
return $credentials;
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
return $this->httpUtils->createRedirectResponse($request, 'login', Response::HTTP_FOUND);
}
}