keycloak-aplcache

auth spi datamodel

5/22/2015 5:03:26 PM

Changes

Details

diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 5bcb77e..6399049 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -24,6 +24,9 @@
         <class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
         <class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
         <class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
+        <class>org.keycloak.models.jpa.entities.AuthenticatorEntity</class>
 
         <!-- JpaUserSessionProvider -->
         <class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
index 27869eb..516b9f4 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
@@ -21,6 +21,44 @@
             <column name="REPRESENTATION" type="VARCHAR(25500)"/>
             <column name="ERROR" type="VARCHAR(255)"/>
         </createTable>
+        <createTable tableName="AUTHENTICATOR">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ALIAS" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+            <column name="PROVIDER_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="AUTHENTICATION_FLOW">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ALIAS" type="VARCHAR(255)"/>
+            <column name="DESCRIPTION" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="AUTHENTICATION_EXECUTION">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ALIAS" type="VARCHAR(255)"/>
+            <column name="AUTHENTICATOR" type="VARCHAR(36)"/>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+            <column name="FLOW_ID" type="VARCHAR(36)"/>
+            <column name="REQUIREMENT" type="INT"/>
+            <column name="PRIORITY" type="INT"/>
+            <column name="USER_SETUP_ALLOWED" type="BOOLEAN" defaultValueBoolean="false"/>
+            <column name="AUTHENTICATOR_FLOW" type="BOOLEAN" defaultValueBoolean="false"/>
+        </createTable>
+        <createTable tableName="AUTHENTICATOR_CONFIG">
+            <column name="AUTHENTICATOR_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="CLOB"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
         <addColumn tableName="REALM">
             <column name="ADMIN_EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
                 <constraints nullable="false"/>
@@ -30,7 +68,7 @@
             </column>
         </addColumn>
         <createTable tableName="CLIENT_SESSION_AUTH_STATUS">
-            <column name="AUTHENTICATOR" type="VARCHAR(32)">
+            <column name="AUTHENTICATOR" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
             <column name="STATUS" type="INT"/>
@@ -39,10 +77,10 @@
             </column>
         </createTable>
         <addColumn tableName="CLIENT_SESSION">
-            <column name="AUTH_USER_ID" type="VARCHAR(32)"/>
+            <column name="AUTH_USER_ID" type="VARCHAR(36)"/>
         </addColumn>
         <addColumn tableName="USER_REQUIRED_ACTION">
-            <column name="REQUIRED_ACTION" type="VARCHAR(32)">
+            <column name="REQUIRED_ACTION" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
         </addColumn>
@@ -63,10 +101,18 @@
             <column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
             <where>ACTION = 3</where>
         </update>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATOR_PK" tableName="AUTHENTICATOR"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATION_FLOW_PK" tableName="AUTHENTICATION_FLOW"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATION_EXECUTION_PK" tableName="AUTHENTICATION_EXECUTION"/>
