keycloak-aplcache

Changes

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 7568e04..eb34666 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -978,7 +978,7 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 clients : function(ClientListLoader) {
                     return ClientListLoader();
-                },
+                }
             },
             controller : 'UserFederationMapperCtrl'
         })
@@ -1066,6 +1066,36 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmPasswordPolicyCtrl'
         })
+        .when('/realms/:realm/authentication/config/:provider/:config', {
+            templateUrl : resourceUrl + '/partials/authenticator-config.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                configType : function(AuthenticationConfigDescriptionLoader) {
+                    return AuthenticationConfigDescriptionLoader();
+                },
+                config : function(AuthenticationConfigLoader) {
+                    return AuthenticationConfigLoader();
+                }
+            },
+            controller : 'AuthenticationConfigCtrl'
+        })
+        .when('/create/authentication/:realm/execution/:executionId/provider/:provider', {
+            templateUrl : resourceUrl + '/partials/authenticator-config.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                configType : function(AuthenticationConfigDescriptionLoader) {
+                    return AuthenticationConfigDescriptionLoader();
+                },
+                execution : function(ExecutionIdLoader) {
+                    return ExecutionIdLoader();
+                }
+            },
+            controller : 'AuthenticationConfigCreateCtrl'
+        })
         .when('/server-info', {
             templateUrl : resourceUrl + '/partials/server-info.html'
         })
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 93e7cc3..e9341b3 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1634,6 +1634,93 @@ module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions
 
 });
 
+module.controller('AuthenticationConfigCtrl', function($scope, realm, configType, config, AuthenticationConfig, Notifications, Dialog, $location) {
+    $scope.realm = realm;
+    $scope.configType = configType;
+    $scope.create = false;
+    $scope.config = angular.copy(config);
+    $scope.changed = false;
+
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+
+    $scope.$watch('config', function() {
+        if (!angular.equals($scope.config, config)) {
+            $scope.changed = true;
+        }
+    }, true);
+
+    $scope.save = function() {
+        AuthenticationConfig.update({
+            realm : realm.realm,
+            config : config.id
+        }, $scope.config, function() {
+            $scope.changed = false;
+            config = angular.copy($scope.config);
+            $location.url("/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + config.id);
+            Notifications.success("Your changes have been saved.");
+        });
+    };
+
+    $scope.reset = function() {
+        $scope.config = angular.copy(config);
+        $scope.changed = false;
+    };
+
+    $scope.cancel = function() {
+        //$location.url("/realms");
+        window.history.back();
+    };
+
+    $scope.remove = function() {
+        Dialog.confirmDelete($scope.config.alias, 'config', function() {
+            AuthenticationConfig.remove({ realm: realm.realm, config : $scope.config.id }, function() {
+                Notifications.success("The config has been deleted.");
+                $location.url("/realms/" + realm.realm + '/authentication/flows');
+            });
+        });
+    };
+
+});
+
+module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, configType, execution, AuthenticationExecutionConfig, Notifications, Dialog, $location) {
+    $scope.realm = realm;
+    $scope.create = true;
+    $scope.config = { config: {}};
+    $scope.configType = configType;
+
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+
+    $scope.save = function() {
+        AuthenticationExecutionConfig.save({
+            realm : realm.realm,
+            execution: execution
+        }, $scope.config, function(data, headers) {
+            var l = headers().location;
+            var id = l.substring(l.lastIndexOf("/") + 1);
+            var url = "/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + id;
+            console.log('redirect url: ' + url);
+            $location.url(url);
+            Notifications.success("Config has been created.");
+        });
+    };
+
+    $scope.cancel = function() {
+        //$location.url("/realms");
+        window.history.back();
+    };
+
+
+});
+
+
 
 
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index da41b3c..72c6b8c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -348,5 +348,29 @@ module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows
     });
 });
 
+module.factory('AuthenticationConfigDescriptionLoader', function(Loader, AuthenticationConfigDescription, $route, $q) {
+    return Loader.get(AuthenticationConfigDescription, function () {
+        return {
+            realm: $route.current.params.realm,
+            provider: $route.current.params.provider
+        }
+    });
+});
+
+module.factory('ExecutionIdLoader', function($route) {
+    return function() { return $route.current.params.executionId; };
+});
+
+module.factory('AuthenticationConfigLoader', function(Loader, AuthenticationConfig, $route, $q) {
+    return Loader.get(AuthenticationConfig, function () {
+        return {
+            realm: $route.current.params.realm,
+            config: $route.current.params.config
+        }
+    });
+});
+
+
+
 
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 6f25efb..f3c4475 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -1094,5 +1094,28 @@ module.factory('AuthenticationFlows', function($resource) {
         realm : '@realm'
     });
 });
