apereo/cas · warning
Unable to locate user based on the given user handle
Error message
Unable to locate user based on the given user handle
What it means
After resolving the WebAuthn session, the action asks webAuthnCredentialRepository.getUsernameForUserHandle(userHandle) for the account tied to the WebAuthn user handle; when empty, the user record is unknown to the credential repository and the flow fails with authentication failure. The device's user handle maps to no known user.
Solutions
- Re-register the device: delete the stale registration and have the user enroll WebAuthn again so a valid user-handle/credential record exists in the repository.
- Check the configured WebAuthn credential repository backend and verify the user handle exists there (query its storage directly); migrate data if you switched backends.
- On clustered deployments, ensure all nodes share the same credential repository storage so getUsernameForUserHandle resolves consistently.
Example fix
// before (records lost after switching to in-memory repo) cas.authn.mfa.web-authn.core.username-attribute-name= # no persistent registration storage // after: persist registrations so user handles resolve cas.authn.mfa.web-authn.json.location=/etc/cas/config/webauthn-accounts.json
Defensive patterns
Strategy: validation
Validate before calling
// before the ceremony, verify the user handle resolves in the credential repository
Optional<String> username = webAuthnCredentialRepository.getUsernameForUserHandle(userHandle);
if (username.isEmpty()) {
// force re-registration of the WebAuthn device
} Try / catch
try {
result = webAuthnCredentialRepository.getUsernameForUserHandle(session.get());
} catch (Exception e) {
// repository backend unreachable: fall back to re-registration prompt
} Prevention
- Use a persistent WebAuthn credential repository (JSON/JDBC/LDAP) rather than in-memory.
- When migrating repository backends, copy existing registration records including user handles.
- In clusters, ensure all nodes point at the same credential repository storage.
When it happens
Trigger: doExecuteInternal calls webAuthnCredentialRepository.getUsernameForUserHandle(session.get()) and the returned Optional is empty — the user handle in the session has no matching entry in the configured WebAuthn credential repository (in-memory, JSON, database, LDAP, etc.).
Common situations: WebAuthn registration records deleted or purged while a session/token still references them; user re-registered on a different backend so the old user handle no longer resolves; switched credential repository backends (e.g. in-memory to JDBC) losing prior registrations; multi-node setup with non-shared credential storage.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Unable to locate registration record for
- No registration records could be found for
- Missing web authn token from the request
- Unable to locate existing session from the current token
- State [ : : ] does not have a matching transition for
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/7481c284b57a0801.
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:60
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)