Repository: symfony/symfony
File: src/Symfony/Component/Security/Http/AccessToken/Oidc/OidcTokenHandler.php (PHP)

Code before:

                       $oidcConfiguration = $response->toArray();
                       $jwkSetResponses[] = $discoveryClient->request('GET', $oidcConfiguration['jwks_uri']);
                   }

                   $keys = [];
                   foreach ($jwkSetResponses as $response) {
                       foreach ($response->toArray()['keys'] as $key) {
                           if ('sig' !== $key['use']) {
                               continue;
                           }

                           $keys[] = $key;
                       }
                   }
               } catch (\Exception $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);
                }

                return \json_encode(['keys' => $keys], \JSON_THROW_ON_ERROR);
            });

            $jwkset = JWKSet::createFromJson($json);
        }

        try {
            $accessToken = $this->decryptIfNeeded($accessToken);
            $claims = $this->loadAndVerifyJws($accessToken, $jwkset);
            $this->verifyClaims($claims);

            if (empty($claims[$this->claim])) {
                throw new MissingClaimException(\sprintf('"%s" claim not found.', $this->claim));
            }

            // UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
            return new UserBadge($claims[$this->claim], new FallbackUserLoader(function () use ($claims) {
                $claims['user_identifier'] = $claims[$this->claim];

                return $this->createUser($claims);
            }), $claims);
        } catch (\Exception $e) {
            $this->logger?->error('An error occurred while decoding and validating the token.', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);


Review comment (performance):
This is now async. It first loads all openid configuration's, 

As soon as it receives one, it will also concurrently fetch the jwks uri. 

Then when done, it will create a single array of signature keys to use.

And this is stored under the `oidcJWKSetCacheKey` cache key.

I did not use the `oidcConfigurationCacheKey`.

Should we deprecate/remove that one? Or is there a way to still cache those separately?

/cc @nicolas-grekas 

Code after:

                    $jwkSetResponses = [];
                    foreach ($client->stream($configResponses) as $response => $chunk) {
                        if ($chunk->isLast()) {
                            $jwkSetResponses[] = $response->getInfo('user_data')->request('GET', $response->toArray()['jwks_uri']);
                        }
                    }

                    $keys = [];
                    foreach ($jwkSetResponses as $response) {
                        foreach ($response->toArray()['keys'] as $key) {
                            if ('sig' === $key['use']) {
                                $keys[] = $key;
                            }
                        }
                    }

                    return $keys;
                } catch (\Exception $e) {
                    $logger?->error('An error occurred while requesting OIDC certs.', [
                        'error' => $e->getMessage(),
                        'trace' => $e->getTraceAsString(),
                    ]);

                    throw new BadCredentialsException('Invalid credentials.', $e->getCode(), $e);
                }
            });

            $jwkset = JWKSet::createFromKeyData(['keys' => $keys]);
        }

        try {
            $accessToken = $this->decryptIfNeeded($accessToken);
            $claims = $this->loadAndVerifyJws($accessToken, $jwkset);
            $this->verifyClaims($claims);

            if (empty($claims[$this->claim])) {
                throw new MissingClaimException(\sprintf('"%s" claim not found.', $this->claim));
            }

            // UserLoader argument can be overridden by a UserProvider on AccessTokenAuthenticator::authenticate
            return new UserBadge($claims[$this->claim], new FallbackUserLoader(function () use ($claims) {
                $claims['user_identifier'] = $claims[$this->claim];

                return $this->createUser($claims);
            }), $claims);
        } catch (\Exception $e) {
            $this->logger?->error('An error occurred while decoding and validating the token.', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);