{"record":{"id":"fc456e32210b6b2c","repo":"apereo/cas","slug":"user-s-already-exists","errorCode":null,"errorMessage":"User %s already exists","messagePattern":"User (.+?) already exists","errorType":"exception","errorClass":"RegistrationFailedException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-webauthn-core/src/main/java/com/yubico/core/WebAuthnServer.java","lineNumber":165,"sourceCode":"\n                if (userStorage.userExists(registrationRequest.username())) {\n                    var permissionGranted = false;\n\n                    val isValidSession = registrationRequest.sessionToken().map(token ->\n                        sessionManager.isSessionForUser(request, registrationRequest.publicKeyCredentialCreationOptions().getUser().getId(), token)\n                    ).orElse(false);\n\n                    LOGGER.debug(\"Session token: [{}], valid session [{}]\", registrationRequest.sessionToken(), isValidSession);\n\n                    if (isValidSession) {\n                        permissionGranted = true;\n                        LOGGER.info(\"Session token accepted for user [{}]\", registrationRequest.publicKeyCredentialCreationOptions().getUser().getId());\n                    }\n\n                    LOGGER.debug(\"Permission granted to finish registration: [{}]\", permissionGranted);\n\n                    if (!permissionGranted) {\n                        throw new RegistrationFailedException(new IllegalArgumentException(\"User %s already exists\".formatted(registrationRequest.username())));\n                    }\n                }\n\n                return Either.right(\n                    new SuccessfulRegistrationResult(\n                        registrationRequest,\n                        registrationResponse,\n                        addRegistration(\n                            registrationRequest.publicKeyCredentialCreationOptions().getUser(),\n                            registrationRequest.credentialNickname(),\n                            registration\n                        ),\n                        registration.isAttestationTrusted() || relyingParty.isAllowUntrustedAttestation(),\n                        sessionManager.createSession(request, registrationRequest.publicKeyCredentialCreationOptions().getUser().getId())\n                    )\n                );\n            } catch (final RegistrationFailedException e) {\n                LOGGER.debug(\"Finishing registration failed with: [{}]\", responseJson, e);","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-webauthn-core/src/main/java/com/yubico/core/WebAuthnServer.java#L147-L183","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before (repository forbids known users)\npublic boolean userExists(String username) { return findByUsername(username).isPresent(); }\n// after (allow additional credentials for existing users)\npublic boolean userExists(String username) { return findByUsername(username).isPresent(); }\n// and ensure registration permission logic allows re-enrollment:\npermissionGranted = !userExists(username) || allowAdditionalCredentials(username);","handlingStrategy":"try-catch","validationCode":"boolean alreadyRegistered = credentialRepository.userExists(registrationRequest.username());\nif (alreadyRegistered && !allowReRegistration) {\n    // route to authentication or account recovery before calling finishRegistration\n}","typeGuard":null,"tryCatchPattern":"try {\n    return server.finishRegistration(registrationRequest, responseJSON);\n} catch (RegistrationFailedException e) {\n    if (e.getMessage().contains(\"already exists\")) {\n        // treat as duplicate enrollment: prompt authentication or allow additional credentials\n    }\n}","preventionTips":["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"],"tags":["webauthn","registration","duplicate-user"],"backgroundTag":"record-already-exists","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}