diff --git a/README.md b/README.md index faf3537..af1eec9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Configuration (config/packages/api_platform_extras.yaml): ```yaml api_platform_extras: features: + # NOT IMPLEMENTED YET http_cache: enabled: false schema_decoration: @@ -13,12 +14,14 @@ api_platform_extras: default_required_properties: false #Add @id as an optional property to all POST, PUT and PATCH schemas. jsonld_update_schema: false + # NOT IMPLEMENTED YET simple_normalizer: enabled: false jwt_refresh: enabled: false auto_refresh_cookie: false auto_refresh_header: false + user_aware: false ignored_routes: [] ignored_paths: [] allowed_firewalls: [] @@ -41,6 +44,8 @@ Enable features by setting the corresponding flag to true. If both auto-refresh flags are `false`, behavior is effectively the same as feature disabled. +`user_aware` defaults to `false`. When enabled, refresh token handling validates that the selected user provider supports the user class stored on the refresh token. + ### Related bundle config JWT/refresh token names and header prefix are taken from Lexik/Gesdinet config (with bundle defaults): @@ -52,6 +57,29 @@ JWT/refresh token names and header prefix are taken from Lexik/Gesdinet config ( When Lexik extractor parameters are not exposed as container parameters, values are read from Lexik extractor service definition arguments. +### Refresh token entity + +When using custom refresh token entities, extend the bundle entity: + +```php + + + + + + + + + + + + + + + diff --git a/src/DependencyInjection/CompilerPass/JwtRefreshCompilerPass.php b/src/DependencyInjection/CompilerPass/JwtRefreshCompilerPass.php index ed98019..83bccd7 100644 --- a/src/DependencyInjection/CompilerPass/JwtRefreshCompilerPass.php +++ b/src/DependencyInjection/CompilerPass/JwtRefreshCompilerPass.php @@ -138,12 +138,13 @@ public function process(ContainerBuilder $container): void sprintf('lexik_jwt_authentication.cookie_provider.%s', $jwtCookieName), ContainerInterface::NULL_ON_INVALID_REFERENCE, ), - $refreshTtl, + $refreshCookieConfig, + $jwtAuthorizationHeaderPrefix, $refreshSingleUse, $jwtAuthorizationHeaderName, - $jwtAuthorizationHeaderPrefix, $jwtCookieName, - $refreshCookieConfig, + $refreshTtl, + $this->resolveBoolParameter($container, sprintf('%s.user_aware', self::BASE_FEATURE_PATH), false), ]); $container->setDefinition( diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index dc43ff4..32ce8c8 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -52,6 +52,10 @@ public function getConfigTreeBuilder(): TreeBuilder ->defaultFalse() ->info('Will refresh jwt header during request cycle if valid refresh token present and enabled.') ->end() + ->booleanNode('user_aware') + ->defaultFalse() + ->info('Will check if user provider supports class refresh token was created from.') + ->end() ->arrayNode('ignored_routes') ->scalarPrototype()->end() ->defaultValue([]) diff --git a/src/Entity/RefreshToken.php b/src/Entity/RefreshToken.php new file mode 100644 index 0000000..b0bddae --- /dev/null +++ b/src/Entity/RefreshToken.php @@ -0,0 +1,9 @@ +setClass($user::class); + } + + public function getClass(): string + { + return $this->class; + } + + public function setClass(string $class): static + { + $this->class = $class; + + return $this; + } +} diff --git a/src/NetgenApiPlatformExtrasBundle.php b/src/NetgenApiPlatformExtrasBundle.php index d532f9c..d68f9b2 100644 --- a/src/NetgenApiPlatformExtrasBundle.php +++ b/src/NetgenApiPlatformExtrasBundle.php @@ -12,8 +12,15 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; +use function dirname; + final class NetgenApiPlatformExtrasBundle extends Bundle { + public function getPath(): string + { + return dirname(__DIR__); + } + public function build(ContainerBuilder $container): void { $container diff --git a/src/Service/TokenRefreshService.php b/src/Service/TokenRefreshService.php index 2120ef5..0611af9 100644 --- a/src/Service/TokenRefreshService.php +++ b/src/Service/TokenRefreshService.php @@ -11,6 +11,7 @@ use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use Netgen\ApiPlatformExtras\JwtRefresh\TokenSourceType; use Netgen\ApiPlatformExtras\JwtRefresh\ValueObject\RefreshToken; +use Netgen\ApiPlatformExtras\Model\UserAwareRefreshToken; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; @@ -36,12 +37,13 @@ public function __construct( private JWTTokenManagerInterface $jwtTokenManager, private ServiceLocator $providerLocator, private ?JWTCookieProvider $jwtCookieProvider, - private int $refreshTtl, + private array $refreshCookieSettings, + private string $jwtHeaderPrefix, private bool $refreshSingleUse, private string $jwtHeaderName, - private string $jwtHeaderPrefix, private string $jwtCookieName, - private array $refreshCookieSettings, + private int $refreshTtl, + private bool $userAware, ) { $this->refreshCookieSettings = array_merge([ 'enabled' => false, @@ -93,7 +95,18 @@ public function refresh( return; } + if ($this->userAware) { + if (!$refreshToken instanceof UserAwareRefreshToken) { + return; + } + + if (!$provider->supportsClass($refreshToken->getClass())) { + return; + } + } + $username = $refreshToken->getUsername(); + if (!is_string($username) || $username === '') { return; }