+module.factory('AuthenticationConfigDescription', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
+        realm : '@realm',
+        provider: '@provider'
+    });
+});
+
+module.factory('AuthenticationConfig', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/config/:config', {
+        realm : '@realm',
+        config: '@config'
+    }, {
+        update: {
+            method : 'PUT'
+        }
+    });
+});
+module.factory('AuthenticationExecutionConfig', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/authentication/executions/:execution/config', {
+        realm : '@realm',
+        execution: '@execution'
+    });
+});
 
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
index 5084ab9..4c65931 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
@@ -6,7 +6,7 @@
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
-            <th colspan="5" class="kc-table-actions">
+            <th colspan="6" class="kc-table-actions">
                 <div class="dropdown pull-left">
                     <select class="form-control" ng-model="flow"
                             ng-options="flow.alias for flow in flows"
@@ -18,6 +18,7 @@
         <tr data-ng-hide="executions.length == 0">
             <th colspan="2">Auth Type</th>
             <th colspan="{{flowmax}}">Requirement</th>
+            <th></th>
         </tr>
         </thead>
         <tbody>
@@ -49,6 +50,10 @@
 
             </td>
             <td ng-repeat="emptee in execution.empties"></td>
+            <td>
+                <a data-ng-show="execution.configurable && execution.authenticationConfig == null" class="btn btn-default" href="#/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Configure</a>
+                <a data-ng-show="execution.configurable && execution.authenticationConfig != null" class="btn btn-default" href="#/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Configure</a>
+            </td>
          </tr>
         <tr data-ng-show="executions.length == 0">
             <td>No executions available</td>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authenticator-config.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authenticator-config.html
new file mode 100755
index 0000000..a093240
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authenticator-config.html
@@ -0,0 +1,64 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/authentication/flows">Authentication Flows</a></li>
+        <li class="active" data-ng-show="create">Create Authenticator Config</li>
+        <li class="active" data-ng-hide="create">{{config.alias}}</li>
+    </ol>
+
+    <h1 data-ng-show="create">Create Authenticator Config</h1>
+    <h1 data-ng-hide="create">{{config.alias|capitalize}}<a><i style="padding-left: 20px" class="pficon pficon-delete" data-ng-show="!create && access.manageRealm"
+    	data-ng-hide="changed" data-ng-click="remove()"></i></a></h1>
+
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+
+        <fieldset>
+            <div class="form-group clearfix" data-ng-show="!create">
+                <label class="col-md-2 control-label" for="configId">ID </label>
+                <div class="col-md-6">
+                    <input class="form-control" id="configId" type="text" ng-model="config.id" readonly>
+                </div>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="name">Alias</label>
+                <div class="col-md-6">
+                    <input class="form-control" id="name" type="text" ng-model="config.alias" data-ng-readonly="!create">
+                </div>
+                <kc-tooltip>Name of the configuration.</kc-tooltip>
+            </div>
+            <div data-ng-repeat="option in configType.properties" class="form-group">
+                <label class="col-md-2 control-label">{{option.label}}</label>
+
+                <div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
+                    <input class="form-control" type="text" data-ng-model="config.config[ option.name ]" >
+                </div>
+                <div class="col-sm-4" data-ng-show="option.type == 'boolean'">
+                    <input ng-model="config.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
+                </div>
+                <div class="col-sm-4" data-ng-show="option.type == 'List'">
+                    <select ng-model="config.config[ option.name ]" ng-options="data for data in option.defaultValue">
+                        <option value="" selected> Select one... </option>
+                    </select>
+                </div>
+                <kc-tooltip>{{option.helpText}}</kc-tooltip>
+            </div>
+
+        </fieldset>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageRealm">
+                <button kc-save>Save</button>
+                <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
+                <button kc-save  data-ng-show="changed">Save</button>
+                <button kc-reset data-ng-show="changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/register.ftl b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
index 75a48fc..cf7bd1d 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/register.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
@@ -110,7 +110,7 @@
             <#if recaptchaRequired??>
             <div class="form-group">
                 <div class="${properties.kcInputWrapperClass!}">
-                    <div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}"></div>
+                    <div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
                 </div>
             </div>
             </#if>
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
index 44b8982..9b60192 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
@@ -32,6 +32,8 @@ public class AuthenticatorConfigModel implements Serializable {
         this.alias = alias;
     }
 
+
+
     public Map<String, String> getConfig() {
         return config;
     }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
