{"record":{"id":"ed45f8ee30ef8ea4","repo":"cloudflare/cloudflared","slug":"failed-to-decode-metadata-jwt-claims","errorCode":null,"errorMessage":"failed to decode metadata JWT claims","messagePattern":"failed to decode metadata JWT claims","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/jwks.go","lineNumber":54,"sourceCode":"\tAuthDomain string `json:\"auth_domain\"`\n\tAUD        string `json:\"aud\"`\n\t// This is the hostname as defined in the Access application, including wildcards.\n\tAppHostname string `json:\"app_hostname\"`\n\tIAT         int64  `json:\"iat\"`\n}\n\n// decodeMetadataUnverified decodes the JWT payload without verifying the\n// signature.\nfunc decodeMetadataUnverified(rawJWT string) (*metadataClaims, error) {\n\tjws, err := jose.ParseSigned(rawJWT, signatureAlgs)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to parse metadata JWT\")\n\t}\n\n\tpayload := jws.UnsafePayloadWithoutVerification()\n\tvar claims metadataClaims\n\tif err := json.Unmarshal(payload, &claims); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to decode metadata JWT claims\")\n\t}\n\treturn &claims, nil\n}\n\n// verifyMetadataJWT verifies the metadata JWT signature against the provided\n// JWKS and returns the decoded claims.\nfunc verifyMetadataJWT(rawJWT string, keySet *jose.JSONWebKeySet) (*metadataClaims, error) {\n\tjws, err := jose.ParseSigned(rawJWT, signatureAlgs)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to parse metadata JWT\")\n\t}\n\n\tpayload, err := jws.Verify(keySet)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to verify metadata JWT signature\")\n\t}\n\n\tvar claims metadataClaims","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/jwks.go#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the token is the Access metadata JWT (obtained via CF-Access-Metadata-Request: true), not an ID or session token.","Base64url-decode the payload segment and inspect it with `jq` to confirm it contains hostname/auth_domain/aud claims.","Do not modify or re-sign the token before passing it in.","Check that no proxy rewrites the token value (trailing newlines, double encoding)."],"exampleFix":"// sanity-check before calling\nparts := strings.Split(token, \".\")\nif len(parts) != 2 && len(parts) != 3 {\n    return errors.New(\"unexpected JWT shape\")\n}\npayload, _ := base64.RawURLEncoding.DecodeString(parts[1])\nvar probe map[string]any\nif err := json.Unmarshal(payload, &probe); err != nil {\n    return fmt.Errorf(\"not an Access metadata JWT payload: %w\", err)\n}\nclaims, err := GetAppInfo(token)","handlingStrategy":"validation","validationCode":"func payloadIsJSONClaims(token string) error {\n    parts := strings.Split(strings.TrimSpace(token), \".\")\n    if len(parts) < 2 {\n        return errors.New(\"not a JWT\")\n    }\n    payload, err := base64.RawURLEncoding.DecodeString(parts[1])\n    if err != nil {\n        return err\n    }\n    var probe map[string]any\n    return json.Unmarshal(payload, &probe)\n}","typeGuard":null,"tryCatchPattern":"claims, err := GetAppInfo(rawJWT)\nif err != nil && strings.Contains(err.Error(), \"failed to decode metadata JWT claims\") {\n    return fmt.Errorf(\"payload is not an Access metadata claims set — is this the right token type? %w\", err)\n}","preventionTips":["Only feed tokens obtained via the metadata endpoint (CF-Access-Metadata-Request: true).","Inspect the payload once with jq during integration to confirm expected claims.","Do not re-serialize or transform tokens in middleware."],"tags":["jwt","json","claims","cloudflare-access"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}