infiniflow/ragflow · critical · ValueError
Error parsing ID Token: {e}
Error message
Error parsing ID Token: {e} What it means
OIDCClient.parse_id_token verifies the JWT signature via JWKS and python-jwt.decode with an allowlist of algorithms pinned from discovery metadata, plus audience (client_id) and issuer checks. Any failure - key lookup, signature, exp/iat, aud, iss, or alg allowlist - is re-raised as ValueError('Error parsing ID Token: {e}') (api/apps/auth/oidc.py:143).
Source
Thrown at api/apps/auth/oidc.py:143
try:
# Use PyJWT's PyJWKClient to fetch JWKS and find signing key.
# The client reads the ``kid`` from the JWT header internally to
# look up the key — that's fine: ``kid`` is not a security
# decision, the signature still proves which key was used.
jwks_cli = jwt.PyJWKClient(self.jwks_uri)
signing_key = jwks_cli.get_signing_key_from_jwt(id_token).key
# Decode and verify signature against the pinned allowlist.
decoded_token = jwt.decode(
id_token,
key=signing_key,
algorithms=list(self.id_token_signing_algs),
audience=str(self.client_id),
issuer=self.issuer,
)
return decoded_token
except Exception as e:
raise ValueError(f"Error parsing ID Token: {e}")
def fetch_user_info(self, access_token, id_token=None, **kwargs):
"""
Fetch user info.
"""
user_info = {}
if id_token:
user_info = self.parse_id_token(id_token)
user_info.update(super().fetch_user_info(access_token).to_dict())
return self.normalize_user_info(user_info)
async def async_fetch_user_info(self, access_token, id_token=None, **kwargs):
user_info = {}
if id_token:
user_info = self.parse_id_token(id_token)
user_info.update((await super().async_fetch_user_info(access_token)).to_dict())
return self.normalize_user_info(user_info)
View on GitHub (pinned to 554fb1133a)
Solutions
- Ensure RAGFlow's client_id exactly equals the IdP client ID (the aud in the token).
- Make the configured issuer byte-identical to the iss claim - watch trailing slashes and realm paths.
- Paste the ID token into a JWT debugger and check exp/iss/aud against your config and current time; fix clock skew (NTP) if expiry fires early.
- If keys rotated, verify the jwks_uri from discovery is reachable and serves the current keys; confirm the IdP's signing alg (e.g. RS256) is in the discovery metadata's supported list.
Example fix
# inspect the token's claims against config # decode header.payload (no verification) and compare: # iss == https://sso.example.com/realms/main (exact, incl. no trailing slash) # aud == <your client_id> # exp > now
Defensive patterns
Strategy: try-catch
Validate before calling
# no caller-side code can make a provider-issued JWT valid; precheck config invariants assert cfg["client_id"] == expected_aud assert cfg["issuer"] == expected_iss assert abs(time.time() - ntp_now()) < 60 # clock skew guard on the host
Try / catch
try:
claims = client.parse_id_token(id_token)
except ValueError as e:
msg = str(e)
if "exp" in msg.lower() or "expired" in msg.lower():
restart_authorization() # stale token - do not retry the same one
elif "aud" in msg.lower():
raise ConfigError("client_id does not match token audience")
elif "iss" in msg.lower():
raise ConfigError("issuer config does not match token issuer")
else:
raise Prevention
- Keep client_id and issuer identical across RAGFlow config and IdP client settings.
- Run NTP on servers validating JWTs to avoid premature exp failures.
- Never disable signature/audience/issuer verification to 'make it work'.
- On IdP signing-key rotation, verify JWKS freshness; this error is often the first symptom.
When it happens
Trigger: client_id in RAGFlow differs from the token's aud claim; issuer config differs from the token's iss claim (trailing slash!); expired token (exp passed, e.g. replayed callback or clock skew); JWKS endpoint unreachable or token signed with a key absent from JWKS (IdP key rotation); token alg not in the allowlist intersected with _ALLOWED_OIDC_SIGNING_ALGS; malformed token string.
Common situations: Client ID changed in RAGFlow but not at the IdP (or vice versa); Keycloak/other IdP realm URL with inconsistent trailing slash between discovery issuer and configured issuer; IdP signing-key rotation with cached JWKS; server clock drift causing premature expiry.
Related errors
- Missing issuer in configuration.
- Unsupported type: {channel_type}
- Failed to fetch OIDC metadata: {e}
- Unsupported auth_type: {auth_type}
- Invalid {label} format: {value!r}
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/f4cc1202f3ea72ab.
Report an issue: GitHub.