From 33f20810d3a36febecc65cc958c6b1a0519eb325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= Date: Wed, 18 Dec 2024 11:29:11 +0100 Subject: [PATCH] Use JWKS JSON string as Trust Anchor JWKS config --- config-templates/module_oidc.php | 32 ++++--------------- src/ModuleConfig.php | 11 ++----- .../RequestRules/Rules/ClientIdRule.php | 13 ++++++++ templates/config/federation.twig | 2 +- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/config-templates/module_oidc.php b/config-templates/module_oidc.php index 30064f74..c4f8d03b 100644 --- a/config-templates/module_oidc.php +++ b/config-templates/module_oidc.php @@ -327,32 +327,14 @@ ModuleConfig::OPTION_FEDERATION_ENABLED => false, // Trust Anchors which are valid for this entity. The key represents the Trust Anchor Entity ID, while the value can - // be the Trust Anchor's JWKS array value, or null. If JWKS is provided, it will be used to validate Trust Anchor - // Configuration Statement in addition to using JWKS acquired during Trust Chain resolution. If JWKS is not - // provided (value null), the validity of Trust Anchor Configuration Statement will "only" be validated - // by the JWKS acquired during Trust Chain resolution, meaning that security will rely "only" on - // protection implied from using TLS on endpoints used during Trust Chain resolution. + // be the Trust Anchor's JWKS JSON object string value, or null. If JWKS is provided, it will be used to validate + // Trust Anchor Configuration Statement in addition to using JWKS acquired during Trust Chain resolution. If + // JWKS is not provided (value null), the validity of Trust Anchor Configuration Statement will "only" be + // validated by the JWKS acquired during Trust Chain resolution, meaning that security will rely "only" + // on protection implied from using TLS on endpoints used during Trust Chain resolution. ModuleConfig::OPTION_FEDERATION_TRUST_ANCHORS => [ -// 'https://ta.example.org/' => [ -// 'keys' => [ -// [ -// 'alg' => 'RS256', -// 'use' => 'sig', -// 'kty' => 'RSA', -// 'n' => 'abc...def', -// 'e' => 'AQAB', -// 'kid' => '123', -// ], -// [ -// 'alg' => 'RS256', -// 'use' => 'sig', -// 'kty' => 'RSA', -// 'n' => 'ghi...jkl', -// 'e' => 'AQAB', -// 'kid' => '456', -// ], -// ], -// ], + // phpcs:ignore +// 'https://ta.example.org/' => '{"keys":[{"kty": "RSA","alg": "RS256","use": "sig","kid": "Nzb...9Xs","e": "AQAB","n": "pnXB...ub9J"}]}', // 'https://ta2.example.org/' => null, ], diff --git a/src/ModuleConfig.php b/src/ModuleConfig.php index 0ed106de..6c905eb3 100644 --- a/src/ModuleConfig.php +++ b/src/ModuleConfig.php @@ -24,7 +24,6 @@ use SimpleSAML\Error\ConfigurationError; use SimpleSAML\Module\oidc\Bridges\SspBridge; use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException; -use SimpleSAML\OpenID\Codebooks\ClaimsEnum; use SimpleSAML\OpenID\Codebooks\ScopesEnum; class ModuleConfig @@ -650,20 +649,16 @@ public function getFederationTrustAnchorIds(): array /** * @throws \SimpleSAML\Error\ConfigurationError */ - public function getTrustAnchorJwks(string $trustAnchorId): ?array + public function getTrustAnchorJwksJson(string $trustAnchorId): ?string { /** @psalm-suppress MixedAssignment */ $jwks = $this->getFederationTrustAnchors()[$trustAnchorId] ?? null; - if ($jwks === null) { + if (is_null($jwks)) { return null; } - if ( - is_array($jwks) && - array_key_exists(ClaimsEnum::Keys->value, $jwks) && - (!empty($jwks[ClaimsEnum::Keys->value])) - ) { + if (is_string($jwks)) { return $jwks; } diff --git a/src/Server/RequestRules/Rules/ClientIdRule.php b/src/Server/RequestRules/Rules/ClientIdRule.php index bd66acec..199e9cde 100644 --- a/src/Server/RequestRules/Rules/ClientIdRule.php +++ b/src/Server/RequestRules/Rules/ClientIdRule.php @@ -141,6 +141,19 @@ public function checkRule( ); } + // Validate TA with locally saved JWKS, if available. + $trustAnchorEntityConfiguration = $trustChain->getResolvedTrustAnchor(); + $localTrustAnchorJwksJson = $this->moduleConfig + ->getTrustAnchorJwksJson($trustAnchorEntityConfiguration->getIssuer()); + if (!is_null($localTrustAnchorJwksJson)) { + /** @psalm-suppress MixedArgument */ + $localTrustAnchorJwks = $this->federation->helpers()->json()->decode($localTrustAnchorJwksJson); + if (!is_array($localTrustAnchorJwks)) { + throw OidcServerException::serverError('Unexpected JWKS format.'); + } + $trustAnchorEntityConfiguration->verifyWithKeySet($localTrustAnchorJwks); + } + $clientFederationEntity = $trustChain->getResolvedLeaf(); if ($clientFederationEntity->getIssuer() !== $clientEntityId) { diff --git a/templates/config/federation.twig b/templates/config/federation.twig index 51a0d116..8bb739ee 100644 --- a/templates/config/federation.twig +++ b/templates/config/federation.twig @@ -64,7 +64,7 @@ {{ 'JWKS'|trans }}: {% if jwks|default is not empty %} - {{- jwks|json_encode(constant('JSON_PRETTY_PRINT')) -}} + {{- jwks -}} {% else %} {{ 'N/A'|trans }}