{"record":{"id":"98f1ef1191fe0f8a","repo":"BookStackApp/BookStack","slug":"id-token-validation-failed-with-error-exception","errorCode":null,"errorMessage":"ID token validation failed with error: {$exception->getMessage()}","messagePattern":"ID token validation failed with error: (.+?)","errorType":"exception","errorClass":"OidcException","httpStatus":null,"severity":"error","filePath":"app/Access/Oidc/OidcService.php","lineNumber":205,"sourceCode":"\n        $returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [\n            'access_token' => $accessToken->getToken(),\n            'expires_in' => $accessToken->getExpires(),\n            'refresh_token' => $accessToken->getRefreshToken(),\n        ]);\n\n        if (!is_null($returnClaims)) {\n            $idToken->replaceClaims($returnClaims);\n        }\n\n        if ($this->config()['dump_user_details']) {\n            throw new JsonDebugException($idToken->getAllClaims());\n        }\n\n        try {\n            $idToken->validate($settings->clientId);\n        } catch (OidcInvalidTokenException $exception) {\n            throw new OidcException(\"ID token validation failed with error: {$exception->getMessage()}\");\n        }\n\n        $userDetails = $this->getUserDetailsFromToken($idToken, $accessToken, $settings);\n        if (empty($userDetails->email)) {\n            throw new OidcException(trans('errors.oidc_no_email_address'));\n        }\n        if (empty($userDetails->name)) {\n            $userDetails->name = $userDetails->externalId;\n        }\n\n        $isLoggedIn = auth()->check();\n        if ($isLoggedIn) {\n            throw new OidcException(trans('errors.oidc_already_logged_in'));\n        }\n\n        try {\n            $user = $this->registrationService->findOrRegister(\n                $userDetails->name,","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Oidc/OidcService.php#L187-L223","documentation":"BookStack's OIDC service throws this OidcException when the ID token returned by the identity provider fails local validation ($idToken->validate($settings->clientId)). Validation covers signature, issuer, audience, and expiry claims. The inner OidcInvalidTokenException message is wrapped to tell you exactly which check failed.","triggerScenarios":"During processAccessTokenCallback, the JWT received from the OIDC provider fails signature verification, has an iss that does not match the configured issuer, has aud that does not match the configured client_id, or is expired/not-yet-valid.","commonSituations":"Wrong APP_URL or issuer mismatch, wrong client_id/audience in .env, provider rotated signing keys (e.g. after key rotation or container restart with stale cached discovery/JWKS), clock skew between server and IdP.","solutions":["Check OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET in .env match the IdP exactly (no trailing slash mismatch on issuer)","Clear any cached OIDC discovery/JWKS data and restart so fresh signing keys are fetched","Verify server clock is synchronized (ntp/chrony) — skew can cause expiry/iat validation failures","Check the wrapped exception message in logs — it names the exact failed validation check","Confirm APP_URL and redirect URI are consistent so token audience checks pass"],"exampleFix":"// before (wrong issuer, trailing slash)\nOIDC_ISSUER=https://idp.example.com/\n// after (must exactly match the IdP's issuer claim)\nOIDC_ISSUER=https://idp.example.com","handlingStrategy":"validation","validationCode":"// Before login, sanity-check env config matches the IdP\n$checks = [\n    'OIDC_ISSUER' => env('OIDC_ISSUER'),\n    'OIDC_CLIENT_ID' => env('OIDC_CLIENT_ID'),\n];\nforeach ($checks as $k => $v) {\n    if (empty($v)) { throw new RuntimeException(\"$k must be set\"); }\n}\n// Ensure server clock is synced and issuer has no trailing-slash mismatch\n// php -r 'echo time();' vs IdP 'iat'/'exp'; ntpdate/chrony if skewed","typeGuard":null,"tryCatchPattern":"try {\n    auth()->attemptOidcLogin();\n} catch (BookStack\\Access\\Oidc\\OidcException $e) {\n    Log::error('OIDC login failed', ['msg' => $e->getMessage()]);\n    abort(401, 'OIDC token validation failed — check OIDC_ISSUER/CLIENT_ID and server clock');\n}","preventionTips":["Keep OIDC_ISSUER byte-exact (watch trailing slashes) with the IdP's issuer claim","Run NTP/chrony so server clock skew stays minimal","Monitor IdP signing-key rotation and ensure discovery/JWKS is re-fetched","Test OIDC login after every IdP config change in staging"],"tags":["oidc","jwt","authentication"],"backgroundTag":"jwt-validation-failed","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}