src/EventListener/KeycloakRefreshTokenListener.php line 48

Open in your IDE?
  1. <?php
  2. namespace App\EventListener;
  3. use App\Security\Dto\TokensBag;
  4. use App\Service\KeycloakApiService;
  5. use App\Specification\KeycloakModeUsed;
  6. use Psr\Log\LoggerInterface;
  7. use RuntimeException;
  8. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. use Symfony\Component\HttpFoundation\RedirectResponse;
  11. use Symfony\Component\HttpKernel\Event\RequestEvent;
  12. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  13. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  14. use Symfony\Component\Security\Core\User\UserProviderInterface;
  15. use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
  16. class KeycloakRefreshTokenListener implements EventSubscriberInterface
  17. {
  18.     private LoggerInterface $logger;
  19.     private UserProviderInterface $userProvider;
  20.     private TokenStorageInterface $tokenStorage;
  21.     private UrlGeneratorInterface $urlGenerator;
  22.     private KeycloakApiService $keycloakApiService;
  23.     private ParameterBagInterface $param;
  24.     private string|bool $vistoryRedirect;
  25.     private string $mcotPortailRedirect;
  26.     public function __construct(
  27.         LoggerInterface $logger,
  28.         UserProviderInterface $userProvider,
  29.         TokenStorageInterface $tokenStorage,
  30.         UrlGeneratorInterface $urlGenerator,
  31.         KeycloakApiService $keycloakApiService,
  32.         ParameterBagInterface $param,
  33.     ) {
  34.         $this->logger $logger;
  35.         $this->userProvider $userProvider;
  36.         $this->tokenStorage $tokenStorage;
  37.         $this->urlGenerator $urlGenerator;
  38.         $this->keycloakApiService $keycloakApiService;
  39.         $this->param $param;
  40.         $this->vistoryRedirect $this->param->get('vistory_redirect');
  41.         $this->mcotPortailRedirect $this->param->get('mcot_portail_domain_name');
  42.     }
  43.     public function onKernelRequest(RequestEvent $event): void
  44.     {
  45.         $request $event->getRequest();
  46.         $session $request->getSession();
  47.         if (KeycloakModeUsed::isSatisfiedBy($this->param)) {
  48.             $token $this->tokenStorage->getToken();
  49.             if (null === $token) {
  50.                 return;
  51.             }
  52.             $tokens $token->getAttribute(TokensBag::class);
  53.             if (null === $tokens) {
  54.                 throw new \LogicException(sprintf('%s token attribute is empty'TokensBag::class));
  55.             }
  56.             if (time() < $tokens->getJwtExpires()) {
  57.                 return;
  58.             }
  59.             $refreshToken $session->get('oidc_refresh_token');
  60.             try {
  61.                 $response $this->keycloakApiService->getTokenFromRefreshToken($refreshToken);
  62.             } catch (HttpExceptionInterface $e) {
  63.                 $response $e->getResponse();
  64.                 if (400 === $response->getStatusCode() && 'invalid_grant' === ($response->toArray(false)['error'] ?? null)) {
  65.                     // Logout when SSO session idle is reached
  66.                     $this->tokenStorage->setToken(null);
  67.                     $redirect $this->vistoryRedirect == 'true' $this->urlGenerator->generate('login') : $this->mcotPortailRedirect;
  68.                     $event->setResponse(new RedirectResponse($redirect));
  69.                     return;
  70.                 }
  71.                 throw new RuntimeException(
  72.                     sprintf('Bad status code returned by openID server (%s)'$e->getResponse()->getStatusCode()),
  73.                     previous$e,
  74.                 );
  75.             }
  76.             $responseData json_decode($responsetrue);
  77.             if (false === $responseData) {
  78.                 throw new RuntimeException(sprintf('Can\'t parse json in response: %s'$response));
  79.             }
  80.             $jwtToken $responseData['id_token'] ?? null;
  81.             if (null === $jwtToken) {
  82.                 throw new RuntimeException(sprintf('No access token found in response %s'$response));
  83.             }
  84.             $refreshToken $responseData['refresh_token'] ?? null;
  85.             if (null === $refreshToken) {
  86.                 throw new RuntimeException(sprintf('No refresh token found in response %s'$response));
  87.             }
  88.             $this->logger->info(">>>>>>>>>>>>>>>>>> DEBUT REFRESH TOKEN >>>>>>>>>>>>>>>>>>");
  89.             $this->logger->debug($responseData['refresh_token']);
  90.             $this->logger->info("<<<<<<<<<<<<<<<<<< FIN REFRESH TOKEN <<<<<<<<<<<<<<<<<<");
  91.             $session->remove('oidc_access_token');
  92.             $session->remove('oidc_refresh_token');
  93.             $session->remove('oidc_expires_token');
  94.             $session->remove('oidc_expires_refresh_token');
  95.             $session->set('oidc_access_token'$responseData['access_token']);
  96.             $session->set('oidc_refresh_token'$responseData['refresh_token']);
  97.             $session->set('oidc_expires_token'$responseData['expires_in']);
  98.             $session->set('oidc_expires_refresh_token'$responseData['refresh_expires_in']);
  99.         }
  100.     }
  101.     public static function getSubscribedEvents(): array
  102.     {
  103.         return [RequestEvent::class => 'onKernelRequest'];
  104.     }
  105. }