googleapis/mcp-toolbox · error

google ID token verification failure: %w

Error message

google ID token verification failure: %w

What it means

GetClaimsFromHeader verifies the Google ID token from the <name>_token header using google.golang.org/api/idtoken.Validate with the configured ClientID as audience. Any verification error (bad signature, expired token, wrong issuer/audience, malformed JWT) is wrapped as 'google ID token verification failure'.

Source

Thrown at internal/auth/google/google.go:128

func (a AuthService) IsMCPEnabled() bool {
	return a.McpEnabled
}

func (a AuthService) GetScopesRequired() []string {
	return a.ScopesRequired
}

func (a AuthService) GetAuthorizationServer() string {
	return "https://accounts.google.com"
}

// Verifies Google ID token and return claims
func (a AuthService) GetClaimsFromHeader(ctx context.Context, h http.Header) (map[string]any, error) {
	if token := h.Get(a.Name + "_token"); token != "" {
		payload, err := idtoken.Validate(ctx, token, a.ClientID)
		if err != nil {
			return nil, fmt.Errorf("google ID token verification failure: %w", err)
		}
		return payload.Claims, nil
	}
	return nil, nil
}

// ValidateMCPAuth handles MCP auth token validation for Google
func (a AuthService) ValidateMCPAuth(ctx context.Context, h http.Header) (map[string]any, error) {
	tokenString := h.Get("Authorization")
	if tokenString == "" {
		return nil, &auth.MCPAuthError{Code: http.StatusUnauthorized, Message: "missing access token", ScopesRequired: a.ScopesRequired}
	}

	headerParts := strings.Split(tokenString, " ")
	if len(headerParts) != 2 || strings.ToLower(headerParts[0]) != "bearer" {
		return nil, &auth.MCPAuthError{Code: http.StatusUnauthorized, Message: "authorization header must be in the format 'Bearer <token>'", ScopesRequired: a.ScopesRequired}
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Fetch a fresh ID token for the configured audience (e.g. gcloud auth print-identity-token or a Google auth library) and resend
  2. Ensure the clientId/audience configured in the google auth service matches the token's aud claim
  3. Verify you are sending an ID token, not an access token, in the <name>_token header
  4. Check machine clock sync (NTP) and outbound connectivity to token verification endpoints
  5. Inspect the wrapped inner error (%w) for the exact verification reason

Example fix

// before
curl -H "my_service_token: ya29.access-token..." ...
// after
curl -H "my_service_token: $(gcloud auth print-identity-token --audiences=https://my-app.apps.googleusercontent.com)" ...
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: ensure an ID token is fresh and audience-correct before sending
tok, err := idtoken.NewTokenSource(ctx, audience).Token() // or gcloud print-identity-token
if err != nil || tok == nil {
    return errors.New("no valid ID token available")
}

Try / catch

claims, err := svc.GetClaimsFromHeader(ctx, header)
if err != nil {
    var ue *idtoken.Error // or inspect wrapped cause
    if strings.Contains(err.Error(), "ID token verification failure") {
        // refresh token and retry once
        header.Set(name+"_token", freshIDToken())
        claims, err = svc.GetClaimsFromHeader(ctx, header)
    }
}

Prevention

When it happens

Trigger: Sending an expired, malformed, tampered, or wrongly signed ID token in the <serviceName>_token header; token issued for a different client than the configured ClientID; no network access to Google's cert endpoints (offline environments).

Common situations: Clock skew on client machine causing 'token used too early'/'expired'; swapping access token for ID token in the header; forgetting to configure clientId/audience so validation target mismatches; firewall blocking fetch of Google public keys.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/99959700839f23a8. Report an issue: GitHub.