+        <addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTHENTICATOR_CONFIG_PK" tableName="AUTHENTICATOR_CONFIG"/>
         <dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
         <dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/>
         <addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/>
         <addPrimaryKey columnNames="CLIENT_SESSION, AUTHENTICATOR" constraintName="CONSTRAINT_AUTH_STATUS_PK" tableName="CLIENT_SESSION_AUTH_STATUS"/>
         <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTHENTICATOR_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTHENTICATION_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTHENTICATION_EXECUTION_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="FLOW_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTHENTICATION_EXECUTION_FLOW" referencedColumnNames="ID" referencedTableName="AUTHENTICATION_FLOW"/>
     </changeSet>
 </databaseChangeLog>
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
new file mode 100755
index 0000000..8cea65b
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
@@ -0,0 +1,83 @@
+package org.keycloak.models;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class AuthenticationExecutionModel {
+
+    private String id;
+    private String authenticator;
+    private boolean autheticatorFlow;
+    private Requirement requirement;
+    private boolean userSetupAllowed;
+    private int priority;
+    private String parentFlow;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAuthenticator() {
+        return authenticator;
+    }
+
+    public void setAuthenticator(String authenticator) {
+        this.authenticator = authenticator;
+    }
+
+    public Requirement getRequirement() {
+        return requirement;
+    }
+
+    public void setRequirement(Requirement requirement) {
+        this.requirement = requirement;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public boolean isUserSetupAllowed() {
+        return userSetupAllowed;
+    }
+
+    public void setUserSetupAllowed(boolean userSetupAllowed) {
+        this.userSetupAllowed = userSetupAllowed;
+    }
+
+    public String getParentFlow() {
+        return parentFlow;
+    }
+
+    public void setParentFlow(String parentFlow) {
+        this.parentFlow = parentFlow;
+    }
+
+    /**
+     * Is the referenced authenticator a flow?
+     *
+     * @return
+     */
+    public boolean isAutheticatorFlow() {
+        return autheticatorFlow;
+    }
+
+    public void setAutheticatorFlow(boolean autheticatorFlow) {
+        this.autheticatorFlow = autheticatorFlow;
+    }
+
+    public enum Requirement {
+        REQUIRED,
+        OPTIONAL,
+        ALTERNATIVE
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
new file mode 100755
index 0000000..b9e5322
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
@@ -0,0 +1,35 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlowModel {
+    private String id;
+    private String alias;
+    private String description;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java
index 0ca9322..0a6cacd 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java
@@ -9,23 +9,9 @@ import java.util.Map;
 */
 public class AuthenticatorModel {
 
-    public enum Requirement {
-        REQUIRED,
-        OPTIONAL,
-        ALTERNATIVE
-    }
-
     private String id;
     private String alias;
     private String providerId;
-    private boolean masterAuthenticator;
-    private boolean formBased;
-    private String inputPage;
-    private String actionUrl;
-    private String setupUrl;
-    private Requirement requirement;
-    private boolean userSetupAllowed;
-    private int priority;
     private Map<String, String> config = new HashMap<String, String>();
 
 
@@ -53,70 +39,6 @@ public class AuthenticatorModel {
         this.providerId = providerId;
     }
 
-    public boolean isFormBased() {
-        return formBased;
-    }
-
-    public void setFormBased(boolean formBased) {
-        this.formBased = formBased;
-    }
-
-    public String getInputPage() {
-        return inputPage;
-    }
-
-    public void setInputPage(String inputPage) {
-        this.inputPage = inputPage;
-    }
-
-    public String getActionUrl() {
-        return actionUrl;
-    }
-
-    public void setActionUrl(String actionUrl) {
-        this.actionUrl = actionUrl;
-    }
-
-    public String getSetupUrl() {
-        return setupUrl;
-    }
-
-    public void setSetupUrl(String setupUrl) {
-        this.setupUrl = setupUrl;
-    }
-
-    public Requirement getRequirement() {
-        return requirement;
-    }
-
-    public void setRequirement(Requirement requirement) {
-        this.requirement = requirement;
-    }
-
-    public int getPriority() {
-        return priority;
-    }
-
-    public void setPriority(int priority) {
-        this.priority = priority;
-    }
-
-    public boolean isUserSetupAllowed() {
-        return userSetupAllowed;
-    }
-
-    public void setUserSetupAllowed(boolean userSetupAllowed) {
-        this.userSetupAllowed = userSetupAllowed;
-    }
-
-    public boolean isMasterAuthenticator() {
-        return masterAuthenticator;
-    }
-
-    public void setMasterAuthenticator(boolean masterAuthenticator) {
-        this.masterAuthenticator = masterAuthenticator;
-    }
-
     public Map<String, String> getConfig() {
         return config;
     }
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
new file mode 100755
index 0000000..c90a657
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationExecutionEntity.java
@@ -0,0 +1,74 @@
+package org.keycloak.models.entities;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationExecutionEntity {
+    protected String id;
+    protected String authenticator;
+    protected AuthenticationExecutionModel.Requirement requirement;
+    protected int priority;
+    private boolean userSetupAllowed;
+    private boolean autheticatorFlow;
+    private String parentFlow;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAuthenticator() {
+        return authenticator;
+    }
+
+    public void setAuthenticator(String authenticator) {
+        this.authenticator = authenticator;
+    }
+
+    public AuthenticationExecutionModel.Requirement getRequirement() {
+        return requirement;
+    }
+
+    public void setRequirement(AuthenticationExecutionModel.Requirement requirement) {
+        this.requirement = requirement;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public boolean isUserSetupAllowed() {
+        return userSetupAllowed;
+    }
+
+    public void setUserSetupAllowed(boolean userSetupAllowed) {
+        this.userSetupAllowed = userSetupAllowed;
+    }
+
+    public boolean isAutheticatorFlow() {
+        return autheticatorFlow;
+    }
+
+    public void setAutheticatorFlow(boolean autheticatorFlow) {
+        this.autheticatorFlow = autheticatorFlow;
+    }
+
+    public String getParentFlow() {
+        return parentFlow;
+    }
+
+    public void setParentFlow(String parentFlow) {
+        this.parentFlow = parentFlow;
+    }
+}
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
new file mode 100755
index 0000000..b79d1a5
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/AuthenticationFlowEntity.java
@@ -0,0 +1,47 @@
+package org.keycloak.models.entities;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlowEntity {
+    protected String id;
+    protected String alias;
+    protected String description;
+    List<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<AuthenticationExecutionEntity> getExecutions() {
+        return executions;
+    }
+
+    public void setExecutions(List<AuthenticationExecutionEntity> executions) {
+        this.executions = executions;
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/AuthenticatorEntity.java b/model/api/src/main/java/org/keycloak/models/entities/AuthenticatorEntity.java
new file mode 100755
index 0000000..c9077c0
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/AuthenticatorEntity.java
@@ -0,0 +1,46 @@
+package org.keycloak.models.entities;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticatorEntity {
+    protected String id;
+    protected String alias;
+    protected String providerId;
+    private Map<String, String> config;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
+
+    public Map<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 718a1c5..822edc4 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -73,6 +73,8 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     private List<String> supportedLocales = new ArrayList<String>();
     private String defaultLocale;
     private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
+    private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
+    private List<AuthenticatorEntity> authenticators = new ArrayList<>();
 
 
     public String getName() {
@@ -473,6 +475,22 @@ public class RealmEntity extends AbstractIdentifiableEntity {
     public void setIdentityProviderMappers(List<IdentityProviderMapperEntity> identityProviderMappers) {
         this.identityProviderMappers = identityProviderMappers;
     }
+
+    public List<AuthenticationFlowEntity> getAuthenticationFlows() {
+        return authenticationFlows;
+    }
+
+    public void setAuthenticationFlows(List<AuthenticationFlowEntity> authenticationFlows) {
+        this.authenticationFlows = authenticationFlows;
+    }
+
+    public List<AuthenticatorEntity> getAuthenticators() {
+        return authenticators;
+    }
+
+    public void setAuthenticators(List<AuthenticatorEntity> authenticators) {
+        this.authenticators = authenticators;
+    }
 }
 
 
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index dca2067..90afbb3 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -168,6 +168,25 @@ public interface RealmModel extends RoleContainerModel {
 
     void setSmtpConfig(Map<String, String> smtpConfig);
 
+    List<AuthenticationFlowModel> getAuthenticationFlows();
+    AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model);
+    AuthenticationFlowModel getAuthenticationFlowById(String id);
+    void removeAuthenticationFlow(AuthenticationFlowModel model);
+    void updateAuthenticationFlow(AuthenticationFlowModel model);
+
+    List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId);
+    AuthenticationExecutionModel getAuthenticationExecutionById(String id);
+    AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model);
+    void updateAuthenticatorExecution(AuthenticationExecutionModel model);
+    void removeAuthenticatorExecution(AuthenticationExecutionModel model);
+
+
+    List<AuthenticatorModel> getAuthenticators();
+    AuthenticatorModel addAuthenticator(AuthenticatorModel model);
+    void updateAuthenticator(AuthenticatorModel model);
+    void removeAuthenticator(AuthenticatorModel model);
+    AuthenticatorModel getAuthenticatorById(String id);
+
     List<IdentityProviderModel> getIdentityProviders();
     IdentityProviderModel getIdentityProviderByAlias(String alias);
     void addIdentityProvider(IdentityProviderModel identityProvider);
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 d835102..feae198 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
@@ -18,6 +18,9 @@ package org.keycloak.models.file.adapter;
 
 import org.keycloak.connections.file.InMemoryModel;
 import org.keycloak.enums.SslRequired;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -29,6 +32,9 @@ import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.entities.AuthenticationExecutionEntity;
+import org.keycloak.models.entities.AuthenticationFlowEntity;
+import org.keycloak.models.entities.AuthenticatorEntity;
 import org.keycloak.models.entities.ClientEntity;
 import org.keycloak.models.entities.IdentityProviderMapperEntity;
 import org.keycloak.models.entities.RealmEntity;
@@ -1184,4 +1190,232 @@ public class RealmAdapter implements RealmModel {
         return mapping;
     }
 
+    @Override
+    public List<AuthenticationFlowModel> getAuthenticationFlows() {
+        List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
+        if (flows.size() == 0) return Collections.EMPTY_LIST;
+        List<AuthenticationFlowModel> models = new LinkedList<>();
+        for (AuthenticationFlowEntity entity : flows) {
+            AuthenticationFlowModel model = entityToModel(entity);
+            models.add(model);
+        }
+        return models;
+    }
+
+    protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
+        AuthenticationFlowModel model = new AuthenticationFlowModel();
+        model.setId(entity.getId());
+        model.setAlias(entity.getAlias());
+        model.setDescription(entity.getDescription());
+        return model;
+    }
+
+    @Override
+    public AuthenticationFlowModel getAuthenticationFlowById(String id) {
+        for (AuthenticationFlowModel model : getAuthenticationFlows()) {
+            if (model.getId().equals(id)) return model;
+        }
+        return null;
+    }
+
+    protected AuthenticationFlowEntity getFlowEntity(String id) {
+        List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
+        for (AuthenticationFlowEntity entity : flows) {
+            if (id.equals(entity.getId())) return entity;
+        }
+        return null;
+
+    }
+
+    @Override
+    public void removeAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity toDelete = getFlowEntity(model.getId());
+        if (toDelete == null) return;
+        realm.getAuthenticationFlows().remove(toDelete);
+    }
+
+    @Override
+    public void updateAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity toUpdate = getFlowEntity(model.getId());
+        if (toUpdate == null) return;
+        toUpdate.setAlias(model.getAlias());
+        toUpdate.setDescription(model.getDescription());
+
+    }
+
+    @Override
+    public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAlias(model.getAlias());
+        entity.setDescription(model.getDescription());
+        realm.getAuthenticationFlows().add(entity);
+        model.setId(entity.getId());
+        return model;
+    }
+
+    @Override
+    public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
+        AuthenticationFlowEntity flow = getFlowEntity(flowId);
+        if (flow == null) return Collections.EMPTY_LIST;
+
+        List<AuthenticationExecutionEntity> queryResult = flow.getExecutions();
+        List<AuthenticationExecutionModel> executions = new LinkedList<>();
+        for (AuthenticationExecutionEntity entity : queryResult) {
+            AuthenticationExecutionModel model = entityToModel(entity);
+            executions.add(model);
+        }
+        return executions;
+    }
+
+    public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
+        AuthenticationExecutionModel model = new AuthenticationExecutionModel();
+        model.setId(entity.getId());
+        model.setUserSetupAllowed(entity.isUserSetupAllowed());
+        model.setRequirement(entity.getRequirement());
+        model.setPriority(entity.getPriority());
+        model.setAuthenticator(entity.getAuthenticator());
+        model.setParentFlow(entity.getParentFlow());
+        model.setAutheticatorFlow(entity.isAutheticatorFlow());
+        return model;
+    }
+
+    @Override
+    public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
+        AuthenticationExecutionEntity execution = getAuthenticationExecutionEntity(id);
+        return entityToModel(execution);
+    }
+
+    public AuthenticationExecutionEntity getAuthenticationExecutionEntity(String id) {
+        List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
+        for (AuthenticationFlowEntity entity : flows) {
+            for (AuthenticationExecutionEntity exe : entity.getExecutions()) {
+                if (exe.getId().equals(id)) {
+                    return exe;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        AuthenticationFlowEntity flow = getFlowEntity(model.getId());
+        flow.getExecutions().add(entity);
+        model.setId(entity.getId());
+        return model;
+
+    }
+
+    @Override
+    public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = null;
+        AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
+        for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
+            if (exe.getId().equals(model.getId())) {
+                entity = exe;
+            }
+        }
+        if (entity == null) return;
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+    }
+
+    @Override
+    public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = null;
+        AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
+        for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
+            if (exe.getId().equals(model.getId())) {
+                entity = exe;
+            }
+        }
+        if (entity == null) return;
+        flow.getExecutions().remove(entity);
+
+    }
+
+    @Override
+    public List<AuthenticatorModel> getAuthenticators() {
+        List<AuthenticatorModel> authenticators = new LinkedList<>();
+        for (AuthenticatorEntity entity : realm.getAuthenticators()) {
+            authenticators.add(entityToModel(entity));
+        }
+        return authenticators;
+    }
+
+    @Override
+    public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity auth = new AuthenticatorEntity();
+        auth.setId(KeycloakModelUtils.generateId());
+        auth.setAlias(model.getAlias());
+        auth.setProviderId(model.getProviderId());
+        auth.setConfig(model.getConfig());
+        realm.getAuthenticators().add(auth);
+        model.setId(auth.getId());
+        return model;
+    }
+
+    @Override
+    public void removeAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
+        if (entity == null) return;
+        realm.getAuthenticators().remove(entity);
+
+    }
+
+    @Override
+    public AuthenticatorModel getAuthenticatorById(String id) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(id);
+        if (entity == null) return null;
+        return entityToModel(entity);
+    }
+
+    public AuthenticatorEntity getAuthenticatorEntity(String id) {
+        AuthenticatorEntity entity = null;
+        for (AuthenticatorEntity auth : realm.getAuthenticators()) {
+            if (auth.getId().equals(id)) {
+                entity = auth;
+                break;
+            }
+        }
+        return entity;
+    }
+
+    public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
+        AuthenticatorModel model = new AuthenticatorModel();
+        model.setId(entity.getId());
+        model.setProviderId(entity.getProviderId());
+        model.setAlias(entity.getAlias());
+        Map<String, String> config = new HashMap<>();
+        if (entity.getConfig() != null) config.putAll(entity.getConfig());
+        model.setConfig(config);
+        return model;
+    }
+
+    @Override
+    public void updateAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
+        if (entity == null) return;
+        entity.setAlias(model.getAlias());
+        entity.setProviderId(model.getProviderId());
+        if (entity.getConfig() == null) {
+            entity.setConfig(model.getConfig());
+        } else {
+            entity.getConfig().clear();
+            entity.getConfig().putAll(model.getConfig());
+        }
+    }
+
+
 }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index 22f57a9..5329a9c 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -1,6 +1,9 @@
 package org.keycloak.models.cache.entities;
 
 import org.keycloak.enums.SslRequired;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -74,6 +77,10 @@ public class CachedRealm {
 
     private Map<String, String> browserSecurityHeaders = new HashMap<String, String>();
     private Map<String, String> smtpConfig = new HashMap<String, String>();
+    private Map<String, AuthenticationFlowModel> authenticationFlows = new HashMap<>();
+    private Map<String, AuthenticatorModel> authenticators = new HashMap<>();
+    private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
+    private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
 
     private boolean eventsEnabled;
     private long eventsExpiration;
@@ -178,6 +185,16 @@ public class CachedRealm {
         internationalizationEnabled = model.isInternationalizationEnabled();
         supportedLocales.addAll(model.getSupportedLocales());
         defaultLocale = model.getDefaultLocale();
+        for (AuthenticationFlowModel flow : model.getAuthenticationFlows()) {
+            authenticationFlows.put(flow.getId(), flow);
+            for (AuthenticationExecutionModel execution : model.getAuthenticationExecutions(flow.getId())) {
+                authenticationExecutions.add(flow.getId(), execution);
+                executionsById.put(execution.getId(), execution);
+            }
+        }
+        for (AuthenticatorModel authenticator : model.getAuthenticators()) {
+            authenticators.put(authenticator.getId(), authenticator);
+        }
 
     }
 
@@ -396,4 +413,20 @@ public class CachedRealm {
     public MultivaluedHashMap<String, IdentityProviderMapperModel> getIdentityProviderMappers() {
         return identityProviderMappers;
     }
+
+    public Map<String, AuthenticationFlowModel> getAuthenticationFlows() {
+        return authenticationFlows;
+    }
+
+    public Map<String, AuthenticatorModel> getAuthenticators() {
+        return authenticators;
+    }
+
+    public MultivaluedHashMap<String, AuthenticationExecutionModel> getAuthenticationExecutions() {
+        return authenticationExecutions;
+    }
+
+    public Map<String, AuthenticationExecutionModel> getExecutionsById() {
+        return executionsById;
+    }
 }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index 33f2356..2f6f8d6 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -2,6 +2,9 @@ package org.keycloak.models.cache;
 
 import org.keycloak.Config;
 import org.keycloak.enums.SslRequired;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -17,6 +20,7 @@ import java.security.Key;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -948,4 +952,104 @@ public class RealmAdapter implements RealmModel {
         return null;
     }
 
+    @Override
+    public List<AuthenticationFlowModel> getAuthenticationFlows() {
+        if (updated != null) return updated.getAuthenticationFlows();
+        List<AuthenticationFlowModel> models = new ArrayList<>();
+        models.addAll(cached.getAuthenticationFlows().values());
+        return models;
+    }
+
+    @Override
+    public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
+        getDelegateForUpdate();
+        return updated.addAuthenticationFlow(model);
+    }
+
+    @Override
+    public AuthenticationFlowModel getAuthenticationFlowById(String id) {
+        if (updated != null) return updated.getAuthenticationFlowById(id);
+        return cached.getAuthenticationFlows().get(id);
+    }
+
+    @Override
+    public void removeAuthenticationFlow(AuthenticationFlowModel model) {
+        getDelegateForUpdate();
+        updated.removeAuthenticationFlow(model);
+
+    }
+
+    @Override
+    public void updateAuthenticationFlow(AuthenticationFlowModel model) {
+        getDelegateForUpdate();
+        updated.updateAuthenticationFlow(model);
+
+    }
+
+    @Override
+    public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
+        if (updated != null) return updated.getAuthenticationExecutions(flowId);
+        List<AuthenticationExecutionModel> models = new ArrayList<>();
+        return cached.getAuthenticationExecutions().get(flowId);
+    }
+
+    @Override
+    public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
+        if (updated != null) return updated.getAuthenticationExecutionById(id);
+        return cached.getExecutionsById().get(id);
+    }
+
+    @Override
+    public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
+        getDelegateForUpdate();
+        return updated.addAuthenticatorExecution(model);
+    }
+
+    @Override
+    public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
+        getDelegateForUpdate();
+        updated.updateAuthenticatorExecution(model);
+
+    }
+
+    @Override
+    public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
+        getDelegateForUpdate();
+        updated.removeAuthenticatorExecution(model);
+
+    }
+
+    @Override
+    public List<AuthenticatorModel> getAuthenticators() {
+        if (updated != null) return updated.getAuthenticators();
+        List<AuthenticatorModel> models = new ArrayList<>();
+        models.addAll(cached.getAuthenticators().values());
+        return models;
+    }
+
+    @Override
+    public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
+        getDelegateForUpdate();
+        return updated.addAuthenticator(model);
+    }
+
+    @Override
+    public void updateAuthenticator(AuthenticatorModel model) {
+        getDelegateForUpdate();
+        updated.updateAuthenticator(model);
+
+    }
+
+    @Override
+    public void removeAuthenticator(AuthenticatorModel model) {
+        getDelegateForUpdate();
+        updated.removeAuthenticator(model);
+
+    }
+
+    @Override
+    public AuthenticatorModel getAuthenticatorById(String id) {
+        if (updated != null) return updated.getAuthenticatorById(id);
+        return cached.getAuthenticators().get(id);
+    }
 }
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
new file mode 100755
index 0000000..be8720a
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
@@ -0,0 +1,117 @@
+package org.keycloak.models.jpa.entities;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@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="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"),
+})
+public class AuthenticationExecutionEntity {
+    @Id
+    @Column(name="ID", length = 36)
+    protected String id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "REALM_ID")
+    protected RealmEntity realm;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "FLOW_ID")
+    protected AuthenticationFlowEntity flow;
+
+    @Column(name="AUTHENTICATOR")
+    protected String authenticator;
+
+    @Column(name="REQUIREMENT")
+    protected AuthenticationExecutionModel.Requirement requirement;
+
+    @Column(name="PRIORITY")
+    protected int priority;
+
+    @Column(name="USER_SETUP_ALLOWED")
+    private boolean userSetupAllowed;
+
+    @Column(name="AUTHENTICATOR_FLOW")
+    private boolean autheticatorFlow;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+
+    public String getAuthenticator() {
+        return authenticator;
+    }
+
+    public void setAuthenticator(String authenticator) {
+        this.authenticator = authenticator;
+    }
+
+    public AuthenticationExecutionModel.Requirement getRequirement() {
+        return requirement;
+    }
+
+    public void setRequirement(AuthenticationExecutionModel.Requirement requirement) {
+        this.requirement = requirement;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    public void setPriority(int priority) {
+        this.priority = priority;
+    }
+
+    public boolean isUserSetupAllowed() {
+        return userSetupAllowed;
+    }
+
+    public void setUserSetupAllowed(boolean userSetupAllowed) {
+        this.userSetupAllowed = userSetupAllowed;
+    }
+
+    public boolean isAutheticatorFlow() {
+        return autheticatorFlow;
+    }
+
+    public void setAutheticatorFlow(boolean autheticatorFlow) {
+        this.autheticatorFlow = autheticatorFlow;
+    }
+
+    public AuthenticationFlowEntity getFlow() {
+        return flow;
+    }
+
+    public void setFlow(AuthenticationFlowEntity flow) {
+        this.flow = flow;
+    }
+}
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
new file mode 100755
index 0000000..464fb4a
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationFlowEntity.java
@@ -0,0 +1,89 @@
+package org.keycloak.models.jpa.entities;
+
+import org.keycloak.models.AuthenticatorModel;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Table(name="AUTHENTICATION_FLOW")
+@Entity
+@NamedQueries({
+        @NamedQuery(name="getAuthenticationFlowsByRealm", query="select flow from AuthenticationFlowEntity flow where flow.realm = :realm"),
+        @NamedQuery(name="deleteAuthenticationFlowByRealm", query="delete from AuthenticationFlowEntity flow where flow.realm = :realm")
+})
+public class AuthenticationFlowEntity {
+    @Id
+    @Column(name="ID", length = 36)
+    protected String id;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "REALM_ID")
+    protected RealmEntity realm;
+
+    @Column(name="ALIAS")
+    protected String alias;
+
+    @Column(name="DESCRIPTION")
+    protected String description;
+
+    @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "flow")
+    Collection<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Collection<AuthenticationExecutionEntity> getExecutions() {
+        return executions;
+    }
+
+    public void setExecutions(Collection<AuthenticationExecutionEntity> executions) {
+        this.executions = executions;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticatorEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticatorEntity.java
new file mode 100755
index 0000000..1e97cd2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticatorEntity.java
@@ -0,0 +1,89 @@
+package org.keycloak.models.jpa.entities;
+
+import org.keycloak.models.AuthenticatorModel;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Table(name="AUTHENTICATOR")
+@Entity
+@NamedQueries({
+        @NamedQuery(name="deleteAuthenticatorsByRealm", query="delete from AuthenticatorEntity authenticator where authenticator.realm = :realm"),})
+public class AuthenticatorEntity {
+    @Id
+    @Column(name="ID", length = 36)
+    protected String id;
+
+    @Column(name="ALIAS")
+    protected String alias;
+
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "REALM_ID")
+    protected RealmEntity realm;
+
+    @Column(name="PROVIDER_ID")
+    protected String providerId;
+
+    @ElementCollection
+    @MapKeyColumn(name="NAME")
+    @Column(name="VALUE")
+    @CollectionTable(name="AUTHENTICATOR_CONFIG", joinColumns={ @JoinColumn(name="AUTHENTICATOR_ID") })
+    private Map<String, String> config;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(String providerId) {
+        this.providerId = providerId;
+    }
+
+    public RealmEntity getRealm() {
+        return realm;
+    }
+
+    public void setRealm(RealmEntity realm) {
+        this.realm = realm;
+    }
+
+    public Map<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index cdf314c..9ac1c3e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -151,6 +151,12 @@ public class RealmEntity {
     @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
     Collection<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
 
+    @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+    Collection<AuthenticatorEntity> authenticators = new ArrayList<>();
+
+    @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+    Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
+
 
 
     @Column(name="INTERNATIONALIZATION_ENABLED")
@@ -535,5 +541,21 @@ public class RealmEntity {
     public void setIdentityProviderMappers(Collection<IdentityProviderMapperEntity> identityProviderMappers) {
         this.identityProviderMappers = identityProviderMappers;
     }
+
+    public Collection<AuthenticatorEntity> getAuthenticators() {
+        return authenticators;
+    }
+
+    public void setAuthenticators(Collection<AuthenticatorEntity> authenticators) {
+        this.authenticators = authenticators;
+    }
+
+    public Collection<AuthenticationFlowEntity> getAuthenticationFlows() {
+        return authenticationFlows;
+    }
+
+    public void setAuthenticationFlows(Collection<AuthenticationFlowEntity> authenticationFlows) {
+        this.authenticationFlows = authenticationFlows;
+    }
 }
 
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 66b94d0..9a7eb97 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
@@ -1,6 +1,9 @@
 package org.keycloak.models.jpa;
 
 import org.keycloak.enums.SslRequired;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -10,6 +13,9 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.jpa.entities.AuthenticationExecutionEntity;
+import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
+import org.keycloak.models.jpa.entities.AuthenticatorEntity;
 import org.keycloak.models.jpa.entities.ClientEntity;
 import org.keycloak.models.jpa.entities.IdentityProviderEntity;
 import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
@@ -1351,4 +1357,207 @@ public class RealmAdapter implements RealmModel {
         return mapping;
     }
 
+    @Override
+    public List<AuthenticationFlowModel> getAuthenticationFlows() {
+        TypedQuery<AuthenticationFlowEntity> query = em.createNamedQuery("getAuthenticationFlowsByRealm", AuthenticationFlowEntity.class);
+        query.setParameter("realm", realm);
+        List<AuthenticationFlowEntity> flows = query.getResultList();
+        if (flows.size() == 0) return Collections.EMPTY_LIST;
+        List<AuthenticationFlowModel> models = new LinkedList<>();
+        for (AuthenticationFlowEntity entity : flows) {
+            AuthenticationFlowModel model = entityToModel(entity);
+            models.add(model);
+        }
+        return models;
+    }
+
+    protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
+        AuthenticationFlowModel model = new AuthenticationFlowModel();
+        model.setId(entity.getId());
+        model.setAlias(entity.getAlias());
+        model.setDescription(entity.getDescription());
+        return model;
+    }
+
+    @Override
+    public AuthenticationFlowModel getAuthenticationFlowById(String id) {
+        AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, id);
+        if (entity == null) return null;
+        return entityToModel(entity);
+    }
+
+    @Override
+    public void removeAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, model.getId());
+        if (entity == null) return;
+        em.remove(entity);
+        em.flush();
+    }
+
+    @Override
+    public void updateAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, model.getId());
+        if (entity == null) return;
+        entity.setAlias(model.getAlias());
+        entity.setDescription(model.getDescription());
+
+    }
+
+    @Override
+    public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAlias(model.getAlias());
+        entity.setDescription(model.getDescription());
+        entity.setRealm(realm);
+        realm.getAuthenticationFlows().add(entity);
+        em.persist(entity);
+        em.flush();
+        model.setId(entity.getId());
+        return model;
+    }
+
+    @Override
+    public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
+        TypedQuery<AuthenticationExecutionEntity> query = em.createNamedQuery("getAuthenticationExecutionsByFlow", AuthenticationExecutionEntity.class);
+        AuthenticationFlowEntity flow = em.getReference(AuthenticationFlowEntity.class, flowId);
+        query.setParameter("realm", realm);
+        query.setParameter("flow", flow);
+        List<AuthenticationExecutionEntity> queryResult = query.getResultList();
+        List<AuthenticationExecutionModel> executions = new LinkedList<>();
+        for (AuthenticationExecutionEntity entity : queryResult) {
+            AuthenticationExecutionModel model = entityToModel(entity);
+            executions.add(model);
+        }
+        return executions;
+    }
+
+    public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
+        AuthenticationExecutionModel model = new AuthenticationExecutionModel();
+        model.setId(entity.getId());
+        model.setUserSetupAllowed(entity.isUserSetupAllowed());
+        model.setRequirement(entity.getRequirement());
+        model.setPriority(entity.getPriority());
+        model.setAuthenticator(entity.getAuthenticator());
+        model.setParentFlow(entity.getFlow().getId());
+        model.setAutheticatorFlow(entity.isAutheticatorFlow());
+        return model;
+    }
+
+    @Override
+    public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
+        AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, id);
+        if (entity == null) return null;
+        return entityToModel(entity);
+    }
+
+    @Override
+    public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        AuthenticationFlowEntity flow = em.find(AuthenticationFlowEntity.class, model.getParentFlow());
+        entity.setFlow(flow);
+        flow.getExecutions().add(entity);
+        entity.setRealm(realm);
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        em.persist(entity);
+        em.flush();
+        model.setId(entity.getId());
+        return model;
+
+    }
+
+    @Override
+    public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
+        if (entity == null) return;
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        em.flush();
+    }
+
+    @Override
+    public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
+        if (entity == null) return;
+        em.remove(entity);
+        em.flush();
+
+    }
+
+    @Override
+    public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity auth = new AuthenticatorEntity();
+        auth.setId(KeycloakModelUtils.generateId());
+        auth.setAlias(model.getAlias());
+        auth.setRealm(realm);
+        auth.setProviderId(model.getProviderId());
+        auth.setConfig(model.getConfig());
+        realm.getAuthenticators().add(auth);
+        em.persist(auth);
+        em.flush();
+        model.setId(auth.getId());
+        return model;
+    }
+
+    @Override
+    public void removeAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, model.getId());
+        if (entity == null) return;
+        em.remove(entity);
+        em.flush();
+
+    }
+
+    @Override
+    public AuthenticatorModel getAuthenticatorById(String id) {
+        AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, id);
+        if (entity == null) return null;
+        return entityToModel(entity);
+    }
+
+    public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
+        AuthenticatorModel model = new AuthenticatorModel();
+        model.setId(entity.getId());
+        model.setProviderId(entity.getProviderId());
+        model.setAlias(entity.getAlias());
+        Map<String, String> config = new HashMap<>();
+        if (entity.getConfig() != null) config.putAll(entity.getConfig());
+        model.setConfig(config);
+        return model;
+    }
+
+    @Override
+    public void updateAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, model.getId());
+        if (entity == null) return;
+        entity.setAlias(model.getAlias());
+        entity.setProviderId(model.getProviderId());
+        if (entity.getConfig() == null) {
+            entity.setConfig(model.getConfig());
+        } else {
+            entity.getConfig().clear();
+            entity.getConfig().putAll(model.getConfig());
+        }
+        em.flush();
+
+    }
+
+    @Override
+    public List<AuthenticatorModel> getAuthenticators() {
+        List<AuthenticatorModel> authenticators = new LinkedList<>();
+        for (AuthenticatorEntity entity : realm.getAuthenticators()) {
+            authenticators.add(entityToModel(entity));
+        }
+        return authenticators;
+    }
+
+
 }
