cloudflare/cloudflared · error
auth_domain %q does not end with %q
Error message
auth_domain %q does not end with %q
What it means
After parsing, parseAuthDomain lowercases the hostname and enforces that the auth_domain ends with the Cloudflare Access suffix ".cloudflareaccess.com". This is a security check: JWKS requests and cache paths must only ever target Cloudflare Access domains, preventing a tampered metadata JWT from redirecting token verification to an attacker-controlled host. A non-matching auth_domain is rejected with "auth_domain %q does not end with %q".
Source
Thrown at token/jwks.go:88
}
var claims metadataClaims
if err := json.Unmarshal(payload, &claims); err != nil {
return nil, errors.Wrap(err, "failed to decode verified metadata JWT claims")
}
return &claims, nil
}
// parseAuthDomain extracts the canonical hostname used for JWKS requests and
// cache paths from the auth_domain claim.
func parseAuthDomain(authDomain string) (url.URL, error) {
parsed, err := url.Parse(httpsScheme + "://" + authDomain)
if err != nil {
return url.URL{}, fmt.Errorf("failed to parse auth_domain %q: %w", authDomain, err)
}
hostname := strings.ToLower(parsed.Hostname())
if !strings.HasSuffix(hostname, accessDomainSuffix) {
return url.URL{}, fmt.Errorf("auth_domain %q does not end with %q", authDomain, accessDomainSuffix)
}
return url.URL{Scheme: httpsScheme, Host: hostname}, nil
}
// fetchJWKS fetches the JWKS from the auth domain's certs endpoint over HTTPS.
func fetchJWKS(authDomain url.URL) (*jose.JSONWebKeySet, error) {
jwksURL := authDomain
jwksURL.Path = accessCertPath
client := &http.Client{
CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: time.Second * 10,
}
resp, err := client.Get(jwksURL.String()) // nolint: gosec
if err != nil {
return nil, errors.Wrapf(err, "failed to fetch JWKS from %s", jwksURL.String())View on GitHub (pinned to 2253eeeb25)
Solutions
- Use the standard Cloudflare Access team domain ending in .cloudflareaccess.com for auth_domain (find it in the Zero Trust dashboard under Access).
- Re-issue/re-download the tunnel token from the Cloudflare dashboard — a stale or hand-edited token may carry a wrong auth_domain.
- If the JWT came from an untrusted source, treat this as a possible tampering attempt and fetch metadata only from Cloudflare edge.
- If you legitimately need a custom auth domain, this library does not support it; do not bypass the suffix check — keep the standard domain for JWKS verification.
Example fix
// before "auth_domain": "myteam.example.com" // after "auth_domain": "myteam.cloudflareaccess.com"
Defensive patterns
Strategy: validation
Validate before calling
func isCloudflareAccessDomain(authDomain string) bool {
u, err := url.Parse("https://" + authDomain)
if err != nil {
return false
}
return strings.HasSuffix(strings.ToLower(u.Hostname()), ".cloudflareaccess.com")
} Type guard
func isAccessAuthDomain(u url.URL) bool {
return u.Scheme == "https" && strings.HasSuffix(u.Host, ".cloudflareaccess.com")
} Try / catch
ad, err := parseAuthDomain(claims.AuthDomain)
if err != nil {
if strings.Contains(err.Error(), "does not end with") {
log.Error().Str("authDomain", claims.AuthDomain).
Msg("auth_domain is not a Cloudflare Access domain; possible tampering or misconfiguration — refusing JWKS fetch")
}
return err
} Prevention
- Only trust metadata JWTs obtained from Cloudflare edge with signature verification.
- Configure Access applications with the standard .cloudflareaccess.com team domain.
- Treat a suffix-check failure on signed metadata as a security incident, not just a config bug.
- Never bypass or weaken the domain suffix allowlist.
When it happens
Trigger: GetAppInfo / testAuthDomain / metadata processing receive an auth_domain claim whose hostname does not end in ".cloudflareaccess.com" — e.g. "myteam.example.com", "myteam.cloudflareaccess.com.evil.io", an empty string, or a claim missing entirely so the value is blank.
Common situations: A self-hosted or third-party Access-like setup whose domain is not on cloudflareaccess.com is plugged into cloudflared; the metadata JWT was tampered with or generated by a mock/stub in a custom environment; the team domain in the dashboard was customized and doesn't use the standard Access suffix; a typo in a hand-edited tunnel token.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- metadata JWT auth_domain validation failed: %w
- metadata JWT verification failed: %w
- metadata JWT hostname %q does not match request host %q
- invalid token
- metadata JWT aud is empty
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d53546259467459c.
Report an issue: GitHub.