cloudflare/cloudflared · error

failed to decode metadata JWT claims

Error message

failed to decode metadata JWT claims

What it means

After parsing the JWS, decodeMetadataUnverified extracts the payload with UnsafePayloadWithoutVerification and json.Unmarshal's it into metadataClaims. This error means the payload decodes but is not valid JSON matching the claims structure (e.g. `aud` is a non-string). It is surfaced by GetAppInfo.

Source

Thrown at token/jwks.go:54

	AuthDomain string `json:"auth_domain"`
	AUD        string `json:"aud"`
	// This is the hostname as defined in the Access application, including wildcards.
	AppHostname string `json:"app_hostname"`
	IAT         int64  `json:"iat"`
}

// decodeMetadataUnverified decodes the JWT payload without verifying the
// signature.
func decodeMetadataUnverified(rawJWT string) (*metadataClaims, error) {
	jws, err := jose.ParseSigned(rawJWT, signatureAlgs)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse metadata JWT")
	}

	payload := jws.UnsafePayloadWithoutVerification()
	var claims metadataClaims
	if err := json.Unmarshal(payload, &claims); err != nil {
		return nil, errors.Wrap(err, "failed to decode metadata JWT claims")
	}
	return &claims, nil
}

// verifyMetadataJWT verifies the metadata JWT signature against the provided
// JWKS and returns the decoded claims.
func verifyMetadataJWT(rawJWT string, keySet *jose.JSONWebKeySet) (*metadataClaims, error) {
	jws, err := jose.ParseSigned(rawJWT, signatureAlgs)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse metadata JWT")
	}

	payload, err := jws.Verify(keySet)
	if err != nil {
		return nil, errors.Wrap(err, "failed to verify metadata JWT signature")
	}

	var claims metadataClaims

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Ensure the token is the Access metadata JWT (obtained via CF-Access-Metadata-Request: true), not an ID or session token.
  2. Base64url-decode the payload segment and inspect it with `jq` to confirm it contains hostname/auth_domain/aud claims.
  3. Do not modify or re-sign the token before passing it in.
  4. Check that no proxy rewrites the token value (trailing newlines, double encoding).

Example fix

// sanity-check before calling
parts := strings.Split(token, ".")
if len(parts) != 2 && len(parts) != 3 {
    return errors.New("unexpected JWT shape")
}
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var probe map[string]any
if err := json.Unmarshal(payload, &probe); err != nil {
    return fmt.Errorf("not an Access metadata JWT payload: %w", err)
}
claims, err := GetAppInfo(token)
Defensive patterns

Strategy: validation

Validate before calling

func payloadIsJSONClaims(token string) error {
    parts := strings.Split(strings.TrimSpace(token), ".")
    if len(parts) < 2 {
        return errors.New("not a JWT")
    }
    payload, err := base64.RawURLEncoding.DecodeString(parts[1])
    if err != nil {
        return err
    }
    var probe map[string]any
    return json.Unmarshal(payload, &probe)
}

Try / catch

claims, err := GetAppInfo(rawJWT)
if err != nil && strings.Contains(err.Error(), "failed to decode metadata JWT claims") {
    return fmt.Errorf("payload is not an Access metadata claims set — is this the right token type? %w", err)
}

Prevention

When it happens

Trigger: Calling GetAppInfo with a JWT whose payload segment contains valid base64 but not JSON-decodable metadataClaims — empty payload, binary payload, JSON with incompatible types (aud as array), or a token from a non-Access issuer.

Common situations: Passing an OIDC ID token or other JWT instead of the Cloudflare Access metadata JWT; a payload that was re-encoded/modified client-side; automated tools that re-serialize the token and mangle the payload.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/ed45f8ee30ef8ea4. Report an issue: GitHub.