apereo/cas · warning
Ticket is issued after the allowed drift. Retrieved on
Error message
Ticket is issued after the allowed drift. Retrieved on [{}] and issued on [{}] while allowed drift is [{}] What it means
isValid() also rejects tokens whose IssuedOn timestamp is in the future relative to the retrieval time plus the allowed drift. If getIssuedOn().isAfter(getRetrievedOn().plus(timeDrift, MILLIS)), the token claims to have been issued later than plausible (clock skew in the other direction), so it is rejected with this warning.
Solutions
- Enable NTP/chrony on both CAS and the IdP and let clocks converge.
- Increase the configured time tolerance (timeDrift) so modest forward skew is tolerated.
- Verify token integrity — a future IssuedOn can indicate tampering; check signature validation settings.
- If skew is expected and benign, re-test after confirming both servers report the same UTC time.
Example fix
// before val timeDrift = 60000L; // 1 minute, too small for skewed VMs // after val timeDrift = 300000L; // 5 minutes, ADFS-style tolerance
Defensive patterns
Strategy: validation
Validate before calling
Instant now = Instant.now();
if (credential.getIssuedOn().isAfter(now.plusMillis(timeDrift))) {
throw new IllegalStateException("Token issuedOn is in the future: " + credential.getIssuedOn());
} Prevention
- Monitor clock drift between CAS and the IdP with NTP alerting.
- Keep signature validation enabled so fabricated future-dated tokens are rejected upstream.
- Re-test federation flows after any host clock resync or VM migration.
When it happens
Trigger: Calling isValid(expectedAudience, expectedIssuer, timeDrift) where the token's IssuedOn exceeds retrievedOn + timeDrift — the IdP clock is ahead of the CAS server clock, or a forged/fabricated assertion carries a future issuance time.
Common situations: ADFS host clock running ahead of CAS server time; VM clocks drifting without NTP; new token tests failing immediately after clock changes on either host.
Related errors
- Ticket is issued before the allowed drift. Issued on
- Ticket is too late because it's retrieved on
- Issuer [ ] is invalid since the expected issuer should be […
- Found multiple values for id attribute
- Denied
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/c917852527355a34.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredential.java:75
*/
public boolean isValid(final String expectedAudience, final String expectedIssuer, final long timeDrift) {
if (!this.audience.equalsIgnoreCase(expectedAudience)) {
LOGGER.warn("Audience [{}] is invalid where the expected audience should be [{}]", this.audience, expectedAudience);
return false;
}
if (!this.issuer.equalsIgnoreCase(expectedIssuer)) {
LOGGER.warn("Issuer [{}] is invalid since the expected issuer should be [{}]", this.issuer, expectedIssuer);
return false;
}
val retrievedOnTimeDrift = getRetrievedOn().minus(timeDrift, ChronoUnit.MILLIS);
if (getIssuedOn().isBefore(retrievedOnTimeDrift)) {
LOGGER.warn("Ticket is issued before the allowed drift. Issued on [{}] while allowed drift is [{}]",
this.issuedOn, retrievedOnTimeDrift);
return false;
}
val retrievedOnTimeAfterDrift = getRetrievedOn().plus(timeDrift, ChronoUnit.MILLIS);
if (getIssuedOn().isAfter(retrievedOnTimeAfterDrift)) {
LOGGER.warn("Ticket is issued after the allowed drift. Retrieved on [{}] and issued on [{}] while allowed drift is [{}]",
getRetrievedOn(), getIssuedOn(), retrievedOnTimeAfterDrift);
return false;
}
if (getRetrievedOn().isAfter(this.notOnOrAfter)) {
LOGGER.warn("Ticket is too late because it's retrieved on [{}] which is after [{}].",
getRetrievedOn(), this.notOnOrAfter);
return false;
}
LOGGER.debug("WsFed Credential is validated for [{}] and [{}].", expectedAudience, expectedIssuer);
return true;
}
}
View on GitHub (pinned to e7288fc434)