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

  1. Configure/extend the CredentialRepository so that existing users are allowed to register additional credentials (userExists + registration permissions consistent with your policy)
  2. If the user should not re-register, direct them to authentication instead of registration
  3. Remove the stale/duplicate credential record from the WebAuthn storage backend if it is orphaned, then retry registration
  4. 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

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


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)