keycloak-aplcache

Changes

Details

diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml
index 52930a3..d5619d9 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml
@@ -61,6 +61,12 @@
             </column>
         </addColumn>
 
+        <addColumn tableName="CLIENT">
+            <column name="CLIENT_AUTHENTICATOR_TYPE" type="VARCHAR(255)">
+                <constraints nullable="true"/>
+            </column>
+        </addColumn>
+
         <!-- Sybase specific hacks -->
         <modifySql dbms="sybase">
             <regExpReplace replace=".*(SET DEFAULT NULL)" with="SELECT 1" />
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index e5ab503..020445e 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -15,6 +15,7 @@ public class ClientRepresentation {
     protected String baseUrl;
     protected Boolean surrogateAuthRequired;
     protected Boolean enabled;
+    protected String clientAuthenticatorType;
     protected String secret;
     protected String[] defaultRoles;
     protected List<String> redirectUris;
@@ -89,6 +90,14 @@ public class ClientRepresentation {
         this.baseUrl = baseUrl;
     }
 
+    public String getClientAuthenticatorType() {
+        return clientAuthenticatorType;
+    }
+
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        this.clientAuthenticatorType = clientAuthenticatorType;
+    }
+
     public String getSecret() {
         return secret;
     }
diff --git a/docbook/reference/en/en-US/modules/auth-spi.xml b/docbook/reference/en/en-US/modules/auth-spi.xml
index 81b7d9d..ff7f061 100755
--- a/docbook/reference/en/en-US/modules/auth-spi.xml
+++ b/docbook/reference/en/en-US/modules/auth-spi.xml
@@ -921,7 +921,7 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
                                                 in the location accessible to your client application
                                             </listitem>
                                             <listitem>
-                                                Uploaded in Keycloak admin console - This option is useful if you already has existing private key of your client.
+                                                Uploaded in Keycloak admin console - This option is useful if you already have existing private key of your client.
                                                 In this case, you just need to upload the public key and certificate to the Keycloak server.
                                             </listitem>
                                         </itemizedlist>
@@ -993,7 +993,8 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
                             <para>
                                 Finally you need to configure admin console . You need to create new client authentication flow and define execution
                                 with your authenticator (you can also add the builtin authenticators and configure requirements etc)
-                                and finally configure Clients binding . See <link linkend="adding_authenticator">Adding Authenticator</link> for more details.
+                                and finally configure Clients binding . See <link linkend="adding_authenticator">Adding Authenticator</link> for more details. Then
+                                you need to go to Client credentials tab and choose the method for authentication your client and configure client credentials (if possible).
                             </para>
                         </listitem>
                     </varlistentry>
diff --git a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
index 6c07d1e..3e90c34 100644
--- a/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
+++ b/examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-signed-jwt.json
@@ -3,7 +3,7 @@
   "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
   "auth-server-url" : "http://localhost:8080/auth",
   "ssl-required" : "external",
-  "resource" : "product-sa-client",
+  "resource" : "product-sa-client-jwt-auth",
   "credentials": {
     "jwt": {
       "client-keystore-file": "classpath:keystore-client.jks",
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index 8d3e597..0ba235f 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -78,6 +78,13 @@
             "email" : "service-account-product-sa-client@placeholder.org",
             "serviceAccountClientId": "product-sa-client",
             "realmRoles": [ "user" ]
+        },
+        {
+            "username" : "service-account-product-sa-client-jwt-auth",
+            "enabled": true,
+            "email" : "service-account-product-sa-client-jwt-auth@placeholder.org",
+            "serviceAccountClientId": "product-sa-client-jwt-auth",
+            "realmRoles": [ "user" ]
         }
     ],
     "roles" : {
@@ -173,7 +180,13 @@
             "clientId": "product-sa-client",
             "enabled": true,
             "secret": "password",
+            "serviceAccountsEnabled": true
+        },
+        {
+            "clientId": "product-sa-client-jwt-auth",
+            "enabled": true,
             "serviceAccountsEnabled": true,
+            "clientAuthenticatorType": "client-jwt",
             "attributes": {
                 "jwt.credential.certificate": "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ=="
             }
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 2e3a251..4ec4c22 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -30,10 +30,42 @@ module.controller('ClientRoleListCtrl', function($scope, $location, realm, clien
     });
 });
 
