{"record":{"id":"b7c3b948e25c23dd","repo":"BookStackApp/BookStack","slug":"error-reading-keys-from-issuer-jwks-uri","errorCode":null,"errorMessage":"Error reading keys from issuer jwks_uri","messagePattern":"Error reading keys from issuer jwks_uri","errorType":"exception","errorClass":"OidcIssuerDiscoveryException","httpStatus":null,"severity":"error","filePath":"app/Access/Oidc/OidcProviderSettings.php","lineNumber":181,"sourceCode":"\n            return $key['kty'] === 'RSA' && $use === 'sig' && $alg === 'RS256';\n        });\n    }\n\n    /**\n     * Return an array of jwks as PHP key=>value arrays.\n     *\n     * @throws ClientExceptionInterface\n     * @throws OidcIssuerDiscoveryException\n     */\n    protected function loadKeysFromUri(string $uri, ClientInterface $httpClient): array\n    {\n        $request = new Request('GET', $uri);\n        $response = $httpClient->sendRequest($request);\n        $result = json_decode($response->getBody()->getContents(), true);\n\n        if (empty($result) || !is_array($result) || !isset($result['keys'])) {\n            throw new OidcIssuerDiscoveryException('Error reading keys from issuer jwks_uri');\n        }\n\n        return $result['keys'];\n    }\n\n    /**\n     * Get the settings needed by an OAuth provider, as a key=>value array.\n     */\n    public function arrayForOAuthProvider(): array\n    {\n        $settingKeys = ['clientId', 'clientSecret', 'authorizationEndpoint', 'tokenEndpoint', 'userinfoEndpoint'];\n        $settings = [];\n        foreach ($settingKeys as $setting) {\n            $settings[$setting] = $this->$setting;\n        }\n\n        return $settings;\n    }","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Oidc/OidcProviderSettings.php#L163-L199","documentation":"loadKeysFromUri() fetches the JWKS from the IdP's jwks_uri and throws OidcIssuerDiscoveryException if the body is empty, invalid JSON, or lacks a 'keys' array. Without signing keys, incoming ID tokens cannot be verified, so the library refuses to continue.","triggerScenarios":"GET jwks_uri returns non-JSON (HTML error page, empty 200), a proxy/WAF interstitial, or a document without the 'keys' member; also raised if the JWKS endpoint errors with a body that still parses as empty/non-array.","commonSituations":"JWKS URL unreachable through internal DNS while the main discovery doc is cached and reachable; IdP returning 5xx HTML pages; jwks_uri pointing to an internal hostname BookStack can't resolve; intermediate proxy stripping/altering the response.","solutions":["curl the jwks_uri from the BookStack host and confirm it returns {\"keys\":[...]}.","Fix DNS/firewall/proxy so the JWKS host is reachable from the BookStack container.","Clear the discovery cache ('oidc-discovery::<issuer>') after restoring access, since a cached discovery doc holds the jwks_uri.","Check IdP health/logs; rotate to a known-good realm if keys were removed from the JWKS."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"$jwksUri = json_decode(file_get_contents($discoveryUrl), true)['jwks_uri'] ?? null;\n$jwks = json_decode(@file_get_contents($jwksUri) ?? '', true);\nif (!is_array($jwks) || !isset($jwks['keys'])) {\n    throw new RuntimeException('JWKS endpoint did not return a keys array');\n}","typeGuard":"function isValidJwks(?array $decoded): bool {\n    return is_array($decoded) && isset($decoded['keys']) && is_array($decoded['keys']) && $decoded['keys'] !== [];\n}","tryCatchPattern":"try {\n    $settings->discoverFromIssuer($client, $cache, 15);\n} catch (OidcIssuerDiscoveryException $e) {\n    if (str_contains($e->getMessage(), 'jwks_uri')) {\n        Log::error('JWKS fetch failed: check jwks_uri reachability from app host');\n    }\n}","preventionTips":["Ensure the jwks_uri host is resolvable and reachable from the BookStack container.","Verify with curl that the JWKS URL returns {\"keys\":[...]} before enabling OIDC.","Purge cached discovery data after JWKS endpoint changes.","Monitor IdP health; JWKS outages break token validation on every login."],"tags":["jwks","discovery","network","oidc"],"backgroundTag":"jwks-fetch-failed","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}