From 93f369aff304c87140766a854b9fd38bba6a5430 Mon Sep 17 00:00:00 2001 From: Vincent Chalamon <407859+vincentchalamon@users.noreply.github.com> Date: Tue, 14 May 2024 13:34:06 +0200 Subject: [PATCH] feat(security): OIDC discovery --- UPGRADE-7.3.md | 2 + .../Bundle/SecurityBundle/CHANGELOG.md | 1 + .../AccessToken/OidcTokenHandlerFactory.php | 50 +++++++++- .../OidcUserInfoTokenHandlerFactory.php | 33 ++++++- .../security_authenticator_access_token.php | 5 + .../Factory/AccessTokenFactoryTest.php | 96 ++++++++++++++++++- .../app/AccessToken/config_oidc.yml | 2 +- .../AccessToken/Oidc/OidcTokenHandler.php | 64 ++++++++++++- .../Oidc/OidcUserInfoTokenHandler.php | 31 +++++- .../Component/Security/Http/CHANGELOG.md | 1 + 10 files changed, 271 insertions(+), 14 deletions(-) diff --git a/UPGRADE-7.3.md b/UPGRADE-7.3.md index c4fff7bd2301c..dd982a1b392e9 100644 --- a/UPGRADE-7.3.md +++ b/UPGRADE-7.3.md @@ -60,6 +60,8 @@ Security * Add argument `$accessDecision` to `AccessDecisionManagerInterface::decide()` and `AuthorizationCheckerInterface::isGranted()`; it should be used to report the reason of a decision, including all the related votes. + * Add discovery support to `OidcTokenHandler` and `OidcUserInfoTokenHandler` + Console ------- diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 89e4906821978..77aa957331bd1 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Deprecate the `security.hide_user_not_found` config option in favor of `security.expose_security_errors` * Add ability to fetch LDAP roles * Add `OAuth2TokenHandlerFactory` for `AccessTokenFactory` + * Add discovery support to `OidcTokenHandler` and `OidcUserInfoTokenHandler` 7.2 --- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php index 0f5bc2895b6d4..63f1077a560a1 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcTokenHandlerFactory.php @@ -17,6 +17,8 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * Configures a token handler for decoding and validating an OIDC token. @@ -38,9 +40,29 @@ public function create(ContainerBuilder $container, string $id, array|string $co $tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature')) ->replaceArgument(0, $config['algorithms'])); + if (isset($config['discovery'])) { + if (!ContainerBuilder::willBeAvailable('symfony/http-client', HttpClientInterface::class, ['symfony/security-bundle'])) { + throw new LogicException('You cannot use the "oidc" token handler with "discovery" since the HttpClient component is not installed. Try running "composer require symfony/http-client".'); + } + + // disable JWKSet argument + $tokenHandlerDefinition->replaceArgument(1, null); + $tokenHandlerDefinition->addMethodCall( + 'enableDiscovery', + [ + new Reference($config['discovery']['cache']['id']), + (new ChildDefinition('security.access_token_handler.oidc_discovery.http_client')) + ->replaceArgument(0, ['base_uri' => $config['discovery']['base_uri']]), + "$id.oidc_configuration", + "$id.oidc_jwk_set", + ] + ); + + return; + } + $tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwkset')) - ->replaceArgument(0, $config['keyset']) - ); + ->replaceArgument(0, $config['keyset'])); if ($config['encryption']['enabled']) { $algorithmManager = (new ChildDefinition('security.access_token_handler.oidc.encryption')) @@ -74,8 +96,8 @@ public function addConfiguration(NodeBuilder $node): void ->thenInvalid('You must set either "algorithm" or "algorithms".') ->end() ->validate() - ->ifTrue(static fn ($v) => !isset($v['key']) && !isset($v['keyset'])) - ->thenInvalid('You must set either "key" or "keyset".') + ->ifTrue(static fn ($v) => !isset($v['discovery']) && !isset($v['key']) && !isset($v['keyset'])) + ->thenInvalid('You must set either "discovery" or "key" or "keyset".') ->end() ->beforeNormalization() ->ifTrue(static fn ($v) => isset($v['algorithm']) && \is_string($v['algorithm'])) @@ -101,6 +123,25 @@ public function addConfiguration(NodeBuilder $node): void }) ->end() ->children() + ->arrayNode('discovery') + ->info('Enable the OIDC discovery.') + ->children() + ->scalarNode('base_uri') + ->info('Base URI of the OIDC server.') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->arrayNode('cache') + ->children() + ->scalarNode('id') + ->info('Cache service id to use to cache the OIDC discovery configuration.') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->end() + ->end() + ->end() + ->end() ->scalarNode('claim') ->info('Claim which contains the user identifier (e.g.: sub, email..).') ->defaultValue('sub') @@ -129,7 +170,6 @@ public function addConfiguration(NodeBuilder $node): void ->end() ->scalarNode('keyset') ->info('JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys).') - ->isRequired() ->end() ->arrayNode('encryption') ->canBeEnabled() diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php index 3e30acabaf5dd..c6308ff342242 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/AccessToken/OidcUserInfoTokenHandlerFactory.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -34,9 +35,23 @@ public function create(ContainerBuilder $container, string $id, array|string $co throw new LogicException('You cannot use the "oidc_user_info" token handler since the HttpClient component is not installed. Try running "composer require symfony/http-client".'); } - $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info')) + $tokenHandlerDefinition = $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info')) ->replaceArgument(0, $clientDefinition) ->replaceArgument(2, $config['claim']); + + if (isset($config['discovery'])) { + if (!ContainerBuilder::willBeAvailable('symfony/cache', CacheInterface::class, ['symfony/security-bundle'])) { + throw new LogicException('You cannot use the "oidc_user_info" token handler with "discovery" since the Cache component is not installed. Try running "composer require symfony/cache".'); + } + + $tokenHandlerDefinition->addMethodCall( + 'enableDiscovery', + [ + new Reference($config['discovery']['cache']['id']), + "$id.oidc_configuration", + ] + ); + } } public function getKey(): string @@ -55,10 +70,24 @@ public function addConfiguration(NodeBuilder $node): void ->end() ->children() ->scalarNode('base_uri') - ->info('Base URI of the userinfo endpoint on the OIDC server.') + ->info('Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured).') ->isRequired() ->cannotBeEmpty() ->end() + ->arrayNode('discovery') + ->info('Enable the OIDC discovery.') + ->children() + ->arrayNode('cache') + ->children() + ->scalarNode('id') + ->info('Cache service id to use to cache the OIDC discovery configuration.') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->end() + ->end() + ->end() + ->end() ->scalarNode('claim') ->info('Claim which contains the user identifier (e.g. sub, email, etc.).') ->defaultValue('sub') diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php index 6f2c57249a966..9099bad41c385 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php @@ -92,6 +92,11 @@ service('clock'), ]) + ->set('security.access_token_handler.oidc_discovery.http_client', HttpClientInterface::class) + ->abstract() + ->factory([service('http_client'), 'withOptions']) + ->args([abstract_arg('http client options')]) + ->set('security.access_token_handler.oidc.jwk', JWK::class) ->abstract() ->deprecate('symfony/security-http', '7.1', 'The "%service_id%" service is deprecated. Please use "security.access_token_handler.oidc.jwkset" instead') diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php index 85c9171933f93..88b782363dbf9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AccessTokenFactoryTest.php @@ -114,7 +114,7 @@ public function testInvalidOidcTokenHandlerConfigurationKeyMissing() $factory = new AccessTokenFactory($this->createTokenHandlerFactories()); $this->expectException(InvalidConfigurationException::class); - $this->expectExceptionMessage('The child config "keyset" under "access_token.token_handler.oidc" must be configured: JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys).'); + $this->expectExceptionMessage('You must set either "discovery" or "key" or "keyset".'); $this->processConfig($config, $factory); } @@ -340,6 +340,58 @@ public function testInvalidOidcTokenHandlerConfigurationMissingAlgorithm() $this->processConfig($config, $factory); } + public function testOidcTokenHandlerConfigurationWithDiscovery() + { + $container = new ContainerBuilder(); + $jwkset = '{"keys":[{"kty":"EC","crv":"P-256","x":"FtgMtrsKDboRO-Zo0XC7tDJTATHVmwuf9GK409kkars","y":"rWDE0ERU2SfwGYCo1DWWdgFEbZ0MiAXLRBBOzBgs_jY","d":"4G7bRIiKih0qrFxc0dtvkHUll19tTyctoCR3eIbOrO0"},{"kty":"EC","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220"}]}'; + $config = [ + 'token_handler' => [ + 'oidc' => [ + 'discovery' => [ + 'base_uri' => 'https://www.example.com/realms/demo/', + 'cache' => [ + 'id' => 'oidc_cache', + ], + ], + 'algorithms' => ['RS256', 'ES256'], + 'issuers' => ['https://www.example.com'], + 'audience' => 'audience', + ], + ], + ]; + + $factory = new AccessTokenFactory($this->createTokenHandlerFactories()); + $finalizedConfig = $this->processConfig($config, $factory); + + $factory->createAuthenticator($container, 'firewall1', $finalizedConfig, 'userprovider'); + + $this->assertTrue($container->hasDefinition('security.authenticator.access_token.firewall1')); + $this->assertTrue($container->hasDefinition('security.access_token_handler.firewall1')); + + $expectedArgs = [ + 'index_0' => (new ChildDefinition('security.access_token_handler.oidc.signature')) + ->replaceArgument(0, ['RS256', 'ES256']), + 'index_1' => null, + 'index_2' => 'audience', + 'index_3' => ['https://www.example.com'], + 'index_4' => 'sub', + ]; + $expectedCalls = [ + [ + 'enableDiscovery', + [ + new Reference('oidc_cache'), + (new ChildDefinition('security.access_token_handler.oidc_discovery.http_client')) + ->replaceArgument(0, ['base_uri' => 'https://www.example.com/realms/demo/']), + 'security.access_token_handler.firewall1.oidc_configuration', + 'security.access_token_handler.firewall1.oidc_jwk_set', + ], + ], + ]; + $this->assertEquals($expectedArgs, $container->getDefinition('security.access_token_handler.firewall1')->getArguments()); + $this->assertEquals($expectedCalls, $container->getDefinition('security.access_token_handler.firewall1')->getMethodCalls()); + } + public function testOidcUserInfoTokenHandlerConfigurationWithExistingClient() { $container = new ContainerBuilder(); @@ -407,6 +459,48 @@ public static function getOidcUserInfoConfiguration(): iterable yield ['https://www.example.com/realms/demo/protocol/openid-connect/userinfo']; } + public function testOidcUserInfoTokenHandlerConfigurationWithDiscovery() + { + $container = new ContainerBuilder(); + $config = [ + 'token_handler' => [ + 'oidc_user_info' => [ + 'discovery' => [ + 'cache' => [ + 'id' => 'oidc_cache', + ], + ], + 'base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo', + ], + ], + ]; + + $factory = new AccessTokenFactory($this->createTokenHandlerFactories()); + $finalizedConfig = $this->processConfig($config, $factory); + + $factory->createAuthenticator($container, 'firewall1', $finalizedConfig, 'userprovider'); + + $this->assertTrue($container->hasDefinition('security.authenticator.access_token.firewall1')); + $this->assertTrue($container->hasDefinition('security.access_token_handler.firewall1')); + + $expectedArgs = [ + 'index_0' => (new ChildDefinition('security.access_token_handler.oidc_user_info.http_client')) + ->replaceArgument(0, ['base_uri' => 'https://www.example.com/realms/demo/protocol/openid-connect/userinfo']), + 'index_2' => 'sub', + ]; + $expectedCalls = [ + [ + 'enableDiscovery', + [ + new Reference('oidc_cache'), + 'security.access_token_handler.firewall1.oidc_configuration', + ], + ], + ]; + $this->assertEquals($expectedArgs, $container->getDefinition('security.access_token_handler.firewall1')->getArguments()); + $this->assertEquals($expectedCalls, $container->getDefinition('security.access_token_handler.firewall1')->getMethodCalls()); + } + public function testMultipleTokenHandlersSet() { $config = [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml index 94b46501544dd..a087604782bec 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_oidc.yml @@ -24,7 +24,7 @@ security: claim: 'username' audience: 'Symfony OIDC' issuers: [ 'https://www.example.com' ] - algorithm: 'ES256' + algorithms: [ 'ES256' ] # tip: use https://mkjwk.org/ to generate a JWK keyset: '{"keys":[{"kty":"EC","d":"iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220","crv":"P-256","x":"0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4","y":"KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo"}]}' encryption: diff --git a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php index 8260470cc2597..b656cbc8d1cef 100644 --- a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php +++ b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php @@ -34,6 +34,8 @@ use Symfony\Component\Security\Http\AccessToken\Oidc\Exception\MissingClaimException; use Symfony\Component\Security\Http\Authenticator\FallbackUserLoader; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; /** * The token handler decodes and validates the token, and retrieves the user identifier from it. @@ -45,9 +47,14 @@ final class OidcTokenHandler implements AccessTokenHandlerInterface private ?AlgorithmManager $decryptionAlgorithms = null; private bool $enforceEncryption = false; + private ?CacheInterface $discoveryCache = null; + private ?HttpClientInterface $discoveryClient = null; + private ?string $oidcConfigurationCacheKey = null; + private ?string $oidcJWKSetCacheKey = null; + public function __construct( private Algorithm|AlgorithmManager $signatureAlgorithm, - private JWK|JWKSet $signatureKeyset, + private JWK|JWKSet|null $signatureKeyset, private string $audience, private array $issuers, private string $claim = 'sub', @@ -71,15 +78,64 @@ public function enabledJweSupport(JWKSet $decryptionKeyset, AlgorithmManager $de $this->enforceEncryption = $enforceEncryption; } + public function enabledDiscovery(CacheInterface $cache, HttpClientInterface $client, string $oidcConfigurationCacheKey, string $oidcJWKSetCacheKey): void + { + $this->discoveryCache = $cache; + $this->discoveryClient = $client; + $this->oidcConfigurationCacheKey = $oidcConfigurationCacheKey; + $this->oidcJWKSetCacheKey = $oidcJWKSetCacheKey; + } + public function getUserBadgeFrom(string $accessToken): UserBadge { if (!class_exists(JWSVerifier::class) || !class_exists(Checker\HeaderCheckerManager::class)) { throw new \LogicException('You cannot use the "oidc" token handler since "web-token/jwt-signature" and "web-token/jwt-checker" are not installed. Try running "composer require web-token/jwt-signature web-token/jwt-checker".'); } + if (!$this->discoveryCache && !$this->signatureKeyset) { + throw new \LogicException('You cannot use the "oidc" token handler without JWKSet nor "discovery". Please configure JWKSet in the constructor, or call "enableDiscovery" method.'); + } + + $jwkset = $this->signatureKeyset; + if ($this->discoveryCache) { + try { + $oidcConfiguration = json_decode($this->discoveryCache->get($this->oidcConfigurationCacheKey, function (): string { + $response = $this->discoveryClient->request('GET', '.well-known/openid-configuration'); + + return $response->getContent(); + }), true, 512, \JSON_THROW_ON_ERROR); + } catch (\Throwable $e) { + $this->logger?->error('An error occurred while requesting OIDC configuration.', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + throw new BadCredentialsException('Invalid credentials.', $e->getCode(), $e); + } + + try { + $jwkset = JWKSet::createFromJson( + $this->discoveryCache->get($this->oidcJWKSetCacheKey, function () use ($oidcConfiguration): string { + $response = $this->discoveryClient->request('GET', $oidcConfiguration['jwks_uri']); + // we only need signature key + $keys = array_filter($response->toArray()['keys'], static fn (array $key) => 'sig' === $key['use']); + + return json_encode(['keys' => $keys]); + }) + ); + } catch (\Throwable $e) { + $this->logger?->error('An error occurred while requesting OIDC certs.', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + throw new BadCredentialsException('Invalid credentials.', $e->getCode(), $e); + } + } + try { $accessToken = $this->decryptIfNeeded($accessToken); - $claims = $this->loadAndVerifyJws($accessToken); + $claims = $this->loadAndVerifyJws($accessToken, $jwkset); $this->verifyClaims($claims); if (empty($claims[$this->claim])) { @@ -98,7 +154,7 @@ public function getUserBadgeFrom(string $accessToken): UserBadge } } - private function loadAndVerifyJws(string $accessToken): array + private function loadAndVerifyJws(string $accessToken, JWKSet $jwkset): array { // Decode the token $jwsVerifier = new JWSVerifier($this->signatureAlgorithm); @@ -106,7 +162,7 @@ private function loadAndVerifyJws(string $accessToken): array $jws = $serializerManager->unserialize($accessToken); // Verify the signature - if (!$jwsVerifier->verifyWithKeySet($jws, $this->signatureKeyset, 0)) { + if (!$jwsVerifier->verifyWithKeySet($jws, $jwkset, 0)) { throw new InvalidSignatureException(); } diff --git a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php index 855d29557c2a6..2ab7c8a5b0e31 100644 --- a/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php +++ b/src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcUserInfoTokenHandler.php @@ -17,6 +17,7 @@ use Symfony\Component\Security\Http\AccessToken\Oidc\Exception\MissingClaimException; use Symfony\Component\Security\Http\Authenticator\FallbackUserLoader; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -26,6 +27,9 @@ final class OidcUserInfoTokenHandler implements AccessTokenHandlerInterface { use OidcTrait; + private ?CacheInterface $discoveryCache = null; + private ?string $oidcConfigurationCacheKey = null; + public function __construct( private HttpClientInterface $client, private ?LoggerInterface $logger = null, @@ -33,12 +37,37 @@ public function __construct( ) { } + public function enabledDiscovery(CacheInterface $cache, string $oidcConfigurationCacheKey): void + { + $this->discoveryCache = $cache; + $this->oidcConfigurationCacheKey = $oidcConfigurationCacheKey; + } + public function getUserBadgeFrom(string $accessToken): UserBadge { + if (null !== $this->discoveryCache) { + try { + // Call OIDC discovery to retrieve userinfo endpoint + // OIDC configuration is stored in cache + $oidcConfiguration = json_decode($this->discoveryCache->get($this->oidcConfigurationCacheKey, function (): string { + $response = $this->client->request('GET', '.well-known/openid-configuration'); + + return $response->getContent(); + }), true, 512, \JSON_THROW_ON_ERROR); + } catch (\Throwable $e) { + $this->logger?->error('An error occurred while requesting OIDC configuration.', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + throw new BadCredentialsException('Invalid credentials.', $e->getCode(), $e); + } + } + try { // Call the OIDC server to retrieve the user info // If the token is invalid or expired, the OIDC server will return an error - $claims = $this->client->request('GET', '', [ + $claims = $this->client->request('GET', $this->discoveryCache ? $oidcConfiguration['userinfo_endpoint'] : '', [ 'auth_bearer' => $accessToken, ])->toArray(); diff --git a/src/Symfony/Component/Security/Http/CHANGELOG.md b/src/Symfony/Component/Security/Http/CHANGELOG.md index 5fe39838ccb0a..275180ff87b3b 100644 --- a/src/Symfony/Component/Security/Http/CHANGELOG.md +++ b/src/Symfony/Component/Security/Http/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Support hashing the hashed password using crc32c when putting the user in the session * Add support for closures in `#[IsGranted]` * Add `OAuth2TokenHandler` with OAuth2 Token Introspection support for `AccessTokenAuthenticator` + * Add discovery support to `OidcTokenHandler` and `OidcUserInfoTokenHandler` 7.2 ---