Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docker/conformance.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ INSERT INTO oidc_migration_versions VALUES('20250917163000');
INSERT INTO oidc_migration_versions VALUES('20251021000001');
INSERT INTO oidc_migration_versions VALUES('20251021000002');
INSERT INTO oidc_migration_versions VALUES('20260109000001');
INSERT INTO oidc_migration_versions VALUES('20260218163000');
CREATE TABLE oidc_user (
id VARCHAR(191) PRIMARY KEY NOT NULL,
claims TEXT,
Expand Down Expand Up @@ -59,17 +60,16 @@ CREATE TABLE oidc_client (
updated_at TIMESTAMP NULL DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL,
expires_at TIMESTAMP NULL DEFAULT NULL,
is_federated BOOLEAN NOT NULL DEFAULT false,
is_generic BOOLEAN NOT NULL DEFAULT false,
extra_metadata TEXT NULL
);
-- Used 'httpd' host for back-channel logout url (https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout)
-- since this is the hostname of conformance server while running in container environment
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
CREATE TABLE oidc_access_token (
id VARCHAR(191) PRIMARY KEY NOT NULL,
scopes TEXT,
Expand Down
13 changes: 13 additions & 0 deletions docs/6-oidc-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ apply those relevant to your deployment.

## Version 6 to 7

As the database schema has been updated, you will have to run the DB migrations
to bring your local database schema up to date.

New features:

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

Medium impact changes:

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

## Version 5 to 6

Expand Down
9 changes: 0 additions & 9 deletions routing/routes/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use SimpleSAML\Module\oidc\Controllers\ConfigurationDiscoveryController;
use SimpleSAML\Module\oidc\Controllers\EndSessionController;
use SimpleSAML\Module\oidc\Controllers\Federation\EntityStatementController;
use SimpleSAML\Module\oidc\Controllers\Federation\SubordinateListingsController;
use SimpleSAML\Module\oidc\Controllers\JwksController;
use SimpleSAML\Module\oidc\Controllers\OAuth2\OAuth2ServerConfigurationController;
use SimpleSAML\Module\oidc\Controllers\UserInfoController;
Expand Down Expand Up @@ -114,14 +113,6 @@
->controller([EntityStatementController::class, 'configuration'])
->methods([HttpMethodsEnum::GET->value]);

$routes->add(RoutesEnum::FederationFetch->name, RoutesEnum::FederationFetch->value)
->controller([EntityStatementController::class, 'fetch'])
->methods([HttpMethodsEnum::GET->value]);

$routes->add(RoutesEnum::FederationList->name, RoutesEnum::FederationList->value)
->controller([SubordinateListingsController::class, 'list'])
->methods([HttpMethodsEnum::GET->value]);

/*****************************************************************************************************************
* OpenID for Verifiable Credential Issuance
****************************************************************************************************************/
Expand Down
2 changes: 0 additions & 2 deletions src/Controllers/Admin/ClientController.php
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,6 @@ protected function buildClientEntityFromFormData(
$jwksUri = empty($data[ClientEntity::KEY_JWKS_URI]) ? null : (string)$data[ClientEntity::KEY_JWKS_URI];
$signedJwksUri = empty($data[ClientEntity::KEY_SIGNED_JWKS_URI]) ?
null : (string)$data[ClientEntity::KEY_SIGNED_JWKS_URI];
$isFederated = (bool)$data[ClientEntity::KEY_IS_FEDERATED];

$idTokenSignedResponseAlg = isset($data[ClaimsEnum::IdTokenSignedResponseAlg->value]) &&
is_string($data[ClaimsEnum::IdTokenSignedResponseAlg->value]) ?
Expand Down Expand Up @@ -382,7 +381,6 @@ protected function buildClientEntityFromFormData(
$updatedAt,
$createdAt,
$expiresAt,
$isFederated,
$isGeneric,
$extraMetadata,
);
Expand Down
111 changes: 0 additions & 111 deletions src/Controllers/Federation/EntityStatementController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use SimpleSAML\Module\oidc\Helpers;
use SimpleSAML\Module\oidc\ModuleConfig;
use SimpleSAML\Module\oidc\Repositories\ClientRepository;
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
use SimpleSAML\Module\oidc\Services\LoggerService;
use SimpleSAML\Module\oidc\Services\OpMetadataService;
Expand All @@ -16,11 +15,9 @@
use SimpleSAML\OpenID\Codebooks\ClientRegistrationTypesEnum;
use SimpleSAML\OpenID\Codebooks\ContentTypesEnum;
use SimpleSAML\OpenID\Codebooks\EntityTypesEnum;
use SimpleSAML\OpenID\Codebooks\ErrorsEnum;
use SimpleSAML\OpenID\Codebooks\HttpHeadersEnum;
use SimpleSAML\OpenID\Federation;
use SimpleSAML\OpenID\Jwks;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class EntityStatementController
Expand All @@ -35,7 +32,6 @@ public function __construct(
protected readonly ModuleConfig $moduleConfig,
protected readonly Jwks $jwks,
protected readonly OpMetadataService $opMetadataService,
protected readonly ClientRepository $clientRepository,
protected readonly Helpers $helpers,
protected readonly Routes $routes,
protected readonly Federation $federation,
Expand Down Expand Up @@ -224,113 +220,6 @@ public function configuration(): Response
return $this->prepareEntityStatementResponse($entityConfigurationToken);
}

public function fetch(Request $request): Response
{
$subject = $request->query->getString(ClaimsEnum::Sub->value);

if (empty($subject)) {
return $this->routes->newJsonErrorResponse(
ErrorsEnum::InvalidRequest->value,
sprintf('Missing parameter %s', ClaimsEnum::Sub->value),
400,
);
}

/** @var non-empty-string $subject */

$cachedSubordinateStatement = $this->federationCache?->get(
null,
self::KEY_RP_SUBORDINATE_ENTITY_STATEMENT,
$subject,
);

if (!is_null($cachedSubordinateStatement)) {
return $this->prepareEntityStatementResponse((string)$cachedSubordinateStatement);
}

$client = $this->clientRepository->findFederatedByEntityIdentifier($subject);
if (empty($client)) {
return $this->routes->newJsonErrorResponse(
ErrorsEnum::NotFound->value,
sprintf('Subject not found (%s)', $subject),
404,
);
}

$jwks = $client->getFederationJwks();
if (empty($jwks)) {
return $this->routes->newJsonErrorResponse(
ErrorsEnum::InvalidClient->value,
sprintf('Subject does not contain JWKS claim (%s)', $subject),
401,
);
}

$currentTimestamp = $this->helpers->dateTime()->getUtc()->getTimestamp();

$payload = [
ClaimsEnum::Iss->value => $this->moduleConfig->getIssuer(),
ClaimsEnum::Iat->value => $currentTimestamp,
ClaimsEnum::Jti->value => $this->helpers->random()->getIdentifier(),

ClaimsEnum::Sub->value => $subject,
ClaimsEnum::Exp->value => $this->helpers->dateTime()->getUtc()->add(
$this->moduleConfig->getFederationEntityStatementDuration(),
)->getTimestamp(),
ClaimsEnum::Jwks->value => $jwks,
ClaimsEnum::Metadata->value => [
EntityTypesEnum::OpenIdRelyingParty->value => [
ClaimsEnum::ClientName->value => $client->getName(),
ClaimsEnum::ClientId->value => $client->getIdentifier(),
ClaimsEnum::RedirectUris->value => $client->getRedirectUris(),
ClaimsEnum::Scope->value => implode(' ', $client->getScopes()),
ClaimsEnum::ClientRegistrationTypes->value => $client->getClientRegistrationTypes(),
// Optional claims...
...(array_filter(
[
ClaimsEnum::BackChannelLogoutUri->value => $client->getBackChannelLogoutUri(),
ClaimsEnum::PostLogoutRedirectUris->value => $client->getPostLogoutRedirectUri(),
],
)),
// TODO v7 mivanci Continue
// https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
// https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata
],
],
];

// TODO v7 mivanci Continue
// Note: claims which can be present in subordinate statements:
// * metadata_policy
// * constraints
// * metadata_policy_crit

$signingKeyPair = $this->moduleConfig
->getFederationSignatureKeyPairBag()
->getFirstOrFail();


$header = [
ClaimsEnum::Kid->value => $signingKeyPair->getKeyPair()->getKeyId(),
];

$subordinateStatementToken = $this->federation->entityStatementFactory()->fromData(
$signingKeyPair->getKeyPair()->getPrivateKey(),
$signingKeyPair->getSignatureAlgorithm(),
$payload,
$header,
)->getToken();

$this->federationCache?->set(
$subordinateStatementToken,
$this->moduleConfig->getFederationEntityStatementCacheDurationForProduced(),
self::KEY_RP_SUBORDINATE_ENTITY_STATEMENT,
$subject,
);

return $this->prepareEntityStatementResponse($subordinateStatementToken);
}

protected function prepareEntityStatementResponse(string $entityStatementToken): Response
{
return $this->routes->newResponse(
Expand Down
69 changes: 0 additions & 69 deletions src/Controllers/Federation/SubordinateListingsController.php

This file was deleted.

Loading