vendor/symfony/security-http/Authenticator/AccessTokenAuthenticator.php line 35

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Http\Authenticator;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  14. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  15. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  16. use Symfony\Component\Security\Core\User\UserProviderInterface;
  17. use Symfony\Component\Security\Http\AccessToken\AccessTokenExtractorInterface;
  18. use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
  19. use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
  20. use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
  21. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  22. use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
  23. use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;
  24. use Symfony\Contracts\Translation\TranslatorInterface;
  25. /**
  26.  * Provides an implementation of the RFC6750 of an authentication via
  27.  * an access token.
  28.  *
  29.  * @author Florent Morselli <florent.morselli@spomky-labs.com>
  30.  */
  31. class AccessTokenAuthenticator implements AuthenticatorInterface
  32. {
  33.     private ?TranslatorInterface $translator null;
  34.     public function __construct(
  35.         private readonly AccessTokenHandlerInterface $accessTokenHandler,
  36.         private readonly AccessTokenExtractorInterface $accessTokenExtractor,
  37.         private readonly ?UserProviderInterface $userProvider null,
  38.         private readonly ?AuthenticationSuccessHandlerInterface $successHandler null,
  39.         private readonly ?AuthenticationFailureHandlerInterface $failureHandler null,
  40.         private readonly ?string $realm null,
  41.     ) {
  42.     }
  43.     public function supports(Request $request): ?bool
  44.     {
  45.         return null === $this->accessTokenExtractor->extractAccessToken($request) ? false null;
  46.     }
  47.     public function authenticate(Request $request): Passport
  48.     {
  49.         $accessToken $this->accessTokenExtractor->extractAccessToken($request);
  50.         if (!$accessToken) {
  51.             throw new BadCredentialsException('Invalid credentials.');
  52.         }
  53.         $userBadge $this->accessTokenHandler->getUserBadgeFrom($accessToken);
  54.         if (null === $userBadge->getUserLoader() && $this->userProvider) {
  55.             $userBadge->setUserLoader($this->userProvider->loadUserByIdentifier(...));
  56.         }
  57.         return new SelfValidatingPassport($userBadge);
  58.     }
  59.     public function createToken(Passport $passportstring $firewallName): TokenInterface
  60.     {
  61.         return new PostAuthenticationToken($passport->getUser(), $firewallName$passport->getUser()->getRoles());
  62.     }
  63.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?Response
  64.     {
  65.         return $this->successHandler?->onAuthenticationSuccess($request$token);
  66.     }
  67.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception): Response
  68.     {
  69.         if (null !== $this->failureHandler) {
  70.             return $this->failureHandler->onAuthenticationFailure($request$exception);
  71.         }
  72.         if (null !== $this->translator) {
  73.             $errorMessage $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security');
  74.         } else {
  75.             $errorMessage strtr($exception->getMessageKey(), $exception->getMessageData());
  76.         }
  77.         return new Response(
  78.             null,
  79.             Response::HTTP_UNAUTHORIZED,
  80.             ['WWW-Authenticate' => $this->getAuthenticateHeader($errorMessage)]
  81.         );
  82.     }
  83.     public function setTranslator(?TranslatorInterface $translator)
  84.     {
  85.         $this->translator $translator;
  86.     }
  87.     /**
  88.      * @see https://datatracker.ietf.org/doc/html/rfc6750#section-3
  89.      */
  90.     private function getAuthenticateHeader(string $errorDescription null): string
  91.     {
  92.         $data = [
  93.             'realm' => $this->realm,
  94.             'error' => 'invalid_token',
  95.             'error_description' => $errorDescription,
  96.         ];
  97.         $values = [];
  98.         foreach ($data as $k => $v) {
  99.             if (null === $v || '' === $v) {
  100.                 continue;
  101.             }
  102.             $values[] = sprintf('%s="%s"'$k$v);
  103.         }
  104.         return sprintf('Bearer %s'implode(','$values));
  105.     }
  106. }