{"record":{"id":"84ef60ece124b8bb","repo":"roundcube/roundcubemail","slug":"incorrect-response-from-s","errorCode":null,"errorMessage":"incorrect response from %s","messagePattern":"incorrect response from (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"program/include/rcmail_oauth.php","lineNumber":228,"sourceCode":"        if (empty($config_uri)) {\n            return;\n        }\n\n        $key_cache = 'discovery.' . md5($config_uri);\n\n        try {\n            $data = $this->cache ? $this->cache->get($key_cache) : null;\n\n            if ($data === null) {\n                // Caveat: if .well-known URL is not answering it will break login display (will not display the button)\n                $response = $this->http_client->get($config_uri);\n                $data = json_decode($response->getBody(), true);\n\n                $this->log_debug('fetched OIDC config: %s', json_encode($data));\n\n                // sanity check\n                if (!isset($data['issuer'])) {\n                    throw new \\RuntimeException('incorrect response from %s', $config_uri);\n                }\n\n                // cache answer\n                if ($this->cache) {\n                    $this->cache->set($key_cache, $data);\n                }\n            }\n\n            // map discovery to our options\n            foreach (self::$config_mapper as $config_key => $options_key) {\n                if (!empty($data[$config_key])) {\n                    $this->options[$options_key] = $data[$config_key];\n                }\n            }\n\n            // check if pkce method is supported by this server\n            if ($this->options['pkce'] && isset($data['code_challenge_methods_supported']) && is_array($data['code_challenge_methods_supported'])) {\n                if (!in_array($this->options['pkce'], $data['code_challenge_methods_supported'])) {","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/roundcube/roundcubemail/blob/4b54c2acfb54d5ee3d1c281ca7f143bed0dea804/program/include/rcmail_oauth.php#L210-L246","documentation":"During OAuth discovery, rcmail_oauth::init() fetches the provider's OpenID configuration from the well-known URI and requires an 'issuer' field. If the response is not valid discovery metadata (HTML error page, wrong URL, proxy interference, non-JSON body), json_decode yields no 'issuer' and this exception is thrown.","triggerScenarios":"discover() (called from init()) GETs {issuer}/.well-known/openid-configuration and the decoded JSON has no 'issuer' key — wrong well-known URL, IDP returning an error page, or a truncated/cached bad response.","commonSituations":"oauth_provider option 'issuer' typo; IDP behind a proxy returning HTML error pages; provider config endpoint moved; stale cache entry from an earlier bad discovery.","solutions":["curl the well-known URI and verify it returns JSON containing 'issuer'.","Correct the oauth 'issuer' / discovery URL in Roundcube config.","Clear the discovery cache (the $key_cache entry) so a fixed endpoint is re-fetched.","Check for proxies/firewalls rewriting the response and ensure the request includes required auth headers if the endpoint is protected."],"exampleFix":"// before\n$config_uri = 'https://idp.example.com/.well-known/openid-configuration'; // 404 HTML page\n// after\n$config_uri = 'https://idp.example.com/realms/main/.well-known/openid-configuration';","handlingStrategy":"validation","validationCode":"$resp = file_get_contents($config_uri);\n$data = json_decode($resp, true);\nif (!is_array($data) || !isset($data['issuer'])) { /* abort init: bad discovery document */ }","typeGuard":"function isValidDiscovery($data): bool { return is_array($data) && isset($data['issuer']) && is_string($data['issuer']); }","tryCatchPattern":"try { $rcmail->oauth->init(); } catch (\\RuntimeException $e) { rcube::raise_error(['message' => 'OIDC discovery failed: ' . $e->getMessage()], true, false); }","preventionTips":["curl the well-known URI after any IDP deployment or realm change.","Bypass or correctly configure proxies for the discovery request.","Clear the discovery cache when changing issuer config.","Pin and monitor the discovery endpoint in health checks."],"tags":["oauth","oidc","discovery","http"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"4b54c2acfb54d5ee3d1c281ca7f143bed0dea804","analyzedAt":"2026-09-14T13:23:19.231Z","contentChangedAt":"2026-09-14T13:23:19.231Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}