{"record":{"id":"7e278158253f3ff7","repo":"BookStackApp/BookStack","slug":"userinfo-endpoint-response-validation-failed-with","errorCode":null,"errorMessage":"Userinfo endpoint response validation failed with error: {$exception->getMessage()}","messagePattern":"Userinfo endpoint response validation failed with error: (.+?)","errorType":"exception","errorClass":"OidcException","httpStatus":null,"severity":"error","filePath":"app/Access/Oidc/OidcService.php","lineNumber":270,"sourceCode":"            $idToken,\n            $this->config()['external_id_claim'],\n            $this->config()['display_name_claims'] ?? '',\n            $this->config()['groups_claim'] ?? ''\n        );\n\n        if (!$userDetails->isFullyPopulated($this->shouldSyncGroups()) && !empty($settings->userinfoEndpoint)) {\n            $provider = $this->getProvider($settings);\n            $request = $provider->getAuthenticatedRequest('GET', $settings->userinfoEndpoint, $accessToken->getToken());\n            $response = new OidcUserinfoResponse(\n                $provider->getResponse($request),\n                $settings->issuer,\n                $settings->keys,\n            );\n\n            try {\n                $response->validate($idToken->getClaim('sub'), $settings->clientId);\n            } catch (OidcInvalidTokenException $exception) {\n                throw new OidcException(\"Userinfo endpoint response validation failed with error: {$exception->getMessage()}\");\n            }\n\n            $userDetails->populate(\n                $response,\n                $this->config()['external_id_claim'],\n                $this->config()['display_name_claims'] ?? '',\n                $this->config()['groups_claim'] ?? ''\n            );\n        }\n\n        return $userDetails;\n    }\n\n    /**\n     * Get the OIDC config from the application.\n     */\n    protected function config(): array\n    {","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Oidc/OidcService.php#L252-L288","documentation":"When the userinfo endpoint is used, BookStack validates the userinfo response against the ID token's 'sub' and the configured client_id via OidcUserinfoResponse::validate(). If that raises OidcInvalidTokenException, it is wrapped in this OidcException so the operator sees which userinfo validation failed.","triggerScenarios":"getUserDetailsFromToken fetches the userinfo response, then $response->validate($idToken->getClaim('sub'), $settings->clientId) fails — typically subject mismatch between userinfo and ID token, or missing/invalid sub in the userinfo payload.","commonSituations":"Misbehaving or misconfigured IdP returning a different subject in userinfo vs ID token (e.g. pairwise subject identifiers or per-client sub values), a proxy/gateway rewriting responses, or an IdP not OIDC-spec compliant (missing sub).","solutions":["Check the wrapped message: it distinguishes 'No valid subject value' from 'Subject value ... does not match'","Ensure the IdP returns the same 'sub' in userinfo and ID token (disable pairwise/subject-per-client settings if needed)","Update the IdP or switch to a spec-compliant provider version","Verify no reverse proxy is altering the userinfo response body","If userinfo is unnecessary, disable it (remove userinfo endpoint config) so details come from the ID token only"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify userinfo vs ID token sub before calling the API:\n$ui = json_decode(file_get_contents($userinfoUrl, false, stream_context_create(['http' => ['header' => \"Authorization: Bearer $at\\r\\n\"]])), true);\n$idTokenClaims = json_decode(base64_decode(explode('.', $idToken)[1]), true);\nif (!isset($ui['sub']) || !is_string($ui['sub']) || $ui['sub'] !== $idTokenClaims['sub']) {\n    // IdP is misbehaving — fix subject type / disable userinfo\n}","typeGuard":null,"tryCatchPattern":"try {\n    auth()->attemptOidcLogin();\n} catch (BookStack\\Access\\Oidc\\OidcException $e) {\n    if (str_contains($e->getMessage(), 'Userinfo endpoint response validation failed')) {\n        abort(502, 'IdP userinfo response failed validation — check pairwise/public subject settings');\n    }\n    throw $e;\n}","preventionTips":["Use 'public' subject type so userinfo and ID token subs match","Avoid proxies that rewrite/transform IdP JSON responses","Test with a raw curl userinfo call during IdP setup","Disable the userinfo endpoint if ID-token claims suffice"],"tags":["oidc","userinfo","jwt"],"backgroundTag":"userinfo-subject-mismatch","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}