diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
index 25a28c9969..011e7f3c3b 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
@@ -56,6 +56,9 @@ public class UrlBean {
}
public String getRegistrationAction() {
+ if (this.actionuri != null) {
+ return this.actionuri.toString();
+ }
return Urls.realmRegisterAction(baseURI, realm).toString();
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 751242295b..3f2a1034d9 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -2,7 +2,6 @@ package org.keycloak.models.utils;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel;
/**
@@ -11,10 +10,80 @@ import org.keycloak.models.RealmModel;
*/
public class DefaultAuthenticationFlows {
+ public static final String REGISTRATION_FLOW = "registration";
+ public static final String REGISTRATION_FORM_FLOW = "registration form";
public static final String BROWSER_FLOW = "browser";
- public static final String FORMS_FLOW = "forms";
+ public static final String LOGIN_FORMS_FLOW = "forms";
public static void addFlows(RealmModel realm) {
+ browserFlow(realm);
+ registrationFlow(realm);
+
+ }
+
+ public static void registrationFlow(RealmModel realm) {
+ AuthenticationFlowModel registrationFlow = new AuthenticationFlowModel();
+ registrationFlow.setAlias(REGISTRATION_FLOW);
+ registrationFlow.setDescription("registration flow");
+ registrationFlow.setProviderId("basic-flow");
+ registrationFlow = realm.addAuthenticationFlow(registrationFlow);
+
+ AuthenticationFlowModel registrationFormFlow = new AuthenticationFlowModel();
+ registrationFormFlow.setAlias(REGISTRATION_FORM_FLOW);
+ registrationFormFlow.setDescription("registration form");
+ registrationFormFlow.setProviderId("form-flow");
+ registrationFormFlow = realm.addAuthenticationFlow(registrationFormFlow);
+
+ AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(registrationFlow.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("registration-page-form");
+ execution.setPriority(10);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(true);
+ execution.setFlowId(registrationFormFlow.getId());
+ realm.addAuthenticatorExecution(execution);
+
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(registrationFormFlow.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("username-validation-action");
+ execution.setPriority(20);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(registrationFormFlow.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("profile-validation-action");
+ execution.setPriority(30);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(registrationFormFlow.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("password-validation-action");
+ execution.setPriority(40);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(registrationFormFlow.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("registration-user-creation");
+ execution.setPriority(50);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+
+ }
+
+ public static void browserFlow(RealmModel realm) {
AuthenticationFlowModel browser = new AuthenticationFlowModel();
browser.setAlias(BROWSER_FLOW);
browser.setDescription("browser based authentication");
@@ -39,7 +108,7 @@ public class DefaultAuthenticationFlows {
AuthenticationFlowModel forms = new AuthenticationFlowModel();
- forms.setAlias(FORMS_FLOW);
+ forms.setAlias(LOGIN_FORMS_FLOW);
forms.setDescription("Username, password, otp and other auth forms.");
forms.setProviderId("basic-flow");
forms = realm.addAuthenticationFlow(forms);
@@ -72,8 +141,5 @@ public class DefaultAuthenticationFlows {
execution.setUserSetupAllowed(true);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
-
- //
-
}
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index f5ff56e9b0..472f62f8c9 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -26,6 +26,7 @@ import org.keycloak.util.Time;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import java.util.List;
/**
* @author Bill Burke
@@ -167,10 +168,31 @@ public class AuthenticationProcessor {
Status status;
Response challenge;
Error error;
+ List currentExecutions;
- private Result(AuthenticationExecutionModel execution, Authenticator authenticator) {
+ private Result(AuthenticationExecutionModel execution, Authenticator authenticator, List currentExecutions) {
this.execution = execution;
this.authenticator = authenticator;
+ this.currentExecutions = currentExecutions;
+ }
+
+ @Override
+ public EventBuilder newEvent() {
+ AuthenticationProcessor.this.event = new EventBuilder(realm, session, connection);
+ return AuthenticationProcessor.this.event;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory) {
+ List executions = realm.getAuthenticationExecutions(execution.getParentFlow());
+ for (AuthenticationExecutionModel exe : executions) {
+ AuthenticatorFactory factory = (AuthenticatorFactory) getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, exe.getAuthenticator());
+ if (factory != null && factory.getReferenceCategory().equals(authenticatorCategory)) {
+ return exe.getRequirement();
+ }
+
+ }
+ return null;
}
@Override
@@ -434,8 +456,7 @@ public class AuthenticationProcessor {
throw new AuthException(Error.INTERNAL_ERROR);
}
if (flow.getProviderId() == null || flow.getProviderId().equals("basic-flow")) {
- DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this);
- flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
+ DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this, flow);
return flowExecution;
} else if (flow.getProviderId().equals("form-flow")) {
@@ -448,7 +469,6 @@ public class AuthenticationProcessor {
public Response authenticate() throws AuthException {
checkClientSession();
logger.debug("AUTHENTICATE");
- event.event(EventType.LOGIN);
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
@@ -490,7 +510,6 @@ public class AuthenticationProcessor {
resetFlow(clientSession);
return authenticate();
}
- event.event(EventType.LOGIN);
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
@@ -521,7 +540,6 @@ public class AuthenticationProcessor {
public Response authenticateOnly() throws AuthException {
checkClientSession();
- event.event(EventType.LOGIN);
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
@@ -587,8 +605,8 @@ public class AuthenticationProcessor {
}
- public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator) {
- return new Result(model, authenticator);
+ public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List executions) {
+ return new Result(model, authenticator, executions);
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
index 07a3fd1cb4..80654c797e 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
@@ -21,6 +21,7 @@ import javax.ws.rs.core.UriInfo;
*/
public interface AuthenticatorContext {
EventBuilder getEvent();
+ EventBuilder newEvent();
AuthenticationExecutionModel getExecution();
@@ -54,6 +55,8 @@ public interface AuthenticatorContext {
HttpRequest getHttpRequest();
BruteForceProtector getProtector();
+ AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory);
+
void success();
void failure(AuthenticationProcessor.Error error);
void failure(AuthenticationProcessor.Error error, Response response);
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
index 2e6178c034..f928bea1fd 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
@@ -9,24 +9,7 @@ import org.keycloak.provider.ProviderFactory;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public interface AuthenticatorFactory extends ProviderFactory, ConfiguredProvider {
+public interface AuthenticatorFactory extends ProviderFactory, ConfiguredProvider, ConfigurableAuthenticatorFactory {
Authenticator create();
- String getDisplayType();
-
- /**
- * General authenticator type, i.e. totp, password, cert.
- *
- * @return null if not a referencable type
- */
- String getReferenceType();
-
- boolean isConfigurable();
-
- /**
- * What requirement settings are allowed.
- *
- * @return
- */
- AuthenticationExecutionModel.Requirement[] getRequirementChoices();
}
diff --git a/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
new file mode 100755
index 0000000000..cca47c6d84
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
@@ -0,0 +1,27 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface ConfigurableAuthenticatorFactory {
+ String getDisplayType();
+
+ /**
+ * General authenticator type, i.e. totp, password, cert.
+ *
+ * @return null if not a referencable category
+ */
+ String getReferenceCategory();
+
+ boolean isConfigurable();
+
+ /**
+ * What requirement settings are allowed.
+ *
+ * @return
+ */
+ AuthenticationExecutionModel.Requirement[] getRequirementChoices();
+}
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 8aaaa643e3..54da8add29 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -1,11 +1,13 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel;
import javax.ws.rs.core.Response;
import java.util.Iterator;
+import java.util.List;
/**
* @author Bill Burke
@@ -15,11 +17,16 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
Response alternativeChallenge = null;
AuthenticationExecutionModel challengedAlternativeExecution = null;
boolean alternativeSuccessful = false;
- Iterator executions;
+ List executions;
+ Iterator executionIterator;
AuthenticationProcessor processor;
+ AuthenticationFlowModel flow;
- public DefaultAuthenticationFlow(AuthenticationProcessor processor) {
+ public DefaultAuthenticationFlow(AuthenticationProcessor processor, AuthenticationFlowModel flow) {
this.processor = processor;
+ this.flow = flow;
+ this.executions = processor.getRealm().getAuthenticationExecutions(flow.getId());
+ this.executionIterator = executions.iterator();
}
protected boolean isProcessed(AuthenticationExecutionModel model) {
@@ -34,25 +41,21 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processAction(String actionExecution) {
- while (executions.hasNext()) {
- AuthenticationExecutionModel model = executions.next();
+ while (executionIterator.hasNext()) {
+ AuthenticationExecutionModel model = executionIterator.next();
if (isProcessed(model)) {
AuthenticationProcessor.logger.debug("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
alternativeSuccessful = true;
continue;
}
- if (!model.getId().equals(actionExecution)) {
- if (model.isAutheticatorFlow()) {
- AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
- return authenticationFlow.processAction(actionExecution);
- } else {
- throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
- }
- } else { // we found the action
+ if (model.isAutheticatorFlow()) {
+ AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
+ return authenticationFlow.processAction(actionExecution);
+ } else if (model.getId().equals(actionExecution)) {
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
Authenticator authenticator = factory.create();
- AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator);
+ AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator, executions);
authenticator.action(result);
Response response = processResult(result);
if (response == null) return processFlow();
@@ -64,8 +67,8 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processFlow() {
- while (executions.hasNext()) {
- AuthenticationExecutionModel model = executions.next();
+ while (executionIterator.hasNext()) {
+ AuthenticationExecutionModel model = executionIterator.next();
if (isProcessed(model)) {
AuthenticationProcessor.logger.debug("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
@@ -132,7 +135,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
}
}
- AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator);
+ AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator, executions);
authenticator.authenticate(context);
Response response = processResult(context);
if (response != null) return response;
diff --git a/services/src/main/java/org/keycloak/authentication/FormAction.java b/services/src/main/java/org/keycloak/authentication/FormAction.java
index 90bd084187..b231829d7e 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAction.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAction.java
@@ -10,7 +10,7 @@ import org.keycloak.provider.Provider;
* @version $Revision: 1 $
*/
public interface FormAction extends Provider {
- void authenticate(FormContext context);
+ void authenticate(FormActionContext context);
boolean requiresUser();
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
diff --git a/services/src/main/java/org/keycloak/authentication/FormActionContext.java b/services/src/main/java/org/keycloak/authentication/FormActionContext.java
new file mode 100755
index 0000000000..a519009da9
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormActionContext.java
@@ -0,0 +1,12 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface FormActionContext extends AuthenticatorContext {
+ FormAuthenticator getFormAuthenticator();
+ AuthenticationExecutionModel getFormExecution();
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormActionFactory.java b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
index cbac58b611..866cdfaf98 100755
--- a/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
@@ -6,5 +6,5 @@ import org.keycloak.provider.ProviderFactory;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public interface FormActionFactory extends ProviderFactory {
+public interface FormActionFactory extends ProviderFactory, ConfigurableAuthenticatorFactory {
}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
index b8bb691348..ab4071708a 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -14,8 +14,10 @@ import org.keycloak.services.managers.BruteForceProtector;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
/**
* @author Bill Burke
@@ -23,26 +25,38 @@ import java.util.List;
*/
public class FormAuthenticationFlow implements AuthenticationFlow {
AuthenticationProcessor processor;
- AuthenticationExecutionModel execution;
+ AuthenticationExecutionModel formExecution;
+ private final List formActionExecutions;
+ private final FormAuthenticator formAuthenticator;
public FormAuthenticationFlow(AuthenticationProcessor processor, AuthenticationExecutionModel execution) {
this.processor = processor;
- this.execution = execution;
+ this.formExecution = execution;
+ formActionExecutions = processor.getRealm().getAuthenticationExecutions(execution.getFlowId());
+ formAuthenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
}
- private static class FormActionResult implements FormContext {
- AuthenticatorContext delegate;
- FormAuthenticator authenticator;
+ private class FormContext implements FormActionContext {
+ protected AuthenticatorContext delegate;
- FormActionResult(AuthenticatorContext delegate, FormAuthenticator authenticator) {
+ private FormContext(AuthenticatorContext delegate) {
this.delegate = delegate;
- this.authenticator = authenticator;
+ }
+
+ @Override
+ public EventBuilder newEvent() {
+ return delegate.newEvent();
}
@Override
public FormAuthenticator getFormAuthenticator() {
- return authenticator;
+ return formAuthenticator;
+ }
+
+ @Override
+ public AuthenticationExecutionModel getFormExecution() {
+ return formExecution;
}
@Override
@@ -135,6 +149,18 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
return delegate.getProtector();
}
+ @Override
+ public AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory) {
+ for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
+ FormActionFactory factory = (FormActionFactory) getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
+ if (factory != null && authenticatorCategory.equals(factory.getReferenceCategory())) {
+ return formActionExecution.getRequirement();
+ }
+
+ }
+ return null;
+ }
+
@Override
public void success() {
delegate.success();
@@ -191,19 +217,19 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
}
-
@Override
public Response processAction(String actionExecution) {
- if (!actionExecution.equals(execution.getId())) {
+ if (!actionExecution.equals(formExecution.getId())) {
throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
}
- FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
- for (AuthenticationExecutionModel formActionExecution : processor.getRealm().getAuthenticationExecutions(execution.getFlowId())) {
- FormAction action = processor.getSession().getProvider(FormAction.class, execution.getAuthenticator());
+ Map executionStatus = new HashMap<>();
+ List requiredActions = new LinkedList<>();
+ for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
+ FormAction action = processor.getSession().getProvider(FormAction.class, formActionExecution.getAuthenticator());
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
if (action.requiresUser() && authUser == null) {
- throw new AuthenticationProcessor.AuthException("form action: " + execution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
+ throw new AuthenticationProcessor.AuthException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
}
boolean configuredFor = false;
if (action.requiresUser() && authUser != null) {
@@ -211,23 +237,33 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
if (!configuredFor) {
if (formActionExecution.isRequired()) {
if (formActionExecution.isUserSetupAllowed()) {
- AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", execution.getAuthenticator());
- processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
- action.setRequiredActions(processor.getSession(), processor.getRealm(), authUser);
+ AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
+ executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
+ requiredActions.add(action);
continue;
} else {
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
}
} else if (formActionExecution.isOptional()) {
- processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+ executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
}
}
- FormActionResult result = new FormActionResult(processor.createAuthenticatorContext(formActionExecution, null), authenticator);
+ AuthenticatorContext delegate = processor.createAuthenticatorContext(formActionExecution, null, formActionExecutions);
+ FormActionContext result = new FormContext(delegate);
action.authenticate(result);
- return processResult(result, formActionExecution);
+ Response challenge = processResult(executionStatus, result, formActionExecution);
+ if (challenge != null) return challenge;
+ executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
+ }
+ // set status and required actions only if form is fully successful
+ for (Map.Entry entry : executionStatus.entrySet()) {
+ processor.getClientSession().setExecutionStatus(entry.getKey(), entry.getValue());
+ }
+ for (FormAction action : requiredActions) {
+ action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
}
return null;
@@ -236,42 +272,48 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processFlow() {
- FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
- AuthenticatorContext context = processor.createAuthenticatorContext(execution, null);
- authenticator.authenticate(context);
- return processResult(context, execution);
+ AuthenticatorContext delegate = processor.createAuthenticatorContext(formExecution, null, formActionExecutions);
+ FormActionContext result = new FormContext(delegate);
+ formAuthenticator.authenticate(result);
+ Map executionStatus = new HashMap<>();
+ Response response = processResult(executionStatus, result, formExecution);
+ for (Map.Entry entry : executionStatus.entrySet()) {
+ processor.getClientSession().setExecutionStatus(entry.getKey(), entry.getValue());
+ }
+ return response;
}
- public Response processResult(AuthenticatorContext result, AuthenticationExecutionModel execution) {
+ public Response processResult(Map executionStatus, AuthenticatorContext result, AuthenticationExecutionModel execution) {
AuthenticationProcessor.Status status = result.getStatus();
if (status == AuthenticationProcessor.Status.SUCCESS) {
+ executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
return null;
} else if (status == AuthenticationProcessor.Status.FAILED) {
AuthenticationProcessor.logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
processor.logFailure();
- processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
+ executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
if (result.getChallenge() != null) {
- return sendChallenge(result, execution);
+ return sendChallenge(result);
}
throw new AuthenticationProcessor.AuthException(result.getError());
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
- processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
- return sendChallenge(result, execution);
+ executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.CHALLENGE) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
- return sendChallenge(result, execution);
+ return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.FAILURE_CHALLENGE) {
AuthenticationProcessor.logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
processor.logFailure();
- processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
- return sendChallenge(result, execution);
+ executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+ return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
}
- processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
+ executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
return null;
} else {
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
@@ -281,8 +323,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
- public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
- processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
+ public Response sendChallenge(AuthenticatorContext result) {
+ processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, formExecution.getId());
return result.getChallenge();
}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
index 4804e92635..f33843ea6b 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
@@ -1,8 +1,11 @@
package org.keycloak.authentication;
+import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.Provider;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
+import java.util.List;
/**
* @author Bill Burke
@@ -10,5 +13,5 @@ import javax.ws.rs.core.Response;
*/
public interface FormAuthenticator extends Provider {
void authenticate(AuthenticatorContext context);
- Response createChallenge(FormContext context, String... errorMessages);
+ Response createChallenge(FormActionContext context, MultivaluedMap formData, List errorMessages);
}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
index 5b99f1a764..a388f43e3c 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
@@ -6,5 +6,5 @@ import org.keycloak.provider.ProviderFactory;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public interface FormAuthenticatorFactory extends ProviderFactory {
+public interface FormAuthenticatorFactory extends ProviderFactory, ConfigurableAuthenticatorFactory {
}
diff --git a/services/src/main/java/org/keycloak/authentication/FormContext.java b/services/src/main/java/org/keycloak/authentication/FormContext.java
deleted file mode 100755
index 24c46a71a7..0000000000
--- a/services/src/main/java/org/keycloak/authentication/FormContext.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.keycloak.authentication;
-
-/**
- * @author Bill Burke
- * @version $Revision: 1 $
- */
-public interface FormContext extends AuthenticatorContext {
- FormAuthenticator getFormAuthenticator();
-}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
index d45d2aeb9a..5889b8c2d1 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
@@ -49,7 +49,7 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
- public String getReferenceType() {
+ public String getReferenceCategory() {
return "cookie";
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
index 6e21a52c06..dd222fe954 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
@@ -51,7 +51,7 @@ public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
- public String getReferenceType() {
+ public String getReferenceCategory() {
return UserCredentialModel.TOTP;
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java
index 8310d64f8b..cd23b1ef63 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java
@@ -46,7 +46,7 @@ public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
- public String getReferenceType() {
+ public String getReferenceCategory() {
return UserCredentialModel.KERBEROS;
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
index 36b3d217a8..123c4b405f 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
@@ -51,7 +51,7 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
}
@Override
- public String getReferenceType() {
+ public String getReferenceCategory() {
return UserCredentialModel.PASSWORD;
}
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
new file mode 100755
index 0000000000..0fa38c566c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
@@ -0,0 +1,116 @@
+package org.keycloak.authentication.forms;
+
+import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.FormActionContext;
+import org.keycloak.authentication.FormAuthenticator;
+import org.keycloak.authentication.FormAuthenticatorFactory;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.resources.LoginActionsService;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFactory {
+
+ public static final String EXECUTION = "execution";
+ public static final String FIELD_PASSWORD_CONFIRM = "password-confirm";
+ public static final String FIELD_PASSWORD = "password";
+ public static final String FIELD_EMAIL = "email";
+ public static final String FIELD_USERNAME = "username";
+ public static final String FIELD_LAST_NAME = "lastName";
+ public static final String FIELD_FIRST_NAME = "firstName";
+ public static final String PROVIDER_ID = "registration-page-form";
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ LoginFormsProvider registrationPage = createForm(context, context.getExecution().getId());
+ context.challenge(registrationPage.createRegistration());
+
+ }
+ public URI getActionUrl(AuthenticatorContext context, String executionId, String code) {
+ return LoginActionsService.registrationFormProcessor(context.getUriInfo())
+ .queryParam(OAuth2Constants.CODE, code)
+ .queryParam(EXECUTION, executionId)
+ .build(context.getRealm().getName());
+ }
+
+
+ @Override
+ public Response createChallenge(FormActionContext context, MultivaluedMap formData, List errorMessages) {
+ LoginFormsProvider registrationPage = createForm(context, context.getFormExecution().getId());
+ if (formData != null) registrationPage.setFormData(formData);
+ if (errorMessages != null) {
+ registrationPage.setErrors(errorMessages);
+ }
+ return registrationPage.createRegistration();
+ }
+
+ public LoginFormsProvider createForm(AuthenticatorContext context, String executionId) {
+ AuthenticationExecutionModel.Requirement categoryRequirement = context.getCategoryRequirementFromCurrentFlow(UserCredentialModel.PASSWORD);
+ boolean passwordRequired = categoryRequirement != null && categoryRequirement != AuthenticationExecutionModel.Requirement.DISABLED;
+ String code = context.generateAccessCode();
+ URI actionUrl = getActionUrl(context, executionId, code);
+ return context.getSession().getProvider(LoginFormsProvider.class)
+ .setAttribute("passwordRequired", passwordRequired)
+ .setActionUri(actionUrl)
+ .setClientSessionCode(code);
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Registration Page";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return new AuthenticationExecutionModel.Requirement[0];
+ }
+
+ @Override
+ public FormAuthenticator create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPasswordValidation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPasswordValidation.java
new file mode 100755
index 0000000000..565d7d3a86
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPasswordValidation.java
@@ -0,0 +1,121 @@
+package org.keycloak.authentication.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.FormAction;
+import org.keycloak.authentication.FormActionContext;
+import org.keycloak.authentication.FormActionFactory;
+import org.keycloak.authentication.FormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RegistrationPasswordValidation implements FormAction, FormActionFactory {
+ public static final String PROVIDER_ID = "password-validation-action";
+
+ @Override
+ public void authenticate(FormActionContext context) {
+ MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
+ List errors = new ArrayList<>();
+ context.getEvent().detail(Details.REGISTER_METHOD, "form");
+ if (Validation.isBlank(formData.getFirst(RegistrationPage.FIELD_PASSWORD))) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD, Messages.MISSING_PASSWORD));
+ } else if (!formData.getFirst(RegistrationPage.FIELD_PASSWORD).equals(formData.getFirst(RegistrationPage.FIELD_PASSWORD_CONFIRM))) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD_CONFIRM, Messages.INVALID_PASSWORD_CONFIRM));
+ }
+ if (formData.getFirst(RegistrationPage.FIELD_PASSWORD) != null) {
+ PasswordPolicy.Error err = context.getRealm().getPasswordPolicy().validate(context.getRealm().isRegistrationEmailAsUsername() ? formData.getFirst(RegistrationPage.FIELD_EMAIL) : formData.getFirst(RegistrationPage.FIELD_USERNAME), formData.getFirst(RegistrationPage.FIELD_PASSWORD));
+ if (err != null)
+ errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD, err.getMessage(), err.getParameters()));
+ }
+
+ if (errors.size() > 0) {
+ context.getEvent().error(Errors.INVALID_REGISTRATION);
+ formData.remove(RegistrationPage.FIELD_PASSWORD);
+ formData.remove(RegistrationPage.FIELD_PASSWORD_CONFIRM);
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+ } else {
+ context.success();
+ }
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Password Validation";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return UserCredentialModel.PASSWORD;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return new AuthenticationExecutionModel.Requirement[0];
+ }
+
+ @Override
+ public FormAction create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfileValidation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfileValidation.java
new file mode 100755
index 0000000000..75d9cf36b9
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfileValidation.java
@@ -0,0 +1,136 @@
+package org.keycloak.authentication.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.FormAction;
+import org.keycloak.authentication.FormActionContext;
+import org.keycloak.authentication.FormActionFactory;
+import org.keycloak.authentication.FormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RegistrationProfileValidation implements FormAction, FormActionFactory {
+ public static final String PROVIDER_ID = "profile-validation-action";
+
+
+ @Override
+ public void authenticate(FormActionContext context) {
+ MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
+ List errors = new ArrayList<>();
+
+ context.getEvent().detail(Details.REGISTER_METHOD, "form");
+ String eventError = Errors.INVALID_REGISTRATION;
+
+ if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_FIRST_NAME)))) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME));
+ }
+
+ if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_LAST_NAME)))) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_LAST_NAME, Messages.MISSING_LAST_NAME));
+ }
+
+ String email = formData.getFirst(Validation.FIELD_EMAIL);
+ if (Validation.isBlank(email)) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL));
+ } else if (!Validation.isEmailValid(email)) {
+ formData.remove(Validation.FIELD_EMAIL);
+ context.getEvent().detail(Details.EMAIL, email);
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.INVALID_EMAIL));
+ }
+
+ if (context.getSession().users().getUserByEmail(email, context.getRealm()) != null) {
+ eventError = Errors.EMAIL_IN_USE;
+ formData.remove(Validation.FIELD_EMAIL);
+ context.getEvent().detail(Details.EMAIL, email);
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.EMAIL_EXISTS));
+ }
+
+ if (errors.size() > 0) {
+ context.getEvent().error(eventError);
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+
+ } else {
+ context.success();
+ }
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Profile Validation";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return new AuthenticationExecutionModel.Requirement[0];
+ }
+
+ @Override
+ public FormAction create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
new file mode 100755
index 0000000000..e218c54e15
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
@@ -0,0 +1,141 @@
+package org.keycloak.authentication.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.FormAction;
+import org.keycloak.authentication.FormActionContext;
+import org.keycloak.authentication.FormActionFactory;
+import org.keycloak.authentication.FormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.resources.AttributeFormDataProcessor;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RegistrationUserCreation implements FormAction, FormActionFactory {
+
+ public static final String PROVIDER_ID = "registration-user-creation";
+
+ @Override
+ public void authenticate(FormActionContext context) {
+ MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
+ String email = formData.getFirst(Validation.FIELD_EMAIL);
+ String username = formData.getFirst(RegistrationPage.FIELD_USERNAME);
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
+ username = formData.getFirst(RegistrationPage.FIELD_EMAIL);
+ }
+ context.getEvent().detail(Details.USERNAME, username)
+ .detail(Details.REGISTER_METHOD, "form")
+ .detail(Details.EMAIL, email)
+ ;
+ UserModel user = context.getSession().users().addUser(context.getRealm(), username);
+ user.setEnabled(true);
+ user.setFirstName(formData.getFirst("firstName"));
+ user.setLastName(formData.getFirst("lastName"));
+
+ user.setEmail(email);
+ context.getClientSession().setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
+ AttributeFormDataProcessor.process(formData, context.getRealm(), user);
+ context.setUser(user);
+ AuthenticationExecutionModel.Requirement categoryRequirement = context.getCategoryRequirementFromCurrentFlow(UserCredentialModel.PASSWORD);
+ boolean passwordRequired = categoryRequirement != null && categoryRequirement != AuthenticationExecutionModel.Requirement.DISABLED;
+ if (passwordRequired) {
+ String password = formData.getFirst(RegistrationPage.FIELD_PASSWORD);
+ UserCredentialModel credentials = new UserCredentialModel();
+ credentials.setType(CredentialRepresentation.PASSWORD);
+ credentials.setValue(password);
+
+ try {
+ context.getSession().users().updateCredential(context.getRealm(), user, UserCredentialModel.password(formData.getFirst("password")));
+ } catch (Exception me) {
+ user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+ }
+ }
+ context.getEvent().user(user);
+ context.success();
+ context.getEvent().success();
+ context.newEvent().event(EventType.LOGIN);
+ context.getEvent().client(context.getClientSession().getClient().getClientId())
+ .detail(Details.REDIRECT_URI, context.getClientSession().getRedirectUri())
+ .detail(Details.AUTH_METHOD, context.getClientSession().getAuthMethod());
+ String authType = context.getClientSession().getNote(Details.AUTH_TYPE);
+ if (authType != null) {
+ context.getEvent().detail(Details.AUTH_TYPE, authType);
+ }
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Registration User Creation";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return new AuthenticationExecutionModel.Requirement[0];
+ }
+
+ @Override
+ public FormAction create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUsernameValidation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUsernameValidation.java
new file mode 100755
index 0000000000..be406e3187
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUsernameValidation.java
@@ -0,0 +1,152 @@
+package org.keycloak.authentication.forms;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.FormAction;
+import org.keycloak.authentication.FormActionContext;
+import org.keycloak.authentication.FormActionFactory;
+import org.keycloak.authentication.FormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.validation.Validation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RegistrationUsernameValidation implements FormAction, FormActionFactory {
+
+
+ public static final String PROVIDER_ID = "username-validation-action";
+
+ @Override
+ public void authenticate(FormActionContext context) {
+ MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
+ List errors = new ArrayList<>();
+ context.getEvent().detail(Details.REGISTER_METHOD, "form");
+
+ String email = formData.getFirst(Validation.FIELD_EMAIL);
+ String username = formData.getFirst(RegistrationPage.FIELD_USERNAME);
+ context.getEvent().detail(Details.USERNAME, username);
+ context.getEvent().detail(Details.EMAIL, email);
+
+ String usernameField = RegistrationPage.FIELD_USERNAME;
+ if (context.getRealm().isRegistrationEmailAsUsername()) {
+ username = email;
+ context.getEvent().detail(Details.USERNAME, username);
+ usernameField = RegistrationPage.FIELD_EMAIL;
+ if (Validation.isBlank(email)) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL));
+ } else if (!Validation.isEmailValid(email)) {
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.INVALID_EMAIL));
+ formData.remove(Validation.FIELD_EMAIL);
+ }
+ if (errors.size() > 0) {
+ context.getEvent().error(Errors.INVALID_REGISTRATION);
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+ }
+ if (email != null && context.getSession().users().getUserByEmail(email, context.getRealm()) != null) {
+ context.getEvent().error(Errors.USERNAME_IN_USE);
+ formData.remove(Validation.FIELD_EMAIL);
+ errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.USERNAME_EXISTS));
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+ }
+ } else {
+ if (Validation.isBlank(username)) {
+ context.getEvent().error(Errors.INVALID_REGISTRATION);
+ errors.add(new FormMessage(RegistrationPage.FIELD_USERNAME, Messages.MISSING_USERNAME));
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+ }
+
+ }
+ if (context.getSession().users().getUserByUsername(username, context.getRealm()) != null) {
+ context.getEvent().error(Errors.USERNAME_IN_USE);
+ errors.add(new FormMessage(usernameField, Messages.USERNAME_EXISTS));
+ formData.remove(Validation.FIELD_USERNAME);
+ formData.remove(Validation.FIELD_EMAIL);
+ Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
+ context.challenge(challenge);
+ return;
+
+ }
+ context.success();
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Username Validation";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return new AuthenticationExecutionModel.Requirement[0];
+ }
+
+ @Override
+ public FormAction create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index e938732b97..c2a42eb89a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -249,6 +249,7 @@ public class AuthorizationEndpoint {
}
protected Response browserAuthentication(String accessCode) {
+ this.event.event(EventType.LOGIN);
List identityProviders = realm.getIdentityProviders();
for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isAuthenticateByDefault()) {
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index 190750f2f9..c672235f8c 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -29,7 +29,7 @@ import java.util.Set;
public class DefaultKeycloakSession implements KeycloakSession {
private final DefaultKeycloakSessionFactory factory;
- private final Map providers = new HashMap();
+ private final Map providers = new HashMap<>();
private final List closable = new LinkedList();
private final DefaultKeycloakTransactionManager transactionManager;
private RealmProvider model;
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 57b16a7785..9f223d2f7b 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -466,6 +466,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
protected Response browserAuthentication(ClientSessionModel clientSession, String errorMessage) {
+ this.event.event(EventType.LOGIN);
AuthenticationFlowModel flow = realmModel.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 44910cb73b..79d20d5011 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -130,6 +130,10 @@ public class LoginActionsService {
return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "authenticateForm");
}
+ public static UriBuilder registrationFormProcessor(UriInfo uriInfo) {
+ return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "processRegister");
+ }
+
public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getLoginActionsService");
}
@@ -269,6 +273,10 @@ public class LoginActionsService {
protected Response processAuthentication(String execution, ClientSessionModel clientSession) {
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
+ return processFlow(execution, clientSession, flowAlias);
+ }
+
+ protected Response processFlow(String execution, ClientSessionModel clientSession, String flowAlias) {
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
@@ -313,6 +321,12 @@ public class LoginActionsService {
return processAuthentication(execution, clientSession);
}
+ protected Response processRegistration(String execution, ClientSessionModel clientSession) {
+ String flowAlias = DefaultAuthenticationFlows.REGISTRATION_FLOW;
+ return processFlow(execution, clientSession, flowAlias);
+ }
+
+
/**
* protocol independent registration page entry point
@@ -322,7 +336,8 @@ public class LoginActionsService {
*/
@Path("registration")
@GET
- public Response registerPage(@QueryParam("code") String code) {
+ public Response registerPage(@QueryParam("code") String code,
+ @QueryParam("execution") String execution) {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
@@ -330,7 +345,7 @@ public class LoginActionsService {
}
Checks checks = new Checks();
- if (!checks.check(code)) {
+ if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
@@ -340,10 +355,7 @@ public class LoginActionsService {
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
- return session.getProvider(LoginFormsProvider.class)
- .setClientSessionCode(clientSessionCode.getCode())
- .setAttribute("passwordRequired", isPasswordRequired())
- .createRegistration();
+ return processRegistration(execution, clientSession);
}
@@ -353,143 +365,24 @@ public class LoginActionsService {
* @param code
* @return
*/
- @Path("request/registration")
+ @Path("registration")
@POST
- @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- public Response processRegister(@QueryParam("code") String code) {
+ public Response processRegister(@QueryParam("code") String code,
+ @QueryParam("execution") String execution) {
event.event(EventType.REGISTER);
Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
- if (!realm.isRegistrationAllowed()) {
+ if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
}
- MultivaluedMap formData = request.getDecodedFormParameters();
- String username = formData.getFirst(Validation.FIELD_USERNAME);
- String email = formData.getFirst(Validation.FIELD_EMAIL);
- if (realm.isRegistrationEmailAsUsername()) {
- username = email;
- formData.putSingle(AuthenticationManager.FORM_USERNAME, username);
- }
ClientSessionCode clientCode = checks.clientCode;
ClientSessionModel clientSession = clientCode.getClientSession();
- event.client(clientSession.getClient())
- .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
- .detail(Details.RESPONSE_TYPE, "code")
- .detail(Details.USERNAME, username)
- .detail(Details.EMAIL, email)
- .detail(Details.REGISTER_METHOD, "form");
- List requiredCredentialTypes = new LinkedList<>();
- boolean passwordRequired = isPasswordRequired();
- if (passwordRequired) {
- requiredCredentialTypes.add(CredentialRepresentation.PASSWORD);
- }
-
- // Validate here, so user is not created if password doesn't validate to passwordPolicy of current realm
- List errors = Validation.validateRegistrationForm(realm, formData, requiredCredentialTypes, realm.getPasswordPolicy());
-
-
- if (errors != null && !errors.isEmpty()) {
- event.error(Errors.INVALID_REGISTRATION);
- return session.getProvider(LoginFormsProvider.class)
- .setErrors(errors)
- .setFormData(formData)
- .setClientSessionCode(clientCode.getCode())
- .setAttribute("passwordRequired", isPasswordRequired())
- .createRegistration();
- }
-
- // Validate that user with this username doesn't exist in realm or any federation provider
- if (session.users().getUserByUsername(username, realm) != null) {
- event.error(Errors.USERNAME_IN_USE);
- return session.getProvider(LoginFormsProvider.class)
- .setError(Messages.USERNAME_EXISTS)
- .setFormData(formData)
- .setClientSessionCode(clientCode.getCode())
- .setAttribute("passwordRequired", isPasswordRequired())
- .createRegistration();
- }
-
- // Validate that user with this email doesn't exist in realm or any federation provider
- if (email != null && session.users().getUserByEmail(email, realm) != null) {
- event.error(Errors.EMAIL_IN_USE);
- return session.getProvider(LoginFormsProvider.class)
- .setError(Messages.EMAIL_EXISTS)
- .setFormData(formData)
- .setClientSessionCode(clientCode.getCode())
- .setAttribute("passwordRequired", isPasswordRequired())
- .createRegistration();
- }
-
- UserModel user = session.users().addUser(realm, username);
- user.setEnabled(true);
- user.setFirstName(formData.getFirst("firstName"));
- user.setLastName(formData.getFirst("lastName"));
-
- user.setEmail(email);
-
- if (passwordRequired) {
- UserCredentialModel credentials = new UserCredentialModel();
- credentials.setType(CredentialRepresentation.PASSWORD);
- credentials.setValue(formData.getFirst("password"));
-
- boolean passwordUpdateSuccessful;
- String passwordUpdateError = null;
- Object[] passwordUpdateErrorParameters = null;
- try {
- session.users().updateCredential(realm, user, UserCredentialModel.password(formData.getFirst("password")));
- passwordUpdateSuccessful = true;
- } catch (ModelException me) {
- passwordUpdateSuccessful = false;
- passwordUpdateError = me.getMessage();
- passwordUpdateErrorParameters = me.getParameters();
- } catch (Exception ape) {
- passwordUpdateSuccessful = false;
- passwordUpdateError = ape.getMessage();
- }
-
- // User already registered, but force him to update password
- if (!passwordUpdateSuccessful) {
- user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
- return session.getProvider(LoginFormsProvider.class)
- .setError(passwordUpdateError, passwordUpdateErrorParameters)
- .setClientSessionCode(clientCode.getCode())
- .createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
- }
- }
- clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
- AttributeFormDataProcessor.process(formData, realm, user);
-
- event.user(user).success();
- event = new EventBuilder(realm, session, clientConnection);
- clientSession.setAuthenticatedUser(user);
- AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
- AuthenticationProcessor processor = new AuthenticationProcessor();
- processor.setClientSession(clientSession)
- .setFlowId(flow.getId())
- .setConnection(clientConnection)
- .setEventBuilder(event)
- .setProtector(authManager.getProtector())
- .setRealm(realm)
- .setAction(AbstractFormAuthenticator.REGISTRATION_FORM_ACTION)
- .setSession(session)
- .setUriInfo(uriInfo)
- .setRequest(request);
-
- try {
- return processor.authenticate();
- } catch (Exception e) {
- return processor.handleBrowserException(e);
- }
- }
-
- public boolean isPasswordRequired() {
- AuthenticationFlowModel browserFlow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
- return AuthenticatorUtil.isRequired(realm, browserFlow.getId(), UsernamePasswordFormFactory.PROVIDER_ID);
+ return processRegistration(execution, clientSession);
}
/**
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 7f1edbd61e..a78baaa657 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -8,7 +8,6 @@ import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
@@ -139,8 +138,8 @@ public class AuthenticationManagementResource {
rep.setSubFlow(true);
}
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, execution.getAuthenticator());
- if (factory.getReferenceType() == null) continue;
- rep.setReferenceType(factory.getReferenceType());
+ if (factory.getReferenceCategory() == null) continue;
+ rep.setReferenceType(factory.getReferenceCategory());
rep.setConfigurable(factory.isConfigurable());
for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
rep.getRequirementChoices().add(choice.name());
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormActionFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormActionFactory
new file mode 100755
index 0000000000..47a023c5f1
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormActionFactory
@@ -0,0 +1,4 @@
+org.keycloak.authentication.forms.RegistrationPasswordValidation
+org.keycloak.authentication.forms.RegistrationProfileValidation
+org.keycloak.authentication.forms.RegistrationUserCreation
+org.keycloak.authentication.forms.RegistrationUsernameValidation
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormAuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormAuthenticatorFactory
new file mode 100755
index 0000000000..4be1af4c28
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.FormAuthenticatorFactory
@@ -0,0 +1 @@
+org.keycloak.authentication.forms.RegistrationPage
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
index e6851b5324..3d70977937 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -167,7 +167,6 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
.user(user != null ? user.getId() : null)
.detail(Details.USERNAME, username)
.detail(Details.EMAIL, email)
- .detail(Details.RESPONSE_TYPE, "code")
.detail(Details.REGISTER_METHOD, "form")
.detail(Details.REDIRECT_URI, DEFAULT_REDIRECT_URI);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
index cf2fd77d1a..7c3143aeb9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
@@ -80,7 +80,9 @@ public class RegisterTest {
registerPage.assertCurrent();
Assert.assertEquals("Username already exists.", registerPage.getError());
- events.expectRegister("test-user@localhost", "registerExistingUser@email").user((String) null).error("username_in_use").assertEvent();
+ events.expectRegister("test-user@localhost", "registerExistingUser@email")
+ .removeDetail(Details.EMAIL)
+ .user((String) null).error("username_in_use").assertEvent();
}
@Test
@@ -94,7 +96,10 @@ public class RegisterTest {
registerPage.assertCurrent();
Assert.assertEquals("Password confirmation doesn't match.", registerPage.getError());
- events.expectRegister("registerUserInvalidPasswordConfirm", "registerUserInvalidPasswordConfirm@email").user((String) null).error("invalid_registration").assertEvent();
+ events.expectRegister("registerUserInvalidPasswordConfirm", "registerUserInvalidPasswordConfirm@email")
+ .removeDetail(Details.USERNAME)
+ .removeDetail(Details.EMAIL)
+ .user((String) null).error("invalid_registration").assertEvent();
}
@Test
@@ -108,7 +113,10 @@ public class RegisterTest {
registerPage.assertCurrent();
Assert.assertEquals("Please specify password.", registerPage.getError());
- events.expectRegister("registerUserMissingPassword", "registerUserMissingPassword@email").user((String) null).error("invalid_registration").assertEvent();
+ events.expectRegister("registerUserMissingPassword", "registerUserMissingPassword@email")
+ .removeDetail(Details.USERNAME)
+ .removeDetail(Details.EMAIL)
+ .user((String) null).error("invalid_registration").assertEvent();
}
@Test
@@ -130,7 +138,10 @@ public class RegisterTest {
registerPage.assertCurrent();
Assert.assertEquals("Invalid password: minimum length 8.", registerPage.getError());
- events.expectRegister("registerPasswordPolicy", "registerPasswordPolicy@email").user((String) null).error("invalid_registration").assertEvent();
+ events.expectRegister("registerPasswordPolicy", "registerPasswordPolicy@email")
+ .removeDetail(Details.USERNAME)
+ .removeDetail(Details.EMAIL)
+ .user((String) null).error("invalid_registration").assertEvent();
registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "password", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
@@ -159,7 +170,10 @@ public class RegisterTest {
registerPage.assertCurrent();
Assert.assertEquals("Please specify username.", registerPage.getError());
- events.expectRegister(null, "registerUserMissingUsername@email").removeDetail("username").error("invalid_registration").assertEvent();
+ events.expectRegister(null, "registerUserMissingUsername@email")
+ .removeDetail(Details.USERNAME)
+ .removeDetail(Details.EMAIL)
+ .error("invalid_registration").assertEvent();
}
@Test
@@ -171,12 +185,15 @@ public class RegisterTest {
registerPage.register("firstName", "lastName", null, "registerUserMissingEmail", "password", "password");
registerPage.assertCurrent();
Assert.assertEquals("Please specify email.", registerPage.getError());
- events.expectRegister("registerUserMissingEmail", null).removeDetail("email").error("invalid_registration").assertEvent();
+ events.expectRegister("registerUserMissingEmail", null)
+ .removeDetail("email")
+ .error("invalid_registration").assertEvent();
registerPage.register("firstName", "lastName", "registerUserInvalidEmailemail", "registerUserInvalidEmail", "password", "password");
registerPage.assertCurrent();
Assert.assertEquals("Invalid email address.", registerPage.getError());
- events.expectRegister("registerUserInvalidEmail", "registerUserInvalidEmailemail").error("invalid_registration").assertEvent();
+ events.expectRegister("registerUserInvalidEmail", "registerUserInvalidEmailemail")
+ .error("invalid_registration").assertEvent();
}
@Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java
index 557fa7ef99..6a03f3e540 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/utils/CredentialHelper.java
@@ -5,7 +5,6 @@ import org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory;
import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -29,7 +28,7 @@ public class CredentialHelper {
public static void setCredentialRequirement(String type, RealmModel realm, AuthenticationExecutionModel.Requirement requirement) {
if (type.equals(CredentialRepresentation.TOTP)) {
String providerId = OTPFormAuthenticatorFactory.PROVIDER_ID;
- String flowAlias = DefaultAuthenticationFlows.FORMS_FLOW;
+ String flowAlias = DefaultAuthenticationFlows.LOGIN_FORMS_FLOW;
authenticationRequirement(realm, providerId, flowAlias, requirement);
} else if (type.equals(CredentialRepresentation.KERBEROS)) {
String providerId = SpnegoAuthenticatorFactory.PROVIDER_ID;
@@ -37,7 +36,7 @@ public class CredentialHelper {
authenticationRequirement(realm, providerId, flowAlias, requirement);
} else if (type.equals(CredentialRepresentation.PASSWORD)) {
String providerId = UsernamePasswordFormFactory.PROVIDER_ID;
- String flowAlias = DefaultAuthenticationFlows.FORMS_FLOW;
+ String flowAlias = DefaultAuthenticationFlows.LOGIN_FORMS_FLOW;
authenticationRequirement(realm, providerId, flowAlias, requirement);
}
}