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
- Curl the discovery URL and inspect the jwks_uri value; it must be an absolute http(s) URL.
- Fix the IdP configuration so it publishes absolute URLs.
- Check any reverse proxy/rewrite layer modifying the discovery body.
- 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
- Require absolute http(s) URLs in IdP metadata; reject relative URIs
- Verify published metadata after any IdP base-URL change
- Watch for proxies rewriting URLs in the discovery body
- Add URL validation of all OIDC endpoints in your deployment checks
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
- Invalid response. Invalid authorization endpoint.
- Invalid response. Invalid token endpoint.
- Invalid response. Missing JWKS URI
- Invalid response. Expected array, got
- Invalid response. Missing authorization endpoint.
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)