apereo/cas · warning

Unable to locate existing session from the current token

Error message

Unable to locate existing session from the current token [{}]

What it means

A WebAuthn token was provided, but sessionManager.getSession(request, ...) for the token's derived identity returns empty, so the action cannot correlate the token to an existing server-side WebAuthn session and fails authentication. The token references a session CAS no longer holds.

Solutions

  1. Restart the WebAuthn MFA flow from the login page to obtain a fresh token and session.
  2. If running multiple CAS nodes, configure a shared session store (e.g. distributed sessions via Redis/Hazelcast) or sticky sessions so getSession can find the token's session.
  3. Increase the WebAuthn/session timeout configuration if legitimate users take too long to complete the ceremony.

Example fix

// before (in-memory session per node)
// no shared session config

// after: enable shared session storage across CAS nodes
# e.g. spring session backed by redis
spring.session.store-type=redis
spring.redis.host=redis.internal
Defensive patterns

Strategy: fallback

Validate before calling

// before starting the WebAuthn ceremony, confirm an active session exists server-side
if (!casSessionStore.exists(webAuthnSessionKey)) {
  // redirect user to re-initiate the MFA flow instead of failing
}

Try / catch

try {
    var session = sessionManager.getSession(request, WebAuthnCredential.from(credential));
} catch (Exception e) {
    // fall back to restarting the WebAuthn flow with a fresh token
}

Prevention

When it happens

Trigger: In doExecuteInternal, after building WebAuthnCredential from the token, WebAuthnCredential.from(credential) is used to look up the managed session; session.isEmpty() fires when the session expired, was evicted (restart or session store flush), or the token was issued by a different CAS node without shared session storage.

Common situations: User delays and the WebAuthn session times out; CAS restarted or session store (e.g. in-memory, Redis without proper persistence/TTL config) lost entries; load-balanced CAS without sticky sessions or shared session backend.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-webauthn-core-webflow/src/main/java/org/apereo/cas/webauthn/web/flow/WebAuthnValidateSessionCredentialTokenAction.java:55

    protected final TenantExtractor tenantExtractor;
    
    @Override
    protected @Nullable Event doExecuteInternal(final RequestContext requestContext) {
        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
        val token = request.getParameter("token");
        if (StringUtils.isBlank(token)) {
            LOGGER.warn("Missing web authn token from the request");
            return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE);
        }

        LOGGER.debug("Received web authn token [{}]", token);
        val credential = new WebAuthnCredential(token);
        WebUtils.putCredential(requestContext, credential);

        val session = sessionManager.getSession(request, WebAuthnCredential.from(credential));
        if (session.isEmpty()) {
            LOGGER.warn("Unable to locate existing session from the current token [{}]", token);
            return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE);
        }
        val result = webAuthnCredentialRepository.getUsernameForUserHandle(session.get());
        if (result.isEmpty()) {
            LOGGER.warn("Unable to locate user based on the given user handle");
            return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE);
        }
        val username = result.get();
        return FunctionUtils.doUnchecked(() -> {
            val authentication = DefaultAuthenticationBuilder.newInstance()
                .addCredential(credential)
                .setPrincipal(principalFactory.createPrincipal(username))
                .build();
            LOGGER.debug("Finalized authentication attempt based on [{}]", authentication);
            WebUtils.putAuthentication(authentication, requestContext);
            return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_FINALIZE);
        });
    }

View on GitHub (pinned to e7288fc434)