index f928bea..5f32b41 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
@@ -9,7 +9,7 @@ import org.keycloak.provider.ProviderFactory;
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
-public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider, ConfigurableAuthenticatorFactory {
+public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfigurableAuthenticatorFactory {
     Authenticator create();
 
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
index 123c4b4..cee4f7d 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
@@ -57,7 +57,7 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
 
     @Override
     public boolean isConfigurable() {
-        return true;
+        return false;
     }
     public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
             AuthenticationExecutionModel.Requirement.REQUIRED
diff --git a/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
index cca47c6..5297674 100755
--- a/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
@@ -1,12 +1,13 @@
 package org.keycloak.authentication;
 
 import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.provider.ConfiguredProvider;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public interface ConfigurableAuthenticatorFactory {
+public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
     String getDisplayType();
 
     /**
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
index 90657fe..4b64333 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
@@ -8,8 +8,10 @@ import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
 
 import javax.ws.rs.core.Response;
+import java.util.List;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -42,6 +44,16 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
     }
 
     @Override
+    public String getHelpText() {
+        return null;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
     public String getReferenceCategory() {
         return null;
     }
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
index f20b3eb..648f9de 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
@@ -16,6 +16,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.validation.Validation;
@@ -32,6 +33,16 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
     public static final String PROVIDER_ID = "registration-password-action";
 
     @Override
+    public String getHelpText() {
+        return null;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
     public void validate(ValidationContext context) {
         MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
         List<FormMessage> errors = new ArrayList<>();
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
index f67fbea..5ac7341 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
@@ -14,6 +14,7 @@ import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.validation.Validation;
 
@@ -28,6 +29,15 @@ import java.util.List;
 public class RegistrationProfile implements FormAction, FormActionFactory {
     public static final String PROVIDER_ID = "registration-profile-action";
 
+    @Override
+    public String getHelpText() {
+        return null;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
 
     @Override
     public void validate(ValidationContext context) {
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
index 3e8d191..7009704 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
@@ -23,6 +23,7 @@ import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
+import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.services.messages.Messages;
@@ -43,6 +44,8 @@ import java.util.Map;
 public class RegistrationRecaptcha implements FormAction, FormActionFactory, ConfiguredProvider {
     public static final String G_RECAPTCHA_RESPONSE = "g-recaptcha-response";
     public static final String RECAPTCHA_REFERENCE_CATEGORY = "recaptcha";
+    public static final String SITE_KEY = "site.key";
+    public static final String SITE_SECRET = "secret";
     protected static Logger logger = Logger.getLogger(RegistrationRecaptcha.class);
 
     public static final String PROVIDER_ID = "registration-recaptcha-action";
@@ -74,13 +77,13 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
     public void buildPage(FormContext context, LoginFormsProvider form) {
         AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
         if (captchaConfig == null || captchaConfig.getConfig() == null
-                || captchaConfig.getConfig().get("site.key") == null
-                || captchaConfig.getConfig().get("secret") == null
+                || captchaConfig.getConfig().get(SITE_KEY) == null
+                || captchaConfig.getConfig().get(SITE_SECRET) == null
                 ) {
             form.addError(new FormMessage(null, Messages.RECAPTCHA_NOT_CONFIGURED));
             return;
         }
-        String siteKey = captchaConfig.getConfig().get("site.key");
+        String siteKey = captchaConfig.getConfig().get(SITE_KEY);
         form.setAttribute("recaptchaRequired", true);
         form.setAttribute("recaptchaSiteKey", siteKey);
         List<String> scripts = new LinkedList<>();
@@ -98,7 +101,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
         String captcha = formData.getFirst(G_RECAPTCHA_RESPONSE);
         if (!Validation.isBlank(captcha)) {
             AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
-            String secret = captchaConfig.getConfig().get("secret");
+            String secret = captchaConfig.getConfig().get(SITE_SECRET);
 
             HttpClient httpClient = context.getSession().getProvider(HttpClientProvider.class).getHttpClient();
             HttpPost post = new HttpPost("https://www.google.com/recaptcha/api/siteverify");
@@ -185,8 +188,28 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
         return null;
     }
 
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+    static {
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
+        property.setName(SITE_KEY);
+        property.setLabel("Recaptcha Site Key");
+        property.setType(ProviderConfigProperty.STRING_TYPE);
+        property.setHelpText("Google Recaptcha Site Key");
+        configProperties.add(property);
+        property = new ProviderConfigProperty();
+        property.setName(SITE_SECRET);
+        property.setLabel("Recaptcha Secret");
+        property.setType(ProviderConfigProperty.STRING_TYPE);
+        property.setHelpText("Google Recaptcha Secret");
+        configProperties.add(property);
+
+    }
+
+
     @Override
     public List<ProviderConfigProperty> getConfigProperties() {
-        return null;
+        return configProperties;
     }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
index 28d6239..75f2818 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
@@ -16,6 +16,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.AttributeFormDataProcessor;
 import org.keycloak.services.validation.Validation;
@@ -33,6 +34,16 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
     public static final String PROVIDER_ID = "registration-user-creation";
 
     @Override
+    public String getHelpText() {
+        return null;
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return null;
+    }
+
+    @Override
     public void validate(ValidationContext context) {
         MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
         List<FormMessage> errors = new ArrayList<>();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index fa221c3..39ef7c3 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -8,24 +8,29 @@ import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
 import org.keycloak.authentication.AuthenticatorUtil;
 import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
-import org.keycloak.authentication.DefaultAuthenticationFlow;
 import org.keycloak.authentication.FormAction;
 import org.keycloak.authentication.FormActionFactory;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorConfigModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredActionProviderModel;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -42,6 +47,9 @@ public class AuthenticationManagementResource {
     private final KeycloakSession session;
     private RealmAuth auth;
     private AdminEventBuilder adminEvent;
+    @Context
+    private UriInfo uriInfo;
+
     private static Logger logger = Logger.getLogger(AuthenticationManagementResource.class);
 
     public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
@@ -53,19 +61,21 @@ public class AuthenticationManagementResource {
     }
 
     public static class AuthenticationExecutionRepresentation {
-        protected String execution;
+        protected String id;
         protected String referenceType;
         protected String requirement;
         protected List<String> requirementChoices;
         protected Boolean configurable;
         protected Boolean subFlow;
+        protected String providerId;
+        protected String authenticationConfig;
 
-        public String getExecution() {
-            return execution;
+        public String getId() {
+            return id;
         }
 
-        public void setExecution(String execution) {
-            this.execution = execution;
+        public void setId(String execution) {
+            this.id = execution;
         }
 
         public String getReferenceType() {
@@ -107,6 +117,22 @@ public class AuthenticationManagementResource {
         public void setSubFlow(Boolean subFlow) {
             this.subFlow = subFlow;
         }
+
+        public String getProviderId() {
+            return providerId;
+        }
+
+        public void setProviderId(String providerId) {
+            this.providerId = providerId;
+        }
+
+        public String getAuthenticationConfig() {
+            return authenticationConfig;
+        }
+
+        public void setAuthenticationConfig(String authenticationConfig) {
+            this.authenticationConfig = authenticationConfig;
+        }
     }
 
     @Path("/flows")
@@ -150,27 +176,30 @@ public class AuthenticationManagementResource {
                 } else if (AuthenticationFlow.FORM_FLOW.equals(flowRef.getProviderId())) {
                     rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.REQUIRED.name());
                     rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
+                    rep.setProviderId(execution.getAuthenticator());
+                    rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
+
                 }
                 rep.setReferenceType(flowRef.getAlias());
                 rep.setConfigurable(false);
-                rep.setExecution(execution.getId());
+                rep.setId(execution.getId());
                 rep.setRequirement(execution.getRequirement().name());
                 result.add(rep);
             } else {
                 if (!flow.getId().equals(execution.getParentFlow())) {
                     rep.setSubFlow(true);
                 }
-                ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, execution.getAuthenticator());
-                if (factory == null) {
-                    factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, execution.getAuthenticator());
-                }
+                String providerId = execution.getAuthenticator();
+                ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(providerId);
                 rep.setReferenceType(factory.getDisplayType());
                 rep.setConfigurable(factory.isConfigurable());
                 for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
                     rep.getRequirementChoices().add(choice.name());
                 }
-                rep.setExecution(execution.getId());
+                rep.setId(execution.getId());
                 rep.setRequirement(execution.getRequirement().name());
+                rep.setProviderId(execution.getAuthenticator());
+                rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
                 result.add(rep);
 
             }
@@ -179,6 +208,14 @@ public class AuthenticationManagementResource {
         return Response.ok(result).build();
     }
 
+    public ConfigurableAuthenticatorFactory getConfigurableAuthenticatorFactory(String providerId) {
+        ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, providerId);
+        if (factory == null) {
+            factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, providerId);
+        }
+        return factory;
+    }
+
     @Path("/flows/{flowAlias}/executions")
     @PUT
     @NoCache
@@ -192,7 +229,7 @@ public class AuthenticationManagementResource {
             throw new NotFoundException("flow not found");
         }
 
-        AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(rep.getExecution());
+        AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(rep.getId());
         if (model == null) {
             session.getTransaction().setRollbackOnly();
             throw new NotFoundException("Illegal execution");
@@ -204,6 +241,39 @@ public class AuthenticationManagementResource {
         }
     }
 
+    @Path("/executions/{executionId}/config")
+    @POST
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigModel config) {
+        this.auth.requireManage();
+
+        AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
+        if (model == null) {
+            session.getTransaction().setRollbackOnly();
+            throw new NotFoundException("Illegal execution");
+
+        }
+        config = realm.addAuthenticatorConfig(config);
+        model.setAuthenticatorConfig(config.getId());
+        realm.updateAuthenticatorExecution(model);
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
+    }
+
+    @Path("/executions/{executionId}/config/{id}")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) {
+        AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
+        if (config == null) {
+            throw new NotFoundException("Could not find authenticator config");
+
+        }
+        return config;
+    }
+
+
     public static class RequiredActionProviderRepresentation {
         private String alias;
         private String name;
@@ -256,6 +326,7 @@ public class AuthenticationManagementResource {
     @Path("required-actions")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
     public List<RequiredActionProviderRepresentation> getRequiredActions() {
         List<RequiredActionProviderRepresentation> list = new LinkedList<>();
         for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
@@ -278,6 +349,7 @@ public class AuthenticationManagementResource {
     @Path("required-actions/{alias}")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
     public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
         RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
         if (model == null) {
@@ -316,5 +388,131 @@ public class AuthenticationManagementResource {
         realm.removeRequiredActionProvider(model);
     }
 
+    public class AuthenticatorConfigDescription {
+        protected String name;
+        protected String providerId;
+        protected String helpText;
+
+        protected List<ConfigPropertyRepresentation> properties;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getHelpText() {
+            return helpText;
+        }
+
+        public String getProviderId() {
+            return providerId;
+        }
+
+        public void setProviderId(String providerId) {
+            this.providerId = providerId;
+        }
+
+        public void setHelpText(String helpText) {
+            this.helpText = helpText;
+        }
+
+        public List<ConfigPropertyRepresentation> getProperties() {
+            return properties;
+        }
+
+        public void setProperties(List<ConfigPropertyRepresentation> properties) {
+            this.properties = properties;
+        }
+    }
+
+
+    @Path("config-description/{providerId}")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    public AuthenticatorConfigDescription getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) {
+        ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(providerId);
+        if (factory == null) {
+            throw new NotFoundException("Could not find authenticator provider");
+        }
+        AuthenticatorConfigDescription rep = new AuthenticatorConfigDescription();
+        rep.setProviderId(providerId);
+        rep.setName(factory.getDisplayType());
+        rep.setHelpText(factory.getHelpText());
+        rep.setProperties(new LinkedList<ConfigPropertyRepresentation>());
+        List<ProviderConfigProperty> configProperties = factory.getConfigProperties();
+        for (ProviderConfigProperty prop : configProperties) {
+            ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
+            propRep.setName(prop.getName());
+            propRep.setLabel(prop.getLabel());
+            propRep.setType(prop.getType());
+            propRep.setDefaultValue(prop.getDefaultValue());
+            propRep.setHelpText(prop.getHelpText());
+            rep.getProperties().add(propRep);
+        }
+        return rep;
+    }
+
+    @Path("config")
+    @POST
+    @NoCache
+    public Response createAuthenticatorConfig(AuthenticatorConfigModel config) {
+        config = realm.addAuthenticatorConfig(config);
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
+    }
+
+    @Path("config/{id}")
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("id") String id) {
+        AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
+        if (config == null) {
+            throw new NotFoundException("Could not find authenticator config");
+
+        }
+        return config;
+    }
+    @Path("config/{id}")
+    @DELETE
+    @NoCache
+    public void removeAuthenticatorConfig(@PathParam("id") String id) {
+        AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
+        if (config == null) {
+            throw new NotFoundException("Could not find authenticator config");
+
+        }
+        List<AuthenticationFlowModel> flows = new LinkedList<>();
+        for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
+            for (AuthenticationExecutionModel exe : realm.getAuthenticationExecutions(flow.getId())) {
+                if (id.equals(exe.getAuthenticatorConfig())) {
+                    exe.setAuthenticatorConfig(null);
+                    realm.updateAuthenticatorExecution(exe);
+                }
+            }
+        }
+
+        realm.removeAuthenticatorConfig(config);
+    }
+    @Path("config/{id}")
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    @NoCache
+    public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigModel config) {
+        AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
+        if (exists == null) {
+            throw new NotFoundException("Could not find authenticator config");
+
+        }
+        exists.setAlias(config.getAlias());
+        exists.setConfig(config.getConfig());
+        realm.updateAuthenticatorConfig(exists);
+    }
+
+
+
 
 }
\ No newline at end of file