cloudflare/cloudflared · error

invalid token

Error message

invalid token

What it means

SignCert in sshgen returns "invalid token" when the Cloudflare Access JWT passed to it is empty. Before parsing the JWT with jwt.ParseSigned, the function rejects an empty token because no certificate can be signed without credentials identifying the requester.

Source

Thrown at sshgen/sshgen.go:90

	}

	return nil
}

// handleCertificateGeneration takes a JWT and uses it build a signPayload
// to send to the Sign endpoint with the public key from the keypair it generated
func handleCertificateGeneration(token, fullName string) (string, error) {
	pub, err := generateKeyPair(fullName)
	if err != nil {
		return "", err
	}

	return SignCert(token, string(pub))
}

func SignCert(token, pubKey string) (string, error) {
	if token == "" {
		return "", errors.New("invalid token")
	}

	parsedToken, err := jwt.ParseSigned(token, signatureAlgs)
	if err != nil {
		return "", errors.Wrap(err, "failed to parse JWT")
	}

	claims := jwt.Claims{}
	err = parsedToken.UnsafeClaimsWithoutVerification(&claims)
	if err != nil {
		return "", errors.Wrap(err, "failed to retrieve JWT claims")
	}

	buf, err := json.Marshal(&signPayload{
		PublicKey: pubKey,
		JWT:       token,
		Issuer:    claims.Issuer,
	})

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Obtain a valid Cloudflare Access JWT first (cloudflared access login / `cloudflared access token --app <url>`) and pass it to SignCert.
  2. Check that the token source (flag, env var, or token file) is populated and the path/env name is correct before calling SignCert.
  3. Add an explicit empty check in the caller so the failure is reported with context about where the token should come from.
  4. If the token was expected to be refreshed automatically, verify the Access app/service-token configuration and clock/expiry handling.

Example fix

// before
_, err := sshgen.SignCert("", pubKey)
// after
token := os.Getenv("CF_ACCESS_TOKEN")
if token == "" {
    return errors.New("missing Cloudflare Access token; run `cloudflared access login` first")
}
cert, err := sshgen.SignCert(token, pubKey)
Defensive patterns

Strategy: validation

Validate before calling

if token == "" {
    return errors.New("missing Cloudflare Access token; run `cloudflared access login`")
}
cert, err := sshgen.SignCert(token, pubKey)

Type guard

func hasToken(token string) bool { return strings.TrimSpace(token) != "" }

Try / catch

cert, err := sshgen.SignCert(token, pubKey)
if err != nil && strings.Contains(err.Error(), "invalid token") {
    // prompt user to authenticate / fetch a fresh Access JWT
}

Prevention

When it happens

Trigger: Calling cloudflared's SSH certificate generation (handleCertificateGeneration -> SignCert) with a token string that is empty — e.g. the cf_access_token / Cloudflare Access JWT was never fetched, the `--token` flag was omitted, or an environment/config lookup returned "".

Common situations: Running `cloudflared access ssh-gen` or short-lived SSH cert flow without being logged in to Cloudflare Access; the Access token file/env var missing on the host; a token-fetch step failing silently upstream and passing an empty string onward.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/03bdc5eb4fbdacab. Report an issue: GitHub.