{"record":{"id":"b382e6791a61c70c","repo":"passbolt/passbolt_api","slug":"cannot-parse-jwks-endpoint-response","errorCode":null,"errorMessage":"Cannot parse JWKS endpoint response.","messagePattern":"Cannot parse JWKS endpoint response\\.","errorType":"exception","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php","lineNumber":169,"sourceCode":"    }\n\n    /**\n     * Get JWT verification keys from Azure Active Directory.\n     *\n     * @return array\n     */\n    public function getJwtVerificationKeys(): array\n    {\n        $openIdConfiguration = $this->getOpenIdConfiguration();\n        $keysUri = $openIdConfiguration['jwks_uri'];\n\n        $factory = $this->getRequestFactory();\n        $request = $factory->getRequestWithOptions('get', $keysUri, []);\n\n        try {\n            $response = $this->getParsedResponse($request);\n        } catch (Throwable $exception) {\n            throw new InternalErrorException(__('Cannot parse JWKS endpoint response.'), 500, $exception);\n        }\n\n        if (!is_array($response) || !isset($response['keys'])) {\n            throw new InternalErrorException(__('Invalid JWKS endpoint response. Keys missing.'));\n        }\n\n        /**\n         * Here we are using custom method to check JWK key signature as we can't use `JWK::parseKeySet` method directly\n         * because Azure don't provide \"kty\" parameter in the keys.\n         *\n         * @see \\Firebase\\JWT\\JWK::parseKeySet()\n         */\n        return $this->parseJwksKeys($response['keys']);\n    }\n\n    /**\n     * Parse & check JWT keys signature from Azure.\n     *","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/Azure/Provider/AzureProvider.php#L151-L187","documentation":"AzureProvider::getJwtVerificationKeys() fetches the JWKS (public keys) document from Azure's discovery config via getParsedResponse(); any Throwable (network error, HTTP error, JSON parse failure) is wrapped in InternalErrorException with this message, since verification keys are mandatory for validating ID tokens.","triggerScenarios":"HTTP request to the JWKS URI fails or the response cannot be parsed — DNS failure, TLS error, timeout, 4xx/5xx from Azure, or non-JSON body.","commonSituations":"Server has no outbound internet access or blocked by firewall; proxy required but not configured; Azure jwks URI temporarily unreachable; discovery document cached with an outdated keys URI; clock/TLS issues on the server.","solutions":["Test connectivity from the server: curl the JWKS URI found in the OpenID config","Configure outbound proxy/firewall rules to allow HTTPS to login.microsoftonline.com","Clear cached OpenID metadata and retry the SSO setup to refresh the keys URI","Check the wrapped exception in the log for the underlying network/parse cause"],"exampleFix":"// server-side connectivity check\n// before: request fails silently behind firewall\n// after: allow egress\ncurl -i https://login.microsoftonline.com/common/discovery/v2.0/keys","handlingStrategy":"retry","validationCode":"$response = Http::get($keysUri); if (!$response->isOk() || !is_array($response->getJson()['keys'] ?? null)) { /* JWKS unreachable, retry/degrade */ }","typeGuard":"function isValidJwks(mixed $response): bool { return is_array($response) && isset($response['keys']) && is_array($response['keys']); }","tryCatchPattern":"try { $keys = $provider->getJwtVerificationKeys(); } catch (InternalErrorException $e) { Log::error('JWKS fetch failed: ' . $e->getPrevious()?->getMessage()); return $this->respondError(503, 'Unable to reach identity provider keys.'); }","preventionTips":["Guarantee outbound HTTPS from the server to Azure endpoints","Configure proxy env vars (HTTPS_PROXY) where needed","Cache JWKS with a sensible TTL and refresh on failure","Alert on JWKS fetch failures before users attempt SSO"],"tags":["sso","azure-ad","jwks","network","jwt"],"backgroundTag":"http-request-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}