diff --git a/DependencyInjection/Compiler/ApiPlatformOpenApiPass.php b/DependencyInjection/Compiler/ApiPlatformOpenApiPass.php
new file mode 100644
index 00000000..bab7eecc
--- /dev/null
+++ b/DependencyInjection/Compiler/ApiPlatformOpenApiPass.php
@@ -0,0 +1,52 @@
+hasDefinition('lexik_jwt_authentication.api_platform.openapi.factory') || !$container->hasParameter('security.firewalls')) {
+ return;
+ }
+
+ $checkPath = null;
+ $usernamePath = null;
+ $passwordPath = null;
+ $firewalls = $container->getParameter('security.firewalls');
+ foreach ($firewalls as $firewallName) {
+ if ($container->hasDefinition('security.authenticator.json_login.' . $firewallName)) {
+ $firewallOptions = $container->getDefinition('security.authenticator.json_login.' . $firewallName)->getArgument(4);
+ $checkPath = $firewallOptions['check_path'];
+ $usernamePath = $firewallOptions['username_path'];
+ $passwordPath = $firewallOptions['password_path'];
+
+ break;
+ }
+ }
+
+ $openApiFactoryDefinition = $container->getDefinition('lexik_jwt_authentication.api_platform.openapi.factory');
+ $checkPathArg = $openApiFactoryDefinition->getArgument(1);
+ $usernamePathArg = $openApiFactoryDefinition->getArgument(2);
+ $passwordPathArg = $openApiFactoryDefinition->getArgument(3);
+
+ if (!$checkPath && !$checkPathArg) {
+ $container->removeDefinition('lexik_jwt_authentication.api_platform.openapi.factory');
+
+ return;
+ }
+
+ if (!$checkPathArg) {
+ $openApiFactoryDefinition->replaceArgument(1, $checkPath);
+ }
+ if (!$usernamePathArg) {
+ $openApiFactoryDefinition->replaceArgument(2, $usernamePath ?? 'username');
+ }
+ if (!$passwordPathArg) {
+ $openApiFactoryDefinition->replaceArgument(3, $passwordPath ?? 'password');
+ }
+ }
+}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 66aa1a71..72136211 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -116,11 +116,19 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->arrayNode('api_platform')
- ->info('API Platform compatibility: add check_path in OpenApi documentation.')
+ ->info('API Platform compatibility: add check_path in OpenAPI documentation.')
->children()
->scalarNode('check_path')
->defaultNull()
- ->info('The login check path to document on OpenApi.')
+ ->info('The login check path to add in OpenAPI.')
+ ->end()
+ ->scalarNode('username_path')
+ ->defaultNull()
+ ->info('The path to the username in the JSON body.')
+ ->end()
+ ->scalarNode('password_path')
+ ->defaultNull()
+ ->info('The path to the password in the JSON body.')
->end()
->end()
->end()
diff --git a/DependencyInjection/LexikJWTAuthenticationExtension.php b/DependencyInjection/LexikJWTAuthenticationExtension.php
index 0ad7ba87..11e9e89a 100644
--- a/DependencyInjection/LexikJWTAuthenticationExtension.php
+++ b/DependencyInjection/LexikJWTAuthenticationExtension.php
@@ -144,15 +144,18 @@ public function load(array $configs, ContainerBuilder $container)
->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".');
- }
+ if (!class_exists(ApiPlatformBundle::class) && (isset($config['api_platform']['check_path']) || isset($config['api_platform']['username_path']) || isset($config['api_platform']['password_path']))) {
+ throw new LogicException('API Platform cannot be detected. Try running "composer require api-platform/core".');
+ }
+ if (class_exists(ApiPlatformBundle::class)) {
$loader->load('api_platform.xml');
+
$container
->getDefinition('lexik_jwt_authentication.api_platform.openapi.factory')
- ->replaceArgument(1, $config['api_platform']['check_path']);
+ ->replaceArgument(1, $config['api_platform']['check_path'] ?? null)
+ ->replaceArgument(2, $config['api_platform']['username_path'] ?? null)
+ ->replaceArgument(3, $config['api_platform']['password_path'] ?? null);
}
}
diff --git a/LexikJWTAuthenticationBundle.php b/LexikJWTAuthenticationBundle.php
index cacdfa2e..2f7b9ee1 100644
--- a/LexikJWTAuthenticationBundle.php
+++ b/LexikJWTAuthenticationBundle.php
@@ -2,6 +2,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle;
+use Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection\Compiler\ApiPlatformOpenApiPass;
use Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection\Compiler\DeprecateLegacyGuardAuthenticatorPass;
use Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection\Compiler\RegisterLegacyGuardAuthenticatorPass;
use Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection\Compiler\WireGenerateTokenCommandPass;
@@ -32,6 +33,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new WireGenerateTokenCommandPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
$container->addCompilerPass(new DeprecateLegacyGuardAuthenticatorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
+ $container->addCompilerPass(new ApiPlatformOpenApiPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
/** @var SecurityExtension $extension */
$extension = $container->getExtension('security');
diff --git a/OpenApi/OpenApiFactory.php b/OpenApi/OpenApiFactory.php
index ed0386fb..c214236a 100644
--- a/OpenApi/OpenApiFactory.php
+++ b/OpenApi/OpenApiFactory.php
@@ -25,11 +25,15 @@ class OpenApiFactory implements OpenApiFactoryInterface
private $decorated;
private $checkPath;
+ private $usernamePath;
+ private $passwordPath;
- public function __construct(OpenApiFactoryInterface $decorated, string $checkPath)
+ public function __construct(OpenApiFactoryInterface $decorated, string $checkPath, string $usernamePath, string $passwordPath)
{
$this->decorated = $decorated;
$this->checkPath = $checkPath;
+ $this->usernamePath = $usernamePath;
+ $this->passwordPath = $passwordPath;
}
/**
@@ -41,7 +45,8 @@ public function __invoke(array $context = []): OpenApi
$openApi
->getPaths()
- ->addPath($this->checkPath, (new PathItem())->withPost((new Operation())
+ ->addPath($this->checkPath, (new PathItem())->withPost(
+ (new Operation())
->withOperationId('login_check_post')
->withTags(['Login Check'])
->withResponses([
@@ -65,22 +70,14 @@ public function __invoke(array $context = []): OpenApi
],
])
->withSummary('Creates a user token.')
- ->withRequestBody((new RequestBody())
+ ->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'],
+ 'properties' => $properties = array_merge_recursive($this->getJsonSchemaFromPathParts(explode('.', $this->usernamePath)), $this->getJsonSchemaFromPathParts(explode('.', $this->passwordPath))),
+ 'required' => array_keys($properties),
]))),
]))
->withRequired(true)
@@ -89,4 +86,28 @@ public function __invoke(array $context = []): OpenApi
return $openApi;
}
+
+ private function getJsonSchemaFromPathParts(array $pathParts): array
+ {
+ $jsonSchema = [];
+
+ if (count($pathParts) === 1) {
+ $jsonSchema[array_shift($pathParts)] = [
+ 'type' => 'string',
+ 'nullable' => false,
+ ];
+
+ return $jsonSchema;
+ }
+
+ $pathPart = array_shift($pathParts);
+ $properties = $this->getJsonSchemaFromPathParts($pathParts);
+ $jsonSchema[$pathPart] = [
+ 'type' => 'object',
+ 'properties' => $properties,
+ 'required' => array_keys($properties),
+ ];
+
+ return $jsonSchema;
+ }
}
diff --git a/Resources/config/api_platform.xml b/Resources/config/api_platform.xml
index 80119029..d3061b37 100644
--- a/Resources/config/api_platform.xml
+++ b/Resources/config/api_platform.xml
@@ -5,9 +5,11 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
+
+
+
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
index 8b636593..13b9d46b 100644
--- a/Resources/doc/index.rst
+++ b/Resources/doc/index.rst
@@ -145,11 +145,12 @@ Configure application routing
api_login_check:
path: /api/login_check
-Enable API Platform compatibility
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+API Platform compatibility
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If `API Platform `__ is detected, the integration will be done with your security configuration.
-To enable the `API Platform `__ compatibility, add the
-``lexik_jwt_authentication.api_platform.check_path`` configuration option as following:
+If you wish to change some parameters, you can do it with this configuration:
.. code-block:: yaml
@@ -158,6 +159,8 @@ To enable the `API Platform `__ compatibility, add th
# ...
api_platform:
check_path: /api/login_check
+ username_path: email
+ password_path: security.credentials.password
Usage
-----
diff --git a/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php b/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php
index ea29ece8..38c3a26b 100644
--- a/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php
+++ b/Tests/Functional/Command/ApiPlatformOpenApiExportCommandTest.php
@@ -2,7 +2,6 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Functional\Command;
-use ApiPlatform\Symfony\Bundle\ApiPlatformBundle;
use Lexik\Bundle\JWTAuthenticationBundle\Tests\Functional\TestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
@@ -12,7 +11,7 @@
*
* @author Vincent Chalamon
*
- * @requires function ApiPlatformBundle::build
+ * @requires function ApiPlatform\Symfony\Bundle\ApiPlatformBundle::build
*/
class ApiPlatformOpenApiExportCommandTest extends TestCase
{
@@ -43,10 +42,7 @@ public function testCheckOpenApiExportCommand()
"paths": {
"/login_check": {
"post": {
- "deprecated": false,
- "description": "",
"operationId": "login_check_post",
- "parameters": [],
"requestBody": {
"description": "The login data",
"required": true,
@@ -55,18 +51,30 @@ public function testCheckOpenApiExportCommand()
"schema": {
"type": "object",
"properties": {
- "_username": {
+ "email": {
"nullable": false,
"type": "string"
},
- "_password": {
- "nullable": false,
- "type": "string"
+ "security": {
+ "type": "object",
+ "properties": {
+ "credentials": {
+ "type": "object",
+ "properties": {
+ "password": {
+ "nullable": false,
+ "type": "string"
+ }
+ },
+ "required": ["password"]
+ }
+ },
+ "required": ["credentials"]
}
},
"required": [
- "_username",
- "_password"
+ "email",
+ "security"
]
}
}
diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php
index b2ff4e31..6ce14f75 100644
--- a/Tests/Functional/app/AppKernel.php
+++ b/Tests/Functional/app/AppKernel.php
@@ -119,6 +119,8 @@ public function registerContainerConfiguration(LoaderInterface $loader)
$container->prependExtensionConfig('lexik_jwt_authentication', [
'api_platform' => [
'check_path' => '/login_check',
+ 'username_path' => 'email',
+ 'password_path' => 'security.credentials.password',
],
]);
$router['resource'] = '%kernel.root_dir%/config/routing_api_platform.yml';