cloudflare/cloudflared · error
failed to parse metadata JWT
Error message
failed to parse metadata JWT
What it means
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.
Source
Thrown at token/jwks.go:48
// metadataClaims represents the claims in the signed metadata JWT returned
// by the Cloudflare Access edge when CF-Access-Metadata-Request: true is set.
type metadataClaims struct {
Type string `json:"type"`
Hostname string `json:"hostname"`
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")
}
View on GitHub (pinned to 2253eeeb25)
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.
Example fix
// before: token may carry surrounding whitespace
token := r.Header.Get("Cf-Access-Jwt-Assertion")
claims, err := GetAppInfo(token)
// after
token := strings.TrimSpace(r.Header.Get("Cf-Access-Jwt-Assertion"))
if strings.Count(token, ".") != 2 {
return errors.New("malformed Access JWT")
}
claims, err := GetAppInfo(token) Defensive patterns
Strategy: validation
Validate before calling
func looksLikeCompactJWS(token string) bool {
token = strings.TrimSpace(token)
parts := strings.Split(token, ".")
return len(parts) == 3 && len(parts[0]) > 0 && len(parts[1]) > 0
} Try / catch
claims, err := GetAppInfo(rawJWT)
if err != nil && strings.Contains(err.Error(), "failed to parse metadata JWT") {
return fmt.Errorf("token is not a well-formed Access metadata JWT (check truncation/encoding): %w", err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse JWT
- invalid token
- metadata JWT aud is empty
- metadata JWT iat is missing or invalid
- failed to parse auth_domain %q: %w
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/cb5f90035dfc3351.
Report an issue: GitHub.