keycloak-aplcache

Merge pull request #1413 from patriot1burke/master refactor

6/24/2015 10:29:26 PM

Changes

Details

diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.4.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.4.0.xml
index 3134b12..ff0311d 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.4.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.4.0.xml
@@ -12,6 +12,16 @@
                 <constraints nullable="true"/>
             </column>
         </addColumn>
+        <addColumn tableName="AUTHENTICATION_FLOW">
+            <column name="PROVIDER_ID" type="VARCHAR(36)" defaultValue="basic-flow">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+        <addColumn tableName="AUTHENTICATION_EXECUTION">
+            <column name="AUTH_FLOW_ID" type="VARCHAR(36)">
+                <constraints nullable="true"/>
+            </column>
+        </addColumn>
         <dropColumn tableName="AUTHENTICATOR"  columnName="PROVIDER_ID"/>
         <renameTable oldTableName="AUTHENTICATOR_CONFIG" newTableName="AUTHENTICATOR_CONFIG_ENTRY"/>
         <renameTable oldTableName="AUTHENTICATOR" newTableName="AUTHENTICATOR_CONFIG"/>
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
index 15a599f..3b959d9 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
@@ -22,6 +22,7 @@ public class AuthenticationExecutionModel implements Serializable {
     private String id;
     private String authenticatorConfig;
     private String authenticator;
+    private String flowId;
     private boolean autheticatorFlow;
     private Requirement requirement;
     private boolean userSetupAllowed;
@@ -85,6 +86,19 @@ public class AuthenticationExecutionModel implements Serializable {
     }
 
     /**
+     * If this execution is a flow, this is the flowId pointing to an AuthenticationFlowModel
+     *
+     * @return
+     */
+    public String getFlowId() {
+        return flowId;
+    }
+
+    public void setFlowId(String flowId) {
+        this.flowId = flowId;
+    }
+
+    /**
      * Is the referenced authenticator a flow?
      *
      * @return
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
index 194a6b3..db5970a 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
@@ -12,6 +12,7 @@ public class AuthenticationFlowModel implements Serializable {
     private String id;
     private String alias;
     private String description;
+    private String providerId;
 
     public String getId() {
         return id;
@@ -36,4 +37,12 @@ public class AuthenticationFlowModel implements Serializable {
     public void setDescription(String description) {
         this.description = description;
     }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
index 0a9744c..8531b5d 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -51,6 +51,7 @@ public interface ClientSessionModel {
     public String getNote(String name);
     public void setNote(String name, String value);
     public void removeNote(String name);
+    public Map<String, String> getNotes();
 
     /**
      * These are notes you want applied to the UserSessionModel when the client session is attached to it.
diff --git a/model/api/src/main/java/org/keycloak/models/entities/AuthenticationExecutionEntity.java b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationExecutionEntity.java
index e98b371..123e625 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/AuthenticationExecutionEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationExecutionEntity.java
@@ -10,6 +10,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
 public class AuthenticationExecutionEntity {
     protected String id;
     protected String authenticator;
+    protected String flowId;
     protected AuthenticationExecutionModel.Requirement requirement;
     protected int priority;
     private boolean userSetupAllowed;
@@ -71,4 +72,12 @@ public class AuthenticationExecutionEntity {
     public void setParentFlow(String parentFlow) {
         this.parentFlow = parentFlow;
     }
+
+    public String getFlowId() {
+        return flowId;
+    }
+
+    public void setFlowId(String flowId) {
+        this.flowId = flowId;
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/entities/AuthenticationFlowEntity.java b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationFlowEntity.java
index b79d1a5..0db9560 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/AuthenticationFlowEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationFlowEntity.java
@@ -12,6 +12,8 @@ public class AuthenticationFlowEntity {
     protected String id;
     protected String alias;
     protected String description;
+    protected String providerId;
+
     List<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
     public String getId() {
         return id;
@@ -44,4 +46,12 @@ public class AuthenticationFlowEntity {
     public void setExecutions(List<AuthenticationExecutionEntity> executions) {
         this.executions = executions;
     }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
 }
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 df2e677..7512422 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
@@ -18,6 +18,7 @@ public class DefaultAuthenticationFlows {
         AuthenticationFlowModel browser = new AuthenticationFlowModel();
         browser.setAlias(BROWSER_FLOW);
         browser.setDescription("browser based authentication");
+        browser.setProviderId("basic-flow");
         browser = realm.addAuthenticationFlow(browser);
         AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
         execution.setParentFlow(browser.getId());
@@ -40,11 +41,12 @@ public class DefaultAuthenticationFlows {
         AuthenticationFlowModel forms = new AuthenticationFlowModel();
         forms.setAlias(FORMS_FLOW);
         forms.setDescription("Username, password, otp and other auth forms.");
+        forms.setProviderId("basic-flow");
         forms = realm.addAuthenticationFlow(forms);
         execution = new AuthenticationExecutionModel();
         execution.setParentFlow(browser.getId());
         execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
-        execution.setAuthenticator(forms.getId());
+        execution.setFlowId(forms.getId());
         execution.setPriority(30);
         execution.setUserSetupAllowed(false);
         execution.setAutheticatorFlow(true);
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
index 1dc6fab..7df3f6f 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
@@ -1233,6 +1233,7 @@ public class RealmAdapter implements RealmModel {
         model.setId(entity.getId());
         model.setAlias(entity.getAlias());
         model.setDescription(entity.getDescription());
+        model.setProviderId(entity.getProviderId());
         return model;
     }
 
@@ -1266,6 +1267,7 @@ public class RealmAdapter implements RealmModel {
         if (toUpdate == null) return;
         toUpdate.setAlias(model.getAlias());
         toUpdate.setDescription(model.getDescription());
+        toUpdate.setProviderId(model.getProviderId());
 
     }
 
@@ -1275,6 +1277,7 @@ public class RealmAdapter implements RealmModel {
         entity.setId(KeycloakModelUtils.generateId());
         entity.setAlias(model.getAlias());
         entity.setDescription(model.getDescription());
+        entity.setProviderId(model.getProviderId());
         realm.getAuthenticationFlows().add(entity);
         model.setId(entity.getId());
         return model;
@@ -1303,6 +1306,7 @@ public class RealmAdapter implements RealmModel {
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
         model.setParentFlow(entity.getParentFlow());
+        model.setFlowId(entity.getFlowId());
         model.setAutheticatorFlow(entity.isAuthenticatorFlow());
         return model;
     }
@@ -1334,6 +1338,7 @@ public class RealmAdapter implements RealmModel {
         entity.setRequirement(model.getRequirement());
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
         entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setFlowId(model.getFlowId());
         AuthenticationFlowEntity flow = getFlowEntity(model.getId());
         flow.getExecutions().add(entity);
         model.setId(entity.getId());
@@ -1355,6 +1360,7 @@ public class RealmAdapter implements RealmModel {
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
+        entity.setFlowId(model.getFlowId());
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
index be8720a..b27659f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
@@ -19,9 +19,9 @@ import javax.persistence.Table;
 @Table(name="AUTHENTICATION_EXECUTION")
 @Entity
 @NamedQueries({
-        @NamedQuery(name="getAuthenticationExecutionsByFlow", query="select authenticator from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
+        @NamedQuery(name="getAuthenticationExecutionsByFlow", query="select authenticator from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.parentFlow = :parentFlow"),
         @NamedQuery(name="deleteAuthenticationExecutionsByRealm", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm"),
-        @NamedQuery(name="deleteAuthenticationExecutionsByRealmAndFlow", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
+        @NamedQuery(name="deleteAuthenticationExecutionsByRealmAndFlow", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.parentFlow = :parentFlow"),
 })
 public class AuthenticationExecutionEntity {
     @Id
@@ -34,11 +34,14 @@ public class AuthenticationExecutionEntity {
 
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "FLOW_ID")
-    protected AuthenticationFlowEntity flow;
+    protected AuthenticationFlowEntity parentFlow;
 
     @Column(name="AUTHENTICATOR")
     protected String authenticator;
 
+    @Column(name="AUTH_FLOW_ID")
+    protected String flowId;
+
     @Column(name="REQUIREMENT")
     protected AuthenticationExecutionModel.Requirement requirement;
 
@@ -107,11 +110,19 @@ public class AuthenticationExecutionEntity {
         this.autheticatorFlow = autheticatorFlow;
     }
 
-    public AuthenticationFlowEntity getFlow() {
-        return flow;
+    public AuthenticationFlowEntity getParentFlow() {
+        return parentFlow;
+    }
+
+    public void setParentFlow(AuthenticationFlowEntity flow) {
+        this.parentFlow = flow;
+    }
+
+    public String getFlowId() {
+        return flowId;
     }
 
-    public void setFlow(AuthenticationFlowEntity flow) {
-        this.flow = flow;
+    public void setFlowId(String flowId) {
+        this.flowId = flowId;
     }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
index 1a60832..2db9722 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
@@ -36,10 +36,13 @@ public class AuthenticationFlowEntity {
     @Column(name="ALIAS")
     protected String alias;
 
+    @Column(name="PROVIDER_ID")
+    protected String providerId;
+
     @Column(name="DESCRIPTION")
     protected String description;
 
-    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "flow")
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "parentFlow")
     Collection<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
     public String getId() {
         return id;
@@ -80,4 +83,12 @@ public class AuthenticationFlowEntity {
     public void setExecutions(Collection<AuthenticationExecutionEntity> executions) {
         this.executions = executions;
     }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index de47499..eec43ba 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1542,6 +1542,7 @@ public class RealmAdapter implements RealmModel {
         AuthenticationFlowModel model = new AuthenticationFlowModel();
         model.setId(entity.getId());
         model.setAlias(entity.getAlias());
+        model.setProviderId(entity.getProviderId());
         model.setDescription(entity.getDescription());
         return model;
     }
@@ -1567,6 +1568,7 @@ public class RealmAdapter implements RealmModel {
         if (entity == null) return;
         entity.setAlias(model.getAlias());
         entity.setDescription(model.getDescription());
+        entity.setProviderId(model.getProviderId());
 
     }
 
@@ -1576,6 +1578,7 @@ public class RealmAdapter implements RealmModel {
         entity.setId(KeycloakModelUtils.generateId());
         entity.setAlias(model.getAlias());
         entity.setDescription(model.getDescription());
+        entity.setProviderId(model.getProviderId());
         entity.setRealm(realm);
         realm.getAuthenticationFlows().add(entity);
         em.persist(entity);
@@ -1589,7 +1592,7 @@ public class RealmAdapter implements RealmModel {
         TypedQuery<AuthenticationExecutionEntity> query = em.createNamedQuery("getAuthenticationExecutionsByFlow", AuthenticationExecutionEntity.class);
         AuthenticationFlowEntity flow = em.getReference(AuthenticationFlowEntity.class, flowId);
         query.setParameter("realm", realm);
-        query.setParameter("flow", flow);
+        query.setParameter("parentFlow", flow);
         List<AuthenticationExecutionEntity> queryResult = query.getResultList();
         List<AuthenticationExecutionModel> executions = new LinkedList<>();
         for (AuthenticationExecutionEntity entity : queryResult) {
@@ -1607,7 +1610,8 @@ public class RealmAdapter implements RealmModel {
         model.setRequirement(entity.getRequirement());
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
-        model.setParentFlow(entity.getFlow().getId());
+        model.setFlowId(entity.getFlowId());
+        model.setParentFlow(entity.getParentFlow().getId());
         model.setAutheticatorFlow(entity.isAutheticatorFlow());
         return model;
     }
@@ -1625,9 +1629,10 @@ public class RealmAdapter implements RealmModel {
         entity.setId(KeycloakModelUtils.generateId());
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
+        entity.setFlowId(model.getFlowId());
         entity.setRequirement(model.getRequirement());
         AuthenticationFlowEntity flow = em.find(AuthenticationFlowEntity.class, model.getParentFlow());
-        entity.setFlow(flow);
+        entity.setParentFlow(flow);
         flow.getExecutions().add(entity);
         entity.setRealm(realm);
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
@@ -1648,6 +1653,7 @@ public class RealmAdapter implements RealmModel {
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        entity.setFlowId(model.getFlowId());
         em.flush();
     }
 
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index dee96e2..935746e 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1341,6 +1341,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         if (toUpdate == null) return;
         toUpdate.setAlias(model.getAlias());
         toUpdate.setDescription(model.getDescription());
+        toUpdate.setProviderId(model.getProviderId());
         updateMongoEntity();
     }
 
@@ -1350,6 +1351,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setId(KeycloakModelUtils.generateId());
         entity.setAlias(model.getAlias());
         entity.setDescription(model.getDescription());
+        entity.setProviderId(model.getProviderId());
         getMongoEntity().getAuthenticationFlows().add(entity);
         model.setId(entity.getId());
         updateMongoEntity();
@@ -1378,6 +1380,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         model.setRequirement(entity.getRequirement());
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
+        model.setFlowId(entity.getFlowId());
         model.setParentFlow(entity.getParentFlow());
         model.setAutheticatorFlow(entity.isAuthenticatorFlow());
         return model;
@@ -1410,6 +1413,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setRequirement(model.getRequirement());
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
         entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setFlowId(model.getFlowId());
         entity.setParentFlow(model.getParentFlow());
         AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
         flow.getExecutions().add(entity);
@@ -1433,6 +1437,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
+        entity.setFlowId(model.getFlowId());
         entity.setUserSetupAllowed(model.isUserSetupAllowed());
         updateMongoEntity();
     }
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
index 32a2df6..af9f436 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
@@ -166,6 +166,14 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
+    public Map<String, String> getNotes() {
+        if (entity.getNotes() == null || entity.getNotes().isEmpty()) return Collections.emptyMap();
+        Map<String, String> copy = new HashMap<>();
+        copy.putAll(entity.getNotes());
+        return copy;
+    }
+
+    @Override
     public void setUserSessionNote(String name, String value) {
         if (entity.getUserSessionNotes() == null) {
             entity.setUserSessionNotes(new HashMap<String, String>());
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
index 979e610..4c340c5 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
@@ -82,6 +82,16 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
+    public Map<String, String> getNotes() {
+        Map<String, String> copy = new HashMap<>();
+        for (ClientSessionNoteEntity attr : entity.getNotes()) {
+            copy.put(attr.getName(), attr.getValue());
+        }
+
+        return copy;
+    }
+
+    @Override
     public void setUserSessionNote(String name, String value) {
         for (ClientUserSessionNoteEntity attr : entity.getUserSessionNotes()) {
             if (attr.getName().equals(name)) {
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
index 2c344f6..7fcd35f 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
@@ -9,6 +9,8 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.sessions.mem.entities.ClientSessionEntity;
 import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -137,6 +139,14 @@ public class ClientSessionAdapter implements ClientSessionModel {
     }
 
     @Override
+    public Map<String, String> getNotes() {
+        if (entity.getNotes() == null || entity.getNotes().isEmpty()) return Collections.emptyMap();
+        Map<String, String> copy = new HashMap<>();
+        copy.putAll(entity.getNotes());
+        return copy;
+   }
+
+    @Override
     public void setUserSessionNote(String name, String value) {
         entity.getUserSessionNotes().put(name, value);
     }
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
index 1313d36..2120044 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
@@ -10,6 +10,7 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity;
 import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -158,6 +159,15 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
     }
 
     @Override
+    public Map<String, String> getNotes() {
+        if (entity.getNotes() == null || entity.getNotes().isEmpty()) return Collections.emptyMap();
+        Map<String, String> copy = new HashMap<>();
+        copy.putAll(entity.getNotes());
+        return copy;
+    }
+
+
+    @Override
     public void setUserSessionNote(String name, String value) {
         entity.getUserSessionNotes().put(name, value);
         updateMongoEntity();
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlow.java
new file mode 100755
index 0000000..d77772e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlow.java
@@ -0,0 +1,12 @@
+package org.keycloak.authentication;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AuthenticationFlow {
+    Response processAction(String actionExecution);
+    Response processFlow();
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 509e5c5..f5ff56e 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -26,7 +26,6 @@ import org.keycloak.util.Time;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.Iterator;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -64,6 +63,7 @@ public class AuthenticationProcessor {
         ATTEMPTED
 
     }
+
     public static enum Error {
         EXPIRED_CODE,
         INVALID_CLIENT_SESSION,
@@ -229,12 +229,14 @@ public class AuthenticationProcessor {
             this.challenge = challenge;
 
         }
+
         @Override
         public void forceChallenge(Response challenge) {
             this.status = Status.FORCE_CHALLENGE;
             this.challenge = challenge;
 
         }
+
         @Override
         public void failureChallenge(Error error, Response challenge) {
             this.error = error;
@@ -242,6 +244,7 @@ public class AuthenticationProcessor {
             this.challenge = challenge;
 
         }
+
         @Override
         public void failure(Error error, Response challenge) {
             this.error = error;
@@ -264,7 +267,8 @@ public class AuthenticationProcessor {
         @Override
         public void setUser(UserModel user) {
             UserModel previousUser = getUser();
-            if (previousUser != null && !user.getId().equals(previousUser.getId())) throw new AuthException(Error.USER_CONFLICT);
+            if (previousUser != null && !user.getId().equals(previousUser.getId()))
+                throw new AuthException(Error.USER_CONFLICT);
             validateUser(user);
             getClientSession().setAuthenticatedUser(user);
         }
@@ -325,6 +329,16 @@ public class AuthenticationProcessor {
             clientSession.setTimestamp(Time.currentTime());
             return accessCode.getCode();
         }
+
+        @Override
+        public Response getChallenge() {
+            return challenge;
+        }
+
+        @Override
+        public Error getError() {
+            return error;
+        }
     }
 
     public static class AuthException extends RuntimeException {
@@ -372,15 +386,6 @@ public class AuthenticationProcessor {
         }
     }
 
-    protected boolean isProcessed(AuthenticationExecutionModel model) {
-        if (model.isDisabled()) return true;
-        ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
-        if (status == null) return false;
-        return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
-                || status == ClientSessionModel.ExecutionStatus.ATTEMPTED
-                || status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
-    }
-
     public boolean isSuccessful(AuthenticationExecutionModel model) {
         ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
         if (status == null) return false;
@@ -389,7 +394,7 @@ public class AuthenticationProcessor {
 
     public Response handleBrowserException(Exception failure) {
         if (failure instanceof AuthException) {
-            AuthException e = (AuthException)failure;
+            AuthException e = (AuthException) failure;
             logger.error("failed authentication: " + e.getError().toString(), e);
             if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
                 event.error(Errors.USER_NOT_FOUND);
@@ -405,11 +410,11 @@ public class AuthenticationProcessor {
                 event.error(Errors.INVALID_CODE);
                 return ErrorPage.error(session, Messages.INVALID_CODE);
 
-            }  else if (e.getError() == Error.EXPIRED_CODE) {
+            } else if (e.getError() == Error.EXPIRED_CODE) {
                 event.error(Errors.EXPIRED_CODE);
                 return ErrorPage.error(session, Messages.EXPIRED_CODE);
 
-            }else {
+            } else {
                 event.error(Errors.INVALID_USER_CREDENTIALS);
                 return ErrorPage.error(session, Messages.INVALID_USER);
             }
@@ -422,15 +427,22 @@ public class AuthenticationProcessor {
 
     }
 
-    public FlowExecution createFlowExecution(String flowId) {
+    public AuthenticationFlow createFlowExecution(String flowId, AuthenticationExecutionModel execution) {
         AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
         if (flow == null) {
             logger.error("Unknown flow to execute with");
             throw new AuthException(Error.INTERNAL_ERROR);
         }
-        FlowExecution flowExecution = new FlowExecution();
-        flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
-        return flowExecution;
+        if (flow.getProviderId() == null || flow.getProviderId().equals("basic-flow")) {
+            DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this);
+            flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
+            return flowExecution;
+
+        } else if (flow.getProviderId().equals("form-flow")) {
+            FormAuthenticationFlow flowExecution = new FormAuthenticationFlow(this, execution);
+            return flowExecution;
+        }
+        throw new AuthException("Unknown flow provider type", Error.INTERNAL_ERROR);
     }
 
     public Response authenticate() throws AuthException {
@@ -446,8 +458,8 @@ public class AuthenticationProcessor {
         }
         UserModel authUser = clientSession.getAuthenticatedUser();
         validateUser(authUser);
-        FlowExecution flowExecution = createFlowExecution(this.flowId);
-        Response challenge = flowExecution.processFlow();
+        AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
+        Response challenge = authenticationFlow.processFlow();
         if (challenge != null) return challenge;
         if (clientSession.getAuthenticatedUser() == null) {
             throw new AuthException(Error.UNKNOWN_USER);
@@ -455,7 +467,7 @@ public class AuthenticationProcessor {
         return authenticationComplete();
     }
 
-    public static  void resetFlow(ClientSessionModel clientSession) {
+    public static void resetFlow(ClientSessionModel clientSession) {
         clientSession.setAuthenticatedUser(null);
         clientSession.clearExecutionStatus();
         clientSession.clearUserSessionNotes();
@@ -486,13 +498,9 @@ public class AuthenticationProcessor {
         if (authType != null) {
             event.detail(Details.AUTH_TYPE, authType);
         }
-        AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
-        Authenticator authenticator = factory.create();
-        Result context = new Result(model, authenticator);
-        authenticator.action(context);
 
-        FlowExecution flowExecution = createFlowExecution(this.flowId);
-        Response challenge = flowExecution.action(execution, context);
+        AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, model);
+        Response challenge = authenticationFlow.processAction(execution);
         if (challenge != null) return challenge;
         if (clientSession.getAuthenticatedUser() == null) {
             throw new AuthException(Error.UNKNOWN_USER);
@@ -523,8 +531,8 @@ public class AuthenticationProcessor {
         }
         UserModel authUser = clientSession.getAuthenticatedUser();
         validateUser(authUser);
-        FlowExecution flowExecution = createFlowExecution(this.flowId);
-        Response challenge = flowExecution.processFlow();
+        AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
+        Response challenge = authenticationFlow.processFlow();
         if (challenge != null) return challenge;
 
         String username = clientSession.getAuthenticatedUser().getUsername();
@@ -572,175 +580,16 @@ public class AuthenticationProcessor {
         }
         TokenManager.attachClientSession(userSession, clientSession);
         event.user(userSession.getUser())
-             .detail(Details.USERNAME, username)
-             .session(userSession);
+                .detail(Details.USERNAME, username)
+                .session(userSession);
 
         return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, event);
 
     }
 
-     class FlowExecution {
-        Response alternativeChallenge = null;
-        AuthenticationExecutionModel challengedAlternativeExecution = null;
-        boolean alternativeSuccessful = false;
-        Iterator<AuthenticationExecutionModel> executions;
-
-        public Response action(String actionExecution, Result actionResult) {
-            while (executions.hasNext()) {
-                AuthenticationExecutionModel model = executions.next();
-                if (isProcessed(model)) {
-                    logger.debug("execution is processed");
-                    if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
-                    continue;
-                }
-                if (!model.getId().equals(actionExecution)) {
-                    if (model.isAutheticatorFlow()) {
-                        FlowExecution flowExecution = createFlowExecution(model.getAuthenticator());
-                        return flowExecution.action(actionExecution, actionResult);
-                    } else {
-                        throw new AuthException("action is not current execution", Error.INTERNAL_ERROR);
-                    }
-                } else { // we found the action
-                    Response response = processResult(actionResult);
-                    if (response == null) return processFlow();
-                    else return response;
-                }
-            }
-            throw new AuthException("action is not in current execution", Error.INTERNAL_ERROR);
-        }
-
-        public Response processFlow() {
-            while (executions.hasNext()) {
-                AuthenticationExecutionModel model = executions.next();
-                if (isProcessed(model)) {
-                    logger.debug("execution is processed");
-                    if (!alternativeSuccessful && model.isAlternative() && isSuccessful(model)) alternativeSuccessful = true;
-                    continue;
-                }
-                if (model.isAlternative() && alternativeSuccessful) {
-                    clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
-                    continue;
-                }
-                if (model.isAutheticatorFlow()) {
-                    FlowExecution flowExecution = createFlowExecution(model.getAuthenticator());
-                    Response flowResponse = flowExecution.processFlow();
-                    if (flowResponse == null) {
-                        clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
-                        if (model.isAlternative()) alternativeSuccessful = true;
-                        continue;
-                    } else {
-                        return flowResponse;
-                    }
-
-                }
-
-                AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
-                Authenticator authenticator = factory.create();
-                logger.debugv("authenticator: {0}", factory.getId());
-                UserModel authUser = clientSession.getAuthenticatedUser();
-
-                if (authenticator.requiresUser() && authUser == null){
-                    if (alternativeChallenge != null) {
-                        clientSession.setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
-                        return alternativeChallenge;
-                    }
-                    throw new AuthException("authenticator: " + factory.getId(), Error.UNKNOWN_USER);
-                }
-                boolean configuredFor = false;
-                if (authenticator.requiresUser() && authUser != null) {
-                    configuredFor = authenticator.configuredFor(session, realm, authUser);
-                    if (!configuredFor) {
-                        if (model.isRequired()) {
-                            if (model.isUserSetupAllowed()) {
-                                logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
-                                clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
-                                authenticator.setRequiredActions(session, realm, clientSession.getAuthenticatedUser());
-                                continue;
-                            } else {
-                                throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED);
-                            }
-                        } else if (model.isOptional()) {
-                            clientSession.setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
-                            continue;
-                        }
-                    }
-                }
-                Result context = new Result(model, authenticator);
-                authenticator.authenticate(context);
-                Response response = processResult(context);
-                if (response != null) return response;
-            }
-            return null;
-        }
-
-
-        public Response processResult(Result result) {
-            AuthenticationExecutionModel execution = result.getExecution();
-            Status status = result.getStatus();
-            if (status == Status.SUCCESS){
-                logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
-                clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
-                if (execution.isAlternative()) alternativeSuccessful = true;
-                return null;
-            } else if (status == Status.FAILED) {
-                logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
-                logFailure();
-                clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
-                if (result.challenge != null) {
-                    return sendChallenge(result, execution);
-                }
-                throw new AuthException(result.error);
-            } else if (status == Status.FORCE_CHALLENGE) {
-                clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
-                return sendChallenge(result, execution);
-            } else if (status == Status.CHALLENGE) {
-                logger.debugv("authenticator CHALLENGE: {0}", execution.getAuthenticator());
-                if (execution.isRequired()) {
-                    clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
-                    return sendChallenge(result, execution);
-                }
-                UserModel authenticatedUser = clientSession.getAuthenticatedUser();
-                if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(session, realm, authenticatedUser)) {
-                    clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
-                    return sendChallenge(result, execution);
-                }
-                if (execution.isAlternative()) {
-                    alternativeChallenge = result.challenge;
-                    challengedAlternativeExecution = execution;
-                } else {
-                    clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
-                }
-                return null;
-            } else if (status == Status.FAILURE_CHALLENGE) {
-                logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
-                logFailure();
-                clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
-                return sendChallenge(result, execution);
-            } else if (status == Status.ATTEMPTED) {
-                logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
-                if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
-                    throw new AuthException(Error.INVALID_CREDENTIALS);
-                }
-                clientSession.setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
-                return null;
-            } else {
-                logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
-                logger.error("Unknown result status");
-                throw new AuthException(Error.INTERNAL_ERROR);
-            }
-
-        }
-
-         public Response sendChallenge(Result result, AuthenticationExecutionModel execution) {
-             clientSession.setNote(CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
-             return result.challenge;
-         }
-
-
-     }
-
-
-
+    public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator) {
+        return new Result(model, authenticator);
+    }
 
 
 }
diff --git a/services/src/main/java/org/keycloak/authentication/Authenticator.java b/services/src/main/java/org/keycloak/authentication/Authenticator.java
index 36e6d52..ddde806 100755
--- a/services/src/main/java/org/keycloak/authentication/Authenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/Authenticator.java
@@ -10,8 +10,8 @@ import org.keycloak.provider.Provider;
 * @version $Revision: 1 $
 */
 public interface Authenticator extends Provider {
-    boolean requiresUser();
     void authenticate(AuthenticatorContext context);
+    boolean requiresUser();
     boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
 
     /**
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
index 49e663c..07a3fd1 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
@@ -77,4 +77,8 @@ public interface AuthenticatorContext {
      * @return
      */
     String generateAccessCode();
+
+    Response getChallenge();
+
+    AuthenticationProcessor.Error getError();
 }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
index 591ca4b..04962dd 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
@@ -24,7 +24,7 @@ public class AuthenticatorUtil {
         for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
             executions.add(model);
             if (model.isAutheticatorFlow() && model.isEnabled()) {
-                recurseExecutions(realm, model.getAuthenticator(), executions);
+                recurseExecutions(realm, model.getFlowId(), executions);
             }
         }
     }
