From a1b5021b61adebb418ecc07da757b171eb605b9d Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Wed, 1 Nov 2023 13:25:48 -0400 Subject: [PATCH] [OIDC] Handle providers that don't provide a 'kid' parameter Some OpenID Connect providers (ie. Globus) don't specify 'kid' in their JWKS response. The field is optional according to the spec, despite the fact that JWK::parseKeySet errors if it's not provided. As a workaround, this manually tries each key returned until one works. Partially resolves #8926. --- composer.lock | 19 ++++++++++--------- modules/oidc/php/callback.class.inc | 28 ++++++++++++++++++---------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 065b51271f9..b571fea7248 100644 --- a/composer.lock +++ b/composer.lock @@ -207,30 +207,31 @@ }, { "name": "firebase/php-jwt", - "version": "v6.3.1", + "version": "v6.9.0", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "ddfaddcb520488b42bca3a75e17e9dd53c3667da" + "reference": "f03270e63eaccf3019ef0f32849c497385774e11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/ddfaddcb520488b42bca3a75e17e9dd53c3667da", - "reference": "ddfaddcb520488b42bca3a75e17e9dd53c3667da", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/f03270e63eaccf3019ef0f32849c497385774e11", + "reference": "f03270e63eaccf3019ef0f32849c497385774e11", "shasum": "" }, "require": { - "php": "^7.1||^8.0" + "php": "^7.4||^8.0" }, "require-dev": { "guzzlehttp/guzzle": "^6.5||^7.4", - "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^7.5||^9.5", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", "psr/cache": "^1.0||^2.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" }, "type": "library", @@ -263,9 +264,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.3.1" + "source": "https://github.com/firebase/php-jwt/tree/v6.9.0" }, - "time": "2022-11-01T21:20:08+00:00" + "time": "2023-10-05T00:24:42+00:00" }, { "name": "google/recaptcha", diff --git a/modules/oidc/php/callback.class.inc b/modules/oidc/php/callback.class.inc index 93810ff43bc..cf583a812af 100644 --- a/modules/oidc/php/callback.class.inc +++ b/modules/oidc/php/callback.class.inc @@ -96,8 +96,7 @@ class Callback extends \NDB_Page "Could not get token" ); } - - if ($userinfo->email_verified == true) { + if (isset($userinfo->email_verified) && $userinfo->email_verified == true) { $DB = $this->loris->getDatabaseConnection(); $UserID = $DB->pselectOne( @@ -233,16 +232,25 @@ class Callback extends \NDB_Page $idtoken = $respjson['id_token']; $jwks = $client->request('GET', $jwksendpoint, []); $jwks_json = json_decode($jwks->getBody(), true); - try { - $decoded = JWT::decode($idtoken, JWK::parseKeySet($jwks_json)); - if ($decoded->nonce != $nonce) { - return null; + // JWK::parseKeySet requires a 'kid' parameter because it uses the + // kid (key id) internally as a key in an associative array. The + // parameter is not required by JWKS and some providers (ie. Globus) + // don't provide it, so instead we manually try each key until + // we find one that works. + foreach ($jwks_json['keys'] ?? [] as $key) { + try { + $decoded = JWT::decode($idtoken, JWK::parseKey($key)); + if ($decoded->nonce != $nonce) { + // it was decoded but the nonce doesn't match, error out. + return null; + } + return $decoded; + } catch (\Exception $e) { + // it couldn't be decoded with this key, try the next. } - return $decoded; - } catch (\Exception $e) { - // ID token was invalid JWT. - return null; } + // No keys could decode the token. + return null; } /**