Skip to content

Commit dee2868

Browse files
Merge pull request #4649 from vincentchalamon/fix/swaggerui-pkce
feat: support PKCE on Swagger UI
2 parents 92d8f53 + 92f9060 commit dee2868

File tree

12 files changed

+34
-4
lines changed

12 files changed

+34
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 2.7.0
44

5+
* Swagger UI: Add `usePkceWithAuthorizationCodeGrant` to Swagger UI initOAuth (#4649)
56
* **BC**: `mapping.paths` in configuration should override bundles configuration (#4465)
67
* GraphQL: Add ability to use different pagination types for the queries of a resource (#4453)
78
* Security: **BC** Fix `ApiProperty` `security` attribute expression being passed a class string for the `object` variable on updates/creates - null is now passed instead if the object is not available (#4184)

src/Core/Bridge/Symfony/Bundle/Action/SwaggerUiAction.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ final class SwaggerUiAction
5656
private $oauthTokenUrl;
5757
private $oauthAuthorizationUrl;
5858
private $oauthScopes;
59+
private $oauthPkce;
5960
private $formatsProvider;
6061
private $swaggerUiEnabled;
6162
private $reDocEnabled;
@@ -81,7 +82,7 @@ final class SwaggerUiAction
8182
* @param mixed $oauthScopes
8283
* @param mixed $resourceMetadataFactory
8384
*/
84-
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, $resourceMetadataFactory, NormalizerInterface $normalizer, ?TwigEnvironment $twig, UrlGeneratorInterface $urlGenerator, string $title = '', string $description = '', string $version = '', $formats = [], $oauthEnabled = false, $oauthClientId = '', $oauthClientSecret = '', $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = [], bool $showWebby = true, bool $swaggerUiEnabled = false, bool $reDocEnabled = false, bool $graphqlEnabled = false, bool $graphiQlEnabled = false, bool $graphQlPlaygroundEnabled = false, array $swaggerVersions = [2, 3], OpenApiSwaggerUiAction $swaggerUiAction = null, $assetPackage = null, array $swaggerUiExtraConfiguration = [])
85+
public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, $resourceMetadataFactory, NormalizerInterface $normalizer, ?TwigEnvironment $twig, UrlGeneratorInterface $urlGenerator, string $title = '', string $description = '', string $version = '', $formats = [], $oauthEnabled = false, $oauthClientId = '', $oauthClientSecret = '', $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = [], bool $showWebby = true, bool $swaggerUiEnabled = false, bool $reDocEnabled = false, bool $graphqlEnabled = false, bool $graphiQlEnabled = false, bool $graphQlPlaygroundEnabled = false, array $swaggerVersions = [2, 3], OpenApiSwaggerUiAction $swaggerUiAction = null, $assetPackage = null, array $swaggerUiExtraConfiguration = [], bool $oauthPkce = false)
8586
{
8687
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
8788
$this->resourceMetadataFactory = $resourceMetadataFactory;
@@ -109,6 +110,7 @@ public function __construct(ResourceNameCollectionFactoryInterface $resourceName
109110
$this->swaggerUiAction = $swaggerUiAction;
110111
$this->swaggerUiExtraConfiguration = $swaggerUiExtraConfiguration;
111112
$this->assetPackage = $assetPackage;
113+
$this->oauthPkce = $oauthPkce;
112114

113115
if (null === $this->twig) {
114116
throw new \RuntimeException('The documentation cannot be displayed since the Twig bundle is not installed. Try running "composer require symfony/twig-bundle".');
@@ -183,6 +185,7 @@ private function getContext(Request $request, Documentation $documentation): arr
183185
'enabled' => $this->oauthEnabled,
184186
'clientId' => $this->oauthClientId,
185187
'clientSecret' => $this->oauthClientSecret,
188+
'pkce' => $this->oauthPkce,
186189
'type' => $this->oauthType,
187190
'flow' => $this->oauthFlow,
188191
'tokenUrl' => $this->oauthTokenUrl,

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,14 @@ private function registerOAuthConfiguration(ContainerBuilder $container, array $
427427
$container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
428428
$container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
429429
$container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
430+
$container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
431+
432+
if ($container->hasDefinition('api_platform.swagger.action.ui')) {
433+
$container->getDefinition('api_platform.swagger.action.ui')->setArgument(27, $config['oauth']['pkce']);
434+
}
435+
if ($container->hasDefinition('api_platform.swagger_ui.action')) {
436+
$container->getDefinition('api_platform.swagger_ui.action')->setArgument(10, $config['oauth']['pkce']);
437+
}
430438
}
431439

432440
/**

src/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,11 @@ private function addOAuthSection(ArrayNodeDefinition $rootNode): void
260260
->addDefaultsIfNotSet()
261261
->children()
262262
->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
263-
->scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
263+
->scalarNode('clientSecret')
264+
->defaultValue('')
265+
->info('The OAuth client secret. Never use this parameter in your production environment. It exposes crucial security information. This feature is intended for dev/test environments only. Enable "oauth.pkce" instead')
266+
->end()
267+
->booleanNode('pkce')->defaultFalse()->info('Enable the oauth PKCE.')->end()
264268
->scalarNode('type')->defaultValue('oauth2')->info('The oauth type.')->end()
265269
->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
266270
->scalarNode('tokenUrl')->defaultValue('')->info('The oauth token url.')->end()

src/Symfony/Bundle/Resources/config/legacy/swagger_ui.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<argument>%api_platform.formats%</argument>
1515
<argument>%api_platform.oauth.clientId%</argument>
1616
<argument>%api_platform.oauth.clientSecret%</argument>
17+
<argument>%api_platform.oauth.pkce%</argument>
1718
</service>
1819
</services>
1920
</container>

src/Symfony/Bundle/Resources/config/swagger_ui.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<argument type="service" id="api_platform.swagger_ui.action" />
4848
<argument>%api_platform.asset_package%</argument>
4949
<argument>%api_platform.swagger_ui.extra_configuration%</argument>
50+
<argument>%api_platform.oauth.pkce%</argument>
5051
</service>
5152
</services>
5253
</container>

src/Symfony/Bundle/Resources/config/v3/swagger_ui.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<argument>%api_platform.formats%</argument>
1616
<argument>%api_platform.oauth.clientId%</argument>
1717
<argument>%api_platform.oauth.clientSecret%</argument>
18+
<argument>%api_platform.oauth.pkce%</argument>
1819
</service>
1920
</services>
2021
</container>

src/Symfony/Bundle/Resources/public/init-swagger-ui.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ window.onload = function() {
6363
realm: data.oauth.type,
6464
appName: data.spec.info.title,
6565
scopeSeparator: ' ',
66-
additionalQueryStringParams: {}
66+
additionalQueryStringParams: {},
67+
usePkceWithAuthorizationCodeGrant: data.oauth.pkce,
6768
});
6869
}
6970

src/Symfony/Bundle/SwaggerUi/SwaggerUiAction.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ final class SwaggerUiAction
4040
private $resourceMetadataFactory;
4141
private $oauthClientId;
4242
private $oauthClientSecret;
43+
private $oauthPkce;
4344

44-
public function __construct($resourceMetadataFactory, ?TwigEnvironment $twig, UrlGeneratorInterface $urlGenerator, NormalizerInterface $normalizer, OpenApiFactoryInterface $openApiFactory, Options $openApiOptions, SwaggerUiContext $swaggerUiContext, array $formats = [], string $oauthClientId = null, string $oauthClientSecret = null)
45+
public function __construct($resourceMetadataFactory, ?TwigEnvironment $twig, UrlGeneratorInterface $urlGenerator, NormalizerInterface $normalizer, OpenApiFactoryInterface $openApiFactory, Options $openApiOptions, SwaggerUiContext $swaggerUiContext, array $formats = [], string $oauthClientId = null, string $oauthClientSecret = null, bool $oauthPkce = false)
4546
{
4647
$this->resourceMetadataFactory = $resourceMetadataFactory;
4748
$this->twig = $twig;
@@ -53,6 +54,7 @@ public function __construct($resourceMetadataFactory, ?TwigEnvironment $twig, Ur
5354
$this->formats = $formats;
5455
$this->oauthClientId = $oauthClientId;
5556
$this->oauthClientSecret = $oauthClientSecret;
57+
$this->oauthPkce = $oauthPkce;
5658

5759
if (null === $this->twig) {
5860
throw new \RuntimeException('The documentation cannot be displayed since the Twig bundle is not installed. Try running "composer require symfony/twig-bundle".');
@@ -89,6 +91,7 @@ public function __invoke(Request $request)
8991
'scopes' => $this->openApiOptions->getOAuthScopes(),
9092
'clientId' => $this->oauthClientId,
9193
'clientSecret' => $this->oauthClientSecret,
94+
'pkce' => $this->oauthPkce,
9295
],
9396
'extraConfiguration' => $this->swaggerUiContext->getExtraConfiguration(),
9497
];

tests/Symfony/Bundle/Action/SwaggerUiActionTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public function getInvokeParameters()
102102
'tokenUrl' => '',
103103
'authorizationUrl' => '',
104104
'scopes' => [],
105+
'pkce' => false,
105106
],
106107
'shortName' => 'F',
107108
'operationId' => 'getFCollection',
@@ -137,6 +138,7 @@ public function getInvokeParameters()
137138
'tokenUrl' => '',
138139
'authorizationUrl' => '',
139140
'scopes' => [],
141+
'pkce' => false,
140142
],
141143
'shortName' => 'F',
142144
'operationId' => 'getFItem',
@@ -195,6 +197,7 @@ public function testDoNotRunCurrentRequest(Request $request)
195197
'tokenUrl' => '',
196198
'authorizationUrl' => '',
197199
'scopes' => [],
200+
'pkce' => false,
198201
],
199202
],
200203
])->shouldBeCalled()->willReturn('');

0 commit comments

Comments
 (0)