<?php
namespace App\Security;
use App\Repository\UserRepository;
use App\Utils\UserUtils;
use Doctrine\ORM\EntityManagerInterface;
use Evo\Infrastructure\MappingORM\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
private const ROUTE_NAME = 'api_auth';
private EntityManagerInterface $em;
private UserPasswordEncoderInterface $passwordEncoder;
private UserUtils $userUtils;
private Security $security;
public function __construct(EntityManagerInterface $em, UserPasswordEncoderInterface $passwordEncoder, UserUtils $userUtils, Security $security)
{
$this->em = $em;
$this->passwordEncoder = $passwordEncoder;
$this->userUtils = $userUtils;
$this->security = $security;
}
/**
* Called on every request to decide if this authenticator should be
* used for the request. Returning `false` will cause this authenticator
* to be skipped.
*/
public function supports(Request $request): bool
{
return self::ROUTE_NAME === $request->attributes->get('_route') && 'POST' === $request->getMethod();
}
/**
* Called on every request. Return whatever credentials you want to
* be passed to getUser() as $credentials.
*/
public function getCredentials(Request $request)
{
$body = json_decode($request->getContent(), null, 512, JSON_THROW_ON_ERROR);
if (null === $body) {
return [];
}
$credentials = [
'username' => $body->username,
'password' => $body->password,
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider): ?UserInterface
{
if (empty($credentials)) {
// The token header was empty, authentication fails with HTTP Status
// Code 401 "Unauthorized"
return null;
}
// The user identifier in this case is the apiToken, see the key `property`
// of `your_db_provider` in `security.yaml`.
// If this returns a user, checkCredentials() is called next:
/** @var UserRepository $userRepository */
$userRepository = $this->em->getRepository(User::class);
return $userRepository->loadUserByUsername($credentials['username'], true);
}
public function checkCredentials($credentials, UserInterface $user): bool
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$data = [
// you may want to customize or obfuscate the message first
'message' => strtr($exception->getMessageKey(), $exception->getMessageData()),
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
];
$data['message'] .= ' Or your user is deactivated';
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
/**
* Called when authentication is needed, but it's not sent.
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
$data = [
// you might translate this message
'message' => 'Authentication Required',
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
public function supportsRememberMe(): bool
{
return false;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($request->query->get('da')) {
$code = $this->userUtils->generateCodeConfirmation();
/** @var User $user */
$user = $this->security->getUser();
$user->setCodeConfirmation($code);
$this->em->persist($user);
$this->em->flush();
}
return null;
}
}