{"record":{"id":"7b2121973f071f98","repo":"passbolt/passbolt_api","slug":"invalid-response-missing-token-endpoint","errorCode":null,"errorMessage":"Invalid response. Missing token endpoint.","messagePattern":"Invalid response\\. Missing token endpoint\\.","errorType":"exception","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php","lineNumber":181,"sourceCode":"    {\n        if (!is_array($response)) {\n            $msg = sprintf('Invalid response. Expected array, got \"%s\".', gettype($response));\n            if (is_string($response)) {\n                // Cap excerpt to limit log volume on large/HTML responses; mb_strcut is UTF-8-safe.\n                $excerpt = mb_strcut($response, 0, 200, 'UTF-8');\n                // Escape newlines and control characters via JSON encoding so they don't corrupt log output.\n                $msg .= ' ' . sprintf('Response text (truncated): %s', json_encode($excerpt));\n            }\n            throw new InternalErrorException($msg);\n        }\n        if (!isset($response['jwks_uri'])) {\n            throw new InternalErrorException('Invalid response. Missing JWKS URI');\n        }\n        if (!isset($response['authorization_endpoint'])) {\n            throw new InternalErrorException('Invalid response. Missing authorization endpoint.');\n        }\n        if (!isset($response['token_endpoint'])) {\n            throw new InternalErrorException('Invalid response. Missing token endpoint.');\n        }\n        if (!Validation::url($response['jwks_uri'])) {\n            throw new InternalErrorException('Invalid response. Invalid JWKS URI');\n        }\n        if (!Validation::url($response['authorization_endpoint'])) {\n            throw new InternalErrorException('Invalid response. Invalid authorization endpoint.');\n        }\n        if (!Validation::url($response['token_endpoint'])) {\n            throw new InternalErrorException('Invalid response. Invalid token endpoint.');\n        }\n    }\n\n    /**\n     * @inheritDoc\n     */\n    protected function getAuthorizationParameters(array $options)\n    {\n        $options = parent::getAuthorizationParameters($options);","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php#L163-L199","documentation":"validateOpenIdConfiguration() requires token_endpoint in the discovery document because passbolt exchanges the authorization code for tokens there (getBaseAccessTokenUrl). Its absence indicates an incomplete OIDC discovery payload, so an InternalErrorException is thrown.","triggerScenarios":"getBaseAccessTokenUrl -> getOpenIdConfiguration -> validateOpenIdConfiguration with decoded JSON missing the token_endpoint key.","commonSituations":"IdP publishing partial metadata; discovery URL resolving to a different app's document; proxy/cache serving stale metadata; misconfigured multi-tenant issuer.","solutions":["Curl the discovery URL and verify token_endpoint is present.","Fix the issuer/WellKnownURI configured in passbolt SSO settings.","Purge intermediary caches/proxies serving stale discovery metadata.","Reconfigure the IdP to publish compliant discovery metadata."],"exampleFix":"// before\n'{\"jwks_uri\":\"https://auth.example.com/jwks\"}'\n// after\n'{\"token_endpoint\":\"https://auth.example.com/token\",\"jwks_uri\":\"https://auth.example.com/jwks\"}'","handlingStrategy":"validation","validationCode":"$doc = json_decode(file_get_contents($wellKnownUrl), true);\nif (!isset($doc['token_endpoint'])) { throw new UnexpectedValueException('Discovery document missing token_endpoint.'); }","typeGuard":"function hasTokenEndpoint(mixed $doc): bool { return is_array($doc) && isset($doc['token_endpoint']) && is_string($doc['token_endpoint']); }","tryCatchPattern":"try { $tokenUrl = $provider->getBaseAccessTokenUrl(); } catch (InternalErrorException $e) { if (str_contains($e->getMessage(), 'token endpoint')) { /* incomplete discovery metadata */ } throw $e; }","preventionTips":["Check the discovery document lists token_endpoint before enabling code flow","Clear stale caches/proxies that may serve partial metadata","Re-verify metadata after IdP reconfiguration","Use a setup wizard that validates all required OIDC fields"],"tags":["openid-configuration","oidc","response-validation"],"backgroundTag":"unexpected-response-shape","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"}