{"record":{"id":"f0557c49f5b7b28b","repo":"apereo/cas","slug":"ticket-is-too-late-because-it-s-retrieved-on","errorCode":null,"errorMessage":"Ticket is too late because it's retrieved on [{}] which is after [{}].","messagePattern":"Ticket is too late because it's retrieved on \\[(.+?)\\] which is after \\[(.+?)\\]\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredential.java","lineNumber":80,"sourceCode":"        }\n        if (!this.issuer.equalsIgnoreCase(expectedIssuer)) {\n            LOGGER.warn(\"Issuer [{}] is invalid since the expected issuer should be [{}]\", this.issuer, expectedIssuer);\n            return false;\n        }\n        val retrievedOnTimeDrift = getRetrievedOn().minus(timeDrift, ChronoUnit.MILLIS);\n        if (getIssuedOn().isBefore(retrievedOnTimeDrift)) {\n            LOGGER.warn(\"Ticket is issued before the allowed drift. Issued on [{}] while allowed drift is [{}]\",\n                this.issuedOn, retrievedOnTimeDrift);\n            return false;\n        }\n        val retrievedOnTimeAfterDrift = getRetrievedOn().plus(timeDrift, ChronoUnit.MILLIS);\n        if (getIssuedOn().isAfter(retrievedOnTimeAfterDrift)) {\n            LOGGER.warn(\"Ticket is issued after the allowed drift. Retrieved on [{}] and issued on [{}] while allowed drift is [{}]\",\n                getRetrievedOn(), getIssuedOn(), retrievedOnTimeAfterDrift);\n            return false;\n        }\n        if (getRetrievedOn().isAfter(this.notOnOrAfter)) {\n            LOGGER.warn(\"Ticket is too late because it's retrieved on [{}] which is after [{}].\",\n                getRetrievedOn(), this.notOnOrAfter);\n            return false;\n        }\n        LOGGER.debug(\"WsFed Credential is validated for [{}] and [{}].\", expectedAudience, expectedIssuer);\n        return true;\n    }\n}\n","sourceCodeStart":62,"sourceCodeEnd":88,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/authentication/principal/WsFederationCredential.java#L62-L88","documentation":"isValid() enforces the token's NotOnOrAfter condition: the retrieval time must not be after this expiry instant. If getRetrievedOn().isAfter(notOnOrAfter), the WS-Federation assertion's validity window has already closed and the credential is rejected with this warning. This is the standard assertion-expiry check mirroring SAML conditions.","triggerScenarios":"Calling isValid(expectedAudience, expectedIssuer, timeDrift) on a token whose NotOnOrAfter timestamp (from the IdP assertion conditions) is earlier than the moment CAS retrieved/processed it — the assertion expired before validation.","commonSituations":"Severe clock skew (CAS server time far ahead of IdP time); replaying an old assertion after its short lifetime (e.g. 5-minute ADFS token lifetime elapsed); stalled requests or retries arriving after expiry.","solutions":["Synchronize clocks with NTP — an ahead-of-time CAS server makes valid tokens appear expired.","Have the user restart the login flow to obtain a fresh token instead of replaying the old one.","If tokens consistently expire in transit, reduce latency or slightly extend the IdP's token lifetime (ADFS TokenLifetime).","Confirm the configured time drift accounts for any deliberate skew you rely on."],"exampleFix":"// before: replaying a cached assertion from minutes ago\nString token = session.getAttribute(\"wsfedToken\");\n// after: always fetch a fresh token on each authentication\nString token = request.getParameter(\"wresult\");","handlingStrategy":"validation","validationCode":"if (Instant.now().isAfter(credential.getNotOnOrAfter())) {\n    throw new IllegalStateException(\"Assertion expired at \" + credential.getNotOnOrAfter() + \"; restart the login flow.\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always initiate a fresh redirect to the IdP when a previous assertion is expired.","Keep CAS server clocks accurate; a fast clock shortens perceived token lifetime.","If latency is chronic, increase the IdP TokenLifetime rather than disabling the check."],"tags":["wsfederation","token-expired","clock-skew","sso"],"backgroundTag":"jwt-token-expired","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}