apereo/cas · warning

Security token ticket

Error message

Security token ticket [{}] is not found or has expired

What it means

The WS-Federation IdP controller looks up the security-token ticket (RST/wresult reference prefixed with the SecurityTokenTicket prefix) in the ticket registry; if the ticket is absent or expired the controller returns null and the token cannot be obtained. This is a warn log, not an exception.

Solutions

  1. Raise the security token ticket expiration (cas.authn.wsfed-idp ticket time-to-kill) to cover your flow length
  2. Ensure all CAS nodes share the same ticket registry backend
  3. Have the user restart the sign-in flow instead of replaying stale URLs
  4. Enable ticket-registry logging to check whether the ticket was evicted or expired

Example fix

// before (default short TTL)
cas.authn.wsfed-idp.security-token-tickets.time-to-kill-in-seconds=60
// after
cas.authn.wsfed-idp.security-token-tickets.time-to-kill-in-seconds=600
Defensive patterns

Strategy: retry

Try / catch

if (token == null) { /* restart the WS-Federation flow to obtain a fresh ticket */ }

Prevention

When it happens

Trigger: getSecurityTokenFromRequest() extracts a wctx/ticket id starting with SecurityTokenTicket.PREFIX, calls ticketRegistry.getTicket(sts, SecurityTokenTicket.class), and gets null or an expired ticket.

Common situations: Long-running browser flow exceeded the security-token-ticket expiration time; ticket registry (e.g. clustered Redis/Memcached) lost the ticket or nodes point at different stores; user replayed an old bookmarked URL; ticket cleanup ran between request steps.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/6ec1b648963b1e00. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-ws-idp/src/main/java/org/apereo/cas/ws/idp/web/BaseWSFederationRequestController.java:85

    /**
     * Gets security token from request.
     *
     * @param request the request
     * @return the security token from request
     */
    protected SecurityToken getSecurityTokenFromRequest(final HttpServletRequest request) {
        val cookieValue = configContext.getTicketGrantingTicketCookieGenerator().retrieveCookieValue(request);
        if (StringUtils.isNotBlank(cookieValue)) {
            val tgt = configContext.getTicketRegistry().getTicket(cookieValue, TicketGrantingTicket.class);
            if (tgt != null) {
                val sts = tgt.getDescendantTickets().stream()
                    .filter(t -> t.startsWith(SecurityTokenTicket.PREFIX))
                    .findFirst()
                    .orElse(null);
                if (StringUtils.isNotBlank(sts)) {
                    val stt = configContext.getTicketRegistry().getTicket(sts, SecurityTokenTicket.class);
                    if (stt == null || stt.isExpired()) {
                        LOGGER.warn("Security token ticket [{}] is not found or has expired", sts);
                        return null;
                    }
                    if (stt.getSecurityToken() == null || stt.getSecurityToken().isExpired()) {
                        LOGGER.warn("Security token linked to ticket [{}] has expired", sts);
                        return null;
                    }
                    return stt.getSecurityToken();
                }
            }
        }
        return null;
    }


    protected boolean shouldRenewAuthentication(final WSFederationRequest fedRequest,
                                                final HttpServletRequest request) {
        if (StringUtils.isBlank(fedRequest.wfresh()) || !NumberUtils.isCreatable(fedRequest.wfresh())) {
            return false;

View on GitHub (pinned to e7288fc434)