vxcontrol/pentagi · error

failed to parse Google ID Token claims: %w

Error message

failed to parse Google ID Token claims: %w

What it means

Thrown in newGoogleEmailResolver when idToken.Claims(&claims) fails to unmarshal the verified Google ID Token payload into googleTokenClaims (nonce, email, email_verified). With go-oidc v3 this almost always means the ID token payload is not valid JSON or the json.Unmarshal into the claims struct failed, which is practically impossible for a genuinely verified Google token — so it usually indicates a corrupted, truncated, or substituted id_token string, or a custom/misrouted OIDC provider whose claims shape differs.

Source

Thrown at backend/pkg/server/oauth/google.go:46

		}

		verifier := provider.Verifier(&oidc.Config{ClientID: clientID})
		idToken, err := verifier.Verify(ctx, oidToken)
		if err != nil {
			return "", false, fmt.Errorf("could not verify Google ID Token: %w", err)
		}

		if idToken.Nonce != nonce {
			return "", false, fmt.Errorf("nonce mismatch in Google ID Token")
		}

		if err = idToken.VerifyAccessToken(token.AccessToken); err != nil {
			return "", false, fmt.Errorf("failed to verify Google Access Token: %w", err)
		}

		claims := googleTokenClaims{}
		if err := idToken.Claims(&claims); err != nil {
			return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w", err)
		}

		if claims.Nonce != nonce {
			return "", false, fmt.Errorf("nonce mismatch in Google ID Token claims")
		}

		if claims.Email == "" {
			return "", false, fmt.Errorf("email is empty in Google ID Token claims")
		}

		return claims.Email, claims.EmailVerified, nil
	}
}

func NewGoogleOAuthClient(clientID, clientSecret, redirectURL string) OAuthClient {
	return NewOAuthClient("google", &oauth2.Config{
		ClientID:     clientID,
		ClientSecret: clientSecret,

View on GitHub (pinned to ea665308ba)

Solutions

  1. Log the raw id_token (first ~50 chars) and run it through jwt.io / a JWKS decode to inspect the payload for corruption.
  2. Verify no proxy or middleware rewrites token response bodies (check HTTP_PROXY, ingress, service mesh MITM settings).
  3. Ensure the clientID passed to oidc.Config matches the OAuth client that issued the token.
  4. Update github.com/coreos/go-oidc/v3 to the latest version; very old versions had stricter claim decoding behavior.
  5. If the issuer is intentionally not Google, use the matching provider discovery URL instead of hard-coded accounts.google.com.

Example fix

// before
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
    return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w", err)
}
// after
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
    return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w (raw token prefix: %.40s)", err, oidToken)
}
Defensive patterns

Strategy: try-catch

Validate before calling

const raw = token.Extra("id_token")
if s, ok := raw.(string); !ok || s == "" {
    return fmt.Errorf("id_token missing or not a string")
}
parts := strings.Split(s, ".")
if len(parts) != 3 {
    return fmt.Errorf("id_token is not a JWT")
}
if payload, err := base64.RawURLEncoding.DecodeString(parts[1]); err != nil || !json.Valid(payload) {
    return fmt.Errorf("id_token payload is not valid JSON")
}

Type guard

func hasValidIDToken(t *oauth2.Token) bool {
    s, ok := t.Extra("id_token").(string)
    return ok && strings.Count(s, ".") == 2
}

Try / catch

email, verified, err := resolver.Resolve(ctx, nonce, token)
if err != nil {
    if strings.Contains(err.Error(), "failed to parse Google ID Token claims") {
        logger.WithError(err).Error("corrupt or non-standard Google ID token payload; inspect raw token and proxy config")
        return fmt.Errorf("oauth login failed: malformed ID token")
    }
    return err
}

Prevention

When it happens

Trigger: Google OAuth callback processed via AuthAuthorize: the id_token passed verifier.Verify, VerifyAccessToken succeeded, but Claims() cannot decode the token payload into the googleTokenClaims struct (malformed JSON payload, wrong token supplied as id_token, or a proxy/MTM returning mangled token bodies).

Common situations: An HTTP proxy or service mesh (MITM) altering response bodies; a misconfigured OIDC base URL pointing at a non-Google issuer that issues tokens without standard claim JSON; token string truncated by logging/rewriting middleware; storing/replaying the token through something that corrupts it (e.g. HTML-escaped state).

Understand the failure class

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/2da6cbe826885481. Report an issue: GitHub.