cloudflare/cloudflared · error

token has non-cloudflare issuer of %s: %s

Error message

token has non-cloudflare issuer of %s: %s

What it means

After verifying the JWT's signature, Validate checks that the token's issuer ends with 'cloudflareaccess.com'. A validly-signed token issued by something other than Cloudflare Access is rejected — usually a misconfiguration where the application AUD/issuer domain doesn't match the token's actual Access team.

Source

Thrown at validation/validation.go:208

	keySet := oidc.NewRemoteKeySet(ctx, domainURL+accessCertPath)
	return &Access{oidc.NewVerifier(issuerURL, keySet, &oidc.Config{ClientID: applicationAUD})}, nil
}

func (a *Access) Validate(ctx context.Context, jwt string) error {
	token, err := a.verifier.Verify(ctx, jwt)

	if err != nil {
		return errors.Wrapf(err, "token is invalid: %s", jwt)
	}

	// Perform extra sanity checks, just to be safe.

	if token == nil {
		return fmt.Errorf("token is nil: %s", jwt)
	}

	if !strings.HasSuffix(token.Issuer, accessDomain) {
		return fmt.Errorf("token has non-cloudflare issuer of %s: %s", token.Issuer, jwt)
	}

	return nil
}

func (a *Access) ValidateRequest(ctx context.Context, r *http.Request) error {
	return a.Validate(ctx, r.Header.Get(accessJwtHeader))
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Confirm the JWT's iss claim (decode the token payload) and configure NewAccessValidator with that team's exact domain/issuer
  2. Ensure the request actually came through the Cloudflare Access deployment for this team, not another team or a direct origin hit
  3. Check the application AUD passed to NewAccessValidator matches this Access application
  4. Reject or log the offending token source — something is injecting non-Access JWTs into the header

Example fix

// before
validator, err := validation.NewAccessValidator(ctx, "old-team.cloudflareaccess.com", "https://old-team.cloudflareaccess.com", aud)
// after — match the team that actually issues the tokens
validator, err := validation.NewAccessValidator(ctx, "myteam.cloudflareaccess.com", "https://myteam.cloudflareaccess.com", aud)
Defensive patterns

Strategy: validation

Validate before calling

func issuerIsCloudflare(jwt string) bool {
    parts := strings.Split(jwt, ".")
    if len(parts) != 3 { return false }
    var claims struct{ Iss string `json:"iss"` }
    raw, _ := base64.RawURLEncoding.DecodeString(parts[1])
    _ = json.Unmarshal(raw, &claims)
    return strings.HasSuffix(claims.Iss, "cloudflareaccess.com")
}

Try / catch

if err := validator.Validate(ctx, jwt); err != nil {
    if strings.Contains(err.Error(), "non-cloudflare issuer") {
        log.Warn().Msg("token from wrong Access team or foreign OIDC provider")
    }
    http.Error(w, "Unauthorized", http.StatusUnauthorized)
}

Prevention

When it happens

Trigger: ValidateRequest receiving a Cf-Access-Jwt-Assertion JWT whose 'iss' claim is not a *.cloudflareaccess.com URL — e.g. token minted by a different Access team domain, another OIDC provider's token forwarded by a proxy, or a stale issuer configured in NewAccessValidator.

Common situations: Multiple Access applications/teams where the wrong team's token reaches this app; self-hosted or third-party OIDC tokens in the header; after migrating teams the validator still points at the old issuer; load balancers forwarding foreign JWTs.

Related errors


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