{"record":{"id":"ee3d9be8c678a21c","repo":"googleapis/mcp-toolbox","slug":"audience-validation-failed-expected-s-got-v","errorCode":null,"errorMessage":"audience validation failed: expected %s, got %v","messagePattern":"audience validation failed: expected (.+?), got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/generic/generic.go","lineNumber":260,"sourceCode":"\t\treturn nil, fmt.Errorf(\"invalid JWT claims format\")\n\t}\n\n\t// Validate 'aud' (audience) claim\n\taud, err := claims.GetAudience()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse audience from token: %w\", err)\n\t}\n\n\tisAudValid := false\n\tfor _, audItem := range aud {\n\t\tif audItem == a.Audience {\n\t\t\tisAudValid = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !isAudValid {\n\t\treturn nil, fmt.Errorf(\"audience validation failed: expected %s, got %v\", a.Audience, aud)\n\t}\n\n\treturn claims, nil\n}\n\n// MCPAuthError represents an error during MCP authentication validation.\ntype MCPAuthError = auth.MCPAuthError\n\n// ValidateMCPAuth handles MCP auth token validation\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, &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, &MCPAuthError{Code: http.StatusUnauthorized, Message: \"authorization header must be in the format 'Bearer <token>'\", ScopesRequired: a.ScopesRequired}","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/auth/generic/generic.go#L242-L278","documentation":"The token's audience claim parsed fine, but none of its audience values matches the Audience configured on this generic auth service. The library compares each aud entry against a.Audience and rejects tokens not issued for this deployment, preventing token confusion between services.","triggerScenarios":"GetClaimsFromHeader receives a validly-signed token whose aud values (e.g. ['api://other-service']) do not include the configured Audience string, so isAudValid stays false.","commonSituations":"Audience mismatch after renaming or reconfiguring the server, reusing tokens minted for another API, copying config from one environment to another, or an IdP defaulting aud to a client-id different from the value set in the toolbox auth config.","solutions":["Compare the 'expected' value in the error with the token's aud claim and set the auth service's audience config to the exact value the IdP emits (or vice versa).","Re-request the token with the correct audience/resource parameter from your authorization server.","Verify you are not using a token issued for a different environment or service."],"exampleFix":"// before: server expects audience \"my-toolbox\" but token has aud \"other-api\"\n{\"aud\":\"other-api\"}\n// after: request token with audience=my-toolbox\n{\"aud\":\"my-toolbox\"}","handlingStrategy":"validation","validationCode":"claims := decodeJWTPayload(tokenString)\nexpected := cfg.Audience // value from your auth config\naudList, _ := claims[\"aud\"].([]any)\nfound := false\nfor _, a := range audList {\n    if a == expected { found = true }\n}\nif s, ok := claims[\"aud\"].(string); ok && s == expected { found = true }\nif !found { return fmt.Errorf(\"token aud %v does not include configured audience %q\", claims[\"aud\"], expected) }","typeGuard":"func audienceMatches(claims map[string]any, want string) bool {\n    switch a := claims[\"aud\"].(type) {\n    case string:\n        return a == want\n    case []any:\n        for _, v := range range a { if v == want { return true } }\n    }\n    return false\n}","tryCatchPattern":"claims, err := svc.GetClaimsFromHeader(ctx, header)\nif err != nil {\n    if strings.Contains(err.Error(), \"audience validation failed\") {\n        // compare err's expected vs got values with your IdP's aud and fix config or token request\n        return http.StatusForbidden\n    }\n    return http.StatusUnauthorized\n}","preventionTips":["Keep the audience value in one config source shared by clients and server","After changing the IdP app registration, re-issue tokens before testing","Log the token's aud claim (dev only) when troubleshooting 401/403s"],"tags":["jwt","auth","audience","configuration"],"backgroundTag":"jwt-audience-mismatch","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"}