{"record":{"id":"99959700839f23a8","repo":"googleapis/mcp-toolbox","slug":"google-id-token-verification-failure-w","errorCode":null,"errorMessage":"google ID token verification failure: %w","messagePattern":"google ID token verification failure: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/google/google.go","lineNumber":128,"sourceCode":"\nfunc (a AuthService) IsMCPEnabled() bool {\n\treturn a.McpEnabled\n}\n\nfunc (a AuthService) GetScopesRequired() []string {\n\treturn a.ScopesRequired\n}\n\nfunc (a AuthService) GetAuthorizationServer() string {\n\treturn \"https://accounts.google.com\"\n}\n\n// Verifies Google ID token and return claims\nfunc (a AuthService) GetClaimsFromHeader(ctx context.Context, h http.Header) (map[string]any, error) {\n\tif token := h.Get(a.Name + \"_token\"); token != \"\" {\n\t\tpayload, err := idtoken.Validate(ctx, token, a.ClientID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"google ID token verification failure: %w\", err)\n\t\t}\n\t\treturn payload.Claims, nil\n\t}\n\treturn nil, nil\n}\n\n// ValidateMCPAuth handles MCP auth token validation for Google\nfunc (a AuthService) ValidateMCPAuth(ctx context.Context, h http.Header) (map[string]any, error) {\n\ttokenString := h.Get(\"Authorization\")\n\tif tokenString == \"\" {\n\t\treturn nil, &auth.MCPAuthError{Code: http.StatusUnauthorized, Message: \"missing access token\", ScopesRequired: a.ScopesRequired}\n\t}\n\n\theaderParts := strings.Split(tokenString, \" \")\n\tif len(headerParts) != 2 || strings.ToLower(headerParts[0]) != \"bearer\" {\n\t\treturn nil, &auth.MCPAuthError{Code: http.StatusUnauthorized, Message: \"authorization header must be in the format 'Bearer <token>'\", ScopesRequired: a.ScopesRequired}\n\t}\n","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/auth/google/google.go#L110-L146","documentation":"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'.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Fetch a fresh ID token for the configured audience (e.g. gcloud auth print-identity-token or a Google auth library) and resend","Ensure the clientId/audience configured in the google auth service matches the token's aud claim","Verify you are sending an ID token, not an access token, in the <name>_token header","Check machine clock sync (NTP) and outbound connectivity to token verification endpoints","Inspect the wrapped inner error (%w) for the exact verification reason"],"exampleFix":"// before\ncurl -H \"my_service_token: ya29.access-token...\" ...\n// after\ncurl -H \"my_service_token: $(gcloud auth print-identity-token --audiences=https://my-app.apps.googleusercontent.com)\" ...","handlingStrategy":"try-catch","validationCode":"// client-side: ensure an ID token is fresh and audience-correct before sending\ntok, err := idtoken.NewTokenSource(ctx, audience).Token() // or gcloud print-identity-token\nif err != nil || tok == nil {\n    return errors.New(\"no valid ID token available\")\n}","typeGuard":null,"tryCatchPattern":"claims, err := svc.GetClaimsFromHeader(ctx, header)\nif err != nil {\n    var ue *idtoken.Error // or inspect wrapped cause\n    if strings.Contains(err.Error(), \"ID token verification failure\") {\n        // refresh token and retry once\n        header.Set(name+\"_token\", freshIDToken())\n        claims, err = svc.GetClaimsFromHeader(ctx, header)\n    }\n}","preventionTips":["Refresh ID tokens before expiry; ID tokens typically last ~1 hour","Send an ID token, never an access token, in the <name>_token header","Keep client clock synchronized (NTP)","Ensure the configured clientId/audience matches the token's aud","Allow egress to Google's token/cert endpoints"],"tags":["go","auth","google","jwt","token-validation"],"backgroundTag":"jwt-verification-failed","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}