<?php
namespace App\Infrastructure\Security\Auth;
use App\Application\EventBus\Event\User\UserAuthenticatedEvent;
use App\Database\Domain\Repository\AccessTokenRepository;
use App\Database\Domain\Repository\UserRepository;
use App\Infrastructure\Messenger\EventBus\EventBusInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
class TokenAuthenticator extends AbstractAuthenticator
{
private AccessTokenRepository $accessTokenRepository;
private EventBusInterface $eventBus;
private UserRepository $userRepository;
public function __construct(
AccessTokenRepository $accessTokenRepository,
EventBusInterface $eventBus,
UserRepository $userRepository
) {
$this->accessTokenRepository = $accessTokenRepository;
$this->eventBus = $eventBus;
$this->userRepository = $userRepository;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('X-AUTH-TOKEN') || $request->headers->has('X-NOT-REGISTERED-TOKEN');
}
public function authenticate(Request $request): ?Passport
{
if ($request->headers->get('X-AUTH-TOKEN')) {
$userBadge = new UserBadge($request->headers->get('X-AUTH-TOKEN'), function($token) {
return $this->accessTokenRepository->findByToken($token)->getUser();
});
} else {
$userBadge = new UserBadge($request->headers->get('X-NOT-REGISTERED-TOKEN'), function($token) {
return $this->userRepository->findOneBy(['notRegisteredToken' => $token]);
});
}
return new Passport(
$userBadge,
new CustomCredentials(function($input) {
return true;
}, null)
);
throw new CustomUserMessageAuthenticationException('Failed to authenticate with given headers');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$this->eventBus->fire(new UserAuthenticatedEvent($token->getUser()));
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
{
return new JsonResponse(['errors' => [$exception->getMessage()]], Response::HTTP_FORBIDDEN);
}
public function start(Request $request, ?AuthenticationException $authException = null): JsonResponse
{
return new JsonResponse(['message' => $authException->getMessage()], Response::HTTP_UNAUTHORIZED);
}
}