googleapis/mcp-toolbox · error
audience validation failed: expected %s, got %v
Error message
audience validation failed: expected %s, got %v
What it means
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.
Source
Thrown at internal/auth/generic/generic.go:260
return nil, fmt.Errorf("invalid JWT claims format")
}
// Validate 'aud' (audience) claim
aud, err := claims.GetAudience()
if err != nil {
return nil, fmt.Errorf("could not parse audience from token: %w", err)
}
isAudValid := false
for _, audItem := range aud {
if audItem == a.Audience {
isAudValid = true
break
}
}
if !isAudValid {
return nil, fmt.Errorf("audience validation failed: expected %s, got %v", a.Audience, aud)
}
return claims, nil
}
// MCPAuthError represents an error during MCP authentication validation.
type MCPAuthError = auth.MCPAuthError
// ValidateMCPAuth handles MCP auth token validation
func (a AuthService) ValidateMCPAuth(ctx context.Context, h http.Header) (map[string]any, error) {
tokenString := h.Get("Authorization")
if tokenString == "" {
return nil, &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, &MCPAuthError{Code: http.StatusUnauthorized, Message: "authorization header must be in the format 'Bearer <token>'", ScopesRequired: a.ScopesRequired}View on GitHub (pinned to 8cc6e09de2)
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.
Example fix
// before: server expects audience "my-toolbox" but token has aud "other-api"
{"aud":"other-api"}
// after: request token with audience=my-toolbox
{"aud":"my-toolbox"} Defensive patterns
Strategy: validation
Validate before calling
claims := decodeJWTPayload(tokenString)
expected := cfg.Audience // value from your auth config
audList, _ := claims["aud"].([]any)
found := false
for _, a := range audList {
if a == expected { found = true }
}
if s, ok := claims["aud"].(string); ok && s == expected { found = true }
if !found { return fmt.Errorf("token aud %v does not include configured audience %q", claims["aud"], expected) } Type guard
func audienceMatches(claims map[string]any, want string) bool {
switch a := claims["aud"].(type) {
case string:
return a == want
case []any:
for _, v := range range a { if v == want { return true } }
}
return false
} Try / catch
claims, err := svc.GetClaimsFromHeader(ctx, header)
if err != nil {
if strings.Contains(err.Error(), "audience validation failed") {
// compare err's expected vs got values with your IdP's aud and fix config or token request
return http.StatusForbidden
}
return http.StatusUnauthorized
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- could not parse audience from token: %w
- failed to check auth requirements: %w
- MCP Auth cannot be enabled together with the legacy HTTP API
- MCP Auth is enabled but Toolbox URL is missing. Please provi
- `introspectionEndpoint` is not allowed when `mcpEnabled` is
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/ee3d9be8c678a21c.
Report an issue: GitHub.