<?php
namespace Platform\SecurityBundle\Service\Authenticator;
use Cms\CoreBundle\Util\Doctrine\EntityManager;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidPayloadException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator as BaseJWTAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\ChainTokenExtractor;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\QueryParameterTokenExtractor;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Platform\SecurityBundle\Doctrine\Identity\AccountRepository;
use Platform\SecurityBundle\Entity\Identity\Account;
use Platform\SecurityBundle\Service\AccountProvider;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use LogicException;
class JWTAuthenticator extends BaseJWTAuthenticator
{
/**
* @var EntityManager
*/
protected EntityManager $entityManager;
/**
* @var JWTTokenManagerInterface
*/
protected JWTTokenManagerInterface $jwtTokenManager;
/**
* @param EntityManager $entityManager
* @param JWTTokenManagerInterface $jwtManager
* @param EventDispatcherInterface $eventDispatcher
* @param TokenExtractorInterface $tokenExtractor
* @param AccountProvider $accountProvider
* @param TranslatorInterface $translator
*/
public function __construct(
EntityManager $entityManager,
JWTTokenManagerInterface $jwtManager,
EventDispatcherInterface $eventDispatcher,
TokenExtractorInterface $tokenExtractor,
AccountProvider $accountProvider,
TranslatorInterface $translator
)
{
parent::__construct(
$jwtManager,
$eventDispatcher,
$tokenExtractor,
$accountProvider,
$translator
);
$this->entityManager = $entityManager;
$this->jwtTokenManager = $jwtManager;
}
/**
* @return TokenExtractorInterface
*/
protected function getTokenExtractor(): TokenExtractorInterface
{
$chainExtractor = parent::getTokenExtractor();
if ( ! $chainExtractor instanceof ChainTokenExtractor) {
throw new LogicException();
}
$chainExtractor->clearMap();
$chainExtractor->addExtractor(new QueryParameterTokenExtractor('token'));
return $chainExtractor;
}
/**
* @param Request $request
* @return Passport
*/
public function doAuthenticate(Request $request): Passport
{
$token = $this->getTokenExtractor()->extract($request);
if ($token === false) {
throw new LogicException('Unable to extract a JWT token from the request. Also, make sure to call `supports()` before `authenticate()` to get a proper client error.');
}
try {
if (!$payload = $this->jwtTokenManager->parse($token)) {
throw new InvalidTokenException('Invalid JWT Token');
}
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
$uid = $payload['id'] ?? null;
if (empty($uid)) {
throw new InvalidPayloadException('id');
}
$passport = new SelfValidatingPassport(
new UserBadge($uid, function ($identifier) {
/** @var AccountRepository $accountRepository */
$accountRepository = $this->entityManager->getRepository(Account::class);
return $accountRepository->findOneBy(['internalUid' => $identifier]);
})
);
$passport->setAttribute('payload', []);
$passport->setAttribute('token', $token);
return $passport;
}
}