\ No newline at end of file
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 d797de9..579aea0 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
@@ -5,6 +5,9 @@ import com.mongodb.QueryBuilder;
 
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.enums.SslRequired;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -15,6 +18,9 @@ import org.keycloak.models.RealmProvider;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.entities.AuthenticationExecutionEntity;
+import org.keycloak.models.entities.AuthenticationFlowEntity;
+import org.keycloak.models.entities.AuthenticatorEntity;
 import org.keycloak.models.entities.IdentityProviderEntity;
 import org.keycloak.models.entities.IdentityProviderMapperEntity;
 import org.keycloak.models.entities.RequiredCredentialEntity;
@@ -1169,6 +1175,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         if (toDelete != null) {
             this.realm.getIdentityProviderMappers().remove(toDelete);
         }
+        updateMongoEntity();
 
     }
 
@@ -1213,4 +1220,241 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         return mapping;
     }
 
+    @Override
+    public List<AuthenticationFlowModel> getAuthenticationFlows() {
+        List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
+        if (flows.size() == 0) return Collections.EMPTY_LIST;
+        List<AuthenticationFlowModel> models = new LinkedList<>();
+        for (AuthenticationFlowEntity entity : flows) {
+            AuthenticationFlowModel model = entityToModel(entity);
+            models.add(model);
+        }
+        return models;
+    }
+
+    protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
+        AuthenticationFlowModel model = new AuthenticationFlowModel();
+        model.setId(entity.getId());
+        model.setAlias(entity.getAlias());
+        model.setDescription(entity.getDescription());
+        return model;
+    }
+
+    @Override
+    public AuthenticationFlowModel getAuthenticationFlowById(String id) {
+        for (AuthenticationFlowModel model : getAuthenticationFlows()) {
+            if (model.getId().equals(id)) return model;
+        }
+        return null;
+    }
+
+    protected AuthenticationFlowEntity getFlowEntity(String id) {
+        List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
+        for (AuthenticationFlowEntity entity : flows) {
+            if (id.equals(entity.getId())) return entity;
+        }
+        return null;
+
+    }
+
+    @Override
+    public void removeAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity toDelete = getFlowEntity(model.getId());
+        if (toDelete == null) return;
+        getMongoEntity().getAuthenticationFlows().remove(toDelete);
+        updateMongoEntity();
+    }
+
+    @Override
+    public void updateAuthenticationFlow(AuthenticationFlowModel model) {
+        List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
+        AuthenticationFlowEntity toUpdate = getFlowEntity(model.getId());;
+        if (toUpdate == null) return;
+        toUpdate.setAlias(model.getAlias());
+        toUpdate.setDescription(model.getDescription());
+        updateMongoEntity();
+    }
+
+    @Override
+    public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
+        AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAlias(model.getAlias());
+        entity.setDescription(model.getDescription());
+        getMongoEntity().getAuthenticationFlows().add(entity);
+        model.setId(entity.getId());
+        updateMongoEntity();
+        return model;
+    }
+
+    @Override
+    public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
+        AuthenticationFlowEntity flow = getFlowEntity(flowId);
+        if (flow == null) return Collections.EMPTY_LIST;
+
+        List<AuthenticationExecutionEntity> queryResult = flow.getExecutions();
+        List<AuthenticationExecutionModel> executions = new LinkedList<>();
+        for (AuthenticationExecutionEntity entity : queryResult) {
+            AuthenticationExecutionModel model = entityToModel(entity);
+            executions.add(model);
+        }
+        return executions;
+    }
+
+    public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
+        AuthenticationExecutionModel model = new AuthenticationExecutionModel();
+        model.setId(entity.getId());
+        model.setUserSetupAllowed(entity.isUserSetupAllowed());
+        model.setRequirement(entity.getRequirement());
+        model.setPriority(entity.getPriority());
+        model.setAuthenticator(entity.getAuthenticator());
+        model.setParentFlow(entity.getParentFlow());
+        model.setAutheticatorFlow(entity.isAutheticatorFlow());
+        return model;
+    }
+
+    @Override
+    public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
+        AuthenticationExecutionEntity execution = getAuthenticationExecutionEntity(id);
+        return entityToModel(execution);
+    }
+
+    public AuthenticationExecutionEntity getAuthenticationExecutionEntity(String id) {
+        List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
+        for (AuthenticationFlowEntity entity : flows) {
+            for (AuthenticationExecutionEntity exe : entity.getExecutions()) {
+                if (exe.getId().equals(id)) {
+                   return exe;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        AuthenticationFlowEntity flow = getFlowEntity(model.getId());
+        flow.getExecutions().add(entity);
+        updateMongoEntity();
+        model.setId(entity.getId());
+        return model;
+
+    }
+
+    @Override
+    public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = null;
+        AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
+        for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
+            if (exe.getId().equals(model.getId())) {
+                entity = exe;
+            }
+        }
+        if (entity == null) return;
+        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticator(model.getAuthenticator());
+        entity.setPriority(model.getPriority());
+        entity.setRequirement(model.getRequirement());
+        entity.setUserSetupAllowed(model.isUserSetupAllowed());
+        updateMongoEntity();
+    }
+
+    @Override
+    public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
+        AuthenticationExecutionEntity entity = null;
+        AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
+        for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
+            if (exe.getId().equals(model.getId())) {
+                entity = exe;
+            }
+        }
+        if (entity == null) return;
+        flow.getExecutions().remove(entity);
+        updateMongoEntity();
+
+    }
+
+    @Override
+    public List<AuthenticatorModel> getAuthenticators() {
+        List<AuthenticatorModel> authenticators = new LinkedList<>();
+        for (AuthenticatorEntity entity : getMongoEntity().getAuthenticators()) {
+            authenticators.add(entityToModel(entity));
+        }
+        return authenticators;
+    }
+
+    @Override
+    public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity auth = new AuthenticatorEntity();
+        auth.setId(KeycloakModelUtils.generateId());
+        auth.setAlias(model.getAlias());
+        auth.setProviderId(model.getProviderId());
+        auth.setConfig(model.getConfig());
+        realm.getAuthenticators().add(auth);
+        model.setId(auth.getId());
+        updateMongoEntity();
+        return model;
+    }
+
+    @Override
+    public void removeAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
+        if (entity == null) return;
+        getMongoEntity().getAuthenticators().remove(entity);
+        updateMongoEntity();
+
+    }
+
+    @Override
+    public AuthenticatorModel getAuthenticatorById(String id) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(id);
+        if (entity == null) return null;
+        return entityToModel(entity);
+    }
+
+    public AuthenticatorEntity getAuthenticatorEntity(String id) {
+        AuthenticatorEntity entity = null;
+        for (AuthenticatorEntity auth : getMongoEntity().getAuthenticators()) {
+            if (auth.getId().equals(id)) {
+                entity = auth;
+                break;
+            }
+        }
+        return entity;
+    }
+
+    public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
+        AuthenticatorModel model = new AuthenticatorModel();
+        model.setId(entity.getId());
+        model.setProviderId(entity.getProviderId());
+        model.setAlias(entity.getAlias());
+        Map<String, String> config = new HashMap<>();
+        if (entity.getConfig() != null) config.putAll(entity.getConfig());
+        model.setConfig(config);
+        return model;
+    }
+
+    @Override
+    public void updateAuthenticator(AuthenticatorModel model) {
+        AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
+        if (entity == null) return;
+        entity.setAlias(model.getAlias());
+        entity.setProviderId(model.getProviderId());
+        if (entity.getConfig() == null) {
+            entity.setConfig(model.getConfig());
+        } else {
+            entity.getConfig().clear();
+            entity.getConfig().putAll(model.getConfig());
+        }
+        updateMongoEntity();
+    }
+
+
 }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 6f63004..5b20edf 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -3,6 +3,7 @@ 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.AuthenticatorModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
