{"record":{"id":"0aa6d3d1f80755da","repo":"SonarSource/sonarqube","slug":"user-with-login-tried-to-login-with-email","errorCode":null,"errorMessage":"User with login '{}' tried to login with email '{}' which doesn't match the email on record '{}'","messagePattern":"User with login '(.+?)' tried to login with email '(.+?)' which doesn't match the email on record '(.+?)'","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java","lineNumber":153,"sourceCode":"\n    if (BITBUCKET_PROVIDER.equals(key)) {\n      validateExternalIdToAvoidLoginRecycling(userIdentity, user, source);\n    }\n\n    return true;\n  }\n\n  private static void validateEmailToAvoidLoginRecycling(UserIdentity userIdentity, UserDto user, Source source) {\n    String dbEmail = user.getEmail();\n\n    if (dbEmail == null) {\n      return;\n    }\n\n    String externalEmail = userIdentity.getEmail();\n\n    if (!dbEmail.equalsIgnoreCase(externalEmail)) {\n      LOGGER.warn(\"User with login '{}' tried to login with email '{}' which doesn't match the email on record '{}'\", userIdentity.getProviderLogin(), externalEmail, dbEmail);\n      throw failAuthenticationException(userIdentity, source);\n    }\n  }\n\n  private static void validateExternalIdToAvoidLoginRecycling(UserIdentity userIdentity, UserDto user, Source source) {\n    if (!userExternalIdMatchesLogin(user)) {\n      LOGGER.warn(\"User '{}' matched by external login, but the stored external ID differs - possible recycled external username\", userIdentity.getProviderLogin());\n      throw failAuthenticationException(userIdentity, source);\n    }\n  }\n\n  private static boolean userExternalIdMatchesLogin(UserDto user) {\n    return Objects.equals(user.getExternalId(), user.getExternalLogin());\n  }\n\n  private static AuthenticationException failAuthenticationException(UserIdentity userIdentity, Source source) {\n    String message = String.format(\"Failed to authenticate with login '%s'\", userIdentity.getProviderLogin());\n    return authException(userIdentity, source, message, message);","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java#L135-L171","documentation":"UserRegistrarImpl.validateEmailToAvoidLoginRecycling throws an authentication failure when an identity provider login (e.g. GitHub/ALM SSO) matches an existing SonarQube user whose stored email differs from the email presented by the provider. It exists to prevent login recycling: someone re-registering a provider login must not silently take over an account tied to a different email. The warning log (with provider login, presented email, and recorded email) precedes a failAuthenticationException, aborting authentication for that user.","triggerScenarios":"A user authenticates via an ALM/identity provider whose providerLogin matches an existing SonarQube user account, but the email claim in the UserIdentity does not case-insensitively equal the email stored in the DB (user.getEmail()); validateAlmSpecificData calls validateEmailToAvoidLoginRecycling during user provisioning.","commonSituations":"User changed their email at the identity provider but the SonarQube account still holds the old address; two different provider accounts share a login after a provider re-provisioned accounts; admin manually edited the SonarQube email out of sync with the IdP; SCIM/provisioning updated email upstream but not in SonarQube.","solutions":["Update the user's email in SonarQube (Users administration page or update web service) to match the email now returned by the identity provider.","Have the user correct their email at the identity provider (GitHub/GitLab/Azure DevOps profile) back to the recorded value if the provider change was unintended.","If the provider login was genuinely recycled by a different person, deactivate or delete the stale SonarQube account and let the new user register fresh.","Check identity-provisioning (SCIM/SAML/JIT) settings so email updates propagate to SonarQube instead of diverging."],"exampleFix":"// before (admin panel: email mismatch blocks login)\nuser email: alice@old-company.com, IdP email: alice@new-company.com\n// after\nUPDATE via Administration > Users > alice > Edit: set email to alice@new-company.com (matching the IdP), or update upstream and re-provision.","handlingStrategy":"validation","validationCode":"// Admin/automation check before enabling ALM login for a user\nboolean emailMatches = userStoredEmail.equalsIgnoreCase(identityProviderEmail);\nif (!emailMatches) { updateUserEmail(userId, identityProviderEmail); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep SonarQube user emails synchronized with the identity provider (enable SCIM or periodic sync).","When users change their IdP email, update the SonarQube account in the same change window.","Avoid hand-editing user emails in SonarQube when ALM/SSO provisioning is active."],"tags":["authentication","login-recycling","email-mismatch","alm-integration"],"backgroundTag":"authentication-required","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}