{"record":{"id":"4cee407c9c4295ee","repo":"cloudflare/cloudflared","slug":"failed-to-parse-auth-domain-q-w","errorCode":null,"errorMessage":"failed to parse auth_domain %q: %w","messagePattern":"failed to parse auth_domain %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/jwks.go","lineNumber":84,"sourceCode":"\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\n\tif err := json.Unmarshal(payload, &claims); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to decode verified metadata JWT claims\")\n\t}\n\treturn &claims, nil\n}\n\n// parseAuthDomain extracts the canonical hostname used for JWKS requests and\n// cache paths from the auth_domain claim.\nfunc parseAuthDomain(authDomain string) (url.URL, error) {\n\tparsed, err := url.Parse(httpsScheme + \"://\" + authDomain)\n\tif err != nil {\n\t\treturn url.URL{}, fmt.Errorf(\"failed to parse auth_domain %q: %w\", authDomain, err)\n\t}\n\thostname := strings.ToLower(parsed.Hostname())\n\tif !strings.HasSuffix(hostname, accessDomainSuffix) {\n\t\treturn url.URL{}, fmt.Errorf(\"auth_domain %q does not end with %q\", authDomain, accessDomainSuffix)\n\t}\n\treturn url.URL{Scheme: httpsScheme, Host: hostname}, nil\n}\n\n// fetchJWKS fetches the JWKS from the auth domain's certs endpoint over HTTPS.\nfunc fetchJWKS(authDomain url.URL) (*jose.JSONWebKeySet, error) {\n\tjwksURL := authDomain\n\tjwksURL.Path = accessCertPath\n\n\tclient := &http.Client{\n\t\tCheckRedirect: func(_ *http.Request, _ []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t\tTimeout: time.Second * 10,","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/jwks.go#L66-L102","documentation":"parseAuthDomain in token/jwks.go normalizes the auth_domain claim from the Cloudflare Access metadata JWT into an https URL used for JWKS fetches and cache paths. The claim is prefixed with \"https://\" and parsed with url.Parse; if that parse fails (malformed input, control characters, invalid URL syntax) the error is wrapped as \"failed to parse auth_domain %q: %w\".","triggerScenarios":"GetAppInfo / testAuthDomain / metadata decoding pass an auth_domain string whose \"https://\" + authDomain cannot be parsed by net/url.Parse — e.g. the claim contains spaces, control characters, or malformed percent-encodings, or is empty in a way that produces an invalid URL.","commonSituations":"A corrupted or tampered metadata JWT yields a bogus auth_domain claim; a misconfigured Access application sets auth_domain to a full URL (\"https://team.cloudflareaccess.com\") or with a scheme/path embedded oddly; environment variables (TUNNEL_TOKEN-based flows) contain a manually edited team name with whitespace or URL-unsafe characters.","solutions":["Check the auth_domain value being passed: it should be a bare hostname like \"myteam.cloudflareaccess.com\" — no scheme, path, spaces, or wildcards.","Re-fetch the Access metadata JWT; if it came from a stale/corrupted cache or token, obtain a fresh tunnel token from the Cloudflare dashboard.","Fix the TUNNEL_TOKEN / Access application configuration in the Cloudflare Zero Trust dashboard so the team domain is a valid hostname.","Validate the team domain string before constructing the token/config (reject empty or non-hostname values)."],"exampleFix":"// before — full URL pasted as team domain\nauthDomain := \"https://myteam.cloudflareaccess.com/\" // url.Parse still succeeds here, but e.g. \"my team\" fails\n// after — bare hostname only\nauthDomain := \"myteam.cloudflareaccess.com\"\nurl, err := parseAuthDomain(authDomain)","handlingStrategy":"validation","validationCode":"func validAuthDomainInput(authDomain string) error {\n\tif authDomain == \"\" {\n\t\treturn errors.New(\"auth_domain is empty\")\n\t}\n\tif strings.ContainsAny(authDomain, \" \\t\\r\\n/\") {\n\t\treturn fmt.Errorf(\"auth_domain %q must be a bare hostname\", authDomain)\n\t}\n\tif _, err := url.Parse(\"https://\" + authDomain); err != nil {\n\t\treturn fmt.Errorf(\"auth_domain %q is not a valid URL: %w\", authDomain, err)\n\t}\n\treturn nil\n}","typeGuard":"func looksLikeHostname(s string) bool {\n\tu, err := url.Parse(\"https://\" + s)\n\treturn err == nil && u.Hostname() != \"\" && !strings.ContainsAny(s, \" /\\t\\n\")\n}","tryCatchPattern":"ad, err := parseAuthDomain(authDomain)\nvar uerr *url.Error\nif err != nil {\n\tif errors.As(err, &uerr) {\n\t\tlog.Error().Err(uerr).Str(\"authDomain\", authDomain).Msg(\"malformed auth_domain claim; re-issue tunnel token\")\n\t}\n\treturn err\n}","preventionTips":["Never hand-edit tunnel tokens; download them fresh from the Zero Trust dashboard.","Store the team domain as a bare hostname without scheme or trailing slash.","Validate team-name inputs for whitespace/URL-unsafe characters before building tokens."],"tags":["url-parsing","cloudflare-access","configuration","jwt"],"backgroundTag":"invalid-url-format","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"}