googleapis/mcp-toolbox · error
could not parse audience from token: %w
Error message
could not parse audience from token: %w
What it means
The JWT parsed successfully but its 'aud' (audience) claim could not be read via MapClaims.GetAudience(). The library requires an audience claim to verify the token was issued for this toolbox audience. GetAudience fails when the claim is absent or is not a string or array of strings.
Source
Thrown at internal/auth/generic/generic.go:248
// Parse and verify the token signature
token, err := jwt.Parse(tokenString, a.kf.Keyfunc)
if err != nil {
return nil, fmt.Errorf("failed to parse and verify JWT token: %w", err)
}
if !token.Valid {
return nil, fmt.Errorf("invalid JWT token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
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.View on GitHub (pinned to 8cc6e09de2)
Solutions
- Re-issue the token including an 'aud' claim matching the audience configured on the auth service.
- Check the token payload at jwt.io and fix the 'aud' type to a string or array of strings.
- If your IdP cannot add 'aud', configure the audience correctly or use a token flow that includes it (e.g. request scope/audience when exchanging credentials).
Example fix
// before
{"iss":"https://idp.example.com","sub":"user1"}
// after
{"iss":"https://idp.example.com","sub":"user1","aud":"toolbox-audience"} Defensive patterns
Strategy: validation
Validate before calling
payload := decodeJWTPayload(tokenString) // base64 decode part 2
aud, ok := payload["aud"]
if !ok {
return fmt.Errorf("token has no aud claim; re-request with audience parameter")
}
switch aud.(type) {
case string, []any:
default:
return fmt.Errorf("aud must be string or array, got %T", aud)
} Type guard
func hasValidAud(claims map[string]any) bool {
a, ok := claims["aud"]
if !ok { return false }
switch v := a.(type) {
case string:
return v != ""
case []any:
return len(v) > 0
default:
return false
}
} Try / catch
claims, err := svc.GetClaimsFromHeader(ctx, header)
if err != nil {
var audErr *jwt.AudienceError
if errors.As(err, &audErr) || strings.Contains(err.Error(), "could not parse audience") {
return http.StatusUnauthorized // token lacks usable aud claim
}
return http.StatusInternalServerError
} Prevention
- Always request tokens with an explicit audience/resource parameter
- Verify tokens at jwt.io include a string-or-array aud claim
- Document the required aud claim for your client-credential flows
When it happens
Trigger: GetClaimsFromHeader verifies a token whose claims lack 'aud' or whose 'aud' has an unexpected JSON type (number, object, null), then calls claims.GetAudience() which returns an error that is wrapped into this message.
Common situations: Identity-provider tokens minted without an audience (e.g. service-account tokens), tokens with 'aud' as a non-string JSON value, or tokens intended for a different product that omit the claim.
Related errors
- audience validation failed: expected %s, got %v
- failed to parse and verify JWT token: %w
- invalid JWT token
- invalid JWT claims format
- google ID token verification failure: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/a8cfd48dca53afd2.
Report an issue: GitHub.