passbolt/passbolt_api · error · InternalErrorException
Invalid JWKS endpoint response. Keys missing.
Error message
Invalid JWKS endpoint response. Keys missing.
What it means
This InternalErrorException is thrown by AzureProvider::getJwtVerificationKeys when the response fetched from the Azure AD JWKS endpoint either is not a decodable array or lacks the required 'keys' member. The JWKS endpoint must return a JSON object containing a 'keys' array of signing keys; anything else (error page, HTML, empty body, proxy interception) is treated as an unusable key set and the SSO token verification cannot proceed.
Solutions
- Verify the jwks_uri URL returned by the OpenID configuration is reachable and returns a JSON object with a 'keys' array (curl the URL from the server).
- Check the server's outbound network path: disable SSL inspection / whitelist the Azure domain so proxies do not substitute their own response.
- Re-check the Azure tenant/endpoint configuration so getOpenIdConfiguration() resolves the correct jwks_uri.
- Retry later if Azure is having an outage; the error is on the provider side, not in local code.
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
$config = json_decode(file_get_contents('https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration'), true);
$jwks = json_decode(file_get_contents($config['jwks_uri']), true);
if (!is_array($jwks) || !isset($jwks['keys'])) {
throw new RuntimeException('JWKS endpoint did not return a keys array');
} Type guard
function isValidJwksResponse(mixed $response): bool {
return is_array($response) && isset($response['keys']) && is_array($response['keys']);
} Try / catch
try {
$keys = $provider->getJwtVerificationKeys();
} catch (InternalErrorException $e) {
if (str_contains($e->getMessage(), 'Keys missing')) {
Log::error('JWKS endpoint returned unexpected payload: ' . $e->getMessage());
}
throw $e;
} Prevention
- Curl the jwks_uri from the production server during setup to confirm a valid keys array is returned
- Whitelist Azure endpoints from SSL-inspection proxies
- Monitor OpenID configuration changes for Azure endpoint updates
- Cache JWKS with a TTL and refresh proactively to avoid outages becoming user-facing errors
When it happens
Trigger: The HTTP GET to the jwks_uri (resolved from the OpenID configuration) returns a 200/JSON body that is not an array, or a JSON object without a 'keys' property — e.g. an authentication/error JSON payload from a proxy, an empty response, or Azure returning an error document instead of the key set. Note that transport-level failures are caught earlier and surface as 'Cannot parse JWKS endpoint response.'
Common situations: Corporate proxy or firewall intercepting the outbound request and returning an HTML/JSON error page; misconfigured or stale OpenID configuration pointing at a wrong jwks_uri; Azure AD outage or throttling returning an error body with 200; network appliance (SSL inspection) rewriting the response.
Related errors
- No JWT key defined for Azure service.
- Cannot parse JWKS endpoint response.
- Failed to public key properties from certificate
- Failed to read certificate
- Failed to read public key from certificate
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/71477f826a2a0456.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php:173
*
* @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.'));
}
/**
* Here we are using custom method to check JWK key signature as we can't use `JWK::parseKeySet` method directly
* because Azure don't provide "kty" parameter in the keys.
*
* @see \Firebase\JWT\JWK::parseKeySet()
*/
return $this->parseJwksKeys($response['keys']);
}
/**
* Parse & check JWT keys signature from Azure.
*
* @param array $responseKeys keys from Jwks endpoint
* @return array of openssl compatible keys
*/
protected function parseJwksKeys(array $responseKeys): arrayView on GitHub (pinned to 31c1bbc10f)