{"record":{"id":"cb5f90035dfc3351","repo":"cloudflare/cloudflared","slug":"failed-to-parse-metadata-jwt","errorCode":null,"errorMessage":"failed to parse metadata JWT","messagePattern":"failed to parse metadata JWT","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/jwks.go","lineNumber":48,"sourceCode":"\n// metadataClaims represents the claims in the signed metadata JWT returned\n// by the Cloudflare Access edge when CF-Access-Metadata-Request: true is set.\ntype metadataClaims struct {\n\tType       string `json:\"type\"`\n\tHostname   string `json:\"hostname\"`\n\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","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/jwks.go#L30-L66","documentation":"decodeMetadataUnverified parses an Access metadata JWT with go-jose's ParseSigned before reading its payload without signature verification. This error means the raw JWT is not a well-formed compact JWS with an allowed signature algorithm (only RS256 is accepted here). It is returned by GetAppInfo when handed a malformed token string.","triggerScenarios":"Calling GetAppInfo (-> decodeMetadataUnverified) with a rawJWT that is empty, truncated, not base64url-encoded in three dot-separated parts, uses a disallowed algorithm header (e.g. ES256), or is a plain (unsigned) JWS token.","commonSituations":"Passing a CF_Authorization cookie value that was URL-decoded or truncated; grabbing the wrong header/cookie (e.g. a session id instead of the Access JWT); a token missing its signature segment; algorithm mismatch after an edge-side key rotation to a non-RS256 alg.","solutions":["Pass the raw CF_Authorization JWT exactly as received (URL-encode it when placing in a query string, never pre-decode).","Verify the token has the compact JWS form header.payload.signature with three dot-separated segments.","Confirm the token is the Access metadata JWT and not a session cookie or opaque token.","Log the first few characters of the failing value (never the whole token) to spot truncation or whitespace/newlines."],"exampleFix":"// before: token may carry surrounding whitespace\ntoken := r.Header.Get(\"Cf-Access-Jwt-Assertion\")\nclaims, err := GetAppInfo(token)\n// after\ntoken := strings.TrimSpace(r.Header.Get(\"Cf-Access-Jwt-Assertion\"))\nif strings.Count(token, \".\") != 2 {\n    return errors.New(\"malformed Access JWT\")\n}\nclaims, err := GetAppInfo(token)","handlingStrategy":"validation","validationCode":"func looksLikeCompactJWS(token string) bool {\n    token = strings.TrimSpace(token)\n    parts := strings.Split(token, \".\")\n    return len(parts) == 3 && len(parts[0]) > 0 && len(parts[1]) > 0\n}","typeGuard":null,"tryCatchPattern":"claims, err := GetAppInfo(rawJWT)\nif err != nil && strings.Contains(err.Error(), \"failed to parse metadata JWT\") {\n    return fmt.Errorf(\"token is not a well-formed Access metadata JWT (check truncation/encoding): %w\", err)\n}","preventionTips":["Pass CF_Authorization / Cf-Access-Jwt-Assertion values verbatim; re-encode only at the transport layer.","Trim whitespace and strip quotes before validation.","Never pre-decode base64url segments yourself before handing the token to the library."],"tags":["jwt","jose","parsing","cloudflare-access"],"backgroundTag":"jwt-parse-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"}