Skip to content

Commit fa20eb5

Browse files
authored
Remove fetch and list federation endpoints (#326)
* Remove fetch and list endpoint * Remove is_federated client property * Update upgrade log
1 parent 62d842d commit fa20eb5

20 files changed

Lines changed: 38 additions & 383 deletions

File tree

docker/conformance.sql

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ INSERT INTO oidc_migration_versions VALUES('20250917163000');
3030
INSERT INTO oidc_migration_versions VALUES('20251021000001');
3131
INSERT INTO oidc_migration_versions VALUES('20251021000002');
3232
INSERT INTO oidc_migration_versions VALUES('20260109000001');
33+
INSERT INTO oidc_migration_versions VALUES('20260218163000');
3334
CREATE TABLE oidc_user (
3435
id VARCHAR(191) PRIMARY KEY NOT NULL,
3536
claims TEXT,
@@ -59,17 +60,16 @@ CREATE TABLE oidc_client (
5960
updated_at TIMESTAMP NULL DEFAULT NULL,
6061
created_at TIMESTAMP NULL DEFAULT NULL,
6162
expires_at TIMESTAMP NULL DEFAULT NULL,
62-
is_federated BOOLEAN NOT NULL DEFAULT false,
6363
is_generic BOOLEAN NOT NULL DEFAULT false,
6464
extra_metadata TEXT NULL
6565
);
6666
-- Used 'httpd' host for back-channel logout url (https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout)
6767
-- since this is the hostname of conformance server while running in container environment
68-
INSERT INTO oidc_client VALUES('_55a99a1d298da921cb27d700d4604352e51171ebc4','_8967dd97d07cc59db7055e84ac00e79005157c1132','Conformance Client 1',replace('Client 1 for Conformance Testing https://openid.net/certification/connect_op_testing/\n','\n',char(10)),'example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone","offline_access"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false, NULL);
69-
INSERT INTO oidc_client VALUES('_34efb61060172a11d62101bc804db789f8f9100b0e','_91a4607a1c10ba801268929b961b3f6c067ff82d21','Conformance Client 2','','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","offline_access"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false, NULL);
70-
INSERT INTO oidc_client VALUES('_0afb7d18e54b2de8205a93e38ca119e62ee321d031','_944e73bbeec7850d32b68f1b5c780562c955967e4e','Conformance Client 3','Client for client_secret_post','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false, NULL);
71-
INSERT INTO oidc_client VALUES('_8957eda35234902ba8343c0cdacac040310f17dfca','_322d16999f9da8b5abc9e9c0c08e853f60f4dc4804','RP-Initiated Logout Client','Client for testing RP-Initiated Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]',NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false, NULL);
72-
INSERT INTO oidc_client VALUES('_9fe2f7589ece1b71f5ef75a91847d71bc5125ec2a6','_3c0beb20194179c01d7796c6836f62801e9ed4b368','Back-Channel Logout Client','Client for testing Back-Channel Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false, NULL);
68+
INSERT INTO oidc_client VALUES('_55a99a1d298da921cb27d700d4604352e51171ebc4','_8967dd97d07cc59db7055e84ac00e79005157c1132','Conformance Client 1',replace('Client 1 for Conformance Testing https://openid.net/certification/connect_op_testing/\n','\n',char(10)),'example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone","offline_access"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, NULL);
69+
INSERT INTO oidc_client VALUES('_34efb61060172a11d62101bc804db789f8f9100b0e','_91a4607a1c10ba801268929b961b3f6c067ff82d21','Conformance Client 2','','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","offline_access"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, NULL);
70+
INSERT INTO oidc_client VALUES('_0afb7d18e54b2de8205a93e38ca119e62ee321d031','_944e73bbeec7850d32b68f1b5c780562c955967e4e','Conformance Client 3','Client for client_secret_post','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, NULL);
71+
INSERT INTO oidc_client VALUES('_8957eda35234902ba8343c0cdacac040310f17dfca','_322d16999f9da8b5abc9e9c0c08e853f60f4dc4804','RP-Initiated Logout Client','Client for testing RP-Initiated Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]',NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, NULL);
72+
INSERT INTO oidc_client VALUES('_9fe2f7589ece1b71f5ef75a91847d71bc5125ec2a6','_3c0beb20194179c01d7796c6836f62801e9ed4b368','Back-Channel Logout Client','Client for testing Back-Channel Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, NULL);
7373
CREATE TABLE oidc_access_token (
7474
id VARCHAR(191) PRIMARY KEY NOT NULL,
7575
scopes TEXT,

docs/6-oidc-upgrade.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ apply those relevant to your deployment.
55

66
## Version 6 to 7
77

8+
As the database schema has been updated, you will have to run the DB migrations
9+
to bring your local database schema up to date.
10+
811
New features:
912

1013
- Instance can now be configured to support multiple algorithms and signature
@@ -80,10 +83,20 @@ This would allow multiple values (array of values) for standard claims which
8083
have a single value by specification. All [standard claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)
8184
are now hardcoded to have a single value, even when the
8285
'are_multiple_claim_values_allowed' option is enabled.
86+
- OpenID Federation specific endpoints for subordinate listing and fetching
87+
statements about subordinates are removed, as the final specification
88+
explicitly states that leaf entities must not have those endpoints.
89+
This effectively means that this OP implementation can only be a leaf entity
90+
in the federation context, and not a federation operator or intermediary entity.
8391

8492
Medium impact changes:
8593

8694
Low-impact changes:
95+
- Client property `is_federated` has been removed, as the OP implementation
96+
can now only be a leaf entity in the federation context, and not a federation
97+
operator or intermediary entity. Previously, this property was used to
98+
indicate whether the client is a federated client or not, but now it is not
99+
needed since the OP implementation can only be a leaf entity
87100

88101
## Version 5 to 6
89102

routing/routes/routes.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use SimpleSAML\Module\oidc\Controllers\ConfigurationDiscoveryController;
1818
use SimpleSAML\Module\oidc\Controllers\EndSessionController;
1919
use SimpleSAML\Module\oidc\Controllers\Federation\EntityStatementController;
20-
use SimpleSAML\Module\oidc\Controllers\Federation\SubordinateListingsController;
2120
use SimpleSAML\Module\oidc\Controllers\JwksController;
2221
use SimpleSAML\Module\oidc\Controllers\OAuth2\OAuth2ServerConfigurationController;
2322
use SimpleSAML\Module\oidc\Controllers\UserInfoController;
@@ -114,14 +113,6 @@
114113
->controller([EntityStatementController::class, 'configuration'])
115114
->methods([HttpMethodsEnum::GET->value]);
116115

117-
$routes->add(RoutesEnum::FederationFetch->name, RoutesEnum::FederationFetch->value)
118-
->controller([EntityStatementController::class, 'fetch'])
119-
->methods([HttpMethodsEnum::GET->value]);
120-
121-
$routes->add(RoutesEnum::FederationList->name, RoutesEnum::FederationList->value)
122-
->controller([SubordinateListingsController::class, 'list'])
123-
->methods([HttpMethodsEnum::GET->value]);
124-
125116
/*****************************************************************************************************************
126117
* OpenID for Verifiable Credential Issuance
127118
****************************************************************************************************************/

src/Controllers/Admin/ClientController.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ protected function buildClientEntityFromFormData(
348348
$jwksUri = empty($data[ClientEntity::KEY_JWKS_URI]) ? null : (string)$data[ClientEntity::KEY_JWKS_URI];
349349
$signedJwksUri = empty($data[ClientEntity::KEY_SIGNED_JWKS_URI]) ?
350350
null : (string)$data[ClientEntity::KEY_SIGNED_JWKS_URI];
351-
$isFederated = (bool)$data[ClientEntity::KEY_IS_FEDERATED];
352351

353352
$idTokenSignedResponseAlg = isset($data[ClaimsEnum::IdTokenSignedResponseAlg->value]) &&
354353
is_string($data[ClaimsEnum::IdTokenSignedResponseAlg->value]) ?
@@ -382,7 +381,6 @@ protected function buildClientEntityFromFormData(
382381
$updatedAt,
383382
$createdAt,
384383
$expiresAt,
385-
$isFederated,
386384
$isGeneric,
387385
$extraMetadata,
388386
);

src/Controllers/Federation/EntityStatementController.php

Lines changed: 0 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use SimpleSAML\Module\oidc\Helpers;
88
use SimpleSAML\Module\oidc\ModuleConfig;
9-
use SimpleSAML\Module\oidc\Repositories\ClientRepository;
109
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
1110
use SimpleSAML\Module\oidc\Services\LoggerService;
1211
use SimpleSAML\Module\oidc\Services\OpMetadataService;
@@ -16,11 +15,9 @@
1615
use SimpleSAML\OpenID\Codebooks\ClientRegistrationTypesEnum;
1716
use SimpleSAML\OpenID\Codebooks\ContentTypesEnum;
1817
use SimpleSAML\OpenID\Codebooks\EntityTypesEnum;
19-
use SimpleSAML\OpenID\Codebooks\ErrorsEnum;
2018
use SimpleSAML\OpenID\Codebooks\HttpHeadersEnum;
2119
use SimpleSAML\OpenID\Federation;
2220
use SimpleSAML\OpenID\Jwks;
23-
use Symfony\Component\HttpFoundation\Request;
2421
use Symfony\Component\HttpFoundation\Response;
2522

2623
class EntityStatementController
@@ -35,7 +32,6 @@ public function __construct(
3532
protected readonly ModuleConfig $moduleConfig,
3633
protected readonly Jwks $jwks,
3734
protected readonly OpMetadataService $opMetadataService,
38-
protected readonly ClientRepository $clientRepository,
3935
protected readonly Helpers $helpers,
4036
protected readonly Routes $routes,
4137
protected readonly Federation $federation,
@@ -224,113 +220,6 @@ public function configuration(): Response
224220
return $this->prepareEntityStatementResponse($entityConfigurationToken);
225221
}
226222

227-
public function fetch(Request $request): Response
228-
{
229-
$subject = $request->query->getString(ClaimsEnum::Sub->value);
230-
231-
if (empty($subject)) {
232-
return $this->routes->newJsonErrorResponse(
233-
ErrorsEnum::InvalidRequest->value,
234-
sprintf('Missing parameter %s', ClaimsEnum::Sub->value),
235-
400,
236-
);
237-
}
238-
239-
/** @var non-empty-string $subject */
240-
241-
$cachedSubordinateStatement = $this->federationCache?->get(
242-
null,
243-
self::KEY_RP_SUBORDINATE_ENTITY_STATEMENT,
244-
$subject,
245-
);
246-
247-
if (!is_null($cachedSubordinateStatement)) {
248-
return $this->prepareEntityStatementResponse((string)$cachedSubordinateStatement);
249-
}
250-
251-
$client = $this->clientRepository->findFederatedByEntityIdentifier($subject);
252-
if (empty($client)) {
253-
return $this->routes->newJsonErrorResponse(
254-
ErrorsEnum::NotFound->value,
255-
sprintf('Subject not found (%s)', $subject),
256-
404,
257-
);
258-
}
259-
260-
$jwks = $client->getFederationJwks();
261-
if (empty($jwks)) {
262-
return $this->routes->newJsonErrorResponse(
263-
ErrorsEnum::InvalidClient->value,
264-
sprintf('Subject does not contain JWKS claim (%s)', $subject),
265-
401,
266-
);
267-
}
268-
269-
$currentTimestamp = $this->helpers->dateTime()->getUtc()->getTimestamp();
270-
271-
$payload = [
272-
ClaimsEnum::Iss->value => $this->moduleConfig->getIssuer(),
273-
ClaimsEnum::Iat->value => $currentTimestamp,
274-
ClaimsEnum::Jti->value => $this->helpers->random()->getIdentifier(),
275-
276-
ClaimsEnum::Sub->value => $subject,
277-
ClaimsEnum::Exp->value => $this->helpers->dateTime()->getUtc()->add(
278-
$this->moduleConfig->getFederationEntityStatementDuration(),
279-
)->getTimestamp(),
280-
ClaimsEnum::Jwks->value => $jwks,
281-
ClaimsEnum::Metadata->value => [
282-
EntityTypesEnum::OpenIdRelyingParty->value => [
283-
ClaimsEnum::ClientName->value => $client->getName(),
284-
ClaimsEnum::ClientId->value => $client->getIdentifier(),
285-
ClaimsEnum::RedirectUris->value => $client->getRedirectUris(),
286-
ClaimsEnum::Scope->value => implode(' ', $client->getScopes()),
287-
ClaimsEnum::ClientRegistrationTypes->value => $client->getClientRegistrationTypes(),
288-
// Optional claims...
289-
...(array_filter(
290-
[
291-
ClaimsEnum::BackChannelLogoutUri->value => $client->getBackChannelLogoutUri(),
292-
ClaimsEnum::PostLogoutRedirectUris->value => $client->getPostLogoutRedirectUri(),
293-
],
294-
)),
295-
// TODO v7 mivanci Continue
296-
// https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
297-
// https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata
298-
],
299-
],
300-
];
301-
302-
// TODO v7 mivanci Continue
303-
// Note: claims which can be present in subordinate statements:
304-
// * metadata_policy
305-
// * constraints
306-
// * metadata_policy_crit
307-
308-
$signingKeyPair = $this->moduleConfig
309-
->getFederationSignatureKeyPairBag()
310-
->getFirstOrFail();
311-
312-
313-
$header = [
314-
ClaimsEnum::Kid->value => $signingKeyPair->getKeyPair()->getKeyId(),
315-
];
316-
317-
$subordinateStatementToken = $this->federation->entityStatementFactory()->fromData(
318-
$signingKeyPair->getKeyPair()->getPrivateKey(),
319-
$signingKeyPair->getSignatureAlgorithm(),
320-
$payload,
321-
$header,
322-
)->getToken();
323-
324-
$this->federationCache?->set(
325-
$subordinateStatementToken,
326-
$this->moduleConfig->getFederationEntityStatementCacheDurationForProduced(),
327-
self::KEY_RP_SUBORDINATE_ENTITY_STATEMENT,
328-
$subject,
329-
);
330-
331-
return $this->prepareEntityStatementResponse($subordinateStatementToken);
332-
}
333-
334223
protected function prepareEntityStatementResponse(string $entityStatementToken): Response
335224
{
336225
return $this->routes->newResponse(

src/Controllers/Federation/SubordinateListingsController.php

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)