@@ -32,7 +32,7 @@ public class AuthenticatorUtil {
     public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) {
         for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
             if (model.isAutheticatorFlow()) {
-                AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getAuthenticator(), authProviderId);
+                AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getFlowId(), authProviderId);
                 if (recurse != null) return recurse;
 
             }
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
new file mode 100755
index 0000000..8aaaa64
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -0,0 +1,207 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.UserModel;
+
+import javax.ws.rs.core.Response;
+import java.util.Iterator;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class DefaultAuthenticationFlow implements AuthenticationFlow {
+    Response alternativeChallenge = null;
+    AuthenticationExecutionModel challengedAlternativeExecution = null;
+    boolean alternativeSuccessful = false;
+    Iterator<AuthenticationExecutionModel> executions;
+    AuthenticationProcessor processor;
+
+    public DefaultAuthenticationFlow(AuthenticationProcessor processor) {
+        this.processor = processor;
+    }
+
+    protected boolean isProcessed(AuthenticationExecutionModel model) {
+        if (model.isDisabled()) return true;
+        ClientSessionModel.ExecutionStatus status = processor.getClientSession().getExecutionStatus().get(model.getId());
+        if (status == null) return false;
+        return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
+                || status == ClientSessionModel.ExecutionStatus.ATTEMPTED
+                || status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
+    }
+
+
+    @Override
+    public Response processAction(String actionExecution) {
+        while (executions.hasNext()) {
+            AuthenticationExecutionModel model = executions.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
+                AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
+                Authenticator authenticator = factory.create();
+                AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator);
+                authenticator.action(result);
+                Response response = processResult(result);
+                if (response == null) return processFlow();
+                else return response;
+            }
+        }
+        throw new AuthenticationProcessor.AuthException("action is not in current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
+    }
+
+    @Override
+    public Response processFlow() {
+        while (executions.hasNext()) {
+            AuthenticationExecutionModel model = executions.next();
+            if (isProcessed(model)) {
+                AuthenticationProcessor.logger.debug("execution is processed");
+                if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
+                    alternativeSuccessful = true;
+                continue;
+            }
+            if (model.isAlternative() && alternativeSuccessful) {
+                processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+                continue;
+            }
+            if (model.isAutheticatorFlow()) {
+                AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
+                Response flowChallenge = authenticationFlow.processFlow();
+                if (flowChallenge == null) {
+                    processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
+                    if (model.isAlternative()) alternativeSuccessful = true;
+                    continue;
+                } else {
+                    if (model.isAlternative()) {
+                        alternativeChallenge = flowChallenge;
+                        challengedAlternativeExecution = model;
+                    } else if (model.isRequired()) {
+                        processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+                        return flowChallenge;
+                    } else if (model.isOptional()) {
+                        processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+                        continue;
+                    } else {
+                        processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+                        continue;
+                    }
+                    return flowChallenge;
+                }
+            }
+
+            AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
+            Authenticator authenticator = factory.create();
+            AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
+            UserModel authUser = processor.getClientSession().getAuthenticatedUser();
+
+            if (authenticator.requiresUser() && authUser == null) {
+                if (alternativeChallenge != null) {
+                    processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+                    return alternativeChallenge;
+                }
+                throw new AuthenticationProcessor.AuthException("authenticator: " + factory.getId(), AuthenticationProcessor.Error.UNKNOWN_USER);
+            }
+            boolean configuredFor = false;
+            if (authenticator.requiresUser() && authUser != null) {
+                configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser);
+                if (!configuredFor) {
+                    if (model.isRequired()) {
+                        if (model.isUserSetupAllowed()) {
+                            AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
+                            processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
+                            authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
+                            continue;
+                        } else {
+                            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
+                        }
+                    } else if (model.isOptional()) {
+                        processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+                        continue;
+                    }
+                }
+            }
+            AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator);
+            authenticator.authenticate(context);
+            Response response = processResult(context);
+            if (response != null) return response;
+        }
+        return null;
+    }
+
+
+    public Response processResult(AuthenticatorContext result) {
+        AuthenticationExecutionModel execution = result.getExecution();
+        AuthenticationProcessor.Status status = result.getStatus();
+        if (status == AuthenticationProcessor.Status.SUCCESS) {
+            AuthenticationProcessor.logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
+            processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
+            if (execution.isAlternative()) alternativeSuccessful = true;
+            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);
+            if (result.getChallenge() != null) {
+                return sendChallenge(result, execution);
+            }
+            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);
+        } else if (status == AuthenticationProcessor.Status.CHALLENGE) {
+            AuthenticationProcessor.logger.debugv("authenticator CHALLENGE: {0}", execution.getAuthenticator());
+            if (execution.isRequired()) {
+                processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+                return sendChallenge(result, execution);
+            }
+            UserModel authenticatedUser = processor.getClientSession().getAuthenticatedUser();
+            if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(processor.getSession(), processor.getRealm(), authenticatedUser)) {
+                processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+                return sendChallenge(result, execution);
+            }
+            if (execution.isAlternative()) {
+                alternativeChallenge = result.getChallenge();
+                challengedAlternativeExecution = execution;
+            } else {
+                processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+            }
+            return null;
+        } 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);
+        } 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);
+            return null;
+        } else {
+            AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
+            AuthenticationProcessor.logger.error("Unknown result status");
+            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INTERNAL_ERROR);
+        }
+
+    }
+
+    public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
+        processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
+        return result.getChallenge();
+    }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAction.java b/services/src/main/java/org/keycloak/authentication/FormAction.java
new file mode 100755
index 0000000..90bd084
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormAction.java
@@ -0,0 +1,24 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface FormAction extends Provider {
+    void authenticate(FormContext context);
+
+    boolean requiresUser();
+    boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
+
+    /**
+     * Set actions to configure authenticator
+     *
+     */
+    void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormActionFactory.java b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
new file mode 100755
index 0000000..cbac58b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface FormActionFactory extends ProviderFactory<FormAction> {
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormActionSpi.java b/services/src/main/java/org/keycloak/authentication/FormActionSpi.java
new file mode 100755
index 0000000..125ffdc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormActionSpi.java
@@ -0,0 +1,32 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class FormActionSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "form-action";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return FormAction.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return FormActionFactory.class;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
new file mode 100755
index 0000000..b8bb691
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -0,0 +1,290 @@
+package org.keycloak.authentication;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticatorConfigModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+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.List;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class FormAuthenticationFlow implements AuthenticationFlow {
+    AuthenticationProcessor processor;
+    AuthenticationExecutionModel execution;
+
+
+    public FormAuthenticationFlow(AuthenticationProcessor processor, AuthenticationExecutionModel execution) {
+        this.processor = processor;
+        this.execution = execution;
+    }
+
+    private static class FormActionResult implements FormContext {
+        AuthenticatorContext delegate;
+        FormAuthenticator authenticator;
+
+        FormActionResult(AuthenticatorContext delegate, FormAuthenticator authenticator) {
+            this.delegate = delegate;
+            this.authenticator = authenticator;
+        }
+
+        @Override
+        public FormAuthenticator getFormAuthenticator() {
+            return authenticator;
+        }
+
+        @Override
+        public EventBuilder getEvent() {
+            return delegate.getEvent();
+        }
+
+        @Override
+        public AuthenticationExecutionModel getExecution() {
+            return delegate.getExecution();
+        }
+
+        @Override
+        public void setExecution(AuthenticationExecutionModel execution) {
+            delegate.setExecution(execution);
+        }
+
+        @Override
+        public AuthenticatorConfigModel getAuthenticatorConfig() {
+            return delegate.getAuthenticatorConfig();
+        }
+
+        @Override
+        public String getAction() {
+            return delegate.getAction();
+        }
+
+        @Override
+        public Authenticator getAuthenticator() {
+            return delegate.getAuthenticator();
+        }
+
+        @Override
+        public void setAuthenticator(Authenticator authenticator) {
+            delegate.setAuthenticator(authenticator);
+        }
+
+        @Override
+        public AuthenticationProcessor.Status getStatus() {
+            return delegate.getStatus();
+        }
+
+        @Override
+        public UserModel getUser() {
+            return delegate.getUser();
+        }
+
+        @Override
+        public void setUser(UserModel user) {
+            delegate.setUser(user);
+        }
+
+        @Override
+        public RealmModel getRealm() {
+            return delegate.getRealm();
+        }
+
+        @Override
+        public ClientSessionModel getClientSession() {
+            return delegate.getClientSession();
+        }
+
+        @Override
+        public void attachUserSession(UserSessionModel userSession) {
+            delegate.attachUserSession(userSession);
+        }
+
+        @Override
+        public ClientConnection getConnection() {
+            return delegate.getConnection();
+        }
+
+        @Override
+        public UriInfo getUriInfo() {
+            return delegate.getUriInfo();
+        }
+
+        @Override
+        public KeycloakSession getSession() {
+            return delegate.getSession();
+        }
+
+        @Override
+        public HttpRequest getHttpRequest() {
+            return delegate.getHttpRequest();
+        }
+
+        @Override
+        public BruteForceProtector getProtector() {
+            return delegate.getProtector();
+        }
+
+        @Override
+        public void success() {
+            delegate.success();
+        }
+
+        @Override
+        public void failure(AuthenticationProcessor.Error error) {
+            delegate.failure(error);
+        }
+
+        @Override
+        public void failure(AuthenticationProcessor.Error error, Response response) {
+            delegate.failure(error, response);
+        }
+
+        @Override
+        public void challenge(Response challenge) {
+            delegate.challenge(challenge);
+        }
+
+        @Override
+        public void forceChallenge(Response challenge) {
+            delegate.forceChallenge(challenge);
+        }
+
+        @Override
+        public void failureChallenge(AuthenticationProcessor.Error error, Response challenge) {
+            delegate.failureChallenge(error, challenge);
+        }
+
+        @Override
+        public void attempted() {
+            delegate.attempted();
+        }
+
+        @Override
+        public String getForwardedErrorMessage() {
+            return delegate.getForwardedErrorMessage();
+        }
+
+        @Override
+        public String generateAccessCode() {
+            return delegate.generateAccessCode();
+        }
+
+        @Override
+        public Response getChallenge() {
+            return delegate.getChallenge();
+        }
+
+        @Override
+        public AuthenticationProcessor.Error getError() {
+            return delegate.getError();
+        }
+    }
+
+
+    @Override
+    public Response processAction(String actionExecution) {
+        if (!actionExecution.equals(execution.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());
+
+            UserModel authUser = processor.getClientSession().getAuthenticatedUser();
+            if (action.requiresUser() && authUser == null) {
+                throw new AuthenticationProcessor.AuthException("form action: " + execution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
+            }
+            boolean configuredFor = false;
+            if (action.requiresUser() && authUser != null) {
+                configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
+                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);
+                            continue;
+                        } else {
+                            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
+                        }
+                    } else if (formActionExecution.isOptional()) {
+                        processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
+                        continue;
+                    }
+                }
+            }
+
+            FormActionResult result = new FormActionResult(processor.createAuthenticatorContext(formActionExecution, null), authenticator);
+            action.authenticate(result);
+            return processResult(result, formActionExecution);
+
+        }
+        return null;
+
+    }
+
+    @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);
+    }
+
+
+    public Response processResult(AuthenticatorContext result, AuthenticationExecutionModel execution) {
+        AuthenticationProcessor.Status status = result.getStatus();
+        if (status == AuthenticationProcessor.Status.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);
+            if (result.getChallenge() != null) {
+                return sendChallenge(result, execution);
+            }
+            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);
+        } else if (status == AuthenticationProcessor.Status.CHALLENGE) {
+            processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
+            return sendChallenge(result, execution);
+        } 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);
+        } 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);
+            return null;
+        } else {
+            AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
+            AuthenticationProcessor.logger.error("Unknown result status");
+            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INTERNAL_ERROR);
+        }
+
+    }
+
+    public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
+        processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.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
new file mode 100755
index 0000000..4804e92
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
@@ -0,0 +1,14 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.Provider;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface FormAuthenticator extends Provider {
+    void authenticate(AuthenticatorContext context);
+    Response createChallenge(FormContext context, String... errorMessages);
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
new file mode 100755
index 0000000..5b99f1a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface FormAuthenticatorFactory extends ProviderFactory<FormAuthenticator> {
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
new file mode 100755
index 0000000..18303f0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java
@@ -0,0 +1,32 @@
+package org.keycloak.authentication;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class FormAuthenticatorSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "form-authenticator";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return FormAuthenticator.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return FormAuthenticatorFactory.class;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/FormContext.java b/services/src/main/java/org/keycloak/authentication/FormContext.java
new file mode 100755
index 0000000..24c46a7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/FormContext.java
@@ -0,0 +1,9 @@
+package org.keycloak.authentication;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface FormContext extends AuthenticatorContext {
+    FormAuthenticator getFormAuthenticator();
+}
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 d048a57..7f1edbd 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
@@ -124,7 +124,7 @@ public class AuthenticationManagementResource {
             rep.setSubFlow(false);
             rep.setRequirementChoices(new LinkedList<String>());
             if (execution.isAutheticatorFlow()) {
-                AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getAuthenticator());
+                AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getFlowId());
                 rep.setReferenceType(flowRef.getAlias());
                 rep.setExecution(execution.getId());
                 rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name());
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 20023c6..44910cb 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -300,7 +300,6 @@ public class LoginActionsService {
      */
     @Path("authenticate")
     @POST
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
     public Response authenticateForm(@QueryParam("code") String code,
                                      @QueryParam("execution") String execution) {
         event.event(EventType.LOGIN);
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
index 8106ec7..a72fdd9 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.RequiredActionFactory
@@ -1,5 +1,5 @@
-org.keycloak.authentication.actions.UpdatePassword
-org.keycloak.authentication.actions.UpdateProfile
-org.keycloak.authentication.actions.UpdateTotp
-org.keycloak.authentication.actions.VerifyEmail
-org.keycloak.authentication.actions.TermsAndConditions
\ No newline at end of file
+org.keycloak.authentication.requiredactions.UpdatePassword
+org.keycloak.authentication.requiredactions.UpdateProfile
+org.keycloak.authentication.requiredactions.UpdateTotp
+org.keycloak.authentication.requiredactions.VerifyEmail
+org.keycloak.authentication.requiredactions.TermsAndConditions
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 050fef2..43d8d10 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -4,4 +4,6 @@ org.keycloak.exportimport.ClientImportSpi
 org.keycloak.wellknown.WellKnownSpi
 org.keycloak.messages.MessagesSpi
 org.keycloak.authentication.AuthenticatorSpi
-org.keycloak.authentication.RequiredActionSpi
\ No newline at end of file
+org.keycloak.authentication.RequiredActionSpi
+org.keycloak.authentication.FormAuthenticatorSpi
+org.keycloak.authentication.FormActionSpi