-
-
Notifications
You must be signed in to change notification settings - Fork 617
Open
Description
Today JWTAuthenticator is a monolitic AbstractAuthenticator which:
- Extracts JWT from request
- Parse and validate JWT
- Creates
UserBadge - Creates
SelfValidatingPassport
Since 6.4, Symfony can authenticate an AccessToken and already handles the SelfValidatingPassport part natively: https://symfony.com/doc/6.4/security/access_token.html
Symfony's Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator already:
- Extracts JWT from request
- Delegates
UserBadgecreation to aAccessTokenHandlerInterface - Creates
SelfValidatingPassport
Could we split the authentication logic into 2 parts:
- An
AccessTokenHandlerInterfaceimplementation - Keep
JWTAuthenticatorwhich will useAccessTokenHandlerInterfaceimplementation for backward compatibility
This will allow to use native Symfony AccessTokenAuthenticator but with lexik AccessTokenHandlerInterface implementation.
For example:
<?php
declare(strict_types=1);
namespace App\Security\AccessToken;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidPayloadException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\PayloadAwareUserProviderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\ChainUserProvider;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
/*
* Extract LexikJWTAuthenticationBundle UserBadge logic to be used with Symfony Access Token system.
*/
final readonly class JWTTokenHandler implements AccessTokenHandlerInterface
{
public function __construct(
#[Autowire(service: 'security.user_providers')]
private UserProviderInterface $userProvider,
private JWTTokenManagerInterface $jwtManager,
) {
}
public function getUserBadgeFrom(#[\SensitiveParameter] string $accessToken): UserBadge
{
try {
if (!$payload = $this->jwtManager->parse($accessToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
$idClaim = $this->jwtManager->getUserIdClaim();
if (!isset($payload[$idClaim])) {
throw new InvalidPayloadException($idClaim);
}
return new UserBadge(
(string) $payload[$idClaim],
fn ($userIdentifier) => $this->loadUser($payload, $userIdentifier)
);
}
/**
* Loads the user to authenticate.
*
* @param array $payload The token payload
* @param string $identity The key from which to retrieve the user "identifier"
*/
protected function loadUser(array $payload, string $identity): UserInterface
{
if ($this->userProvider instanceof PayloadAwareUserProviderInterface) {
return $this->userProvider->loadUserByIdentifierAndPayload($identity, $payload);
}
if ($this->userProvider instanceof ChainUserProvider) {
foreach ($this->userProvider->getProviders() as $provider) {
try {
if ($provider instanceof PayloadAwareUserProviderInterface) {
return $provider->loadUserByIdentifierAndPayload($identity, $payload);
}
return $provider->loadUserByIdentifier($identity);
} catch (AuthenticationException $e) {
// try next one
}
}
$ex = new UserNotFoundException(sprintf('There is no user with identifier "%s".', $identity));
$ex->setUserIdentifier($identity);
throw $ex;
}
return $this->userProvider->loadUserByIdentifier($identity);
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels