On invalid submission, IdpUsernamePasswordForm sends back the user to the standard UsernamePasswordForm template

Signed-off-by: Réda Housni Alaoui <reda-alaoui@hey.com>
This commit is contained in:
Réda Housni Alaoui 2024-01-05 14:18:06 +01:00 committed by Pedro Igor
parent fa23c0b4c6
commit 3c05c123ea
2 changed files with 71 additions and 1 deletions

View file

@ -17,6 +17,7 @@
package org.keycloak.authentication.authenticators.broker;
import jakarta.ws.rs.core.MultivaluedHashMap;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.AuthenticationFlowException;
@ -25,6 +26,7 @@ import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages;
@ -50,6 +52,20 @@ public class IdpUsernamePasswordForm extends UsernamePasswordForm {
.createLoginUsernamePassword();
}
@Override
protected Response challenge(AuthenticationFlowContext context, String error, String field) {
LoginFormsProvider form = setupForm(context, new MultivaluedHashMap<>(), getExistingUser(context))
.setExecution(context.getExecution().getId());
if (error != null) {
if (field != null) {
form.addError(new FormMessage(field, error));
} else {
form.setError(error);
}
}
return createLoginForm(form);
}
@Override
protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
Optional<UserModel> existingUser = getExistingUser(context);

View file

@ -214,6 +214,51 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa
assertNumFederatedIdentities(existingUser, 1);
}
@Test
public void testLinkAccountByReauthenticationWithWrongPassword() {
updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin);
updateExecutions(AbstractBrokerTest::disableExistingUser);
Runnable revertRegistrationAllowedModification = toggleRegistrationAllowed(bc.consumerRealmName(), true);
try {
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) {
}
loginPage.login("consumer", "wrongpassword");
Assert.assertTrue(loginPage.isCurrent(bc.consumerRealmName()));
assertNumFederatedIdentities(existingUser, 0);
assertEquals("Invalid username or password.", loginPage.getInputError());
try {
this.loginPage.clickRegister();
Assert.fail("Not expected to see register link");
} catch (NoSuchElementException expected) {
}
} finally {
revertRegistrationAllowedModification.run();
}
}
/**
* KEYCLOAK-12870
*/
@ -1278,4 +1323,13 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa
assertNumFederatedIdentities(realm.users().search(bc.getUserLogin()).get(0).getId(), 1);
}
}
private Runnable toggleRegistrationAllowed(String realmName, boolean registrationAllowed) {
RealmResource consumerRealm = adminClient.realm(realmName);
RealmRepresentation realmRepresentation = consumerRealm.toRepresentation();
boolean genuineValue = realmRepresentation.isRegistrationAllowed();
realmRepresentation.setRegistrationAllowed(registrationAllowed);
consumerRealm.update(realmRepresentation);
return () -> toggleRegistrationAllowed(realmName, genuineValue);
}
}