Registration flow fixed (#23064)

Closes #21514


Co-authored-by: Vilmos Nagy <vilmos.nagy@outlook.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Marek Posolda <mposolda@gmail.com>
This commit is contained in:
Marek Posolda 2023-09-08 08:05:05 +02:00 committed by GitHub
parent bc31fde4c0
commit 506e2537ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 4 deletions

View file

@ -58,7 +58,7 @@ public interface FormContext {
AuthenticationExecutionModel getExecution(); AuthenticationExecutionModel getExecution();
/** /**
* Current user attached to this flow. It can return null if no uesr has been identified yet * Current user attached to this flow. It can return null if no user has been identified yet
* *
* @return * @return
*/ */

View file

@ -38,6 +38,7 @@ public interface Errors {
String USER_DISABLED = "user_disabled"; String USER_DISABLED = "user_disabled";
String USER_TEMPORARILY_DISABLED = "user_temporarily_disabled"; String USER_TEMPORARILY_DISABLED = "user_temporarily_disabled";
String INVALID_USER_CREDENTIALS = "invalid_user_credentials"; String INVALID_USER_CREDENTIALS = "invalid_user_credentials";
String DIFFERENT_USER_AUTHENTICATING = "different_user_authenticating";
String DIFFERENT_USER_AUTHENTICATED = "different_user_authenticated"; String DIFFERENT_USER_AUTHENTICATED = "different_user_authenticated";
String USER_DELETE_ERROR = "user_delete_error"; String USER_DELETE_ERROR = "user_delete_error";

View file

@ -18,6 +18,8 @@
package org.keycloak.authentication.forms; package org.keycloak.authentication.forms;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.AuthenticationFlowException;
import org.keycloak.authentication.FormAction; import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory; import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormContext; import org.keycloak.authentication.FormContext;
@ -106,11 +108,13 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
@Override @Override
public void buildPage(FormContext context, LoginFormsProvider form) { public void buildPage(FormContext context, LoginFormsProvider form) {
checkNotOtherUserAuthenticating(context);
} }
@Override @Override
public void success(FormContext context) { public void success(FormContext context) {
checkNotOtherUserAuthenticating(context);
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters(); MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
String email = formData.getFirst(UserModel.EMAIL); String email = formData.getFirst(UserModel.EMAIL);
@ -148,6 +152,14 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
} }
} }
private void checkNotOtherUserAuthenticating(FormContext context) {
if (context.getUser() != null) {
// the user probably did some back navigation in the browser, hitting this page in a strange state
context.getEvent().detail(Details.EXISTING_USER, context.getUser().getUsername());
throw new AuthenticationFlowException(AuthenticationFlowError.GENERIC_AUTHENTICATION_ERROR, Errors.DIFFERENT_USER_AUTHENTICATING, Messages.EXPIRED_ACTION);
}
}
@Override @Override
public boolean requiresUser() { public boolean requiresUser() {
return false; return false;

View file

@ -30,17 +30,20 @@ import org.keycloak.authentication.forms.RegistrationRecaptcha;
import org.keycloak.authentication.forms.RegistrationTermsAndConditions; import org.keycloak.authentication.forms.RegistrationTermsAndConditions;
import org.keycloak.authentication.forms.RegistrationUserCreation; import org.keycloak.authentication.forms.RegistrationUserCreation;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
import org.keycloak.testsuite.pages.RegisterPage; import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.VerifyEmailPage; import org.keycloak.testsuite.pages.VerifyEmailPage;
import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
@ -79,12 +82,18 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
@Page @Page
protected LoginPage loginPage; protected LoginPage loginPage;
@Page
protected ErrorPage errorPage;
@Page @Page
protected RegisterPage registerPage; protected RegisterPage registerPage;
@Page @Page
protected VerifyEmailPage verifyEmailPage; protected VerifyEmailPage verifyEmailPage;
@Page
protected LoginPasswordResetPage resetPasswordPage;
@Rule @Rule
public GreenMailRule greenMail = new GreenMailRule(); public GreenMailRule greenMail = new GreenMailRule();
@ -669,7 +678,7 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
.removeDetail(Details.EMAIL) .removeDetail(Details.EMAIL)
.error("invalid_registration").assertEvent(); .error("invalid_registration").assertEvent();
} finally { } finally {
configureRegistrationFlowWithCustomRegistrationPageForm(UUID.randomUUID().toString()); revertRegistrationFlow();
} }
} }
@ -697,6 +706,34 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
} }
} }
@Test
public void testRegisterShouldFailBeforeUserCreationWhenUserIsInContext() {
loginPage.open();
loginPage.clickRegister();
registerPage.clickBackToLogin();
loginPage.assertCurrent(testRealm().toRepresentation().getRealm());
loginPage.resetPassword();
resetPasswordPage.assertCurrent();
resetPasswordPage.changePassword("test-user@localhost");
driver.navigate().back();
driver.navigate().back();
events.clear();
driver.navigate().back();
errorPage.assertCurrent();
Assert.assertEquals("Action expired. Please continue with login now.", errorPage.getError());
events.expectRegister("registerUserMissingTermsAcceptance", "registerUserMissingTermsAcceptance@email")
.removeDetail(Details.USERNAME)
.removeDetail(Details.EMAIL)
.removeDetail(Details.REGISTER_METHOD)
.detail(Details.EXISTING_USER, "test-user@localhost")
.detail(Details.AUTHENTICATION_ERROR_DETAIL, Errors.DIFFERENT_USER_AUTHENTICATING)
.error(Errors.GENERIC_AUTHENTICATION_ERROR).assertEvent();
}
protected RealmAttributeUpdater configureRealmRegistrationEmailAsUsername(final boolean value) { protected RealmAttributeUpdater configureRealmRegistrationEmailAsUsername(final boolean value) {
return getRealmAttributeUpdater().setRegistrationEmailAsUsername(value); return getRealmAttributeUpdater().setRegistrationEmailAsUsername(value);
} }
@ -784,4 +821,11 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
); );
} }
private void revertRegistrationFlow() {
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session)
.selectFlow(DefaultAuthenticationFlows.REGISTRATION_FLOW)
.defineAsRegistrationFlow()
);
}
} }