src/Security/TokenAuthenticator.php line 25

Open in your IDE?
  1. <?php
  2. namespace App\Security;
  3. use App\Business\User;
  4. use App\Security\User\ApiUserProvider;
  5. use Es\FootprintBundle\Services\Auth;
  6. use Symfony\Component\HttpFoundation\JsonResponse;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  10. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  11. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  12. use Symfony\Component\Security\Core\User\UserInterface;
  13. use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
  14. use Symfony\Component\Security\Core\User\UserProviderInterface;
  15. use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
  16. use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
  17. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  18. use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
  19. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  20. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  21. use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
  22. class TokenAuthenticator extends AbstractAuthenticator
  23. {
  24.     private $es_auth_stateless;
  25.     private $apiUserProvider;
  26.     private $clientRegistry;
  27.     private $tokenStorage;
  28.     private $session;
  29.     public function __construct(Auth $authClientRegistry $clientRegistryTokenStorageInterface $tokenStorageSessionInterface $session)
  30.     {
  31.         $this->es_auth_stateless $auth;
  32.         $this->clientRegistry $clientRegistry;
  33.         $this->tokenStorage $tokenStorage;
  34.         $this->session $session;
  35.         $this->apiUserProvider = new ApiUserProvider($this->es_auth_stateless);
  36.     }
  37.     /**
  38.      * Called on every request to decide if this authenticator should be
  39.      * used for the request. Returning `false` will cause this authenticator
  40.      * to be skipped.
  41.      */
  42.     public function supports(Request $request): ?bool
  43.     {
  44.         return true;
  45.     }
  46.     public function authenticate(Request $request): Passport
  47.     {
  48.         $result $this->apiUserProvider->loadUserByIdentifier($request->getUri());
  49.         if (null === $result) {
  50.             // The token header was empty, authentication fails with HTTP Status
  51.             // Code 401 "Unauthorized"
  52.             throw new CustomUserMessageAuthenticationException('No API token provided');
  53.         }
  54.         $passport = new SelfValidatingPassport(new UserBadge($request->getUri()), []);
  55.         return $passport;
  56.     }
  57.     public function getUser($credentialsUserProviderInterface $userProvider)
  58.     {
  59.         $apiKey $credentials['url'];
  60.         return $userProvider->loadUserByUsername($apiKey);
  61.     }
  62.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?Response
  63.     {
  64.         $user $token->getUser();
  65.         if ($user instanceof User) {
  66.             $this->checkAndRefreshToken($user);
  67.         }
  68.         return null;
  69.     }
  70.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception): ?Response
  71.     {
  72.         $data = [
  73.             // you may want to customize or obfuscate the message first
  74.             'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
  75.             // or to translate this message
  76.             // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
  77.         ];
  78.         return new JsonResponse($dataResponse::HTTP_UNAUTHORIZED);
  79.     }
  80.     /**
  81.      * Called when authentication is needed, but it's not sent
  82.      */
  83.     public function start(Request $requestAuthenticationException $authException null)
  84.     {
  85.         $data = [
  86.             'message' => 'Authentication Required'
  87.         ];
  88.         if (!$request->get('myWebServiceID') && !$request->headers->get('myWebServiceID')) {
  89.             $data = [' message' => 'User WebServiceID doesn\' exist!'];
  90.         }
  91.         if (!$request->get('footprint') && !$request->headers->get('footprint')) {
  92.             $data = ['message' => 'User FootPrint is missing!'];
  93.         }
  94.         return new JsonResponse($dataResponse::HTTP_UNAUTHORIZED);
  95.     }
  96.     private function getAzureClient()
  97.     {
  98.         return $this->clientRegistry
  99.             // "facebook_main" is the key used in config/packages/knpu_oauth2_client.yaml
  100.             ->getClient('entra_id_external');
  101.     }
  102.     public function checkAndRefreshToken(UserInterface $user): UserInterface
  103.     {
  104.         if ($this->isTokenExpired($user)) {
  105.             return $this->refreshToken($user);
  106.         }
  107.         return $user;
  108.     }
  109.     private function isTokenExpired(UserInterface $user): bool
  110.     {
  111.         $expiresAt $user->getExpires();
  112.         return $expiresAt <= (new \DateTime())->getTimestamp();
  113.     }
  114.     public function refreshToken(UserInterface $user): UserInterface
  115.     {
  116.         $refreshToken $user->getRefreshToken();
  117.         // Récupérer le client OAuth2
  118.         $client $this->getAzureClient();
  119.         try {
  120.             // Rafraîchir le token
  121.             $newAccessToken $client->refreshAccessToken($refreshToken);
  122.         } catch (IdentityProviderException $e) {
  123.             // Déconnexion de l'utilisateur si le rafraîchissement échoue
  124.             $this->logoutUser();
  125.             throw new AuthenticationException('Failed to refresh access token and user has been logged out.'0$e);
  126.         }
  127.         // Mettre à jour l'utilisateur avec les nouveaux tokens
  128.         $user->setAccessToken($newAccessToken->getToken());
  129.         $user->setRefreshToken($newAccessToken->getRefreshToken() ?? $refreshToken); // Garder l'ancien si pas fourni
  130.         $user->setExpires(((new \DateTime())->setTimestamp($newAccessToken->getExpires()))->getTimestamp());
  131.         return $user;
  132.     }
  133.     private function logoutUser(): void
  134.     {
  135.         // Invalidate the session and clear the security token
  136.         $this->tokenStorage->setToken(null);
  137.         $this->session->invalidate();
  138.     }
  139.     public function supportsRememberMe()
  140.     {
  141.         return false;
  142.     }
  143. }