BookStackApp/BookStack · error · OidcException

ID token validation failed with error: {$exception->getMessa

Error message

ID token validation failed with error: {$exception->getMessage()}

What it means

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.

Source

Thrown at app/Access/Oidc/OidcService.php:205

        $returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [
            'access_token' => $accessToken->getToken(),
            'expires_in' => $accessToken->getExpires(),
            'refresh_token' => $accessToken->getRefreshToken(),
        ]);

        if (!is_null($returnClaims)) {
            $idToken->replaceClaims($returnClaims);
        }

        if ($this->config()['dump_user_details']) {
            throw new JsonDebugException($idToken->getAllClaims());
        }

        try {
            $idToken->validate($settings->clientId);
        } catch (OidcInvalidTokenException $exception) {
            throw new OidcException("ID token validation failed with error: {$exception->getMessage()}");
        }

        $userDetails = $this->getUserDetailsFromToken($idToken, $accessToken, $settings);
        if (empty($userDetails->email)) {
            throw new OidcException(trans('errors.oidc_no_email_address'));
        }
        if (empty($userDetails->name)) {
            $userDetails->name = $userDetails->externalId;
        }

        $isLoggedIn = auth()->check();
        if ($isLoggedIn) {
            throw new OidcException(trans('errors.oidc_already_logged_in'));
        }

        try {
            $user = $this->registrationService->findOrRegister(
                $userDetails->name,

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET in .env match the IdP exactly (no trailing slash mismatch on issuer)
  2. Clear any cached OIDC discovery/JWKS data and restart so fresh signing keys are fetched
  3. Verify server clock is synchronized (ntp/chrony) — skew can cause expiry/iat validation failures
  4. Check the wrapped exception message in logs — it names the exact failed validation check
  5. Confirm APP_URL and redirect URI are consistent so token audience checks pass

Example fix

// before (wrong issuer, trailing slash)
OIDC_ISSUER=https://idp.example.com/
// after (must exactly match the IdP's issuer claim)
OIDC_ISSUER=https://idp.example.com
Defensive patterns

Strategy: validation

Validate before calling

// Before login, sanity-check env config matches the IdP
$checks = [
    'OIDC_ISSUER' => env('OIDC_ISSUER'),
    'OIDC_CLIENT_ID' => env('OIDC_CLIENT_ID'),
];
foreach ($checks as $k => $v) {
    if (empty($v)) { throw new RuntimeException("$k must be set"); }
}
// Ensure server clock is synced and issuer has no trailing-slash mismatch
// php -r 'echo time();' vs IdP 'iat'/'exp'; ntpdate/chrony if skewed

Try / catch

try {
    auth()->attemptOidcLogin();
} catch (BookStack\Access\Oidc\OidcException $e) {
    Log::error('OIDC login failed', ['msg' => $e->getMessage()]);
    abort(401, 'OIDC token validation failed — check OIDC_ISSUER/CLIENT_ID and server clock');
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/98f1ef1191fe0f8a. Report an issue: GitHub.