|
<?php |
|
|
|
declare(strict_types = 1); |
|
|
|
namespace App\EventListener; |
|
|
|
use App\Entity\User; |
|
use App\Entity\RefreshToken; |
|
use Doctrine\ORM\EntityManagerInterface; |
|
use Symfony\Component\HttpKernel\KernelEvents; |
|
use Symfony\Component\HttpKernel\Event\RequestEvent; |
|
use Symfony\Component\HttpKernel\Event\ResponseEvent; |
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
|
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException; |
|
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator; |
|
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler; |
|
|
|
class InvalidJWTEventSubscriber implements EventSubscriberInterface |
|
{ |
|
public static function getSubscribedEvents(): array |
|
{ |
|
return [ |
|
KernelEvents::REQUEST => [ |
|
['onRequest', 32], |
|
], |
|
KernelEvents::RESPONSE => [ |
|
['onResponse', 32], |
|
], |
|
]; |
|
} |
|
|
|
public function __construct( |
|
private EntityManagerInterface $entityManager, |
|
private JWTAuthenticator $jWTAuthenticator, |
|
private AuthenticationSuccessHandler $authenticationSuccessHandler, |
|
private string $refreshTokenHeaderName = 'X-Refresh-Token', |
|
private string $authTokenHeaderName = 'X-Authorization-Token' |
|
) { |
|
} |
|
|
|
public function __invoke(RequestEvent $event): void |
|
{ |
|
$request = $event->getRequest(); |
|
$refreshTokenHeaderName = $this->refreshTokenHeaderName; |
|
|
|
try { |
|
if ($request->headers->has($refreshTokenHeaderName)) { |
|
$this->jWTAuthenticator->authenticate($request); |
|
} |
|
} catch (ExpiredTokenException) { |
|
$oldRefreshToken = $request->headers->get($refreshTokenHeaderName); |
|
|
|
$em = $this->entityManager; |
|
|
|
$refreshToken = $em->getRepository(RefreshToken::class)->findOneBy([ |
|
'refreshToken' => $oldRefreshToken, |
|
]); |
|
|
|
if ($refreshTokenEntity && $refreshTokenEntity->isValid()) { |
|
$user = $em->getRepository(User::class)->findOneBy([ |
|
'userName' => $refreshTokenEntity->getUsername(), |
|
]); |
|
|
|
if ($user) { |
|
$newTokens = json_decode( |
|
$this->authenticationSuccessHandler->handleAuthenticationSuccess($user)->getContent(), |
|
true |
|
); |
|
|
|
$token = $newTokens['token']; |
|
|
|
// remove newly created token |
|
$refreshTokenEntity = $em->getRepository(RefreshToken::class)->findOneBy([ |
|
'refreshToken' => $newTokens['refresh_token'], |
|
]); |
|
$em->remove($refreshTokenEntity); |
|
$em->flush(); |
|
|
|
// set headers |
|
$request->headers->set('Authorization', 'Bearer ' . $token); |
|
$request->headers->set($this->authTokenHeaderName, $token); |
|
$request->headers->set($this->refreshTokenHeaderName, $oldRefreshToken); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public function onResponse(ResponseEvent $event): void |
|
{ |
|
$request = $event->getRequest(); |
|
$response = $event->getResponse(); |
|
|
|
$authTokenHeaderName = $this->authTokenHeaderName; |
|
$refreshTokenHeaderName = $this->refreshTokenHeaderName; |
|
|
|
if ($request->headers->has($authTokenHeaderName)) { |
|
$response->headers->set($authTokenHeaderName, $request->headers->get($authTokenHeaderName)); |
|
$response->headers->set($refreshTokenHeaderName, $request->headers->get($refreshTokenHeaderName)); |
|
} |
|
} |
|
} |