-module.controller('ClientCredentialsCtrl', function($scope, $location, realm, client, clientAuthenticatorProviders, Notifications) {
+module.controller('ClientCredentialsCtrl', function($scope, $location, realm, client, clientAuthenticatorProviders, Client) {
     $scope.realm = realm;
-    $scope.client = client;
+    $scope.client = angular.copy(client);
     $scope.clientAuthenticatorProviders = clientAuthenticatorProviders;
+
+    var updateConfigButtonVisibility = function() {
+        for (var i=0 ; i<clientAuthenticatorProviders.length ; i++) {
+            var authenticator = clientAuthenticatorProviders[i];
+            if ($scope.client.clientAuthenticatorType === authenticator.id) {
+                $scope.configButtonVisible = authenticator.configurablePerClient;
+            }
+        }
+    };
+    updateConfigButtonVisibility();
+
+    $scope.$watch('client', function() {
+        if (!angular.equals($scope.client, client)) {
+
+            console.log("Update client credentials!");
+
+            Client.update({
+                realm : realm.realm,
+                client : client.id
+            }, $scope.client, function() {
+                $scope.changed = false;
+                client = angular.copy($scope.client);
+                updateConfigButtonVisibility();
+            });
+
+        }
+    }, true);
+
+    $scope.configureAuthenticator = function() {
+        $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/credentials/" + client.clientAuthenticatorType);
+    }
+
 });
 
 module.controller('ClientSecretCtrl', function($scope, $location, realm, client, ClientSecret, Notifications) {
@@ -115,7 +147,7 @@ module.controller('ClientGenericCredentialsCtrl', function($scope, $location, re
             client : client.id
         }, $scope.client, function() {
             $scope.changed = false;
-            client = $scope.client;
+            client = angular.copy($scope.client);
             Notifications.success("Client authentication configuration has been saved to the client.");
         });
     };
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 3349c4d..eb26ba3 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
@@ -19,7 +19,7 @@
                     <button class="btn btn-default" data-ng-click="copyFlow()">Copy</button>
                     <button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="removeFlow()">Delete</button>
                     <button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addExecution()">Add Execution</button>
-                    <button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addFlow()">Add Flow</button>
+                    <button class="btn btn-default" data-ng-hide="flow.builtIn || flow.providerId === 'client-flow'" data-ng-click="addFlow()">Add Flow</button>
                 </div>
             </th>
          </tr>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
index 043cee6..8415fbe 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
@@ -7,24 +7,27 @@
 
     <kc-tabs-client></kc-tabs-client>
 
-    <table class="table table-striped table-bordered">
-        <thead>
-            <tr data-ng-hide="executions.length == 0">
-                <th>Client Auth Type</th>
-                <th></th>
-            </tr>
-        </thead>
-        <tbody>
-        <tr ng-repeat="authenticator in clientAuthenticatorProviders" data-ng-show="clientAuthenticatorProviders.length > 0">
-            <td ng-repeat="lev in execution.preLevels"></td>
-            <td>{{authenticator.displayName|capitalize}}</td>
-            <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials/{{authenticator.id}}" data-ng-show="authenticator.configurablePerClient">Configure</a></td>
-        </tr>
-        <tr data-ng-show="clientAuthenticatorProviders.length == 0">
-            <td>No client authenticators available</td>
-        </tr>
-        </tbody>
-    </table>
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+        <fieldset class="border-top">
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="clientAuthenticatorType"> Client Authenticator</label>
+                <div class="col-md-2">
+                    <div>
+                        <select class="form-control" id="clientAuthenticatorType"
+                                ng-model="client.clientAuthenticatorType"
+                                ng-options="authenticator.id as authenticator.displayName for authenticator in clientAuthenticatorProviders"
+                                required>
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>Client Authenticator used for authentication this client against Keycloak server</kc-tooltip>
+                <div class="col-sm-4" data-ng-show="access.manageRealm">
+                    <a class="btn btn-primary" data-ng-show="configButtonVisible" data-ng-click="configureAuthenticator()">Configure chosen authenticator</a>
+                </div>
+            </div>
+        </fieldset>
+    </form>
+
 </div>
 
 <kc-menu></kc-menu>
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java
index dd550dc..95a5224 100755
--- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java
@@ -2,6 +2,7 @@ package org.keycloak.migration.migrators;
 
 import org.keycloak.migration.ModelVersion;
 import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.ClientModel;
 import org.keycloak.models.ImpersonationConstants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.OTPPolicy;
@@ -38,6 +39,10 @@ public class MigrateTo1_5_0 {
             } else {
                 realm.setClientAuthenticationFlow(realm.getFlowByAlias(DefaultAuthenticationFlows.CLIENT_AUTHENTICATION_FLOW));
             }
+
+            for (ClientModel client : realm.getClients()) {
+                client.setClientAuthenticatorType(KeycloakModelUtils.getDefaultClientAuthenticatorType());
+            }
         }
 
     }
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index 96a7dd7..daccf8e 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -75,6 +75,9 @@ public interface ClientModel extends RoleContainerModel {
 
     void setNodeReRegistrationTimeout(int timeout);
 
+    String getClientAuthenticatorType();
+    void setClientAuthenticatorType(String clientAuthenticatorType);
+
     boolean validateSecret(String secret);
     String getSecret();
     public void setSecret(String secret);
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
index 8e0c21b..52e9721 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
@@ -14,6 +14,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
     private String name;
     private String realmId;
     private boolean enabled;
+    private String clientAuthenticatorType;
     private String secret;
     private String protocol;
     private int notBefore;
@@ -67,6 +68,14 @@ public class ClientEntity extends AbstractIdentifiableEntity {
         this.enabled = enabled;
     }
 
+    public String getClientAuthenticatorType() {
+        return clientAuthenticatorType;
+    }
+
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        this.clientAuthenticatorType = clientAuthenticatorType;
+    }
+
     public String getSecret() {
         return secret;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 79a9b12..b64ebb0 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -180,12 +180,17 @@ public final class KeycloakModelUtils {
         return secret;
     }
 
+    public static String getDefaultClientAuthenticatorType() {
+        return "client-secret";
+    }
+
     public static String generateCodeSecret() {
         return UUID.randomUUID().toString();
     }
 
     public static ClientModel createClient(RealmModel realm, String name) {
         ClientModel app = realm.addClient(name);
+        app.setClientAuthenticatorType(getDefaultClientAuthenticatorType());
         generateSecret(app);
         app.setFullScopeAllowed(true);
 
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 1a411c7..f2d4f1e 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -307,6 +307,7 @@ public class ModelToRepresentation {
         rep.setBaseUrl(clientModel.getBaseUrl());
         rep.setNotBefore(clientModel.getNotBefore());
         rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
+        rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType());
 
         Set<String> redirectUris = clientModel.getRedirectUris();
         if (redirectUris != null) {
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 31d2575..4faff01 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -692,6 +692,12 @@ public class RepresentationToModel {
             client.setNotBefore(resourceRep.getNotBefore());
         }
 
+        if (resourceRep.getClientAuthenticatorType() != null) {
+            client.setClientAuthenticatorType(resourceRep.getClientAuthenticatorType());
+        } else {
+            client.setClientAuthenticatorType(KeycloakModelUtils.getDefaultClientAuthenticatorType());
+        }
+
         client.setSecret(resourceRep.getSecret());
         if (client.getSecret() == null) {
             KeycloakModelUtils.generateSecret(client);
@@ -770,6 +776,7 @@ public class RepresentationToModel {
         if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
         if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
         if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout());
+        if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType());
         resource.updateClient();
 
         if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
index 87f0ee3..8003b70 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
@@ -146,6 +146,16 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public String getClientAuthenticatorType() {
+        return entity.getClientAuthenticatorType();
+    }
+
+    @Override
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        entity.setClientAuthenticatorType(clientAuthenticatorType);
+    }
+
+    @Override
     public boolean validateSecret(String secret) {
         return secret.equals(entity.getSecret());
     }
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 9791400..9f79b15 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -95,6 +95,18 @@ public class ClientAdapter implements ClientModel {
         updated.setEnabled(enabled);
     }
 
+    @Override
+    public String getClientAuthenticatorType() {
+        if (updated != null) return updated.getClientAuthenticatorType();
+        return cached.getClientAuthenticatorType();
+    }
+
+    @Override
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        getDelegateForUpdate();
+        updated.setClientAuthenticatorType(clientAuthenticatorType);
+    }
+
     public boolean validateSecret(String secret) {
         return secret.equals(getSecret());
     }
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index 911021e..25749c3 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -29,6 +29,7 @@ public class CachedClient implements Serializable {
     private String realm;
     private Set<String> redirectUris = new HashSet<String>();
     private boolean enabled;
+    private String clientAuthenticatorType;
     private String secret;
     private String protocol;
     private Map<String, String> attributes = new HashMap<String, String>();
@@ -53,6 +54,7 @@ public class CachedClient implements Serializable {
 
     public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
         id = model.getId();
+        clientAuthenticatorType = model.getClientAuthenticatorType();
         secret = model.getSecret();
         clientId = model.getClientId();
         name = model.getName();
@@ -112,6 +114,10 @@ public class CachedClient implements Serializable {
         return enabled;
     }
 
+    public String getClientAuthenticatorType() {
+        return clientAuthenticatorType;
+    }
+
     public String getSecret() {
         return secret;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index fc551c7..b0acc41 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -152,6 +152,16 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public String getClientAuthenticatorType() {
+        return entity.getClientAuthenticatorType();
+    }
+
+    @Override
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        entity.setClientAuthenticatorType(clientAuthenticatorType);
+    }
+
+    @Override
     public String getSecret() {
         return entity.getSecret();
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index 8b57335..1f6ac25 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -40,6 +40,8 @@ public class ClientEntity {
     private boolean enabled;
     @Column(name="SECRET")
     private String secret;
+    @Column(name="CLIENT_AUTHENTICATOR_TYPE")
+    private String clientAuthenticatorType;
     @Column(name="NOT_BEFORE")
     private int notBefore;
     @Column(name="PUBLIC_CLIENT")
@@ -170,6 +172,14 @@ public class ClientEntity {
         this.redirectUris = redirectUris;
     }
 
+    public String getClientAuthenticatorType() {
+        return clientAuthenticatorType;
+    }
+
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        this.clientAuthenticatorType = clientAuthenticatorType;
+    }
+
     public String getSecret() {
         return secret;
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 40ea0d2..26effca 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -142,6 +142,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
+    public String getClientAuthenticatorType() {
+        return getMongoEntity().getClientAuthenticatorType();
+    }
+
+    @Override
+    public void setClientAuthenticatorType(String clientAuthenticatorType) {
+        getMongoEntity().setClientAuthenticatorType(clientAuthenticatorType);
+        updateMongoEntity();
+    }
+
+    @Override
     public boolean validateSecret(String secret) {
         return secret.equals(getMongoEntity().getSecret());
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
index 55d0fde..b4917ec 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
@@ -36,7 +36,6 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator 
     public static final String PROVIDER_ID = "client-secret";
 
     public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
-            AuthenticationExecutionModel.Requirement.REQUIRED,
             AuthenticationExecutionModel.Requirement.ALTERNATIVE,
             AuthenticationExecutionModel.Requirement.DISABLED
     };
@@ -134,16 +133,6 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator 
     }
 
     @Override
-    public boolean requiresClient() {
-        return false;
-    }
-
-    @Override
-    public boolean configuredFor(KeycloakSession session, RealmModel realm, ClientModel client) {
-        return client.getSecret() != null;
-    }
-
-    @Override
     public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
         return REQUIREMENT_CHOICES;
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
index f01731a..48336a9 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
@@ -41,7 +41,6 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
     public static final String CERTIFICATE_ATTR = "jwt.credential.certificate";
 
     public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
-            AuthenticationExecutionModel.Requirement.REQUIRED,
             AuthenticationExecutionModel.Requirement.ALTERNATIVE,
             AuthenticationExecutionModel.Requirement.DISABLED
     };
@@ -136,16 +135,6 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
     }
 
     @Override
-    public boolean requiresClient() {
-        return false;
-    }
-
-    @Override
-    public boolean configuredFor(KeycloakSession session, RealmModel realm, ClientModel client) {
-        return client.getAttribute(CERTIFICATE_ATTR) != null;
-    }
-
-    @Override
     public String getDisplayType() {
         return "Signed Jwt";
     }
diff --git a/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java
index 7c864f1..2065de0 100644
--- a/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java
@@ -1,5 +1,7 @@
 package org.keycloak.authentication;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -11,6 +13,7 @@ import org.keycloak.events.Errors;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -18,19 +21,12 @@ import org.keycloak.models.ClientModel;
 public class ClientAuthenticationFlow implements AuthenticationFlow {
 
     Response alternativeChallenge = null;
-    boolean alternativeSuccessful = false;
-    List<AuthenticationExecutionModel> executions;
-    Iterator<AuthenticationExecutionModel> executionIterator;
     AuthenticationProcessor processor;
     AuthenticationFlowModel flow;
 
-    private List<String> successAuthenticators = new LinkedList<>();
-
     public ClientAuthenticationFlow(AuthenticationProcessor processor, AuthenticationFlowModel flow) {
         this.processor = processor;
         this.flow = flow;
-        this.executions = processor.getRealm().getAuthenticationExecutions(flow.getId());
-        this.executionIterator = executions.iterator();
     }
 
     @Override
@@ -40,131 +36,109 @@ public class ClientAuthenticationFlow implements AuthenticationFlow {
 
     @Override
     public Response processFlow() {
-        while (executionIterator.hasNext()) {
-            AuthenticationExecutionModel model = executionIterator.next();
-
-            if (model.isDisabled()) {
-                continue;
-            }
-
-            if (model.isAlternative() && alternativeSuccessful) {
-                continue;
-            }
-
-            if (model.isAuthenticatorFlow()) {
-                AuthenticationFlow authenticationFlow;
-                authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
-
-                Response flowChallenge = authenticationFlow.processFlow();
-                if (flowChallenge == null) {
-                    if (model.isAlternative()) alternativeSuccessful = true;
-                    continue;
-                } else {
-                    if (model.isAlternative()) {
-                        alternativeChallenge = flowChallenge;
-                    } else if (model.isRequired()) {
-                        return flowChallenge;
-                    } else {
-                        continue;
-                    }
-                    return flowChallenge;
-                }
-            }
+        List<AuthenticationExecutionModel> executions = findExecutionsToRun();
 
+        for (AuthenticationExecutionModel model : executions) {
             ClientAuthenticatorFactory factory = (ClientAuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, model.getAuthenticator());
             if (factory == null) {
                 throw new AuthenticationFlowException("Could not find ClientAuthenticatorFactory for: " + model.getAuthenticator(), AuthenticationFlowError.INTERNAL_ERROR);
             }
             ClientAuthenticator authenticator = factory.create();
             AuthenticationProcessor.logger.debugv("client authenticator: {0}", factory.getId());
-            ClientModel authClient = processor.getClient();
 
-            if (authenticator.requiresClient() && authClient == null) {
-                // Continue if it's alternative or optional flow
-                if (model.isAlternative() || model.isOptional()) {
-                    AuthenticationProcessor.logger.debugv("client authenticator: {0} requires client, but client not available. Skipping", factory.getId());
-                    continue;
+            AuthenticationProcessor.Result context = processor.createClientAuthenticatorContext(model, authenticator, executions);
+            authenticator.authenticateClient(context);
+            Response response = processResult(context);
+            if (response != null) return response;
+
+            ClientModel client = processor.getClient();
+            if (client != null) {
+
+                String expectedClientAuthType = client.getClientAuthenticatorType();
+
+                // Fallback to secret just in case (for backwards compatibility)
+                if (expectedClientAuthType == null) {
+                    expectedClientAuthType = KeycloakModelUtils.getDefaultClientAuthenticatorType();
+                    AuthenticationProcessor.logger.warnv("Client {0} doesn't have have authentication method configured. Fallback to {1}", client.getClientId(), expectedClientAuthType);
                 }
 
-                if (alternativeChallenge != null) {
-                    return alternativeChallenge;
+                // Check if client authentication matches
+                if (factory.getId().equals(expectedClientAuthType)) {
+                    AuthenticationProcessor.logger.debugv("Client {0} authenticated by {1}", client.getClientId(), factory.getId());
+                    processor.getEvent().detail(Details.CLIENT_AUTH_METHOD, factory.getId());
+                    return null;
+                } else {
+                    throw new AuthenticationFlowException("Client " + client.getClientId() + " was authenticated by incorrect method " + factory.getId(),
+                            AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS);
                 }
-                throw new AuthenticationFlowException("client authenticator: " + factory.getId(), AuthenticationFlowError.CLIENT_NOT_FOUND);
             }
+        }
 
-            if (authenticator.requiresClient() && authClient != null) {
-                boolean configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authClient);
-                if (!configuredFor) {
-                    if (model.isRequired()) {
-                        throw new AuthenticationFlowException("Client setup required for authenticator " + factory.getId() + " for client " + authClient.getClientId(),
-                                AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED);
-                    } else if (model.isOptional()) {
-                        continue;
-                    }
-                }
+        // Check if any alternative challenge was identified
+        if (alternativeChallenge != null) {
+            processor.getEvent().error(Errors.INVALID_CLIENT);
+            return alternativeChallenge;
+        }
+        throw new AuthenticationFlowException("Client was not identified by any client authenticator", AuthenticationFlowError.UNKNOWN_CLIENT);
+    }
+
+    protected List<AuthenticationExecutionModel> findExecutionsToRun() {
+        List<AuthenticationExecutionModel> executions = processor.getRealm().getAuthenticationExecutions(flow.getId());
+        List<AuthenticationExecutionModel> executionsToRun = new ArrayList<>();
+
+        for (AuthenticationExecutionModel execution : executions) {
+            if (execution.isRequired()) {
+                executionsToRun = Arrays.asList(execution);
+                break;
             }
-            AuthenticationProcessor.Result context = processor.createClientAuthenticatorContext(model, authenticator, executions);
-            authenticator.authenticateClient(context);
-            Response response = processResult(context);
-            if (response != null) return response;
 
-            authClient = processor.getClient();
-            if (authClient != null && authClient.isPublicClient()) {
-                AuthenticationProcessor.logger.debugv("Public client {0} identified by {1} . Skip next client authenticators", authClient.getClientId(), factory.getId());
-                logSuccessEvent();
-                return null;
+            if (execution.isAlternative()) {
+                executionsToRun.add(execution);
             }
         }
 
-        return finishClientAuthentication();
-    }
+        if (AuthenticationProcessor.logger.isTraceEnabled()) {
+            List<String> exIds = new ArrayList<>();
+            for (AuthenticationExecutionModel execution : executionsToRun) {
+                exIds.add(execution.getId());
+            }
+            AuthenticationProcessor.logger.tracef("Using executions for client authentication: %s", exIds.toString());
+        }
 
+        return executionsToRun;
+    }
 
-    public Response processResult(AuthenticationProcessor.Result result) {
+    protected Response processResult(AuthenticationProcessor.Result result) {
         AuthenticationExecutionModel execution = result.getExecution();
         FlowStatus status = result.getStatus();
+
+        AuthenticationProcessor.logger.debugv("client authenticator {0}: {1}", status.toString(), execution.getAuthenticator());
+
         if (status == FlowStatus.SUCCESS) {
-            AuthenticationProcessor.logger.debugv("client authenticator SUCCESS: {0}", execution.getAuthenticator());
-            if (execution.isAlternative()) alternativeSuccessful = true;
-            successAuthenticators.add(execution.getAuthenticator());
             return null;
         } else if (status == FlowStatus.FAILED) {
-            AuthenticationProcessor.logger.debugv("client authenticator FAILED: {0}", execution.getAuthenticator());
             if (result.getChallenge() != null) {
                 return sendChallenge(result, execution);
+            } else {
+                throw new AuthenticationFlowException(result.getError());
             }
-            throw new AuthenticationFlowException(result.getError());
         } else if (status == FlowStatus.FORCE_CHALLENGE) {
             return sendChallenge(result, execution);
         } else if (status == FlowStatus.CHALLENGE) {
-            AuthenticationProcessor.logger.debugv("client authenticator CHALLENGE: {0}", execution.getAuthenticator());
-            if (execution.isRequired()) {
-                return sendChallenge(result, execution);
-            }
-            ClientModel client = processor.getClient();
-            if (execution.isOptional() && client != null && result.getClientAuthenticator().configuredFor(processor.getSession(), processor.getRealm(), client)) {
-                return sendChallenge(result, execution);
-            }
+
             // Make sure the first priority alternative challenge is used
-            if (execution.isAlternative() && alternativeChallenge == null) {
+            if (alternativeChallenge == null) {
                 alternativeChallenge = result.getChallenge();
             }
             return null;
         } else if (status == FlowStatus.FAILURE_CHALLENGE) {
-            AuthenticationProcessor.logger.debugv("client authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
             return sendChallenge(result, execution);
         } else if (status == FlowStatus.ATTEMPTED) {
-            AuthenticationProcessor.logger.debugv("client authenticator ATTEMPTED: {0}", execution.getAuthenticator());
-            if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
-                throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS);
-            }
             return null;
         } else {
-            AuthenticationProcessor.logger.debugv("client authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
             AuthenticationProcessor.logger.error("Unknown result status");
             throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
         }
-
     }
 
     public Response sendChallenge(AuthenticationProcessor.Result result, AuthenticationExecutionModel execution) {
@@ -183,34 +157,4 @@ public class ClientAuthenticationFlow implements AuthenticationFlow {
 
         return result.getChallenge();
     }
-
-    private Response finishClientAuthentication() {
-        if (processor.getClient() == null) {
-            // Check if any alternative challenge was identified
-            if (alternativeChallenge != null) {
-                processor.getEvent().error(Errors.INVALID_CLIENT);
-                return alternativeChallenge;
-            }
-
-            throw new AuthenticationFlowException("Client was not identified by any client authenticator", AuthenticationFlowError.UNKNOWN_CLIENT);
-        }
-
-        logSuccessEvent();
-        return null;
-    }
-
-    private void logSuccessEvent() {
-        StringBuilder result = new StringBuilder();
-        boolean first = true;
-        for (String authenticator : successAuthenticators) {
-            if (first) {
-                first = false;
-            } else {
-                result.append(" ");
-            }
-            result.append(authenticator);
-        }
-
-        processor.getEvent().detail(Details.CLIENT_AUTH_METHOD, result.toString());
-    }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/ClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/ClientAuthenticator.java
index 47c0d5d..9e26b23 100644
--- a/services/src/main/java/org/keycloak/authentication/ClientAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/ClientAuthenticator.java
@@ -28,21 +28,4 @@ public interface ClientAuthenticator extends Provider {
      */
     void authenticateClient(ClientAuthenticationFlowContext context);
 
-
-    /**
-     * Does this authenticator require that the client has already been identified?  That ClientAuthenticationFlowContext.getClient() is not null?
-     *
-     * @return
-     */
-    boolean requiresClient();
-
-    /**
-     * Is this authenticator configured for this client?
-     *
-     * @param session
-     * @param realm
-     * @param client
-     * @return
-     */
-    boolean configuredFor(KeycloakSession session, RealmModel realm, ClientModel client);
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
index bc7fdb3..c017ba3 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java
@@ -39,7 +39,7 @@ public class AuthorizeClientUtil {
 
         ClientModel client = processor.getClient();
         if (client == null) {
-            throw new ErrorResponseException("invalid_client", "Client authentication was successful, but client is null", Response.Status.BAD_REQUEST);
+            throw new ErrorResponseException("invalid_client", "Client authentication ended, but client is null", Response.Status.BAD_REQUEST);
         }
 
         return new ClientAuthResult(client, processor.getClientAuthAttributes());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index dc49661..bae16d5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -133,6 +133,9 @@ public class AdminAPITest {
                 ClientRepresentation newApp = new ClientRepresentation();
                 if (appRep.getId() != null) newApp.setId(appRep.getId());
                 newApp.setClientId(appRep.getClientId());
+                if (appRep.getClientAuthenticatorType() != null) {
+                    newApp.setClientAuthenticatorType(appRep.getClientAuthenticatorType());
+                }
                 if (appRep.getSecret() != null) {
                     newApp.setSecret(appRep.getSecret());
                 }
@@ -177,6 +180,7 @@ public class AdminAPITest {
         if (appRep.getAdminUrl() != null) Assert.assertEquals(appRep.getAdminUrl(), storedApp.getAdminUrl());
         if (appRep.getBaseUrl() != null) Assert.assertEquals(appRep.getBaseUrl(), storedApp.getBaseUrl());
         if (appRep.isSurrogateAuthRequired() != null) Assert.assertEquals(appRep.isSurrogateAuthRequired(), storedApp.isSurrogateAuthRequired());
+        if (appRep.getClientAuthenticatorType() != null) Assert.assertEquals(appRep.getClientAuthenticatorType(), storedApp.getClientAuthenticatorType());
 
         if (appRep.getNotBefore() != null) {
             Assert.assertEquals(appRep.getNotBefore(), storedApp.getNotBefore());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPMultipleAttributesTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPMultipleAttributesTest.java
index edd210c..0b417e3 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPMultipleAttributesTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPMultipleAttributesTest.java
@@ -30,6 +30,7 @@ import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
+import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.adapter.AdapterTest;
@@ -78,7 +79,7 @@ public class LDAPMultipleAttributesTest {
             ldapFedProvider.getLdapIdentityStore().updatePassword(bruce, "password");
 
             // Create ldap-portal client
-            ClientModel ldapClient = appRealm.addClient("ldap-portal");
+            ClientModel ldapClient = new ClientManager(manager).createClient(appRealm, "ldap-portal");
             ldapClient.addRedirectUri("/ldap-portal");
             ldapClient.addRedirectUri("/ldap-portal/*");
             ldapClient.setManagementUrl("/ldap-portal");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
index 9f7bece..33cc037 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/CustomFlowTest.java
@@ -35,6 +35,7 @@ import org.keycloak.events.EventType;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.BrowserSecurityHeaders;
+import org.keycloak.models.ClientModel;
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
@@ -120,8 +121,6 @@ public class CustomFlowTest {
             execution.setAuthenticatorFlow(false);
             appRealm.addAuthenticatorExecution(execution);
 
-            new ClientManager().createClient(appRealm, "dummy-client");
-
             AuthenticationFlowModel clientFlow = new AuthenticationFlowModel();
             clientFlow.setAlias("client-dummy");
             clientFlow.setDescription("dummy pass through flow");
@@ -138,6 +137,11 @@ public class CustomFlowTest {
             execution.setPriority(10);
             execution.setAuthenticatorFlow(false);
             appRealm.addAuthenticatorExecution(execution);
+
+            // Set passthrough clientAuthenticator for our clients
+            ClientModel dummyClient = new ClientManager().createClient(appRealm, "dummy-client");
+            dummyClient.setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
+            appRealm.getClientByClientId("test-app").setClientAuthenticatorType(PassThroughClientAuthenticator.PROVIDER_ID);
         }
     });
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
index 1d7e74f..cfcd1fe 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
@@ -59,16 +59,6 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator 
     }
 
     @Override
-    public boolean requiresClient() {
-        return false;
-    }
-
-    @Override
-    public boolean configuredFor(KeycloakSession session, RealmModel realm, ClientModel client) {
-        return true;
-    }
-
-    @Override
     public String getDisplayType() {
         return "PassThrough Client Validation";
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java
index 1b5a249..6cd7998 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsBasicAuthTest.java
@@ -21,6 +21,7 @@ import org.junit.rules.ExternalResource;
 import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.Constants;
 import org.keycloak.testsuite.rule.KeycloakRule;
@@ -42,7 +43,7 @@ public class JaxrsBasicAuthTest {
 
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-            ClientModel app = appRealm.addClient("jaxrs-app");
+            ClientModel app = new ClientManager(manager).createClient(appRealm, "jaxrs-app");
             app.setEnabled(true);
             app.setSecret("password");
             app.setFullScopeAllowed(true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 332c94b..558ff39 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -110,6 +110,10 @@ public class ImportTest extends AbstractModelTest {
         Assert.assertTrue(10 == appRegisteredNodes.get("node1"));
         Assert.assertTrue(20 == appRegisteredNodes.get("172.10.15.20"));
 
+        // test clientAuthenticatorType
+        Assert.assertEquals(application.getClientAuthenticatorType(), "client-secret");
+        Assert.assertEquals(otherApp.getClientAuthenticatorType(), "client-jwt");
+
         // Test finding applications by ID
         Assert.assertNull(realm.getClientById("982734"));
         Assert.assertEquals(application, realm.getClientById(application.getId()));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
index c3f04bd..2aa13b2 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java
@@ -58,9 +58,11 @@ public class ClientAuthSignedJWTTest {
             ClientModel app1 = appRealm.addClient("client1");
             new ClientManager(manager).enableServiceAccount(app1);
             app1.setAttribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==");
+            app1.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
 
             ClientModel app2 = appRealm.addClient("client2");
             new ClientManager(manager).enableServiceAccount(app2);
+            app2.setClientAuthenticatorType(JWTClientAuthenticator.PROVIDER_ID);
 
             // This one is for keystore-client2.p12 , which doesn't work on Sun JDK
 //            app2.setAttribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLGHHjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjMzMVoXDTI1MDgxNzE3MjUxMVowEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIsatXj38fFD9fHslNrsWrubobudXYwwdZpGYqkHIhuDeSojGvhBSLmKIFmtbHMVcLEbS0dIEsSbNVrwjdFfuRuvd9Vu6Ng0JUC8fRhSeQniC3jcBuP8P4WlXK4+ir3Wlya+T6Hum9b68BiH0KyNZtFGJ6zLHuCcq9Bl0JifvibnUkDeTZPwgJNA9+GxS/x8fAkApcAbJrgBZvr57PwhbgHoZdB8aAY5f5ogbGzKDtSUMvFh+Jah39gWtn7p3VOuuMXA8SugogoH8C5m2itrPBL1UPhAcKUeWiqx4SmZe/lZo7x2WbSecNiFaiqBhIW+QbqCYW6I4u0YvuLuEe3+TC8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZzW5DZviCxUQdV5Ab07PZkUfvImHZ73oWWHZqzUQtZtbVdzfp3cnbb2wyXtlOvingO3hgpoTxV8vbKgLbIQfvkGGHBG1F5e0QVdtikfdcwWb7cy4/9F80OD7cgG0ZAzFbQ8ZY7iS3PToBp3+4tbIK2NK0ntt/MYgJnPbHeG4V4qfgUbFm1YgEK7WpbSVU8jGuJ5DWE+mlYgECZKZ5TSlaVGs2XOm6WXrJScucNekwcBWWiHyRsFHZEDzWmzt8TLTLnnb0vVjhx3qCYxah3RbyyMZm6WLZlLAaGEcwNDO8jaA3hAjrxoOA1xEaolQfGVsb/ElelHcR1Zfe0u4Ekd4tw==");
@@ -176,7 +178,7 @@ public class ClientAuthSignedJWTTest {
     }
 
     @Test
-    public void testDirectGrantRequest() throws Exception {
+    public void testDirectGrantRequestSuccess() throws Exception {
         oauth.clientId("client2");
         OAuthClient.AccessTokenResponse response = doGrantAccessTokenRequest("test-user@localhost", "password", getClient2SignedJWT());
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
index 7cefa79..6c205ad 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
@@ -29,6 +29,7 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.pages.ErrorPage;
@@ -49,18 +50,18 @@ public class OAuthRedirectUriTest {
     public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-            ClientModel installedApp = appRealm.addClient("test-installed");
+            ClientModel installedApp = new ClientManager(manager).createClient(appRealm, "test-installed");
             installedApp.setEnabled(true);
             installedApp.addRedirectUri(Constants.INSTALLED_APP_URN);
             installedApp.addRedirectUri(Constants.INSTALLED_APP_URL);
             installedApp.setSecret("password");
 
-            ClientModel installedApp2 = appRealm.addClient("test-installed2");
+            ClientModel installedApp2 = new ClientManager(manager).createClient(appRealm, "test-installed2");
             installedApp2.setEnabled(true);
             installedApp2.addRedirectUri(Constants.INSTALLED_APP_URL + "/myapp");
             installedApp2.setSecret("password");
 
-            ClientModel installedApp3 = appRealm.addClient("test-wildcard");
+            ClientModel installedApp3 = new ClientManager(manager).createClient(appRealm, "test-wildcard");
             installedApp3.setEnabled(true);
             installedApp3.addRedirectUri("http://example.com/foo/*");
             installedApp3.addRedirectUri("http://localhost:8081/foo/*");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
index 4deacee..2529091 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
@@ -17,6 +17,7 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.RefreshToken;
+import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
@@ -36,7 +37,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
     public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-            ClientModel app = appRealm.addClient("resource-owner");
+            ClientModel app = new ClientManager(manager).createClient(appRealm, "resource-owner");
             app.setSecret("secret");
 
             UserModel user = session.users().addUser(appRealm, "direct-login");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ServiceAccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ServiceAccountTest.java
index c1b8f84..5c5dc26 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ServiceAccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ServiceAccountTest.java
@@ -34,11 +34,11 @@ public class ServiceAccountTest {
     public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
         @Override
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-            ClientModel app = appRealm.addClient("service-account-cl");
+            ClientModel app = new ClientManager(manager).createClient(appRealm, "service-account-cl");
             app.setSecret("secret1");
             new ClientManager(manager).enableServiceAccount(app);
 
-            ClientModel disabledApp = appRealm.addClient("service-account-disabled");
+            ClientModel disabledApp = new ClientManager(manager).createClient(appRealm, "service-account-disabled");
             disabledApp.setSecret("secret1");
 
             UserModel serviceAccountUser = session.users().getUserByUsername(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "service-account-cl", appRealm);
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index af9a559..5bf2bdd 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -123,6 +123,7 @@
             "enabled": true,
             "adminUrl": "http://localhost:8081/secure-portal",
             "baseUrl": "http://localhost:8081/secure-portal",
+            "clientAuthenticatorType": "client-jwt",
             "redirectUris": [
                 "http://localhost:8081/secure-portal/*"
             ],
diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json
index 9df4385..acbbdf5 100755
--- a/testsuite/integration/src/test/resources/model/testrealm.json
+++ b/testsuite/integration/src/test/resources/model/testrealm.json
@@ -164,6 +164,7 @@
             "name": "Other Application",
             "enabled": true,
             "serviceAccountsEnabled": true,
+            "clientAuthenticatorType": "client-jwt",
             "protocolMappers" : [
                 {
                     "name" : "gss delegation credential",
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
index a0ddce4..b577bfa 100755
--- a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
@@ -123,6 +123,7 @@
             "enabled": true,
             "adminUrl": "http://localhost:8082/secure-portal",
             "baseUrl": "http://localhost:8082/secure-portal",
+            "clientAuthenticatorType": "client-jwt",
             "redirectUris": [
                 "http://localhost:8082/secure-portal/*"
             ],