apereo/cas · error · AuthenticationException
Token has expired: and is after
Error message
Token has expired: %s and is after %s
What it means
During JWT claim validation, the parser compares the token's exp claim to the current time allowing a configurable clock skew (cas.authn.oidc.core.skew). If the expiration time is not after 'now' within the skew, it throws AuthenticationException 'Token has expired: <exp> and is after <now>'.
Solutions
- Obtain a fresh token and retry the request
- Increase the allowed clock skew via cas.authn.oidc.core.skew if servers' clocks differ mildly
- Synchronize clocks with NTP on both issuer and CAS hosts
- Implement token refresh in the client before expiry instead of reusing expired tokens
Example fix
// before cas.authn.oidc.core.skew=PT0S // after cas.authn.oidc.core.skew=PT2M
Defensive patterns
Strategy: validation
Validate before calling
Date exp = claimsSet.getExpirationTime();
if (exp != null && exp.before(new Date())) {
throw new IllegalStateException("Token expired, refresh before calling");
} Try / catch
try {
return parser.claims(token);
} catch (AuthenticationException e) {
// trigger token refresh flow and retry once
} Prevention
- Refresh tokens proactively before expiry
- Synchronize clocks with NTP
- Configure a small positive skew on the CAS side
When it happens
Trigger: validateClaims (called from parseAuthorizationHeader) receives a JWTClaimsSet whose expirationTime is non-null and has passed (minus allowed clock skew).
Common situations: Client cached a token beyond its lifetime; long-running service replaying an old assertion; severe clock drift between issuer and CAS server; token issued for a short-lived flow (e.g. OIDC access token) reused later.
Related errors
- Token cannot be used before
- Proof iat is in the future
- Proof JWT is too old
- Token has expired
- Ticket is issued before the allowed drift. Issued on
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/5abe0b443cafb697.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-heimdall/src/main/java/org/apereo/cas/heimdall/engine/DefaultAuthorizationPrincipalParser.java:137
return Optional.empty();
}
}
protected JWTClaimsSet buildClaimSetFromAuthentication(final String token) throws Throwable {
val usernamePass = Splitter.on(':').splitToList(EncodingUtils.decodeBase64ToString(token));
val credential = new UsernamePasswordCredential(usernamePass.getFirst(), usernamePass.getLast());
val authResultBuilder = authenticationSystemSupport.handleInitialAuthenticationTransaction(null, credential);
val authentication = authenticationSystemSupport.finalizeAllAuthenticationTransactions(authResultBuilder, null);
val claimsMap = buildClaimsFromAuthentication(authentication.getAuthentication());
return JWTClaimsSet.parse(claimsMap);
}
protected JWTClaimsSet validateClaims(final JWTClaimsSet claimsSet) {
val maxClockSkew = Beans.newDuration(casProperties.getAuthn().getOidc().getCore().getSkew()).toSeconds();
val now = new Date();
val exp = claimsSet.getExpirationTime();
if (exp != null && !DateUtils.isAfter(exp, now, maxClockSkew)) {
throw new AuthenticationException("Token has expired: %s and is after %s".formatted(exp, now));
}
val nbf = claimsSet.getNotBeforeTime();
if (nbf != null && !DateUtils.isBefore(nbf, now, maxClockSkew)) {
throw new AuthenticationException("Token cannot be used before %s and now is %s".formatted(nbf, now));
}
return claimsSet;
}
private Optional<JWTClaimsSet> getJwtClaimsSetFromAccessToken(final String token) {
try {
val ticket = ticketRegistry.getTicket(token, OAuth20AccessToken.class);
FunctionUtils.throwIf(ticket == null || ticket.isExpired(),
() -> new AuthenticationException("Token %s is not found or has expired".formatted(token)));
val claimsMap = buildClaimsFromAuthentication(ticket.getAuthentication());
claimsMap.putAll(ticket.getClaims());
claimsMap.put(OAuth20Constants.SCOPE, ticket.getScopes());
claimsMap.put(OAuth20Constants.TOKEN, token);
return Optional.of(JWTClaimsSet.parse(claimsMap));View on GitHub (pinned to e7288fc434)