googleapis/mcp-toolbox · error

invalid JWT claims format

Error message

invalid JWT claims format

What it means

This error means the JWT was signature-valid, but its claims payload could not be represented as jwt.MapClaims (map[string]any). The generic auth service parses tokens with jwt.Parse and type-asserts token.Claims to MapClaims before validating the audience claim; if the assertion fails the claims are in an unexpected shape and validation aborts. This is a defensive check that rarely fires with well-formed JWTs from compliant issuers.

Source

Thrown at internal/auth/generic/generic.go:242

	tokenString := h.Get(a.Name + "_token")
	if tokenString == "" {
		return nil, nil
	}

	// 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)

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Inspect the token payload at jwt.io and confirm claims are a flat JSON object (map), not an array or scalar.
  2. Ensure the token is issued by your configured authorization server as a standard JWT, not an opaque token.
  3. If you customized jwt.Parse with a custom claims type, align it with MapClaims or update the auth service accordingly.

Example fix

// before: token with non-object claims payload
Authorization: eyJhbGciOi... (claims: ["not","an","object"])
// after: reissue token with object claims
Authorization: eyJhbGciOi... (claims: {"aud":"my-audience","exp":1893456000})
Defensive patterns

Strategy: type-guard

Validate before calling

parts := strings.Split(tokenString, ".")
if len(parts) != 3 { return fmt.Errorf("not a JWT") }
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var claims map[string]any
if err := json.Unmarshal(payload, &claims); err != nil {
    return fmt.Errorf("claims are not a JSON object: %w", err)
}

Type guard

func isMapClaims(t *jwt.Token) bool {
    _, ok := t.Claims.(jwt.MapClaims)
    return ok
}

Try / catch

claims, err := svc.GetClaimsFromHeader(ctx, header)
if err != nil {
    if strings.Contains(err.Error(), "invalid JWT claims format") {
        // reject token: non-object claims payload
    }
    return err
}

Prevention

When it happens

Trigger: GetClaimsFromHeader is called with an Authorization-style header '<Name>_token' whose value parses and verifies as a JWT, but token.Claims does not hold a jwt.MapClaims value — e.g. a token parsed into a custom claims struct, or a non-standard claims payload in the token string.

Common situations: Sending a non-JWT opaque string that some parser still marks valid, tokens whose claims are not a JSON object (e.g. a JSON array or string), or a misconfigured JWKS/keyfunc setup where a different parser produced the token object.

Related errors


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