diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionTokenHandler.java b/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionTokenHandler.java index 5b1efd6768..579f29e64b 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionTokenHandler.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionTokenHandler.java @@ -85,12 +85,19 @@ public class ResetCredentialsActionTokenHandler extends AbstractActionTokenHandl boolean firstBrokerLoginInProgress = (authenticationSession.getAuthNote(AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE) != null); if (firstBrokerLoginInProgress) { - UserModel linkingUser = AbstractIdpAuthenticator.getExistingUser(session, realm, authenticationSession); SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromAuthenticationSession(authenticationSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE); authenticationSession.setAuthNote(AbstractIdpAuthenticator.FIRST_BROKER_LOGIN_SUCCESS, serializedCtx.getIdentityProviderId()); + boolean hasExistingUserInfo = (authenticationSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) != null); + String username = ""; + + if (hasExistingUserInfo) { + UserModel linkingUser = AbstractIdpAuthenticator.getExistingUser(session, realm, authenticationSession); + username = linkingUser.getUsername(); + } + logger.debugf("Forget-password flow finished when authenticated user '%s' after first broker login with identity provider '%s'.", - linkingUser.getUsername(), serializedCtx.getIdentityProviderId()); + username, serializedCtx.getIdentityProviderId()); return LoginActionsService.redirectToAfterBrokerLoginEndpoint(session, realm, uriInfo, authenticationSession, true); } else { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java index eb7486bb4e..56cfc55889 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java @@ -453,6 +453,68 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa assertNumFederatedIdentities(existingUser, 1); } + /** + * Reset password during first broker login should work without `AbstractIdpAuthenticator.EXISTING_USER_INFO` set. + * + * This session note is only set by {@link org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator} + * or {@link org.keycloak.authentication.authenticators.broker.IdpDetectExistingBrokerUserAuthenticator}. However, + * the reset password feature should work without them. + * + * For more info see https://github.com/keycloak/keycloak/issues/26323 . + */ + @Test + public void testResetPasswordDuringFirstBrokerFlowWithoutExistingUserAuthenticator() throws InterruptedException { + RealmResource realm = adminClient.realm(bc.consumerRealmName()); + RealmRepresentation realmRep = realm.toRepresentation(); + + realmRep.setResetPasswordAllowed(true); + + realm.update(realmRep); + + updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin); + updateExecutions(AbstractBrokerTest::disableExistingUser); + String existingUser = createUser("consumer"); + + oauth.clientId("broker-app"); + loginPage.open(bc.consumerRealmName()); + + logInWithBroker(bc); + + assertEquals("Authenticate to link your account with " + bc.getIDPAlias(), loginPage.getInfoMessage()); + + try { + this.loginPage.findSocialButton(bc.getIDPAlias()); + Assert.fail("Not expected to see social button with " + bc.getIDPAlias()); + } catch (NoSuchElementException expected) { + } + + try { + this.loginPage.clickRegister(); + Assert.fail("Not expected to see register link"); + } catch (NoSuchElementException expected) { + } + + configureSMTPServer(); + + this.loginPage.resetPassword(); + this.loginPasswordResetPage.assertCurrent(); + this.loginPasswordResetPage.changePassword("consumer"); + assertEquals("You should receive an email shortly with further instructions.", this.loginPage.getSuccessMessage()); + assertEquals(1, MailServer.getReceivedMessages().length); + MimeMessage message = MailServer.getLastReceivedMessage(); + String linkFromMail = assertEmailAndGetUrl(MailServerConfiguration.FROM, USER_EMAIL, + "credentials", false); + + driver.navigate().to(linkFromMail.trim()); + + // Need to update password now + this.passwordUpdatePage.assertCurrent(); + this.passwordUpdatePage.changePassword("password", "password"); + + Assert.assertTrue(appPage.isCurrent()); + assertNumFederatedIdentities(existingUser, 1); + } + /** * Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testLinkAccountByReauthentication_forgetPassword_differentBrowser