From bc189554aa8b8599ca2af033bdb3e4ae0c61cc80 Mon Sep 17 00:00:00 2001 From: mposolda Date: Fri, 4 Sep 2015 00:09:54 +0200 Subject: [PATCH] KEYCLOAK-1795 Add just one clientAuthenticatorType per client --- .../META-INF/jpa-changelog-1.5.0.xml | 6 + .../idm/ClientRepresentation.java | 9 + .../reference/en/en-US/modules/auth-spi.xml | 5 +- .../WEB-INF/keycloak-client-signed-jwt.json | 2 +- examples/demo-template/testrealm.json | 13 ++ .../admin/resources/js/controllers/clients.js | 38 +++- .../partials/authentication-flows.html | 2 +- .../partials/client-credentials.html | 39 ++-- .../migration/migrators/MigrateTo1_5_0.java | 5 + .../java/org/keycloak/models/ClientModel.java | 3 + .../models/entities/ClientEntity.java | 9 + .../models/utils/KeycloakModelUtils.java | 5 + .../models/utils/ModelToRepresentation.java | 1 + .../models/utils/RepresentationToModel.java | 7 + .../models/file/adapter/ClientAdapter.java | 10 + .../cache/infinispan/ClientAdapter.java | 12 ++ .../models/cache/entities/CachedClient.java | 6 + .../keycloak/models/jpa/ClientAdapter.java | 10 + .../models/jpa/entities/ClientEntity.java | 10 + .../keycloak/adapters/ClientAdapter.java | 11 ++ .../ClientAuthenticationFlow.java | 184 ++++++------------ .../authentication/ClientAuthenticator.java | 17 -- .../ClientIdAndSecretAuthenticator.java | 11 -- .../client/JWTClientAuthenticator.java | 11 -- .../oidc/utils/AuthorizeClientUtil.java | 2 +- .../testsuite/admin/AdminAPITest.java | 4 + .../LDAPMultipleAttributesTest.java | 3 +- .../testsuite/forms/CustomFlowTest.java | 8 +- .../forms/PassThroughClientAuthenticator.java | 10 - .../testsuite/jaxrs/JaxrsBasicAuthTest.java | 3 +- .../keycloak/testsuite/model/ImportTest.java | 4 + .../oauth/ClientAuthSignedJWTTest.java | 4 +- .../testsuite/oauth/OAuthRedirectUriTest.java | 7 +- ...urceOwnerPasswordCredentialsGrantTest.java | 3 +- .../testsuite/oauth/ServiceAccountTest.java | 4 +- .../resources/adapter-test/demorealm.json | 1 + .../src/test/resources/model/testrealm.json | 1 + .../resources/adapter-test/demorealm.json | 1 + 38 files changed, 275 insertions(+), 206 deletions(-) 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 52930a3a78..d5619d9f5e 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 @@ + + + + + + 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 e5ab503640..020445e406 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 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 81b7d9d893..ff7f061c46 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 - 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. @@ -993,7 +993,8 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor 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 Adding Authenticator for more details. + and finally configure Clients binding . See Adding Authenticator 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). 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 6c07d1eb90..3e90c34a30 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 8d3e597258..0ba235f2de 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 2e3a251a46..4ec4c22160 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 ; iCopy - + 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 043cee64fc..8415fbe5da 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 @@ - - - - - - - - - - - - - - - - - -
Client Auth Type
{{authenticator.displayName|capitalize}}Configure
No client authenticators available
+
+
+
+ +
+
+ +
+
+ Client Authenticator used for authentication this client against Keycloak server + +
+
+
+ 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 dd550dc13a..95a5224880 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 96a7dd7577..daccf8e0aa 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 8e0c21bd8d..52e972196b 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 79a9b12b51..b64ebb04b6 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 1a411c7109..f2d4f1ebe1 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 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 31d257586d..4faff0194e 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 87f0ee34ee..8003b70ded 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 @@ -145,6 +145,16 @@ public class ClientAdapter implements ClientModel { entity.setEnabled(enabled); } + @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 97914006dc..9f79b157ed 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 911021e2fb..25749c3fa0 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 redirectUris = new HashSet(); private boolean enabled; + private String clientAuthenticatorType; private String secret; private String protocol; private Map attributes = new HashMap(); @@ -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 fc551c7edf..b0acc417fd 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 @@ -151,6 +151,16 @@ public class ClientAdapter implements ClientModel { entity.getRedirectUris().remove(redirectUri); } + @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 8b57335d87..1f6ac251b3 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 40ea0d2f62..26effca9c3 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 @@ -141,6 +141,17 @@ public class ClientAdapter extends AbstractMongoAdapter imple updateMongoEntity(); } + @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/ClientAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/ClientAuthenticationFlow.java index 7c864f10d4..2065de0f02 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 Marek Posolda @@ -18,19 +21,12 @@ import org.keycloak.models.ClientModel; public class ClientAuthenticationFlow implements AuthenticationFlow { Response alternativeChallenge = null; - boolean alternativeSuccessful = false; - List executions; - Iterator executionIterator; AuthenticationProcessor processor; AuthenticationFlowModel flow; - private List 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 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; - } - - if (alternativeChallenge != null) { - return alternativeChallenge; - } - 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; - } - } - } 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; + 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); + } + + // 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); + } } } - return finishClientAuthentication(); + // 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 findExecutionsToRun() { + List executions = processor.getRealm().getAuthenticationExecutions(flow.getId()); + List executionsToRun = new ArrayList<>(); - public Response processResult(AuthenticationProcessor.Result result) { + for (AuthenticationExecutionModel execution : executions) { + if (execution.isRequired()) { + executionsToRun = Arrays.asList(execution); + break; + } + + if (execution.isAlternative()) { + executionsToRun.add(execution); + } + } + + if (AuthenticationProcessor.logger.isTraceEnabled()) { + List exIds = new ArrayList<>(); + for (AuthenticationExecutionModel execution : executionsToRun) { + exIds.add(execution.getId()); + } + AuthenticationProcessor.logger.tracef("Using executions for client authentication: %s", exIds.toString()); + } + + return executionsToRun; + } + + 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 47c0d5de3e..9e26b239b1 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/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java index 55d0fded03..b4917ece33 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 }; @@ -133,16 +132,6 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator return true; } - @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 f01731a79c..48336a9ed2 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 }; @@ -135,16 +134,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/protocol/oidc/utils/AuthorizeClientUtil.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/AuthorizeClientUtil.java index bc7fdb3e8d..c017ba3019 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 dc49661c38..bae16d5a9b 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 edd210cb41..0b417e3c8e 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 9f7becefe2..33cc03753c 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 1d7e74fda8..cfcd1fe688 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 @@ -58,16 +58,6 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator context.success(); } - @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 1b5a249e91..6cd79988ee 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 332c94b56e..558ff39a8b 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 c3f04bd85d..2aa13b26d9 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 7cefa79c55..6c205adbd2 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 4deaceed09..252909154f 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 c1b8f84bda..5c5dc2652a 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 af9a559718..5bf2bdd953 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 9df4385fa2..acbbdf5805 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 a0ddce4ec7..b577bfae95 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/*" ],