@@ -52,7 +53,7 @@ public class AuthenticationProcessor {
     protected ClientConnection connection;
     protected UriInfo uriInfo;
     protected KeycloakSession session;
-    protected List<AuthenticatorModel> authenticators;
+    protected List<AuthenticationExecutionModel> executions;
     protected BruteForceProtector protector;
     protected EventBuilder eventBuilder;
     protected HttpRequest request;
@@ -160,6 +161,13 @@ public class AuthenticationProcessor {
             this.challenge = challenge;
 
         }
+        @Override
+        public void failure(Error error, Response challenge) {
+            this.error = error;
+            this.status = Status.FAILED;
+            this.challenge = challenge;
+
+        }
 
         @Override
         public void attempted() {
@@ -264,28 +272,29 @@ public class AuthenticationProcessor {
         validateUser(authUser);
         Response challenge = null;
         Map<String, UserSessionModel.AuthenticatorStatus> previousAttempts = clientSession.getAuthenticators();
-        for (AuthenticatorModel model : authenticators) {
-            UserSessionModel.AuthenticatorStatus oldStatus = previousAttempts.get(model.getAlias());
+        for (AuthenticationExecutionModel model : executions) {
+            UserSessionModel.AuthenticatorStatus oldStatus = previousAttempts.get(model.getId());
             if (isProcessed(oldStatus)) continue;
 
-            AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getProviderId());
-            Authenticator authenticator = factory.create(model);
+            AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
+            AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
+            Authenticator authenticator = factory.create(authenticatorModel);
             if (authenticator.requiresUser() && authUser == null){
                 if ( authenticator.requiresUser()) {
                     if (challenge != null) return challenge;
                     throw new AuthException(Error.UNKNOWN_USER);
                 }
             }
-            if (authUser != null && model.getRequirement() == AuthenticatorModel.Requirement.ALTERNATIVE) {
-                clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SKIPPED);
+            if (authUser != null && model.getRequirement() == AuthenticationExecutionModel.Requirement.ALTERNATIVE) {
+                clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
                 continue;
             }
             authUser = clientSession.getAuthenticatedUser();
 
             if (authenticator.requiresUser() && authUser != null && !authenticator.configuredFor(authUser)) {
-                if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) {
+                if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
                     if (model.isUserSetupAllowed()) {
-                        clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
+                        clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
                         authUser.addRequiredAction(authenticator.getRequiredAction());
 
                     } else {
@@ -294,25 +303,26 @@ public class AuthenticationProcessor {
                 }
                 continue;
             }
-            Result context = new Result(model, authenticator);
+            Result context = new Result(authenticatorModel, authenticator);
             authenticator.authenticate(context);
             Status result = context.getStatus();
             if (result == Status.SUCCESS){
-                clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SUCCESS);
-                if (model.isMasterAuthenticator()) return authenticationComplete();
+                clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
+                //if (model.isMasterAuthenticator()) return authenticationComplete();
                 continue;
             } else if (result == Status.FAILED) {
+                if (context.challenge != null) return context.challenge;
                 throw new AuthException(context.error);
             } else if (result == Status.CHALLENGE) {
-                if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) return context.challenge;
+                if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) return context.challenge;
                 if (challenge != null) challenge = context.challenge;
                 continue;
             } else if (result == Status.FAILURE_CHALLENGE) {
                 logUserFailure();
                 return context.challenge;
             } else if (result == Status.ATTEMPTED) {
-                if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
-                clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
+                if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
+                clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
                 continue;
             }
         }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
index 162c9b6..8a91d5e 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
@@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.services.managers.BruteForceProtector;
+import org.keycloak.services.managers.ClientSessionCode;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -48,6 +49,7 @@ public interface AuthenticatorContext {
 
     void success();
     void failure(AuthenticationProcessor.Error error);
+    void failure(AuthenticationProcessor.Error error, Response response);
     void challenge(Response challenge);
     void failureChallenge(AuthenticationProcessor.Error error, Response challenge);
     void attempted();
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
index 28290fc..04b445e 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
@@ -1,13 +1,16 @@
 package org.keycloak.authentication;
 
 import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.ProviderFactory;
 
 /**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
-public interface AuthenticatorFactory extends ProviderFactory<Authenticator> {
+public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider {
     Authenticator create(AuthenticatorModel model);
+    String getDisplayCategory();
+    String getDisplayType();
 
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/AuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/authenticators/AuthenticationFlow.java
new file mode 100755
index 0000000..98e5826
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/AuthenticationFlow.java
@@ -0,0 +1,60 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticatorModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlow {
+
+    /**
+     * Hardcoded models just to test this stuff.  It is temporary
+     */
+    static List<AuthenticationExecutionModel> hardcoded = new ArrayList<>();
+
+    /*
+    static {
+        AuthenticationExecutionModel model = new AuthenticationExecutionModel();
+        model.setId("1");
+        model.setAlias("cookie");
+        model.setMasterAuthenticator(true);
+        model.setProviderId(CookieAuthenticatorFactory.PROVIDER_ID);
+        model.setPriority(0);
+        model.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+        model.setUserSetupAllowed(false);
+        hardcoded.add(model);
+        model = new AuthenticatorModel();
+        model.setId("2");
+        model.setAlias("user form");
+        model.setMasterAuthenticator(false);
+        model.setProviderId(LoginFormUsernameAuthenticatorFactory.PROVIDER_ID);
+        model.setPriority(1);
+        model.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+        model.setUserSetupAllowed(false);
+        hardcoded.add(model);
+        model = new AuthenticatorModel();
+        model.setId("3");
+        model.setAlias("password form");
+        model.setMasterAuthenticator(false);
+        model.setProviderId(LoginFormUsernameAuthenticatorFactory.PROVIDER_ID);
+        model.setPriority(2);
+        model.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+        model.setUserSetupAllowed(false);
+        hardcoded.add(model);
+        model = new AuthenticatorModel();
+        model.setId("4");
+        model.setAlias("otp form");
+        model.setMasterAuthenticator(false);
+        model.setProviderId(OTPFormAuthenticatorFactory.PROVIDER_ID);
+        model.setPriority(3);
+        model.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
+        model.setUserSetupAllowed(false);
+        hardcoded.add(model);
+    }
+    */
+}
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 ab50b3d..0f2ec07 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
@@ -6,12 +6,16 @@ import org.keycloak.authentication.AuthenticatorFactory;
 import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class CookieAuthenticatorFactory implements AuthenticatorFactory {
+    public static final String PROVIDER_ID = "auth-cookie";
     static CookieAuthenticator SINGLETON = new CookieAuthenticator();
     @Override
     public Authenticator create(AuthenticatorModel model) {
@@ -40,6 +44,26 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
 
     @Override
     public String getId() {
-        return "auth-cookie";
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return "Complete Authenticator";
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Cookie Authenticator";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Validates the SSO cookie set by the auth server.";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
     }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
index 7cf5b71..f2f2ef2 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
@@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.messages.Messages;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -31,19 +32,23 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
         validateOTP(context);
     }
 
+    protected Response badPassword(AuthenticatorContext context) {
+        return loginForm(context).setError(Messages.INVALID_USER).createLogin();
+    }
+
     public void validateOTP(AuthenticatorContext context) {
         MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.TOTP);
         if (password == null) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = badPassword(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
         credentials.add(UserCredentialModel.totp(password));
         boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
         if (!valid) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = badPassword(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticatorFactory.java
new file mode 100755
index 0000000..ef240cb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticatorFactory.java
@@ -0,0 +1,70 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LoginFormOTPAuthenticatorFactory implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "auth-login-form-otp";
+
+    @Override
+    public Authenticator create(AuthenticatorModel model) {
+        return new LoginFormOTPAuthenticator(model);
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        throw new IllegalStateException("illegal call");
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return "Credential Validation";
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Login Form OTP";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Validates an OTP that is specified on the login page.";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
index 1aae3b2..bcc4c5e 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
@@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticatorModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.messages.Messages;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -31,19 +32,24 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
         validatePassword(context);
     }
 
+    protected Response badPassword(AuthenticatorContext context) {
+        return loginForm(context).setError(Messages.INVALID_USER).createLogin();
+    }
+
+
     public void validatePassword(AuthenticatorContext context) {
         MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
         if (password == null) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = badPassword(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
         credentials.add(UserCredentialModel.password(password));
         boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
         if (!valid) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = badPassword(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticatorFactory.java
new file mode 100755
index 0000000..5da119e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticatorFactory.java
@@ -0,0 +1,70 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LoginFormPasswordAuthenticatorFactory implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "auth-login-form-password";
+
+    @Override
+    public Authenticator create(AuthenticatorModel model) {
+        return new LoginFormPasswordAuthenticator(model);
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        throw new IllegalStateException("illegal call");
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return "Credential Validation";
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Login Form Password";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Validates a user password that is specified on the login page.";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
index 1d1f73f..78b9096 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
@@ -6,11 +6,13 @@ import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.LoginActionsService;
 
 import javax.ws.rs.core.MultivaluedMap;
@@ -59,19 +61,37 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
     }
 
     protected Response challenge(AuthenticatorContext context, MultivaluedMap<String, String> formData) {
-        LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
-                .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode());
+        LoginFormsProvider forms = loginForm(context);
 
         if (formData.size() > 0) forms.setFormData(formData);
 
         return forms.createLogin();
     }
 
+    protected LoginFormsProvider loginForm(AuthenticatorContext context) {
+        ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
+        code.setAction(ClientSessionModel.Action.AUTHENTICATE);
+        return context.getSession().getProvider(LoginFormsProvider.class)
+                    .setClientSessionCode(code.getCode());
+    }
+
+    protected Response invalidUser(AuthenticatorContext context) {
+        return loginForm(context).setError(Messages.INVALID_USER).createLogin();
+    }
+
+    protected Response disabledUser(AuthenticatorContext context) {
+        return loginForm(context).setError(Messages.ACCOUNT_DISABLED).createLogin();
+    }
+
+    protected Response temporarilyDisabledUser(AuthenticatorContext context) {
+        return loginForm(context).setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
+    }
+
     public void validateUser(AuthenticatorContext context) {
         MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
         String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
         if (username == null) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = invalidUser(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
             return;
         }
@@ -82,17 +102,19 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
 
     public boolean invalidUser(AuthenticatorContext context, UserModel user) {
         if (user == null) {
-            Response challengeResponse = challenge(context);
+            Response challengeResponse = invalidUser(context);
             context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
             return true;
         }
         if (!user.isEnabled()) {
-            context.failure(AuthenticationProcessor.Error.USER_DISABLED);
+            Response challengeResponse = disabledUser(context);
+            context.failureChallenge(AuthenticationProcessor.Error.USER_DISABLED, challengeResponse);
             return true;
         }
         if (context.getRealm().isBruteForceProtected()) {
             if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
-                context.failure(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED);
+                Response challengeResponse = temporarilyDisabledUser(context);
+                context.failureChallenge(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED, challengeResponse);
                 return true;
             }
         }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticatorFactory.java
new file mode 100755
index 0000000..de86b08
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticatorFactory.java
@@ -0,0 +1,70 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class LoginFormUsernameAuthenticatorFactory implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "auth-login-form-username";
+
+    @Override
+    public Authenticator create(AuthenticatorModel model) {
+        return new LoginFormUsernameAuthenticator(model);
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        throw new IllegalStateException("illegal call");
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return "User Validation";
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "Login Form Username";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Validates a username that is specified on the login page.";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
new file mode 100755
index 0000000..f5fe0e7
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
@@ -0,0 +1,70 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.Config;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
+
+    public static final String PROVIDER_ID = "auth-otp-form";
+
+    @Override
+    public Authenticator create(AuthenticatorModel model) {
+        return new OTPFormAuthenticator(model);
+    }
+
+    @Override
+    public Authenticator create(KeycloakSession session) {
+        throw new IllegalStateException("illegal call");
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return "Credential Validation";
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "OTP Form";
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Validates a OTP on a separate OTP form.";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java
new file mode 100755
index 0000000..684d074
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java
@@ -0,0 +1,33 @@
+package org.keycloak.authentication;
+
+import org.keycloak.protocol.ProtocolMapper;
+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 AuthenticatorSpi implements Spi {
+
+    @Override
+    public boolean isPrivate() {
+        return false;
+    }
+
+    @Override
+    public String getName() {
+        return "authenticator";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return Authenticator.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return AuthenticatorFactory.class;
+    }
+
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
new file mode 100755
index 0000000..20dff3b
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -0,0 +1,5 @@
+org.keycloak.authentication.authenticators.CookieAuthenticatorFactory
+org.keycloak.authentication.authenticators.LoginFormOTPAuthenticatorFactory
+org.keycloak.authentication.authenticators.LoginFormPasswordAuthenticatorFactory
+org.keycloak.authentication.authenticators.LoginFormUsernameAuthenticatorFactory
+org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory
\ No newline at end of file