<?php
namespace App\Security;
use App\Entity\ApiToken;
use Doctrine\ORM\EntityManagerInterface;
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\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class ApiKeyAuthenticator extends AbstractAuthenticator
{
protected $_em;
public function __construct(EntityManagerInterface $em)
{
$this->_em = $em;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('Authorization') && str_starts_with($request->headers->get('Authorization'), 'Bearer');
}
public function authenticate(Request $request): Passport
{
$authorizationHeader = $request->headers->get('Authorization');
if (null === $authorizationHeader) {
// The token header was empty, authentication fails with HTTP Status
// Code 401 "Unauthorized"
throw new CustomUserMessageAuthenticationException('No API token provided');
}
$apiToken = substr($authorizationHeader, 7);
return new SelfValidatingPassport(new UserBadge($apiToken, function ($userIdentifier) {
$token = $this->_em->getRepository(ApiToken::class)->findOneBy([
'token' => $userIdentifier,
]);
if (!$token) {
throw new CustomUserMessageAuthenticationException(
'Invalid API Token'
);
}
if ($token->isExpired()) {
throw new CustomUserMessageAuthenticationException(
'Token expired'
);
}
return $token->getUser();
}));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response
{
return null;
}
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())
];
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
}