vendor/lexik/jwt-authentication-bundle/Services/JWTManager.php line 136

Open in your IDE?
  1. <?php
  2. namespace Lexik\Bundle\JWTAuthenticationBundle\Services;
  3. use Lexik\Bundle\JWTAuthenticationBundle\Encoder\HeaderAwareJWTEncoderInterface;
  4. use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
  5. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
  6. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTDecodedEvent;
  7. use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTEncodedEvent;
  8. use Lexik\Bundle\JWTAuthenticationBundle\Events;
  9. use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
  10. use Lexik\Bundle\JWTAuthenticationBundle\Services\PayloadEnrichment\NullEnrichment;
  11. use Symfony\Component\PropertyAccess\PropertyAccess;
  12. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  13. use Symfony\Component\Security\Core\User\InMemoryUser;
  14. use Symfony\Component\Security\Core\User\UserInterface;
  15. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  16. /**
  17. * Provides convenient methods to manage JWT creation/verification.
  18. *
  19. * @author Nicolas Cabot <n.cabot@lexik.fr>
  20. * @author Robin Chalas <robin.chalas@gmail.com>
  21. */
  22. class JWTManager implements JWTManagerInterface, JWTTokenManagerInterface
  23. {
  24. /**
  25. * @var JWTEncoderInterface
  26. */
  27. protected $jwtEncoder;
  28. /**
  29. * @var EventDispatcherInterface
  30. */
  31. protected $dispatcher;
  32. /**
  33. * @var string
  34. *
  35. * @deprecated since v2.15
  36. */
  37. protected $userIdentityField;
  38. /**
  39. * @var string
  40. */
  41. protected $userIdClaim;
  42. /**
  43. * @var PayloadEnrichmentInterface
  44. */
  45. private $payloadEnrichment;
  46. /**
  47. * @param string|null $userIdClaim
  48. */
  49. public function __construct(JWTEncoderInterface $encoder, EventDispatcherInterface $dispatcher, $userIdClaim = null, PayloadEnrichmentInterface $payloadEnrichment = null)
  50. {
  51. $this->jwtEncoder = $encoder;
  52. $this->dispatcher = $dispatcher;
  53. $this->userIdentityField = 'username';
  54. $this->userIdClaim = $userIdClaim;
  55. $this->payloadEnrichment = $payloadEnrichment ?? new NullEnrichment();
  56. }
  57. /**
  58. * @return string The JWT token
  59. */
  60. public function create(UserInterface $user): string
  61. {
  62. $payload = ['roles' => $user->getRoles()];
  63. $this->addUserIdentityToPayload($user, $payload);
  64. $this->payloadEnrichment->enrich($user, $payload);
  65. return $this->generateJwtStringAndDispatchEvents($user, $payload);
  66. }
  67. /**
  68. * @return string The JWT token
  69. */
  70. public function createFromPayload(UserInterface $user, array $payload): string
  71. {
  72. $payload = array_merge(['roles' => $user->getRoles()], $payload);
  73. $this->addUserIdentityToPayload($user, $payload);
  74. $this->payloadEnrichment->enrich($user, $payload);
  75. return $this->generateJwtStringAndDispatchEvents($user, $payload);
  76. }
  77. /**
  78. * @return string The JWT token
  79. */
  80. private function generateJwtStringAndDispatchEvents(UserInterface $user, array $payload): string
  81. {
  82. $jwtCreatedEvent = new JWTCreatedEvent($payload, $user);
  83. $this->dispatcher->dispatch($jwtCreatedEvent, Events::JWT_CREATED);
  84. if ($this->jwtEncoder instanceof HeaderAwareJWTEncoderInterface) {
  85. $jwtString = $this->jwtEncoder->encode($jwtCreatedEvent->getData(), $jwtCreatedEvent->getHeader());
  86. } else {
  87. $jwtString = $this->jwtEncoder->encode($jwtCreatedEvent->getData());
  88. }
  89. $jwtEncodedEvent = new JWTEncodedEvent($jwtString);
  90. $this->dispatcher->dispatch($jwtEncodedEvent, Events::JWT_ENCODED);
  91. return $jwtString;
  92. }
  93. /**
  94. * {@inheritdoc}
  95. * @throws JWTDecodeFailureException
  96. */
  97. public function decode(TokenInterface $token)
  98. {
  99. if (!($payload = $this->jwtEncoder->decode($token->getCredentials()))) {
  100. return false;
  101. }
  102. $event = new JWTDecodedEvent($payload);
  103. $this->dispatcher->dispatch($event, Events::JWT_DECODED);
  104. if (!$event->isValid()) {
  105. return false;
  106. }
  107. return $event->getPayload();
  108. }
  109. /**
  110. * {@inheritdoc}
  111. */
  112. public function parse(string $jwtToken): array
  113. {
  114. $payload = $this->jwtEncoder->decode($jwtToken);
  115. $event = new JWTDecodedEvent($payload);
  116. $this->dispatcher->dispatch($event, Events::JWT_DECODED);
  117. if (!$event->isValid()) {
  118. throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'The token was marked as invalid by an event listener after successful decoding.');
  119. }
  120. return $event->getPayload();
  121. }
  122. /**
  123. * Add user identity to payload, username by default.
  124. * Override this if you need to identify it by another property.
  125. *
  126. * @param array &$payload
  127. */
  128. protected function addUserIdentityToPayload(UserInterface $user, array &$payload)
  129. {
  130. $accessor = PropertyAccess::createPropertyAccessor();
  131. $identityField = $this->userIdClaim ?: $this->userIdentityField;
  132. if ($user instanceof InMemoryUser && 'username' === $identityField) {
  133. $payload[$identityField] = $accessor->getValue($user, 'userIdentifier');
  134. return;
  135. }
  136. $payload[$identityField] = $accessor->getValue($user, $accessor->isReadable($user, $identityField) ? $identityField : 'user_identifier');
  137. }
  138. /**
  139. * {@inheritdoc}
  140. */
  141. public function getUserIdentityField(): string
  142. {
  143. if (0 === func_num_args() || func_get_arg(0)) {
  144. trigger_deprecation('lexik/jwt-authentication-bundle', '2.15', 'The "%s()" method is deprecated.', __METHOD__);
  145. }
  146. return $this->userIdentityField;
  147. }
  148. /**
  149. * {@inheritdoc}
  150. */
  151. public function setUserIdentityField($field)
  152. {
  153. if (1 >= func_num_args() || func_get_arg(1)) {
  154. trigger_deprecation('lexik/jwt-authentication-bundle', '2.15', 'The "%s()" method is deprecated.', __METHOD__);
  155. }
  156. $this->userIdentityField = $field;
  157. }
  158. /**
  159. * @return string
  160. */
  161. public function getUserIdClaim(): ?string
  162. {
  163. return $this->userIdClaim;
  164. }
  165. }