diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index edcb46bf..ddaf0b70 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -70,6 +70,10 @@ jobs:
if: "matrix.symfony == '6.0.*' || matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*' || matrix.symfony == '6.3.*@dev'"
run: "composer remove --dev --no-update symfony/security-guard"
+ - name: "Install api-platform/core"
+ if: "matrix.symfony == '6.1.*' || matrix.symfony == '6.2.*' || matrix.symfony == '6.3.*@dev'"
+ run: "composer require --dev --no-update api-platform/core:^3.0"
+
- name: "Install dependencies"
run: "composer update ${{ matrix.composer-flags }} --prefer-dist"
env:
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index f635f985..66aa1a71 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -115,6 +115,15 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->end()
+ ->arrayNode('api_platform')
+ ->info('API Platform compatibility: add check_path in OpenApi documentation.')
+ ->children()
+ ->scalarNode('check_path')
+ ->defaultNull()
+ ->info('The login check path to document on OpenApi.')
+ ->end()
+ ->end()
+ ->end()
->end();
return $treeBuilder;
diff --git a/DependencyInjection/LexikJWTAuthenticationExtension.php b/DependencyInjection/LexikJWTAuthenticationExtension.php
index 2df275b7..0ad7ba87 100644
--- a/DependencyInjection/LexikJWTAuthenticationExtension.php
+++ b/DependencyInjection/LexikJWTAuthenticationExtension.php
@@ -2,6 +2,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection;
+use ApiPlatform\Symfony\Bundle\ApiPlatformBundle;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\FileLocator;
@@ -10,6 +11,7 @@
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
@@ -141,6 +143,17 @@ public function load(array $configs, ContainerBuilder $container)
->replaceArgument(3, $config['pass_phrase'])
->replaceArgument(4, $encoderConfig['signature_algorithm']);
}
+
+ if (isset($config['api_platform']['check_path'])) {
+ if (!class_exists(ApiPlatformBundle::class)) {
+ throw new LogicException('API Platform cannot be detected. Try running "composer require api-platform/core".');
+ }
+
+ $loader->load('api_platform.xml');
+ $container
+ ->getDefinition('lexik_jwt_authentication.api_platform.openapi.factory')
+ ->replaceArgument(1, $config['api_platform']['check_path']);
+ }
}
private static function createTokenExtractors(ContainerBuilder $container, array $tokenExtractorsConfig)
diff --git a/OpenApi/OpenApiFactory.php b/OpenApi/OpenApiFactory.php
new file mode 100644
index 00000000..ed0386fb
--- /dev/null
+++ b/OpenApi/OpenApiFactory.php
@@ -0,0 +1,92 @@
+
+ *
+ * @final
+ */
+class OpenApiFactory implements OpenApiFactoryInterface
+{
+ /**
+ * @var OpenApiFactoryInterface
+ */
+ private $decorated;
+
+ private $checkPath;
+
+ public function __construct(OpenApiFactoryInterface $decorated, string $checkPath)
+ {
+ $this->decorated = $decorated;
+ $this->checkPath = $checkPath;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __invoke(array $context = []): OpenApi
+ {
+ $openApi = ($this->decorated)($context);
+
+ $openApi
+ ->getPaths()
+ ->addPath($this->checkPath, (new PathItem())->withPost((new Operation())
+ ->withOperationId('login_check_post')
+ ->withTags(['Login Check'])
+ ->withResponses([
+ Response::HTTP_OK => [
+ 'description' => 'User token created',
+ 'content' => [
+ 'application/json' => [
+ 'schema' => [
+ 'type' => 'object',
+ 'properties' => [
+ 'token' => [
+ 'readOnly' => true,
+ 'type' => 'string',
+ 'nullable' => false,
+ ],
+ ],
+ 'required' => ['token'],
+ ],
+ ],
+ ],
+ ],
+ ])
+ ->withSummary('Creates a user token.')
+ ->withRequestBody((new RequestBody())
+ ->withDescription('The login data')
+ ->withContent(new \ArrayObject([
+ 'application/json' => new MediaType(new \ArrayObject(new \ArrayObject([
+ 'type' => 'object',
+ 'properties' => [
+ '_username' => [
+ 'type' => 'string',
+ 'nullable' => false,
+ ],
+ '_password' => [
+ 'type' => 'string',
+ 'nullable' => false,
+ ],
+ ],
+ 'required' => ['_username', '_password'],
+ ]))),
+ ]))
+ ->withRequired(true)
+ )
+ ));
+
+ return $openApi;
+ }
+}
diff --git a/README.md b/README.md
index b5a2896b..6a6a1ba3 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ Github Issues are dedicated to bug reports and feature requests.
Contributing
------------
-See the [CONTRIBUTING](CONTRIBUTING.rst) file.
+See the [CONTRIBUTING](CONTRIBUTING.md) file.
Sponsoring
diff --git a/Resources/config/api_platform.xml b/Resources/config/api_platform.xml
new file mode 100644
index 00000000..80119029
--- /dev/null
+++ b/Resources/config/api_platform.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
index eaf84df6..8b636593 100644
--- a/Resources/doc/index.rst
+++ b/Resources/doc/index.rst
@@ -145,6 +145,20 @@ Configure application routing
api_login_check:
path: /api/login_check
+Enable API Platform compatibility
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To enable the `API Platform `__ compatibility, add the
+``lexik_jwt_authentication.api_platform.check_path`` configuration option as following:
+
+.. code-block:: yaml
+
+ # config/packages/lexik_jwt_authentication.yaml
+ lexik_jwt_authentication:
+ # ...
+ api_platform:
+ check_path: /api/login_check
+
Usage
-----
diff --git a/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php b/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php
new file mode 100644
index 00000000..ea29ece8
--- /dev/null
+++ b/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php
@@ -0,0 +1,120 @@
+
+ *
+ * @requires function ApiPlatformBundle::build
+ */
+class ApiPlatformOpenApiExportCommandTest extends TestCase
+{
+ /**
+ * Test command.
+ */
+ public function testCheckOpenApiExportCommand()
+ {
+ $kernel = $this->bootKernel();
+ $app = new Application($kernel);
+ $tester = new CommandTester($app->find('api:openapi:export'));
+
+ $this->assertSame(0, $tester->execute([]));
+ $this->assertJsonStringEqualsJsonString(<<getDisplay());
+ }
+}
diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php
index b68cc384..b2ff4e31 100644
--- a/Tests/Functional/app/AppKernel.php
+++ b/Tests/Functional/app/AppKernel.php
@@ -2,13 +2,13 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Functional;
+use ApiPlatform\Symfony\Bundle\ApiPlatformBundle;
use Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle;
use Lexik\Bundle\JWTAuthenticationBundle\Tests\Functional\Bundle\Bundle;
use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle\Security;
-use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;
@@ -42,12 +42,17 @@ public function __construct($environment, $debug, $testCase = null)
*/
public function registerBundles(): array
{
- return [
+ $bundles = [
new FrameworkBundle(),
new SecurityBundle(),
new LexikJWTAuthenticationBundle(),
new Bundle(),
];
+ if (class_exists(ApiPlatformBundle::class)) {
+ $bundles[] = new ApiPlatformBundle();
+ }
+
+ return $bundles;
}
public function getRootDir()
@@ -100,12 +105,29 @@ public function registerContainerConfiguration(LoaderInterface $loader)
});
}
- $loader->load(function (ContainerBuilder $container) use ($sessionConfig) {
+ $router = [
+ 'resource' => '%kernel.root_dir%/config/routing.yml',
+ 'utf8' => true,
+ ];
+ if (class_exists(ApiPlatformBundle::class)) {
+ $loader->load(function (ContainerBuilder $container) use (&$router) {
+ $container->prependExtensionConfig('api_platform', [
+ 'title' => 'LexikJWTAuthenticationBundle',
+ 'description' => 'API Platform integration in LexikJWTAuthenticationBundle',
+ 'version' => '1.0.0',
+ ]);
+ $container->prependExtensionConfig('lexik_jwt_authentication', [
+ 'api_platform' => [
+ 'check_path' => '/login_check',
+ ],
+ ]);
+ $router['resource'] = '%kernel.root_dir%/config/routing_api_platform.yml';
+ });
+ }
+
+ $loader->load(function (ContainerBuilder $container) use ($router, $sessionConfig) {
$container->prependExtensionConfig('framework', [
- 'router' => [
- 'resource' => '%kernel.root_dir%/config/routing.yml',
- 'utf8' => true,
- ],
+ 'router' => $router,
'session' => $sessionConfig
]);
});
diff --git a/Tests/Functional/app/config/routing_api_platform.yml b/Tests/Functional/app/config/routing_api_platform.yml
new file mode 100644
index 00000000..f254b79a
--- /dev/null
+++ b/Tests/Functional/app/config/routing_api_platform.yml
@@ -0,0 +1,6 @@
+default:
+ resource: routing.yml
+
+api_platform:
+ resource: .
+ type: api_platform