passbolt/passbolt_api · error · InternalErrorException

Invalid response. Invalid JWKS URI

Error message

Invalid response. Invalid JWKS URI

What it means

Beyond presence, validateOpenIdConfiguration() validates each endpoint URL with CakePHP's Validation::url(). A jwks_uri that fails URL validation is unusable for fetching signing keys, so an InternalErrorException 'Invalid response. Invalid JWKS URI' is thrown.

Solutions

  1. Curl the discovery URL and inspect the jwks_uri value; it must be an absolute http(s) URL.
  2. Fix the IdP configuration so it publishes absolute URLs.
  3. Check any reverse proxy/rewrite layer modifying the discovery body.
  4. If the IdP cannot be fixed, switch to an IdP instance emitting compliant absolute URLs.

Example fix

// before
'{"jwks_uri":"/jwks"}'
// after
'{"jwks_uri":"https://auth.example.com/jwks"}'
Defensive patterns

Strategy: validation

Validate before calling

use Cake\Validation\Validation;
$doc = json_decode(file_get_contents($wellKnownUrl), true);
if (!isset($doc['jwks_uri']) || !Validation::url($doc['jwks_uri'])) { throw new UnexpectedValueException('jwks_uri missing or not a valid absolute URL.'); }

Type guard

function isValidJwksUri(mixed $doc): bool { return is_array($doc) && isset($doc['jwks_uri']) && is_string($doc['jwks_uri']) && Validation::url($doc['jwks_uri']); }

Try / catch

try { $keys = $provider->getJwtVerificationKeys(); } catch (InternalErrorException $e) { if ($e->getMessage() === 'Invalid response. Invalid JWKS URI') { /* metadata emits a malformed jwks_uri */ } throw $e; }

Prevention

When it happens

Trigger: Decoded discovery JSON has jwks_uri set but its value fails Validation::url() (e.g. relative path, missing scheme, garbage value).

Common situations: IdP emitting relative URIs instead of absolute URLs; corrupted/proxied metadata injecting a path-only jwks_uri; typo in IdP configuration producing malformed URLs.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/3e0dd4ae9ac51102. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php:184

            if (is_string($response)) {
                // Cap excerpt to limit log volume on large/HTML responses; mb_strcut is UTF-8-safe.
                $excerpt = mb_strcut($response, 0, 200, 'UTF-8');
                // Escape newlines and control characters via JSON encoding so they don't corrupt log output.
                $msg .= ' ' . sprintf('Response text (truncated): %s', json_encode($excerpt));
            }
            throw new InternalErrorException($msg);
        }
        if (!isset($response['jwks_uri'])) {
            throw new InternalErrorException('Invalid response. Missing JWKS URI');
        }
        if (!isset($response['authorization_endpoint'])) {
            throw new InternalErrorException('Invalid response. Missing authorization endpoint.');
        }
        if (!isset($response['token_endpoint'])) {
            throw new InternalErrorException('Invalid response. Missing token endpoint.');
        }
        if (!Validation::url($response['jwks_uri'])) {
            throw new InternalErrorException('Invalid response. Invalid JWKS URI');
        }
        if (!Validation::url($response['authorization_endpoint'])) {
            throw new InternalErrorException('Invalid response. Invalid authorization endpoint.');
        }
        if (!Validation::url($response['token_endpoint'])) {
            throw new InternalErrorException('Invalid response. Invalid token endpoint.');
        }
    }

    /**
     * @inheritDoc
     */
    protected function getAuthorizationParameters(array $options)
    {
        $options = parent::getAuthorizationParameters($options);

        /**
         * The "approval_prompt" MUST be removed as it is not supported by Google, use "prompt" instead:

View on GitHub (pinned to 31c1bbc10f)