passbolt/passbolt_api · error · InternalErrorException

Invalid JWKS endpoint response. Keys missing.

Error message

Invalid JWKS endpoint response. Keys missing.

What it means

After successfully fetching the JWKS endpoint, the provider expects an object/array containing a 'keys' member (per RFC 7517). If the response is not an array or lacks 'keys', the IdP returned something unexpected and the provider throws this InternalErrorException because JWT signature verification is impossible without the key set.

Solutions

  1. Fetch the JWKS URI manually and confirm the response contains a top-level 'keys' array (RFC 7517 format)
  2. Fix the SSO provider configuration/discovery URL if it points to a non-JWKS endpoint
  3. Check for interceptors (proxy, WAF) returning 200 with an error body instead of the actual key set
  4. Clear any caching layer serving a stale or corrupted JWKS response
  5. If the IdP changed its JWKS format/version, upgrade the SSO plugin
Defensive patterns

Strategy: validation

Validate before calling

$payload = json_decode(file_get_contents($provider->getJwksUri()), true);
if (!is_array($payload) || !isset($payload['keys'])) {
    throw new \RuntimeException('IdP returned malformed JWKS; check discovery URL and proxies');
}

Try / catch

try {
    $keys = $provider->getJwtVerificationKeys();
} catch (\Cake\Http\Exception\InternalErrorException $e) {
    if (str_contains($e->getMessage(), 'Keys missing')) {
        // surface IdP misconfiguration guidance to the admin
    }
}

Prevention

When it happens

Trigger: getJwtVerificationKeys() receives a parsed response that is not an array, or an array without a 'keys' index — e.g. the JWKS URI returned JSON like {"error":"..."}, an empty object, or a plain list.

Common situations: A reverse proxy or captive portal returns an HTML/JSON error page with 200 status; misconfigured discovery metadata pointing at the wrong endpoint; an IdP API version change altering the response shape.

Related errors


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

Appendix: source

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

     *
     * @return array
     */
    public function getJwtVerificationKeys(): array
    {
        $openIdConfiguration = $this->getOpenIdConfiguration();
        $keysUri = $openIdConfiguration['jwks_uri'];

        $factory = $this->getRequestFactory();
        $request = $factory->getRequestWithOptions('get', $keysUri, []);

        try {
            $response = $this->getParsedResponse($request);
        } catch (Throwable $exception) {
            throw new InternalErrorException(__('Cannot parse JWKS endpoint response.'), 500, $exception);
        }

        if (!is_array($response) || !isset($response['keys'])) {
            throw new InternalErrorException(__('Invalid JWKS endpoint response. Keys missing.'));
        }

        $defaultAlg = $this->getJwksDefaultAlg();
        $this->assertJwkDefaultAlg($defaultAlg);

        return JWK::parseKeySet($response, $defaultAlg);
    }

    /**
     * Returns the alg of the keys.
     *
     * @return mixed
     */
    protected function getJwksDefaultAlg(): mixed
    {
        return Configure::read('passbolt.plugins.sso.security.jwks.defaultAlg');
    }

View on GitHub (pinned to 31c1bbc10f)