Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-56665

CVE-2026-56665: ZITADEL Auth Bypass Vulnerability

CVE-2026-56665 is an authentication bypass flaw in ZITADEL that allows JWT tokens without expiration claims to be accepted indefinitely. This post covers the technical details, affected versions, and mitigation steps.

Published:

CVE-2026-56665 Overview

CVE-2026-56665 affects ZITADEL, an open source identity management platform. The vulnerability resides in ZITADEL's external JWT Identity Provider (IdP) validation logic in internal/idp/providers/jwt/session.go. The code skips expiration handling when an incoming JSON Web Token (JWT) omits the exp claim. A token issued by a trusted issuer without an expiration claim is treated as valid indefinitely, breaking the assumption that all authentication tokens carry an automatic expiration window. The flaw is categorized as Insufficient Session Expiration [CWE-613]. It is fixed in versions 3.4.12 and 4.15.2.

Critical Impact

An attacker holding a JWT from a trusted external IdP without an exp claim can maintain authenticated access without a bounded session lifetime, undermining session termination controls.

Affected Products

  • ZITADEL versions 3.0.0-rc.1 through 3.4.11
  • ZITADEL versions 4.0.0-rc.1 through 4.15.1
  • Deployments using external JWT Identity Providers for authentication

Discovery Timeline

  • 2026-07-10 - CVE-2026-56665 published to NVD
  • 2026-07-10 - Last updated in NVD database

Technical Details for CVE-2026-56665

Vulnerability Analysis

ZITADEL supports external JWT Identity Providers, where a trusted issuer signs a JWT that ZITADEL validates before establishing a session. The validation logic in internal/idp/providers/jwt/session.go guards expiration and issued-at checks behind conditionals that inspect whether the claims contain those fields. When claims.GetExpiration().IsZero() returns true, the code bypasses the call to oidc.CheckExpiration. The same pattern applies to iat via claims.GetIssuedAt().IsZero(). A token from a trusted issuer that omits exp therefore passes signature validation and proceeds without a temporal constraint. This turns a signed JWT into a long-lived credential, contrary to standard OpenID Connect (OIDC) practice that treats missing exp as a validation failure.

Root Cause

The root cause is defensive-but-incorrect claim handling. Wrapping expiration checks in an IsZero() guard treats missing claims as optional rather than as a validation error. Under RFC 7519, the exp claim is optional in the specification, but security-sensitive consumers must enforce it or reject the token. ZITADEL took the permissive path, disabling expiration enforcement for any JWT lacking the field.

Attack Vector

Exploitation requires an attacker to obtain a JWT signed by a configured trusted issuer without an exp claim. This scenario arises when a misconfigured or compromised upstream IdP mints tokens without expirations, or when an attacker with limited privileges at the IdP can influence token issuance. The attacker submits the token to ZITADEL's JWT IdP endpoint and receives an authenticated session that will not expire based on JWT time bounds.

go
// Patched code in internal/idp/providers/jwt/session.go
// Before: expiration and issued-at checks were skipped when claims were zero.
// After: checks always run, forcing rejection of tokens missing exp/iat.

 		return nil, fmt.Errorf("%w: invalid signature: %v", ErrInvalidToken, err)
 	}
 
-	if !claims.GetExpiration().IsZero() {
-		if err = oidc.CheckExpiration(claims, offset); err != nil {
-			return nil, fmt.Errorf("%w: expired: %v", ErrInvalidToken, err)
-		}
+	if err = oidc.CheckExpiration(claims, offset); err != nil {
+		return nil, fmt.Errorf("%w: expired: %v", ErrInvalidToken, err)
 	}
 
-	if !claims.GetIssuedAt().IsZero() {
-		if err = oidc.CheckIssuedAt(claims, maxAge, offset); err != nil {
-			return nil, fmt.Errorf("%w: %v", ErrInvalidToken, err)
-		}
+	if err = oidc.CheckIssuedAt(claims, maxAge, offset); err != nil {
+		return nil, fmt.Errorf("%w: %v", ErrInvalidToken, err)
 	}
+
 	return claims, nil
 }

Source: ZITADEL commit 4925fab

Detection Methods for CVE-2026-56665

Indicators of Compromise

  • Authentication events in ZITADEL logs originating from an external JWT IdP where the token payload lacks an exp claim.
  • Sessions with unusually long lifetimes tied to accounts federated via JWT IdP.
  • Repeated reuse of the same JWT jti value across sessions separated by long time gaps.

Detection Strategies

  • Enable verbose logging on the JWT IdP session handler and audit decoded claim sets for missing exp and iat fields.
  • Correlate ZITADEL authentication logs with upstream IdP token issuance logs to identify tokens that were never bounded by an expiration.
  • Review session records created before the upgrade to 3.4.12 or 4.15.2 and revoke any that trace back to JWT IdP logins.

Monitoring Recommendations

  • Alert on any successful federated authentication where the source token is missing standard OIDC time claims.
  • Track ZITADEL version banners across deployments to confirm all instances run patched releases.
  • Monitor for anomalous session persistence following external IdP-based logins.

How to Mitigate CVE-2026-56665

Immediate Actions Required

  • Upgrade ZITADEL to version 3.4.12 or 4.15.2 immediately. See the GHSA-v77h-2w3m-94hx security advisory for full details.
  • Revoke active sessions established through external JWT IdPs prior to patching.
  • Audit configured JWT IdPs and confirm upstream issuers emit exp and iat claims on every token.

Patch Information

ZITADEL released fixes in v3.4.12 and v4.15.2. The patches remove the IsZero() guards around oidc.CheckExpiration and oidc.CheckIssuedAt, so tokens missing exp or iat now fail validation. Review both commit 4925fab and commit d1c3aa8 for the exact code changes.

Workarounds

  • Reconfigure upstream JWT Identity Providers to always emit exp and iat claims with short lifetimes.
  • Temporarily disable external JWT IdP integrations until the upgrade to 3.4.12 or 4.15.2 completes.
  • Enforce shorter maximum session durations at the ZITADEL session layer to bound the impact of long-lived tokens.
bash
# Verify installed ZITADEL version and upgrade with Helm
kubectl exec -n zitadel deploy/zitadel -- zitadel --version
helm repo update
helm upgrade zitadel zitadel/zitadel --version 4.15.2 -n zitadel

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.