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 98e960c..40a8999 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
@@ -1,6 +1,8 @@
package org.keycloak.models.utils;
import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import org.keycloak.models.AuthenticationExecutionModel;
@@ -36,7 +38,7 @@ public class DefaultAuthenticationFlows {
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
- if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
+ if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, false);
}
public static void migrateFlows(RealmModel realm) {
if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm, true);
@@ -44,7 +46,7 @@ public class DefaultAuthenticationFlows {
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
if (realm.getFlowByAlias(RESET_CREDENTIALS_FLOW) == null) resetCredentialsFlow(realm);
if (realm.getFlowByAlias(CLIENT_AUTHENTICATION_FLOW) == null) clientAuthFlow(realm);
- if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm);
+ if (realm.getFlowByAlias(FIRST_BROKER_LOGIN_FLOW) == null) firstBrokerLoginFlow(realm, true);
}
public static void registrationFlow(RealmModel realm) {
@@ -322,7 +324,7 @@ public class DefaultAuthenticationFlows {
realm.addAuthenticatorExecution(execution);
}
- public static void firstBrokerLoginFlow(RealmModel realm) {
+ public static void firstBrokerLoginFlow(RealmModel realm, boolean migrate) {
AuthenticationFlowModel firstBrokerLogin = new AuthenticationFlowModel();
firstBrokerLogin.setAlias(FIRST_BROKER_LOGIN_FLOW);
firstBrokerLogin.setDescription("Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account");
@@ -423,10 +425,19 @@ public class DefaultAuthenticationFlows {
execution = new AuthenticationExecutionModel();
execution.setParentFlow(verifyByReauthenticationAccountFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
- // TODO: read the requirement from browser authenticator
-// if (migrate && hasCredentialType(realm, RequiredCredentialModel.TOTP.getType())) {
-// execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
-// }
+
+ if (migrate) {
+ // Try to read OTP requirement from browser flow
+ AuthenticationFlowModel browserFlow = realm.getBrowserFlow();
+ List<AuthenticationExecutionModel> browserExecutions = new LinkedList<>();
+ KeycloakModelUtils.deepFindAuthenticationExecutions(realm, browserFlow, browserExecutions);
+ for (AuthenticationExecutionModel browserExecution : browserExecutions) {
+ if (browserExecution.getAuthenticator().equals("auth-otp-form")) {
+ execution.setRequirement(browserExecution.getRequirement());
+ }
+ }
+ }
+
execution.setAuthenticator("auth-otp-form");
execution.setPriority(20);
execution.setAuthenticatorFlow(false);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index c2fd73e..ad5997a 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -1,6 +1,8 @@
package org.keycloak.models.utils;
import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
@@ -16,6 +18,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.PemUtils;
@@ -31,6 +34,7 @@ import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -386,4 +390,24 @@ public final class KeycloakModelUtils {
realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE);
}
}
+
+
+ /**
+ * Recursively find all AuthenticationExecutionModel from specified flow or all it's subflows
+ *
+ * @param realm
+ * @param flow
+ * @param result input should be empty list. At the end will be all executions added to this list
+ */
+ public static void deepFindAuthenticationExecutions(RealmModel realm, AuthenticationFlowModel flow, List<AuthenticationExecutionModel> result) {
+ List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flow.getId());
+ for (AuthenticationExecutionModel execution : executions) {
+ if (execution.isAuthenticatorFlow()) {
+ AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
+ deepFindAuthenticationExecutions(realm, subFlow, result);
+ } else {
+ result.add(execution);
+ }
+ }
+ }
}