cloudflare/cloudflared · error
token is invalid: %s
Error message
token is invalid: %s
What it means
This error is returned by Access.Validate when the JWT verifier (a.verifier.Verify) rejects the presented Access token. It wraps the underlying verification error (bad signature, expired token, wrong audience, malformed JWT) together with the offending token string. It means the token cannot be trusted and the request must be rejected.
Source
Thrown at validation/validation.go:198
}
issuerURL, err := validateUrlString(issuer)
if err != nil {
return nil, err
}
// An issuerURL from Cloudflare Access will always use HTTPS.
issuerURL = strings.Replace(issuerURL, "http:", "https:", 1)
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
- Have the user re-authenticate to the Access application to obtain a fresh JWT
- Verify the application's AUD tag configured in the validator matches the one in the token (jwt.io decode of the payload)
- Confirm the machine's clock is synchronized (NTP) to avoid expiry misjudgment
- Ensure the token header is forwarded intact (not stripped by a proxy/load balancer)
- Check that the team's public keys/certs used by the verifier are current
Example fix
// before: treating any validate error as fatal misconfiguration
if err := validator.Validate(ctx, jwt); err != nil {
panic(err)
}
// after: distinguish expired token (re-auth needed) from other failures
if err := validator.Validate(ctx, jwt); err != nil {
log.Warn().Err(err).Msg("access token rejected, requiring re-authentication")
http.Error(w, "unauthorized: please re-authenticate via Cloudflare Access", http.StatusUnauthorized)
return
} Defensive patterns
Strategy: try-catch
Validate before calling
parts := strings.Split(jwt, ".")
if len(parts) != 3 {
return errors.New("malformed JWT: expected 3 dot-separated segments")
}
// decode payload to pre-check exp and aud before Validate
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var claims struct{ Exp int64 `json:"exp"`; Aud []string `json:"aud"` }
json.Unmarshal(payload, &claims)
if time.Now().Unix() > claims.Exp {
return errors.New("token already expired; re-authenticate")
} Try / catch
if err := validator.Validate(ctx, jwt); err != nil {
var httpErr *httpError
log.Warn().Err(err).Msg("access JWT rejected")
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
} Prevention
- Prompt re-authentication when tokens are near expiry
- Keep verifier AUD/team config in sync with the Access application
- Synchronize clocks with NTP on validating hosts
- Ensure proxies forward the Cf-Access-Jwt-Assertion header unchanged
When it happens
Trigger: ValidateRequest passes a request's Cf-Access-Jwt-Assertion JWT to Access.Validate, and verifier.Verify fails: token expired, signature invalid, signed by an unexpected team cert, wrong AUD tag, or token truncated/corrupted in transit.
Common situations: User's Access session expired and the browser still sends the old JWT; application AUD changed after config edits; token copied between applications with different audiences; clock skew between validating host and Cloudflare edge; tokens forwarded incorrectly by a proxy.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- failed to verify token
- invalid token
- aud array contains non-string elements
- aud field is not a string or an array of strings
- metadata JWT aud is empty
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/bfd5c3520d10c7b6.
Report an issue: GitHub.