From 3b43540acd196974247923f29a4fbdac83f38a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Sun, 3 May 2026 14:18:01 +0200 Subject: [PATCH 1/6] WIP --- src/FederatedClient.php | 5 +++++ src/Helpers/HttpHelper.php | 2 +- src/Helpers/StringHelper.php | 2 +- src/PreRegisteredClient.php | 2 +- src/Protocol/OpMetadata.php | 6 +++++- src/Protocol/RequestDataHandler.php | 24 +++++++++++++++++++----- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/FederatedClient.php b/src/FederatedClient.php index c4f00b3..bbb2316 100644 --- a/src/FederatedClient.php +++ b/src/FederatedClient.php @@ -22,6 +22,7 @@ use SimpleSAML\OpenID\Exceptions\InvalidValueException; use SimpleSAML\OpenID\Exceptions\OpenIdException; use SimpleSAML\OpenID\Exceptions\TrustChainException; +use SimpleSAML\OpenID\Federation\EntityCollection\EntityCollectionStoreInterface; use SimpleSAML\OpenID\Federation\TrustChain; use SimpleSAML\OpenID\Jwks; use SimpleSAML\OpenID\ValueAbstracts\SignatureKeyPairBag; @@ -140,6 +141,8 @@ public function __construct( ?RequestDataHandler $requestDataHandler = null, // phpcs:ignore protected readonly AuthorizationRequestMethodEnum $defaultAuthorizationRequestMethod = AuthorizationRequestMethodEnum::FormPost, + int $maxDiscoveryDepth = 10, + ?EntityCollectionStoreInterface $entityCollectionStore = null, ) { $this->cache = $cache ?? new FileCache('ofacpc-' . md5($this->entityConfig->getEntityId())); $this->signatureKeyPairFactory = $signatureKeyPairFactory ?? new SignatureKeyPairFactory($this->jwk); @@ -184,6 +187,8 @@ public function __construct( logger: $this->logger, client: $this->httpClient, defaultTrustMarkStatusEndpointUsagePolicyEnum: $this->defaultTrustMarkStatusEndpointUsagePolicyEnum, + maxDiscoveryDepth: $maxDiscoveryDepth, + entityCollectionStore: $entityCollectionStore, ); $this->federationSignatureKeyPairBag = $this->signatureKeyPairBagFactory->fromConfig( diff --git a/src/Helpers/HttpHelper.php b/src/Helpers/HttpHelper.php index 3acfb85..0245540 100644 --- a/src/Helpers/HttpHelper.php +++ b/src/Helpers/HttpHelper.php @@ -61,7 +61,7 @@ public static function normalizeSessionCookieParams(array $cookieParams): array (!is_string($cookieParams['samesite'])) ) { $cookieParams['samesite'] = $defaultSameSiteValue; - } elseif (! in_array($cookieParams['samesite'], $validSameSiteValues)) { + } elseif (! in_array($cookieParams['samesite'], $validSameSiteValues, true)) { error_log('Invalid SameSite session cookie attribute value in php.ini. Reverting to default value.'); $cookieParams['samesite'] = $defaultSameSiteValue; } elseif (strcasecmp($cookieParams['samesite'], 'None') === 0) { diff --git a/src/Helpers/StringHelper.php b/src/Helpers/StringHelper.php index 02c4fa8..449dee2 100644 --- a/src/Helpers/StringHelper.php +++ b/src/Helpers/StringHelper.php @@ -34,7 +34,7 @@ public static function random(int $length = 16, string $randomBytesFunc = 'rando $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); } } catch (Throwable $throwable) { - throw new OidcClientException($throwable->getMessage()); + throw new OidcClientException($throwable->getMessage(), $throwable->getCode(), $throwable); } return $string; diff --git a/src/PreRegisteredClient.php b/src/PreRegisteredClient.php index 3c890ab..9462f66 100644 --- a/src/PreRegisteredClient.php +++ b/src/PreRegisteredClient.php @@ -183,7 +183,7 @@ protected function validateCache(): void } catch (Throwable $throwable) { $error = 'Cache validation error. ' . $throwable->getMessage(); $this->logger?->error($error); - throw new OidcClientException($error); + throw new OidcClientException($error, $throwable->getCode(), $throwable); } } diff --git a/src/Protocol/OpMetadata.php b/src/Protocol/OpMetadata.php index e9eb274..20f04a5 100644 --- a/src/Protocol/OpMetadata.php +++ b/src/Protocol/OpMetadata.php @@ -57,7 +57,11 @@ public function __construct( $this->metadata = $metadata; } catch (Throwable $throwable) { - throw new OidcClientException('OIDC Provider (OP) Metadata fetch error. ' . $throwable->getMessage()); + throw new OidcClientException( + 'OIDC Provider (OP) Metadata fetch error. ' . $throwable->getMessage(), + $throwable->getCode(), + $throwable, + ); } } diff --git a/src/Protocol/RequestDataHandler.php b/src/Protocol/RequestDataHandler.php index 84b5b1f..178bbe3 100644 --- a/src/Protocol/RequestDataHandler.php +++ b/src/Protocol/RequestDataHandler.php @@ -266,7 +266,11 @@ public function requestTokenData( return $this->getDecodedHttpResponseJson($response); } catch (Throwable $throwable) { - throw new OidcClientException('Token data request error. ' . $throwable->getMessage()); + throw new OidcClientException( + 'Token data request error. ' . $throwable->getMessage(), + $throwable->getCode(), + $throwable, + ); } } @@ -301,7 +305,9 @@ public function getDecodedHttpResponseJson(ResponseInterface $response): array ['responseBody' => $responseBody] ); throw new OidcClientException( - 'HTTP request JSON response is not valid.' + 'HTTP request JSON response is not valid.', + $throwable->getCode(), + $throwable, ); } } @@ -440,7 +446,7 @@ public function getDataFromIdToken( } catch (JwsException $jwsException) { $error = 'Error building ID Token: ' . $jwsException->getMessage(); $this->logger?->error($error, ['idToken' => $idToken]); - throw new OidcClientException($error); + throw new OidcClientException($error, $jwsException->getCode(), $jwsException); } try { @@ -449,7 +455,11 @@ public function getDataFromIdToken( // If we have already refreshed our cache (we have fresh JWKS), throw... if ($refreshCache) { $this->logger?->error('ID token is not valid. ' . $throwable->getMessage()); - throw new OidcClientException('ID token is not valid. ' . $throwable->getMessage()); + throw new OidcClientException( + 'ID token is not valid. ' . $throwable->getMessage(), + $throwable->getCode(), + $throwable, + ); } $this->logger?->warning('ID Token signature verification failed, but trying once more with JWKS refresh.'); @@ -538,7 +548,11 @@ public function requestUserDataFromUserInfoEndpoint( return $claims; } catch (Throwable $throwable) { - throw new OidcClientException('UserInfo endpoint error. ' . $throwable->getMessage()); + throw new OidcClientException( + 'UserInfo endpoint error. ' . $throwable->getMessage(), + $throwable->getCode(), + $throwable, + ); } } From 91cc8d1d10275fd096312d91da5f94f1d78f6802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Mon, 4 May 2026 11:02:51 +0200 Subject: [PATCH 2/6] WIP --- docs/3-Federated-Client.md | 38 +++++++++++- .../FederatedClientFactory.php | 15 +++-- .../federated-client-config-example.php | 4 ++ src/FederatedClient.php | 60 +++++++++++++++++++ 4 files changed, 112 insertions(+), 5 deletions(-) diff --git a/docs/3-Federated-Client.md b/docs/3-Federated-Client.md index d1d691d..9c5e95c 100644 --- a/docs/3-Federated-Client.md +++ b/docs/3-Federated-Client.md @@ -12,6 +12,7 @@ from the OpenID Provider (OP) to a configured Trust Anchor. the first authentication request. - **Metadata Management**: Handles the generation of the RP's Entity Configuration and metadata, including keys and trust marks. +- **Federation Discovery**: Supports discovering OPs and their metadata. - **OIDC Flow**: Manages the authorization code flow, including PKCE, state/nonce validation, and ID Token verification. - **Caching**: Efficiently caches resolved trust chains and metadata to improve @@ -130,4 +131,39 @@ exit(); ``` See the [Entity Configuration Endpoint Example](../examples/FederatedClient/FederationConfigurationController.php) -for a sample implementation. \ No newline at end of file +for a sample implementation. + +## Federation Discovery + +The client can discover OPs and their metadata using the `FederationDiscovery` +service. + +```php +/** @var \Cicnavi\Oidc\FederatedClient $client */ + +$trustAnchorId = 'https://example.com/trust-anchor'; + +$opEntities = $client->getFederation() + ->federationDiscovery() + ->discover( + trustAnchorId: $trustAnchorId, + )->filter( + criteria: ['entity_type' => ['openid_provider']], + )->sort( + claimPaths: [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'] + ], + sortOrder: 'asc', + )->getEntities(); + +// $opEntities is an array of OP entities that match the criteria, keyed by +// entity ID, and sorted by display name in this case. Use it to present +// OP available for login to the user. +``` + +Note that this operation can be time-consuming due to the full traversal of the +federation under the specified Trust Anchor. The implementation uses caching +to improve performance on subsequent calls. + +To warmup the cache, you can \ No newline at end of file diff --git a/examples/FederatedClient/FederatedClientFactory.php b/examples/FederatedClient/FederatedClientFactory.php index e984246..ab502f4 100644 --- a/examples/FederatedClient/FederatedClientFactory.php +++ b/examples/FederatedClient/FederatedClientFactory.php @@ -32,6 +32,11 @@ public function __construct( public function build(): FederatedClient { + // For federation discovery, we can inject our own + // EntityCollectionStoreInterface implementation, but in this example, + // we'll just use the default implementation. + $entityCollectionStore = null; + return new FederatedClient( entityConfig: $this->config['entity_config'], relyingPartyConfig: $this->config['relying_party_config'], @@ -40,11 +45,11 @@ public function build(): FederatedClient maxCacheDuration: $this->config['max_cache_duration'], timestampValidationLeeway: $this->config['timestamp_validation_leeway'], supportedAlgorithms: $this->config['supported_algorithms'] ?? new SupportedAlgorithms( - new SignatureAlgorithmBag( - SignatureAlgorithmEnum::ES256, - SignatureAlgorithmEnum::RS256, + new SignatureAlgorithmBag( + SignatureAlgorithmEnum::ES256, + SignatureAlgorithmEnum::RS256, + ), ), - ), logger: $this->logger, maxTrustChainDepth: $this->config['max_trust_chain_depth'] ?? 9, defaultTrustMarkStatusEndpointUsagePolicyEnum: $this->config['default_trust_mark_status_endpoint_usage_policy'] ?? TrustMarkStatusEndpointUsagePolicyEnum::NotUsed, @@ -55,6 +60,8 @@ public function build(): FederatedClient fetchUserinfoClaims: $this->config['fetch_userinfo_claims'] ?? true, pkceCodeChallengeMethod: $this->config['pkce_code_challenge_method'] ?? PkceCodeChallengeMethodEnum::S256, defaultAuthorizationRequestMethod: $this->config['authorization_request_method'] ?? AuthorizationRequestMethodEnum::FormPost, + maxDiscoveryDepth: $this->config['max_discovery_depth'] ?? 10, + entityCollectionStore: $entityCollectionStore, ); } } diff --git a/examples/FederatedClient/federated-client-config-example.php b/examples/FederatedClient/federated-client-config-example.php index d8c0dce..7bd4ba9 100644 --- a/examples/FederatedClient/federated-client-config-example.php +++ b/examples/FederatedClient/federated-client-config-example.php @@ -119,4 +119,8 @@ 'https://idp.mivanci.incubator.hexaa.eu', 'https://oidfed-op-demo.incubator.geant.org', ), + + // Federation Discovery configuration. + 'max_discovery_depth' => 10, + ]; diff --git a/src/FederatedClient.php b/src/FederatedClient.php index bbb2316..fc17790 100644 --- a/src/FederatedClient.php +++ b/src/FederatedClient.php @@ -861,4 +861,64 @@ public function getUserData(?ServerRequestInterface $request = null): array fetchUserinfoClaims: $this->fetchUserinfoClaims ); } + + /** + * Discover OpenID Providers across all configured trus anchors. + * + * @return array>> An associative + * array where each trust anchor ID maps to its list of discovered OPs. + */ + public function discoverOpenIdProviders(): array + { + return $this->discoverEntities( + criteria: ['entity_type' => ['openid_provider']], + sortClaimPaths: [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'] + ], + ); + } + + /** + * Discovers federated entities across all configured trust anchors based on + * the provided criteria and sorting options. + * + * @param array{ + * entity_type?: string[], + * trust_mark_type?: string[], + * query?: string, + * } $criteria $criteria Optional criteria to filter the discovered + * entities. Example: ['entity_type' => ['openid_provider']] + * @param non-empty-array $sortClaimPaths Optional + * claim paths used for sorting the entities (multiple allowed). Example: + * [['metadata', 'federation_entity', 'display_name']] + * @param 'asc'|'desc' $sortOrder The sorting order, either 'asc' (ascending) + * or 'desc' (descending). Defaults to 'asc'. + * @return array>> An associative + * array where each trust anchor ID maps to its list of discovered entities. + * [trustAnchorId => [entityId1 => entityPayload1, entityId2 => entityPayload2, ...]] + */ + public function discoverEntities( + array $criteria = [], + array $sortClaimPaths = [['metadata', 'federation_entity', 'display_name']], + string $sortOrder = 'asc', + ): array { + $entities = []; + + // Do entity discovery for each trust anchor. + foreach ($this->getEntityConfig()->getTrustAnchorBag()->getAllEntityIds() as $trustAnchorId) { + $entities[$trustAnchorId] = $this->getFederation() + ->federationDiscovery() + ->discover( + trustAnchorId: $trustAnchorId, + )->filter( + criteria: $criteria, + )->sort( + claimPaths: $sortClaimPaths, + sortOrder: $sortOrder, + )->getEntities(); + } + + return $entities; + } } From 10c288f567914d16a0f0f636cbdb64468218c31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Mon, 4 May 2026 11:03:43 +0200 Subject: [PATCH 3/6] WIP --- src/FederatedClient.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/FederatedClient.php b/src/FederatedClient.php index fc17790..cc90e78 100644 --- a/src/FederatedClient.php +++ b/src/FederatedClient.php @@ -902,6 +902,7 @@ public function discoverEntities( array $criteria = [], array $sortClaimPaths = [['metadata', 'federation_entity', 'display_name']], string $sortOrder = 'asc', + bool $forceRefresh = false, ): array { $entities = []; @@ -911,6 +912,7 @@ public function discoverEntities( ->federationDiscovery() ->discover( trustAnchorId: $trustAnchorId, + forceRefresh: $forceRefresh, )->filter( criteria: $criteria, )->sort( From 91b1ee8c82c59d55165b5f14c5877c2622e00040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Mon, 4 May 2026 11:38:25 +0200 Subject: [PATCH 4/6] WIP --- docs/3-Federated-Client.md | 80 ++++++++--- .../FederationDiscoveryController.php | 135 ++++++++++++++++++ src/FederatedClient.php | 19 ++- 3 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 examples/FederatedClient/FederationDiscoveryController.php diff --git a/docs/3-Federated-Client.md b/docs/3-Federated-Client.md index 9c5e95c..1192fb4 100644 --- a/docs/3-Federated-Client.md +++ b/docs/3-Federated-Client.md @@ -141,29 +141,63 @@ service. ```php /** @var \Cicnavi\Oidc\FederatedClient $client */ -$trustAnchorId = 'https://example.com/trust-anchor'; - -$opEntities = $client->getFederation() - ->federationDiscovery() - ->discover( - trustAnchorId: $trustAnchorId, - )->filter( - criteria: ['entity_type' => ['openid_provider']], - )->sort( - claimPaths: [ - ['metadata', 'openid_provider', 'display_name'], - ['metadata', 'federation_entity', 'display_name'] - ], - sortOrder: 'asc', - )->getEntities(); - -// $opEntities is an array of OP entities that match the criteria, keyed by -// entity ID, and sorted by display name in this case. Use it to present -// OP available for login to the user. +// Optionally define claim paths to sort discovered OPs by their display names +// (e.g., for user-friendly display in a login UI). The paths are relative to +// the OP's metadata structure. The method also has default paths it checks +// if not provided. +$sortClaimPaths = [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'], +]; +$forceRefresh = false; // Set to true to bypass cache and fetch fresh data + +$openIdProvidersPerTrustAnchor = $client->discoverOpenIdProviders($sortClaimPaths, $forceRefresh); + +// The result $openIdProvidersPerTrustAnchor is an associative array where each +// trust anchor ID maps to its list of discovered entities: +// [trustAnchorId => [entityId1 => entityPayload1, entityId2 => entityPayload2, ...]] +// Use it to display available OPs for users to choose from during login. + +``` + +This operation can be time-consuming on the first run because it may require a +full traversal of the federation under each configured Trust Anchor. Results +are cached and subsequent calls are typically much faster. + +To warm up discovery caches (for example, from a CLI command or scheduled job), +you can trigger discovery in advance: + +```php +/** @var \Cicnavi\Oidc\FederatedClient $client */ + +// Warm up OP discovery caches for all configured trust anchors. +// Keep forceRefresh=true only for explicit refresh jobs. +$client->discoverOpenIdProviders(forceRefresh: true); ``` -Note that this operation can be time-consuming due to the full traversal of the -federation under the specified Trust Anchor. The implementation uses caching -to improve performance on subsequent calls. +### Advanced Discovery + +If you need to discover entities other than OpenID Providers, use +`discoverEntities()` and provide criteria explicitly: + +```php +/** @var \Cicnavi\Oidc\FederatedClient $client */ + +$entitiesPerTrustAnchor = $client->discoverEntities( + criteria: [ + 'entity_type' => ['openid_provider'], + // Optional filters: + // 'trust_mark_type' => ['https://example.org/trust-mark/type'], + // 'query' => 'search text', + ], + sortClaimPaths: [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'], + ], + sortOrder: 'asc', + forceRefresh: false, +); +``` -To warmup the cache, you can \ No newline at end of file +`discoverEntities()` returns the same grouped shape: +`[trustAnchorId => [entityId => entityPayload, ...]]`. \ No newline at end of file diff --git a/examples/FederatedClient/FederationDiscoveryController.php b/examples/FederatedClient/FederationDiscoveryController.php new file mode 100644 index 0000000..e406ec1 --- /dev/null +++ b/examples/FederatedClient/FederationDiscoveryController.php @@ -0,0 +1,135 @@ +getQueryParams(); + $forceRefresh = $this->parseBool($queryParams['force_refresh'] ?? null); + + $providersPerTrustAnchor = $this->federatedClient->discoverOpenIdProviders( + sortClaimPaths: [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'], + ], + forceRefresh: $forceRefresh, + ); + + $this->logger->info('OpenID Provider discovery completed.', [ + 'force_refresh' => $forceRefresh, + 'trust_anchors' => count($providersPerTrustAnchor), + ]); + + return $providersPerTrustAnchor; + } + + /** + * Discover entities grouped by trust anchor using custom criteria. + * + * Query params: + * - entity_type=openid_provider,federation_entity (optional) + * - trust_mark_type=https://example.org/tm/1,https://example.org/tm/2 (optional) + * - query=search text (optional) + * - sort_order=asc|desc (optional, default: asc) + * - force_refresh=1|true (optional) + */ + public function entities(ServerRequestInterface $request): array + { + $queryParams = $request->getQueryParams(); + + $criteria = array_filter([ + 'entity_type' => $this->parseCsv($queryParams['entity_type'] ?? null), + 'trust_mark_type' => $this->parseCsv($queryParams['trust_mark_type'] ?? null), + 'query' => $this->parseString($queryParams['query'] ?? null), + ], static fn (mixed $value): bool => $value !== null && $value !== []); + + $sortOrder = $this->parseSortOrder($queryParams['sort_order'] ?? null); + $forceRefresh = $this->parseBool($queryParams['force_refresh'] ?? null); + + $entitiesPerTrustAnchor = $this->federatedClient->discoverEntities( + criteria: $criteria, + sortClaimPaths: [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'], + ], + sortOrder: $sortOrder, + forceRefresh: $forceRefresh, + ); + + $this->logger->info('Federation entity discovery completed.', [ + 'criteria' => $criteria, + 'sort_order' => $sortOrder, + 'force_refresh' => $forceRefresh, + 'trust_anchors' => count($entitiesPerTrustAnchor), + ]); + + return $entitiesPerTrustAnchor; + } + + private function parseSortOrder(mixed $sortOrder): string + { + if (!is_string($sortOrder)) { + return 'asc'; + } + + return strtolower($sortOrder) === 'desc' ? 'desc' : 'asc'; + } + + private function parseCsv(mixed $value): ?array + { + if (!is_string($value) || trim($value) === '') { + return null; + } + + $values = array_values(array_filter(array_map( + static fn (string $item): string => trim($item), + explode(',', $value) + ))); + + return $values === [] ? null : $values; + } + + private function parseString(mixed $value): ?string + { + if (!is_string($value) || trim($value) === '') { + return null; + } + + return trim($value); + } + + private function parseBool(mixed $value): bool + { + if (is_bool($value)) { + return $value; + } + + if (!is_string($value)) { + return false; + } + + return in_array(strtolower($value), ['1', 'true', 'yes', 'on'], true); + } +} diff --git a/src/FederatedClient.php b/src/FederatedClient.php index cc90e78..6ec3c66 100644 --- a/src/FederatedClient.php +++ b/src/FederatedClient.php @@ -865,17 +865,28 @@ public function getUserData(?ServerRequestInterface $request = null): array /** * Discover OpenID Providers across all configured trus anchors. * + * @param non-empty-array $sortClaimPaths Optional + * claim paths used for sorting the entities (multiple allowed). Example: + * [['metadata', 'openid_provider', 'display_name'], ['metadata', 'federation_entity', 'display_name']] + * @param bool $forceRefresh Whether to force refreshing the cache. + * * @return array>> An associative * array where each trust anchor ID maps to its list of discovered OPs. */ - public function discoverOpenIdProviders(): array - { + public function discoverOpenIdProviders( + array $sortClaimPaths = [ + ['metadata', 'openid_provider', 'display_name'], + ['metadata', 'federation_entity', 'display_name'], + ], + bool $forceRefresh = false, + ): array { return $this->discoverEntities( criteria: ['entity_type' => ['openid_provider']], sortClaimPaths: [ ['metadata', 'openid_provider', 'display_name'], - ['metadata', 'federation_entity', 'display_name'] + ['metadata', 'federation_entity', 'display_name'], ], + forceRefresh: $forceRefresh, ); } @@ -894,6 +905,8 @@ public function discoverOpenIdProviders(): array * [['metadata', 'federation_entity', 'display_name']] * @param 'asc'|'desc' $sortOrder The sorting order, either 'asc' (ascending) * or 'desc' (descending). Defaults to 'asc'. + * @param bool $forceRefresh Whether to force refreshing the cache. + * * @return array>> An associative * array where each trust anchor ID maps to its list of discovered entities. * [trustAnchorId => [entityId1 => entityPayload1, entityId2 => entityPayload2, ...]] From 8c6e4314bb67c7211329cd8b2db2aeafc38fe6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Mon, 4 May 2026 11:45:39 +0200 Subject: [PATCH 5/6] WIP --- .../FederatedClient/federated-client-config-example.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/FederatedClient/federated-client-config-example.php b/examples/FederatedClient/federated-client-config-example.php index 7bd4ba9..b885b20 100644 --- a/examples/FederatedClient/federated-client-config-example.php +++ b/examples/FederatedClient/federated-client-config-example.php @@ -114,12 +114,6 @@ 'fetch_userinfo_claims' => true, 'authorization_request_method' => AuthorizationRequestMethodEnum::FormPost, - // Hardcoded OpenID Providers, until discovery is implemented. - 'open_id_providers' => new \SimpleSAML\OpenID\ValueAbstracts\UniqueStringBag( - 'https://idp.mivanci.incubator.hexaa.eu', - 'https://oidfed-op-demo.incubator.geant.org', - ), - // Federation Discovery configuration. 'max_discovery_depth' => 10, From f51ad2fde5815cb1759bd55eb43f76502af49cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Mon, 4 May 2026 12:58:15 +0200 Subject: [PATCH 6/6] WIP --- UPGRADING.md | 8 ++++++++ docs/3-Federated-Client.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index 797747d..68fae96 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,5 +1,13 @@ # Upgrading +## [3.1.0] - 2026-05-04 + +### Added + +In src/FederatedClient.php added methods `discoverOpenIdProviders` and +`discoverEntities` to `FederatedClient` class, which can be used to discover +OPs and entities in federated environments. + ## [3.0.0] - 2025-12-02 Major release with breaking changes to the client instantiation API. diff --git a/docs/3-Federated-Client.md b/docs/3-Federated-Client.md index 1192fb4..79643d0 100644 --- a/docs/3-Federated-Client.md +++ b/docs/3-Federated-Client.md @@ -133,7 +133,7 @@ exit(); See the [Entity Configuration Endpoint Example](../examples/FederatedClient/FederationConfigurationController.php) for a sample implementation. -## Federation Discovery +## Federation Discovery (from v3.1) The client can discover OPs and their metadata using the `FederationDiscovery` service.