apereo/cas · error · RegistrationFailedException
User already exists
Error message
User %s already exists
What it means
During WebAuthn registration finalization, WebAuthnServer.finishRegistration() checks whether the claimed username is already taken/registered. When permissionGranted is false (the username already exists per the configured CredentialRepository), it throws RegistrationFailedException wrapping an IllegalArgumentException('User %s already exists').
Solutions
- Configure/extend the CredentialRepository so that existing users are allowed to register additional credentials (userExists + registration permissions consistent with your policy)
- If the user should not re-register, direct them to authentication instead of registration
- Remove the stale/duplicate credential record from the WebAuthn storage backend if it is orphaned, then retry registration
- Guard against replayed finishRegistration() calls (fresh session token per registration attempt)
Example fix
// before (repository forbids known users)
public boolean userExists(String username) { return findByUsername(username).isPresent(); }
// after (allow additional credentials for existing users)
public boolean userExists(String username) { return findByUsername(username).isPresent(); }
// and ensure registration permission logic allows re-enrollment:
permissionGranted = !userExists(username) || allowAdditionalCredentials(username); Defensive patterns
Strategy: try-catch
Validate before calling
boolean alreadyRegistered = credentialRepository.userExists(registrationRequest.username());
if (alreadyRegistered && !allowReRegistration) {
// route to authentication or account recovery before calling finishRegistration
} Try / catch
try {
return server.finishRegistration(registrationRequest, responseJSON);
} catch (RegistrationFailedException e) {
if (e.getMessage().contains("already exists")) {
// treat as duplicate enrollment: prompt authentication or allow additional credentials
}
} Prevention
- Configure the CredentialRepository to explicitly permit additional credentials per user when desired
- Use fresh, single-use session tokens for each registration attempt to avoid replays
- Clean up orphaned credentials in the storage backend after account lifecycle events
- Distinguish registration vs authentication flows in the client so existing users authenticate instead of re-registering
When it happens
Trigger: Calling finishRegistration() with a RegistrationRequest whose username is already registered in the underlying credential repository while the flow does not permit re-registration/overwriting, so permissionGranted evaluates false.
Common situations: User attempts to register a second device under an account that already exists in the WebAuthn credential store without allowing additional credentials; stale client session re-submitting an old registration; credential repository persistence layer still holds a record from a previous enrollment; duplicate registration requests replayed after a retry.
Related errors
- Unable to locate registration record for
- No registration records could be found for
- Unauthorized account registration attempt for id
- Authorization of OTP token
- Account registration is not verified for
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/fc456e32210b6b2c.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-webauthn-core/src/main/java/com/yubico/core/WebAuthnServer.java:165
if (userStorage.userExists(registrationRequest.username())) {
var permissionGranted = false;
val isValidSession = registrationRequest.sessionToken().map(token ->
sessionManager.isSessionForUser(request, registrationRequest.publicKeyCredentialCreationOptions().getUser().getId(), token)
).orElse(false);
LOGGER.debug("Session token: [{}], valid session [{}]", registrationRequest.sessionToken(), isValidSession);
if (isValidSession) {
permissionGranted = true;
LOGGER.info("Session token accepted for user [{}]", registrationRequest.publicKeyCredentialCreationOptions().getUser().getId());
}
LOGGER.debug("Permission granted to finish registration: [{}]", permissionGranted);
if (!permissionGranted) {
throw new RegistrationFailedException(new IllegalArgumentException("User %s already exists".formatted(registrationRequest.username())));
}
}
return Either.right(
new SuccessfulRegistrationResult(
registrationRequest,
registrationResponse,
addRegistration(
registrationRequest.publicKeyCredentialCreationOptions().getUser(),
registrationRequest.credentialNickname(),
registration
),
registration.isAttestationTrusted() || relyingParty.isAllowUntrustedAttestation(),
sessionManager.createSession(request, registrationRequest.publicKeyCredentialCreationOptions().getUser().getId())
)
);
} catch (final RegistrationFailedException e) {
LOGGER.debug("Finishing registration failed with: [{}]", responseJson, e);View on GitHub (pinned to e7288fc434)