keycloak-uncached

Changes

.travis.yml 9(+8 -1)

services/src/main/java/org/keycloak/authorization/admin/util/Models.java 321(+0 -321)

Details

.travis.yml 9(+8 -1)

diff --git a/.travis.yml b/.travis.yml
index 4f4d297..53a9440 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,12 @@
 language: java
 
+cache:
+  directories:
+    - $HOME/.m2
+
+before_cache:
+  - rm -rf $HOME/.m2/repository/org/keycloak
+
 env:
   global:
     - MAVEN_SKIP_RC=true
@@ -12,7 +19,7 @@ before_script:
   - export MAVEN_SKIP_RC=true
 
 install: 
-  - ( mvn install -Pdistribution -DskipTests=true -B -V -q ) & ( MYPID=$!; while [  $(ps -ef | grep $MYPID | grep -v grep | wc -l) -gt 0 ]; do    sleep 10;     date; done; )
+  - travis_wait 30 mvn install -Pdistribution -DskipTests=true -B -V -q
 
 script:
   - mvn test -B
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/DirectAccessGrantsLoginModule.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/DirectAccessGrantsLoginModule.java
index ddd1152..78d98e7 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/DirectAccessGrantsLoginModule.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/jaas/DirectAccessGrantsLoginModule.java
@@ -43,6 +43,7 @@ import org.keycloak.common.VerificationException;
 import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
 import org.keycloak.constants.ServiceUrlConstants;
 import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.common.util.KeycloakUriBuilder;
 
@@ -102,9 +103,9 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
             StringBuilder errorBuilder = new StringBuilder("Login failed. Invalid status: " + status);
             if (entity != null) {
                 InputStream is = entity.getContent();
-                Map<String, String> errors = (Map<String, String>) JsonSerialization.readValue(is, Map.class);
-                errorBuilder.append(", OAuth2 error. Error: " + errors.get(OAuth2Constants.ERROR))
-                        .append(", Error description: " + errors.get(OAuth2Constants.ERROR_DESCRIPTION));
+                OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(is, OAuth2ErrorRepresentation.class);
+                errorBuilder.append(", OAuth2 error. Error: " + errorRep.getError())
+                        .append(", Error description: " + errorRep.getErrorDescription());
             }
             String error = errorBuilder.toString();
             log.warn(error);
@@ -161,9 +162,9 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
                     if (entity != null) {
                         InputStream is = entity.getContent();
                         if (status == 400) {
-                            Map<String, String> errors = (Map<String, String>) JsonSerialization.readValue(is, Map.class);
-                            errorBuilder.append(", OAuth2 error. Error: " + errors.get(OAuth2Constants.ERROR))
-                                    .append(", Error description: " + errors.get(OAuth2Constants.ERROR_DESCRIPTION));
+                            OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(is, OAuth2ErrorRepresentation.class);
+                            errorBuilder.append(", OAuth2 error. Error: " + errorRep.getError())
+                                    .append(", Error description: " + errorRep.getErrorDescription());
 
                         } else {
                             if (is != null) is.close();
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWK.java b/core/src/main/java/org/keycloak/jose/jwk/JWK.java
index ce153a4..e75963a 100755
--- a/core/src/main/java/org/keycloak/jose/jwk/JWK.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWK.java
@@ -37,8 +37,20 @@ public class JWK {
 
     public static final String PUBLIC_KEY_USE = "use";
 
-    public static final String SIG_USE = "sig";
-    public static final String ENCRYPTION_USE = "enc";
+    public enum Use {
+        SIG("sig"),
+        ENCRYPTION("enc");
+
+        private String str;
+
+        Use(String str) {
+            this.str = str;
+        }
+
+        public String asString() {
+            return str;
+        }
+    }
 
     @JsonProperty(KEY_ID)
     private String keyId;
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
index 1bad9cf..7a31f72 100755
--- a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
@@ -67,7 +67,7 @@ public class JWKParser {
 
     public PublicKey toPublicKey() {
         String algorithm = jwk.getKeyType();
-        if (RSAPublicJWK.RSA.equals(algorithm)) {
+        if (isAlgorithmSupported(algorithm)) {
             BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString()));
             BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString()));
 
@@ -81,4 +81,8 @@ public class JWKParser {
         }
     }
 
+    public boolean isAlgorithmSupported(String algorithm) {
+        return RSAPublicJWK.RSA.equals(algorithm);
+    }
+
 }
diff --git a/core/src/main/java/org/keycloak/OAuthErrorException.java b/core/src/main/java/org/keycloak/OAuthErrorException.java
index f0bb862..940d434 100755
--- a/core/src/main/java/org/keycloak/OAuthErrorException.java
+++ b/core/src/main/java/org/keycloak/OAuthErrorException.java
@@ -37,12 +37,18 @@ public class OAuthErrorException extends Exception {
     public static final String REQUEST_NOT_SUPPORTED = "request_not_supported";
     public static final String REQUEST_URI_NOT_SUPPORTED = "request_uri_not_supported";
 
+    // OAuth2 Bearer Token Usage
+    public static final String INVALID_TOKEN = "invalid_token";
+    public static final String INSUFFICIENT_SCOPE = "insufficient_scope";
+
+    // OIDC Dynamic Client Registration
+    public static final String INVALID_REDIRECT_URI = "invalid_redirect_uri";
+    public static final String INVALID_CLIENT_METADATA = "invalid_client_metadata";
+
     // Others
     public static final String INVALID_CLIENT = "invalid_client";
     public static final String INVALID_GRANT = "invalid_grant";
     public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
-    public static final String INVALID_TOKEN = "invalid_token";
-    public static final String INSUFFICIENT_SCOPE = "insufficient_scope";
 
     public OAuthErrorException(String error, String description, String message, Throwable cause) {
         super(message, cause);
diff --git a/core/src/main/java/org/keycloak/representations/idm/CertificateRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/CertificateRepresentation.java
index ee8e102..03539ff 100755
--- a/core/src/main/java/org/keycloak/representations/idm/CertificateRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/CertificateRepresentation.java
@@ -25,6 +25,7 @@ package org.keycloak.representations.idm;
 public class CertificateRepresentation {
 
     protected String privateKey;
+    protected String publicKey;
     protected String certificate;
 
     public String getPrivateKey() {
@@ -35,6 +36,14 @@ public class CertificateRepresentation {
         this.privateKey = privateKey;
     }
 
+    public String getPublicKey() {
+        return publicKey;
+    }
+
+    public void setPublicKey(String publicKey) {
+        this.publicKey = publicKey;
+    }
+
     public String getCertificate() {
         return certificate;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRegistrationTrustedHostRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRegistrationTrustedHostRepresentation.java
new file mode 100644
index 0000000..1df7ab4
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRegistrationTrustedHostRepresentation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.representations.idm;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationTrustedHostRepresentation {
+
+    String hostName;
+    Integer count;
+    Integer remainingCount;
+
+    public static ClientRegistrationTrustedHostRepresentation create(String hostName, int count, int remainingCount) {
+        ClientRegistrationTrustedHostRepresentation rep = new ClientRegistrationTrustedHostRepresentation();
+        rep.setHostName(hostName);
+        rep.setCount(count);
+        rep.setRemainingCount(remainingCount);
+        return rep;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+
+    public Integer getRemainingCount() {
+        return remainingCount;
+    }
+
+    public void setRemainingCount(Integer remainingCount) {
+        this.remainingCount = remainingCount;
+    }
+}
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 f18f4eb..cc9e54b 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.representations.idm;
 
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+
 import java.util.List;
 import java.util.Map;
 
@@ -62,6 +64,7 @@ public class ClientRepresentation {
     private Boolean useTemplateConfig;
     private Boolean useTemplateScope;
     private Boolean useTemplateMappers;
+    private ResourceServerRepresentation authorizationSettings;
 
 
     public String getId() {
@@ -241,6 +244,9 @@ public class ClientRepresentation {
     }
 
     public Boolean getAuthorizationServicesEnabled() {
+        if (authorizationSettings != null) {
+            return true;
+        }
         return authorizationServicesEnabled;
     }
 
@@ -353,4 +359,11 @@ public class ClientRepresentation {
         this.useTemplateMappers = useTemplateMappers;
     }
 
+    public ResourceServerRepresentation getAuthorizationSettings() {
+        return authorizationSettings;
+    }
+
+    public void setAuthorizationSettings(ResourceServerRepresentation authorizationSettings) {
+        this.authorizationSettings = authorizationSettings;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuth2ErrorRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuth2ErrorRepresentation.java
new file mode 100644
index 0000000..f988013
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuth2ErrorRepresentation.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.representations.idm;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.OAuth2Constants;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OAuth2ErrorRepresentation {
+
+    private String error;
+    private String errorDescription;
+
+    public OAuth2ErrorRepresentation() {
+    }
+
+    public OAuth2ErrorRepresentation(String error, String errorDescription) {
+        this.error = error;
+        this.errorDescription = errorDescription;
+    }
+
+    @JsonProperty(OAuth2Constants.ERROR)
+    public String getError() {
+        return error;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
+
+    @JsonProperty(OAuth2Constants.ERROR_DESCRIPTION)
+    public String getErrorDescription() {
+        return errorDescription;
+    }
+
+    public void setErrorDescription(String errorDescription) {
+        this.errorDescription = errorDescription;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java
index fc2973d..d7f9939 100644
--- a/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/oidc/OIDCClientRepresentation.java
@@ -18,6 +18,7 @@
 package org.keycloak.representations.oidc;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import org.keycloak.jose.jwk.JSONWebKeySet;
 
 import java.util.List;
 
@@ -27,14 +28,20 @@ import java.util.List;
 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
 public class OIDCClientRepresentation {
 
+    // OIDC Dynamic client registration properties
+
     private List<String> redirect_uris;
 
     private String token_endpoint_auth_method;
 
+    private String token_endpoint_auth_signing_alg;
+
     private List<String> grant_types;
 
     private List<String> response_types;
 
+    private String application_type;
+
     private String client_id;
 
     private String client_secret;
@@ -47,7 +54,7 @@ public class OIDCClientRepresentation {
 
     private String scope;
 
-    private String contacts;
+    private List<String> contacts;
 
     private String tos_uri;
 
@@ -55,12 +62,49 @@ public class OIDCClientRepresentation {
 
     private String jwks_uri;
 
-    private String jwks;
+    private JSONWebKeySet jwks;
+
+    private String sector_identifier_uri;
+
+    private String subject_type;
+
+    private String id_token_signed_response_alg;
+
+    private String id_token_encrypted_response_alg;
+
+    private String id_token_encrypted_response_enc;
+
+    private String userinfo_signed_response_alg;
+
+    private String userinfo_encrypted_response_alg;
+
+    private String userinfo_encrypted_response_enc;
+
+    private String request_object_signing_alg;
 
+    private String request_object_encryption_alg;
+
+    private String request_object_encryption_enc;
+
+    private Integer default_max_age;
+
+    private Boolean require_auth_time;
+
+    private List<String> default_acr_values;
+
+    private String initiate_login_uri;
+
+    private List<String> request_uris;
+
+    // OIDC Session Management
+    private List<String> post_logout_redirect_uris;
+
+    // Not sure from which specs this comes
     private String software_id;
 
     private String software_version;
 
+    // OIDC Dynamic Client Registration Response
     private Integer client_id_issued_at;
 
     private Integer client_secret_expires_at;
@@ -85,6 +129,14 @@ public class OIDCClientRepresentation {
         this.token_endpoint_auth_method = token_endpoint_auth_method;
     }
 
+    public String getTokenEndpointAuthSigningAlg() {
+        return token_endpoint_auth_signing_alg;
+    }
+
+    public void setTokenEndpointAuthSigningAlg(String token_endpoint_auth_signing_alg) {
+        this.token_endpoint_auth_signing_alg = token_endpoint_auth_signing_alg;
+    }
+
     public List<String> getGrantTypes() {
         return grant_types;
     }
@@ -101,6 +153,14 @@ public class OIDCClientRepresentation {
         this.response_types = responseTypes;
     }
 
+    public String getApplicationType() {
+        return application_type;
+    }
+
+    public void setApplicationType(String applicationType) {
+        this.application_type = applicationType;
+    }
+
     public String getClientId() {
         return client_id;
     }
@@ -149,11 +209,11 @@ public class OIDCClientRepresentation {
         this.scope = scope;
     }
 
-    public String getContacts() {
+    public List<String> getContacts() {
         return contacts;
     }
 
-    public void setContacts(String contacts) {
+    public void setContacts(List<String> contacts) {
         this.contacts = contacts;
     }
 
@@ -181,14 +241,150 @@ public class OIDCClientRepresentation {
         this.jwks_uri = jwks_uri;
     }
 
-    public String getJwks() {
+    public JSONWebKeySet getJwks() {
         return jwks;
     }
 
-    public void setJwks(String jwks) {
+    public void setJwks(JSONWebKeySet jwks) {
         this.jwks = jwks;
     }
 
+    public String getSectorIdentifierUri() {
+        return sector_identifier_uri;
+    }
+
+    public void setSectorIdentifierUri(String sectorIdentifierUri) {
+        this.sector_identifier_uri = sectorIdentifierUri;
+    }
+
+    public String getSubjectType() {
+        return subject_type;
+    }
+
+    public void setSubjectType(String subjectType) {
+        this.subject_type = subjectType;
+    }
+
+    public String getIdTokenSignedResponseAlg() {
+        return id_token_signed_response_alg;
+    }
+
+    public void setIdTokenSignedResponseAlg(String idTokenSignedResponseAlg) {
+        this.id_token_signed_response_alg = idTokenSignedResponseAlg;
+    }
+
+    public String getIdTokenEncryptedResponseAlg() {
+        return id_token_encrypted_response_alg;
+    }
+
+    public void setIdTokenEncryptedResponseAlg(String idTokenEncryptedResponseAlg) {
+        this.id_token_encrypted_response_alg = idTokenEncryptedResponseAlg;
+    }
+
+    public String getIdTokenEncryptedResponseEnc() {
+        return id_token_encrypted_response_enc;
+    }
+
+    public void setIdTokenEncryptedResponseEnc(String idTokenEncryptedResponseEnc) {
+        this.id_token_encrypted_response_enc = idTokenEncryptedResponseEnc;
+    }
+
+    public String getUserinfoSignedResponseAlg() {
+        return userinfo_signed_response_alg;
+    }
+
+    public void setUserinfoSignedResponseAlg(String userinfo_signed_response_alg) {
+        this.userinfo_signed_response_alg = userinfo_signed_response_alg;
+    }
+
+    public String getUserinfoEncryptedResponseAlg() {
+        return userinfo_encrypted_response_alg;
+    }
+
+    public void setUserinfoEncryptedResponseAlg(String userinfo_encrypted_response_alg) {
+        this.userinfo_encrypted_response_alg = userinfo_encrypted_response_alg;
+    }
+
+    public String getUserinfoEncryptedResponseEnc() {
+        return userinfo_encrypted_response_enc;
+    }
+
+    public void setUserinfoEncryptedResponseEnc(String userinfo_encrypted_response_enc) {
+        this.userinfo_encrypted_response_enc = userinfo_encrypted_response_enc;
+    }
+
+    public String getRequestObjectSigningAlg() {
+        return request_object_signing_alg;
+    }
+
+    public void setRequestObjectSigningAlg(String request_object_signing_alg) {
+        this.request_object_signing_alg = request_object_signing_alg;
+    }
+
+    public String getRequestObjectEncryptionAlg() {
+        return request_object_encryption_alg;
+    }
+
+    public void setRequestObjectEncryptionAlg(String request_object_encryption_alg) {
+        this.request_object_encryption_alg = request_object_encryption_alg;
+    }
+
+    public String getRequestObjectEncryptionEnc() {
+        return request_object_encryption_enc;
+    }
+
+    public void setRequestObjectEncryptionEnc(String request_object_encryption_enc) {
+        this.request_object_encryption_enc = request_object_encryption_enc;
+    }
+
+    public Integer getDefaultMaxAge() {
+        return default_max_age;
+    }
+
+    public void setDefaultMaxAge(Integer default_max_age) {
+        this.default_max_age = default_max_age;
+    }
+
+    public Boolean getRequireAuthTime() {
+        return require_auth_time;
+    }
+
+    public void setRequireAuthTime(Boolean require_auth_time) {
+        this.require_auth_time = require_auth_time;
+    }
+
+    public List<String> getDefaultAcrValues() {
+        return default_acr_values;
+    }
+
+    public void setDefaultAcrValues(List<String> default_acr_values) {
+        this.default_acr_values = default_acr_values;
+    }
+
+    public String getInitiateLoginUri() {
+        return initiate_login_uri;
+    }
+
+    public void setInitiateLoginUri(String initiate_login_uri) {
+        this.initiate_login_uri = initiate_login_uri;
+    }
+
+    public List<String> getRequestUris() {
+        return request_uris;
+    }
+
+    public void setRequestUris(List<String> requestUris) {
+        this.request_uris = requestUris;
+    }
+
+    public List<String> getPostLogoutRedirectUris() {
+        return post_logout_redirect_uris;
+    }
+
+    public void setPostLogoutRedirectUris(List<String> post_logout_redirect_uris) {
+        this.post_logout_redirect_uris = post_logout_redirect_uris;
+    }
+
     public String getSoftwareId() {
         return software_id;
     }
diff --git a/core/src/test/java/org/keycloak/JsonParserTest.java b/core/src/test/java/org/keycloak/JsonParserTest.java
index 69416b7..e346fe6 100755
--- a/core/src/test/java/org/keycloak/JsonParserTest.java
+++ b/core/src/test/java/org/keycloak/JsonParserTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
 import org.keycloak.representations.IDToken;
 import org.keycloak.representations.JsonWebToken;
 import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.representations.oidc.OIDCClientRepresentation;
 import org.keycloak.util.JsonSerialization;
 
 /**
@@ -125,4 +126,23 @@ public class JsonParserTest {
         System.out.println(sb.toString());
     }
 
+    @Test
+    public void testReadOIDCClientRep() throws IOException {
+        String stringRep = "{\"subject_type\": \"public\", \"jwks_uri\": \"https://op.certification.openid.net:60720/export/jwk_60720.json\", \"contacts\": [\"roland.hedberg@umu.se\"], \"application_type\": \"web\", \"grant_types\": [\"authorization_code\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60720/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60720/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"default_max_age\": 3600}";
+        OIDCClientRepresentation clientRep = JsonSerialization.readValue(stringRep, OIDCClientRepresentation.class);
+        Assert.assertEquals("public", clientRep.getSubjectType());
+        Assert.assertTrue(clientRep.getRequireAuthTime());
+        Assert.assertEquals(3600, clientRep.getDefaultMaxAge().intValue());
+        Assert.assertEquals(1, clientRep.getRedirectUris().size());
+        Assert.assertEquals("https://op.certification.openid.net:60720/authz_cb", clientRep.getRedirectUris().get(0));
+        Assert.assertNull(clientRep.getJwks());
+    }
+
+    @Test
+    public void testReadOIDCClientRepWithJWKS() throws IOException {
+        String stringRep = "{\"token_endpoint_auth_method\": \"private_key_jwt\", \"subject_type\": \"public\", \"jwks_uri\": null, \"jwks\": {\"keys\": [{\"use\": \"enc\", \"e\": \"AQAB\", \"d\": \"lZQv0_81euRLeUYU84Aodh0ar7ymDlzWP5NMra4Jklkb-lTBWkI-u4RMsPqGYyW3KHRoL_pgzZXSzQx8RLQfER6timRWb--NxMMKllZubByU3RqH2ooNuocJurspYiXkznPW1Mg9DaNXL0C2hwWPQHTeUVISpjgi5TCOV1ccWVyksFruya_VNL1CIByB-L0GL1rqbKv32cDwi2A3_jJa61cpzfLSIBe-lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N-poleV8mBfMqBB5fWwy_ZTFCpmQ5AywGmctaik_wNhMoWuA4tUfY6_1LdKld-5Cjq55eLtuJjtvuQ\", \"n\": \"tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q\", \"q\": \"1q-r-bmMFbIzrLK2U3elksZq8CqUqZxlSfkGMZuVkxgYMS-e4FPzEp2iirG-eO11aa0cpMMoBdTnVdGJ_ZUR93w0lGf9XnQAJqxP7eOsrUoiW4VWlWH4WfOiLgpO-pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc\", \"p\": \"2lrYPppRbcQWu4LtWN6tOVUrtCOPv1eLTKTc7q8vCMcem1Ox5QFB7KnUtNZ5Ni7wnZUeVDfimNebtjNsGvDSrpgIlo9dEnFBQsQIkzZ2SkoYfgmF8hNdi6P-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc\", \"kid\": \"a0\", \"kty\": \"RSA\"}, {\"use\": \"sig\", \"e\": \"AQAB\", \"d\": \"DodXDEtkovWWGsMEXYy_nEEMCWyROMOebCnCv0ey3i4M4bh2dmwqgz0e-IKQAFlGiMkidGL1lNbq0uFS04FbuRAR06dYw1cbrNbDdhrWFxKTd1L5D9p-x-gW-YDWhpI8rUGRa76JXkOSxZUbg09_QyUd99CXAHh-FXi_ZkIKD8hK6FrAs68qhLf8MNkUv63DTduw7QgeFfQivdopePxyGuMk5n8veqwsUZsklQkhNlTYQqeM1xb2698ZQcNYkl0OssEsSJKRjXt-LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ\", \"n\": \"zfZzttF7HmnTYwSMPdxKs5AoczbNS2mOPz-tN1g4ljqI_F1DG8cgQDcN_VDufxoFGRERo2FK6WEN41LhbGEyP6uL6wW6Cy29qE9QZcvY5mXrncndRSOkNcMizvuEJes_fMYrmP_lPiC6kWiqItTk9QBWqJfiYKhCx9cSDXsBmJXn3KWQCVHvj1ANFWW0CWLMKlWN-_NMNLIWJN_pEAocTZMzxSFBK1b5_5J8ZS7hfWRF6MQmjsJcz2jzA21SQZNpre3kwnTGRSwo05sAS-TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw\", \"q\": \"5E5XKK5njT-zzRqqTeY2tgP9PJBACeaH_xQRHZ_1ydE7tVd7HdgdaEHfQ1jvKIHFkknWWOBAY1mlBc4YDirLShB_voShD8C-Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU\", \"p\": \"5vJHCSM3H3q4RltYzENC9RyZZV8EUmpkv9moyguT5t-BUGA-T4W_FGIxzOPXRWOckIplKkoDKhavUeNmTZMCUcue0nkICSJpvNE4Nb2p5PZk_QqSdQNvCasQtdojEG0AmfVD85SU551CYxJdLdDFOqyK2entpMr8lhokem189As\", \"kid\": \"a1\", \"kty\": \"RSA\"}, {\"d\": \"S4_OufhLBgXFMgIDMI1zlVe2uCExpcEAQ80J_lXfS8I\", \"use\": \"sig\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"DBdNyq30mXmUs_BIvKMqaTTNO7HDhCi0YiC8GciwNYk\", \"x\": \"cYwzBoyjRjxj334bRTqanONf7DUYK-6TgiuN0DixJAk\", \"kid\": \"a2\"}, {\"d\": \"33TnYgdJtWAiVosKqUnz0zSmvWTbsx5-6pceynW6Xck\", \"use\": \"enc\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"Cula95Eix1Ia77St3OULe6-UKWs5I06nmdfUzhXUQTs\", \"x\": \"wk8HBVxNNzj1gJBxPmmx9XYW1L61ObBGzxpRa6_OqWU\", \"kid\": \"a3\"}]}, \"application_type\": \"web\", \"contacts\": [\"roland.hedberg@umu.se\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60784/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60784/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"grant_types\": [\"authorization_code\"], \"default_max_age\": 3600}";
+        OIDCClientRepresentation clientRep = JsonSerialization.readValue(stringRep, OIDCClientRepresentation.class);
+        Assert.assertNotNull(clientRep.getJwks());
+    }
+
 }
diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl
index 4a18a1d..4ef3f0e 100755
--- a/distribution/demo-dist/src/main/xslt/standalone.xsl
+++ b/distribution/demo-dist/src/main/xslt/standalone.xsl
@@ -89,6 +89,7 @@
                 <local-cache name="sessions"/>
                 <local-cache name="offlineSessions"/>
                 <local-cache name="loginFailures"/>
+                <local-cache name="authorization"/>
                 <local-cache name="work"/>
             </cache-container>
             <xsl:apply-templates select="node()|@*"/>
diff --git a/distribution/server-overlay/cli/keycloak-install-ha.cli b/distribution/server-overlay/cli/keycloak-install-ha.cli
index 6f61189..a84a34a 100644
--- a/distribution/server-overlay/cli/keycloak-install-ha.cli
+++ b/distribution/server-overlay/cli/keycloak-install-ha.cli
@@ -8,6 +8,7 @@ embed-server --server-config=standalone-ha.xml
 /subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:add(mode="SYNC",owners="1")
 /subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:add(mode="SYNC",owners="1")
 /subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:add(mode="SYNC",owners="1")
+/subsystem=infinispan/cache-container=keycloak/distributed-cache=authorization:add(mode="SYNC",owners="1")
 /subsystem=infinispan/cache-container=keycloak/replicated-cache=work:add(mode="SYNC")
 /extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
 /subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientRegistrationTrustedHostResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientRegistrationTrustedHostResource.java
new file mode 100644
index 0000000..4f5ace9
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientRegistrationTrustedHostResource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.admin.client.resource;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ClientRegistrationTrustedHostResource {
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response create(ClientRegistrationTrustedHostRepresentation config);
+
+    @PUT
+    @Path("{hostname}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    Response update(final @PathParam("hostname") String hostName, ClientRegistrationTrustedHostRepresentation config);
+
+    @GET
+    @Path("{hostname}")
+    @Produces(MediaType.APPLICATION_JSON)
+    ClientRegistrationTrustedHostRepresentation get(final @PathParam("hostname") String hostName);
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    List<ClientRegistrationTrustedHostRepresentation> list();
+
+    @DELETE
+    @Path("{hostname}")
+    void delete(final @PathParam("hostname") String hostName);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
index eb25bfb..ba91227 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
@@ -148,6 +148,9 @@ public interface RealmResource {
     @Path("clients-initial-access")
     ClientInitialAccessResource clientInitialAccess();
 
+    @Path("clients-trusted-hosts")
+    public ClientRegistrationTrustedHostResource clientRegistrationTrustedHost();
+
     @Path("partialImport")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
diff --git a/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpErrorException.java b/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpErrorException.java
index 4078b73..640bc0d 100644
--- a/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpErrorException.java
+++ b/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpErrorException.java
@@ -18,6 +18,8 @@
 package org.keycloak.client.registration;
 
 import org.apache.http.StatusLine;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
+import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
 
@@ -26,14 +28,31 @@ import java.io.IOException;
  */
 public class HttpErrorException extends IOException {
 
-    private StatusLine statusLine;
+    private final StatusLine statusLine;
+    private final String errorResponse;
 
-    public HttpErrorException(StatusLine statusLine) {
+    public HttpErrorException(StatusLine statusLine, String errorResponse) {
         this.statusLine = statusLine;
+        this.errorResponse = errorResponse;
     }
 
     public StatusLine getStatusLine() {
         return statusLine;
     }
 
+    public String getErrorResponse() {
+        return errorResponse;
+    }
+
+    public OAuth2ErrorRepresentation toErrorRepresentation() {
+        if (errorResponse == null) {
+            return null;
+        }
+
+        try {
+            return JsonSerialization.readValue(errorResponse, OAuth2ErrorRepresentation.class);
+        } catch (IOException ioe) {
+            throw new RuntimeException("Not OAuth2 error");
+        }
+    }
 }
diff --git a/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpUtil.java b/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpUtil.java
index 8d524ce..66808ed 100644
--- a/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpUtil.java
+++ b/integration/client-registration/src/main/java/org/keycloak/client/registration/HttpUtil.java
@@ -23,9 +23,7 @@ import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.*;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.impl.client.CloseableHttpClient;
-import org.keycloak.client.registration.Auth;
-import org.keycloak.client.registration.ClientRegistrationException;
-import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.common.util.StreamUtil;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -69,8 +67,7 @@ class HttpUtil {
             if (response.getStatusLine().getStatusCode() == 201) {
                 return responseStream;
             } else {
-                responseStream.close();
-                throw new HttpErrorException(response.getStatusLine());
+                throw httpErrorException(response, responseStream);
             }
         } catch (IOException e) {
             throw new ClientRegistrationException("Failed to send request", e);
@@ -97,10 +94,7 @@ class HttpUtil {
                 responseStream.close();
                 return null;
             } else {
-                if (responseStream != null) {
-                    responseStream.close();
-                }
-                throw new HttpErrorException(response.getStatusLine());
+                throw httpErrorException(response, responseStream);
             }
         } catch (IOException e) {
             throw new ClientRegistrationException("Failed to send request", e);
@@ -118,9 +112,6 @@ class HttpUtil {
             addAuth(request);
 
             HttpResponse response = httpClient.execute(request);
-            if (response.getEntity() != null) {
-                response.getEntity().getContent();
-            }
 
             InputStream responseStream = null;
             if (response.getEntity() != null) {
@@ -130,10 +121,7 @@ class HttpUtil {
             if (response.getStatusLine().getStatusCode() == 200) {
                 return responseStream;
             } else {
-                if (responseStream != null) {
-                    responseStream.close();
-                }
-                throw new HttpErrorException(response.getStatusLine());
+                throw httpErrorException(response, responseStream);
             }
         } catch (IOException e) {
             throw new ClientRegistrationException("Failed to send request", e);
@@ -147,12 +135,13 @@ class HttpUtil {
             addAuth(request);
 
             HttpResponse response = httpClient.execute(request);
+            InputStream responseStream = null;
             if (response.getEntity() != null) {
-                response.getEntity().getContent().close();
+                responseStream = response.getEntity().getContent();
             }
 
             if (response.getStatusLine().getStatusCode() != 204) {
-                throw new HttpErrorException(response.getStatusLine());
+                throw httpErrorException(response, responseStream);
             }
         } catch (IOException e) {
             throw new ClientRegistrationException("Failed to send request", e);
@@ -185,4 +174,13 @@ class HttpUtil {
         }
     }
 
+    private HttpErrorException httpErrorException(HttpResponse response, InputStream responseStream) throws IOException {
+        if (responseStream != null) {
+            String errorResponse = StreamUtil.readString(responseStream);
+            return new HttpErrorException(response.getStatusLine(), errorResponse);
+        } else {
+            return new HttpErrorException(response.getStatusLine(), null);
+        }
+    }
+
 }
diff --git a/misc/OIDCConformanceTestsuite.md b/misc/OIDCConformanceTestsuite.md
index 9e4650a..72ec17b 100644
--- a/misc/OIDCConformanceTestsuite.md
+++ b/misc/OIDCConformanceTestsuite.md
@@ -34,7 +34,7 @@ Q: Does the OP have a .well-known/openid-configuration endpoint?
 A: Yes
 
 Q: Do the provider support dynamic client registration?
-A: No (just for easier start)
+A: No (See below for how to run with dynamic client registration)
 
 Q: redirect_uris
 Non-editable value: https://op.certification.openid.net:60720/authz_cb
@@ -62,6 +62,21 @@ Nothing filled
 4) After setup, you will be redirected to the testing application. Something like `https://op.certification.openid.net:60720/` and can run individual tests.
 Some tests require some manual actions (eg. delete cookies). The conformance testsuite should guide you.
 
+Run conformance testsuite with Dynamic client registration
+----------------------------------------------------------
+1) The steps are similar to above, however for question:
+
+Q: Do the provider support dynamic client registration?
+The answer will be: Yes
+
+Then you don't need to configure redirect_uris, client_id and client_secret.
+
+2) With the setup from previous point, OIDC Conformance testsuite will dynamically register new client in Keycloak. But you also need to allow the anonymous
+ client registration requests from the OIDC conformance to register clients.
+ 
+ So you need to login to Keycloak admin console and in tab "Initial Access Tokens" for realm master, you need to fill new trusted host. Fill the hostname "op.certification.openid.net" and enable big 
+ count of registrations for it (1000 or so) as running each test will register new client. 
+
 
 Update the openshift cartridge with latest Keycloak
 ---------------------------------------------------
diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
index 24b772b..7b5f1d9 100755
--- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java
@@ -108,7 +108,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
 
             cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(true, maxEntries));
             cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true);
-
+            cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true);
             logger.debugv("Using container managed Infinispan cache container, lookup={1}", cacheContainerLookup);
         } catch (Exception e) {
             throw new RuntimeException("Failed to retrieve cache container", e);
@@ -161,6 +161,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
         cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration);
         cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration);
         cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration);
+        cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, sessionCacheConfiguration);
 
         ConfigurationBuilder replicationConfigBuilder = new ConfigurationBuilder();
         if (clustered) {
@@ -178,9 +179,6 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
         cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, getRevisionCacheConfig(false, InfinispanConnectionProvider.REALM_REVISIONS_CACHE_DEFAULT_MAX));
         cacheManager.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME, true);
 
-        cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME,
-                new ConfigurationBuilder().eviction().type(EvictionType.COUNT).size(100).simpleCache(true).build());
-
         long maxEntries = cacheManager.getCache(InfinispanConnectionProvider.USER_CACHE_NAME).getCacheConfiguration().eviction().maxEntries();
         if (maxEntries <= 0) {
             maxEntries = InfinispanConnectionProvider.USER_REVISIONS_CACHE_DEFAULT_MAX;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
index 6e3271b..4657633 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java
@@ -389,7 +389,7 @@ public class CachedPolicyStore implements PolicyStore {
                 if (this.updated == null) {
                     this.updated = getDelegate().findById(getId());
                     if (this.updated == null) throw new IllegalStateException("Not found in database");
-                    transaction.whenCommit(() -> cache.evict(getCacheKeyForPolicy(getId())));
+                    transaction.whenCommit(() -> cache.remove(getCacheKeyForPolicy(getId())));
                 }
 
                 return this.updated;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
index 33bed6a..2685135 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceServerStore.java
@@ -169,7 +169,7 @@ public class CachedResourceServerStore implements ResourceServerStore {
                 if (this.updated == null) {
                     this.updated = getDelegate().findById(getId());
                     if (this.updated == null) throw new IllegalStateException("Not found in database");
-                    transaction.whenCommit(() -> cache.evict(getCacheKeyForResourceServer(getId())));
+                    transaction.whenCommit(() -> cache.remove(getCacheKeyForResourceServer(getId())));
                 }
 
                 return this.updated;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
index 495df61..4d9a946 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedResourceStore.java
@@ -270,7 +270,7 @@ public class CachedResourceStore implements ResourceStore {
                 if (this.updated == null) {
                     this.updated = getDelegate().findById(getId());
                     if (this.updated == null) throw new IllegalStateException("Not found in database");
-                    transaction.whenCommit(() -> cache.evict(getCacheKeyForResource(getId())));
+                    transaction.whenCommit(() -> cache.remove(getCacheKeyForResource(getId())));
                 }
 
                 return this.updated;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
index af491c2..72f3f25 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedScopeStore.java
@@ -175,7 +175,7 @@ public class CachedScopeStore implements ScopeStore {
                 if (this.updated == null) {
                     this.updated = getDelegate().findById(getId());
                     if (this.updated == null) throw new IllegalStateException("Not found in database");
-                    transaction.whenCommit(() -> cache.evict(getCacheKeyForScope(getId())));
+                    transaction.whenCommit(() -> cache.remove(getCacheKeyForScope(getId())));
                 }
 
                 return this.updated;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
index 99493bf..775cf64 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java
@@ -22,10 +22,10 @@ import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
-import org.keycloak.models.entities.AbstractIdentifiableEntity;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 import org.keycloak.representations.idm.authorization.Logic;
 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -34,7 +34,9 @@ import java.util.stream.Collectors;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class CachedPolicy implements Policy {
+public class CachedPolicy implements Policy, Serializable {
+
+    private static final long serialVersionUID = -144247681046298128L;
 
     private String id;
     private String type;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java
index 8af333b..ee1212f 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResource.java
@@ -22,6 +22,7 @@ import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 
+import java.io.Serializable;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -29,7 +30,9 @@ import java.util.stream.Collectors;
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class CachedResource implements Resource {
+public class CachedResource implements Resource, Serializable {
+
+    private static final long serialVersionUID = -6886179034626995165L;
 
     private final String id;
     private String resourceServerId;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java
index 08a425a..7dab0b2 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedResourceServer.java
@@ -21,10 +21,14 @@ package org.keycloak.models.authorization.infinispan.entities;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
 
+import java.io.Serializable;
+
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class CachedResourceServer implements ResourceServer {
+public class CachedResourceServer implements ResourceServer, Serializable {
+
+    private static final long serialVersionUID = 5054253390723121289L;
 
     private final String id;
     private String clientId;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java
index 1cba6a2..6450119 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedScope.java
@@ -21,10 +21,14 @@ package org.keycloak.models.authorization.infinispan.entities;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 
+import java.io.Serializable;
+
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class CachedScope implements Scope {
+public class CachedScope implements Scope, Serializable {
+
+    private static final long serialVersionUID = -3919706923417065454L;
 
     private final String id;
     private String resourceServerId;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
index 50e1b69..8256aa2 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
@@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
 
 import org.keycloak.Config;
 import org.keycloak.common.enums.SslRequired;
+import org.keycloak.common.util.StringPropertyReplacer;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.models.*;
 import org.keycloak.models.cache.infinispan.entities.CachedRealm;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientRegistrationTrustedHostAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientRegistrationTrustedHostAdapter.java
new file mode 100644
index 0000000..34e68cc
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientRegistrationTrustedHostAdapter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.sessions.infinispan;
+
+import org.infinispan.Cache;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.sessions.infinispan.entities.ClientRegistrationTrustedHostEntity;
+import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationTrustedHostAdapter implements ClientRegistrationTrustedHostModel {
+
+    private final KeycloakSession session;
+    private final InfinispanUserSessionProvider provider;
+    private final Cache<String, SessionEntity> cache;
+    private final RealmModel realm;
+    private final ClientRegistrationTrustedHostEntity entity;
+
+    public ClientRegistrationTrustedHostAdapter(KeycloakSession session, InfinispanUserSessionProvider provider, Cache<String, SessionEntity> cache, RealmModel realm, ClientRegistrationTrustedHostEntity entity) {
+        this.session = session;
+        this.provider = provider;
+        this.cache = cache;
+        this.realm = realm;
+        this.entity = entity;
+    }
+
+    @Override
+    public RealmModel getRealm() {
+        return realm;
+    }
+
+    @Override
+    public String getHostName() {
+        return entity.getHostName();
+    }
+
+    @Override
+    public int getCount() {
+        return entity.getCount();
+    }
+
+    @Override
+    public void setCount(int count) {
+        entity.setCount(count);
+        update();
+    }
+
+    @Override
+    public int getRemainingCount() {
+        return entity.getRemainingCount();
+    }
+
+    @Override
+    public void setRemainingCount(int remainingCount) {
+        entity.setRemainingCount(remainingCount);
+        update();
+    }
+
+    @Override
+    public void decreaseRemainingCount() {
+        entity.setRemainingCount(entity.getRemainingCount() - 1);
+        update();
+    }
+
+    void update() {
+        provider.getTx().replace(cache, entity.getId(), entity);
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientRegistrationTrustedHostEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientRegistrationTrustedHostEntity.java
new file mode 100644
index 0000000..53b1ec8
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientRegistrationTrustedHostEntity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.sessions.infinispan.entities;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationTrustedHostEntity extends SessionEntity {
+
+    private String hostName;
+
+    private int count;
+
+    private int remainingCount;
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public int getRemainingCount() {
+        return remainingCount;
+    }
+
+    public void setRemainingCount(int remainingCount) {
+        this.remainingCount = remainingCount;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 614fa9f..5452869 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -23,9 +23,11 @@ import org.jboss.logging.Logger;
 import org.keycloak.common.util.Time;
 import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
@@ -33,12 +35,14 @@ import org.keycloak.models.UserSessionProvider;
 import org.keycloak.models.UserLoginFailureModel;
 import org.keycloak.models.session.UserSessionPersisterProvider;
 import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
+import org.keycloak.models.sessions.infinispan.entities.ClientRegistrationTrustedHostEntity;
 import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
 import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
 import org.keycloak.models.sessions.infinispan.stream.ClientInitialAccessPredicate;
+import org.keycloak.models.sessions.infinispan.stream.ClientRegistrationTrustedHostPredicate;
 import org.keycloak.models.sessions.infinispan.stream.ClientSessionPredicate;
 import org.keycloak.models.sessions.infinispan.stream.Comparators;
 import org.keycloak.models.sessions.infinispan.stream.Mappers;
@@ -537,6 +541,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         return entity != null ? new ClientInitialAccessAdapter(session, this, cache, realm, entity) : null;
     }
 
+    ClientRegistrationTrustedHostAdapter wrap(RealmModel realm, ClientRegistrationTrustedHostEntity entity) {
+        Cache<String, SessionEntity> cache = getCache(false);
+        return entity != null ? new ClientRegistrationTrustedHostAdapter(session, this, cache, realm, entity) : null;
+    }
+
 
     UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
         return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
@@ -729,6 +738,63 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         return list;
     }
 
+    @Override
+    public ClientRegistrationTrustedHostModel createClientRegistrationTrustedHostModel(RealmModel realm, String hostName, int count) {
+        if (getClientRegistrationTrustedHostModel(realm, hostName) != null) {
+            throw new ModelDuplicateException("Client registration already exists for this realm and hostName");
+        }
+
+        String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
+
+        ClientRegistrationTrustedHostEntity entity = new ClientRegistrationTrustedHostEntity();
+        entity.setId(id);
+        entity.setHostName(hostName);
+        entity.setRealm(realm.getId());
+        entity.setCount(count);
+        entity.setRemainingCount(count);
+
+        tx.put(sessionCache, id, entity);
+
+        return wrap(realm, entity);
+    }
+
+    @Override
+    public ClientRegistrationTrustedHostModel getClientRegistrationTrustedHostModel(RealmModel realm, String hostName) {
+        String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
+
+        Cache<String, SessionEntity> cache = getCache(false);
+        ClientRegistrationTrustedHostEntity entity = (ClientRegistrationTrustedHostEntity) cache.get(id);
+
+        // If created in this transaction
+        if (entity == null) {
+            entity = (ClientRegistrationTrustedHostEntity) tx.get(cache, id);
+        }
+
+        return wrap(realm, entity);
+    }
+
+    @Override
+    public void removeClientRegistrationTrustedHostModel(RealmModel realm, String hostName) {
+        String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
+        tx.remove(getCache(false), id);
+    }
+
+    @Override
+    public List<ClientRegistrationTrustedHostModel> listClientRegistrationTrustedHosts(RealmModel realm) {
+        Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientRegistrationTrustedHostPredicate.create(realm.getId())).iterator();
+        List<ClientRegistrationTrustedHostModel> list = new LinkedList<>();
+        while (itr.hasNext()) {
+            list.add(wrap(realm, (ClientRegistrationTrustedHostEntity) itr.next().getValue()));
+        }
+        return list;
+    }
+
+    private static final String CLIENT_REG_TRUSTED_HOST_ID_PREFIX = "reg:::";
+
+    private String computeClientRegistrationTrustedHostEntityId(RealmModel realm, String hostName) {
+        return CLIENT_REG_TRUSTED_HOST_ID_PREFIX + realm.getId() + ":::" + hostName;
+    }
+
     class InfinispanKeycloakTransaction implements KeycloakTransaction {
 
         private boolean active;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientRegistrationTrustedHostPredicate.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientRegistrationTrustedHostPredicate.java
new file mode 100644
index 0000000..8663c4e
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientRegistrationTrustedHostPredicate.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.sessions.infinispan.stream;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import org.keycloak.models.sessions.infinispan.entities.ClientRegistrationTrustedHostEntity;
+import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationTrustedHostPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
+
+    public static ClientRegistrationTrustedHostPredicate create(String realm) {
+        return new ClientRegistrationTrustedHostPredicate(realm);
+    }
+
+    private ClientRegistrationTrustedHostPredicate(String realm) {
+        this.realm = realm;
+    }
+
+    private String realm;
+
+
+    @Override
+    public boolean test(Map.Entry<String, SessionEntity> entry) {
+        SessionEntity e = entry.getValue();
+
+        if (!realm.equals(e.getRealm())) {
+            return false;
+        }
+
+        if (!(e instanceof ClientRegistrationTrustedHostEntity)) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 68aa02c..46df41a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -19,6 +19,7 @@ package org.keycloak.models.jpa;
 
 import org.jboss.logging.Logger;
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.common.util.StringPropertyReplacer;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.common.enums.SslRequired;
 import org.keycloak.jose.jwk.JWKBuilder;
diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
index 1481095..e6d2753 100644
--- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
@@ -35,4 +35,5 @@
 
     <include file="META-INF/jpa-changelog-authz-master.xml"/>
     <include file="META-INF/jpa-changelog-2.1.0.xml"/>
+    <include file="META-INF/jpa-changelog-2.2.0.xml"/>
 </databaseChangeLog>
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 701cfb0..e7dc5f6 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -21,6 +21,7 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.common.util.StringPropertyReplacer;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.common.enums.SslRequired;
diff --git a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java b/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java
index 9c3e098..8e3e658 100644
--- a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java
+++ b/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java
@@ -19,6 +19,7 @@ package org.keycloak.authentication;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.keycloak.models.ClientModel;
 import org.keycloak.provider.ProviderConfigProperty;
@@ -60,4 +61,12 @@ public interface ClientAuthenticatorFactory extends ProviderFactory<ClientAuthen
      */
     Map<String, Object> getAdapterConfiguration(ClientModel client);
 
+    /**
+     * Get authentication methods for the specified protocol
+     *
+     * @param loginProtocol corresponds to {@link org.keycloak.protocol.LoginProtocolFactory#getId}
+     * @return name of supported client authenticator methods in the protocol specific "language"
+     */
+    Set<String> getProtocolAuthenticatorMethods(String loginProtocol);
+
 }
diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index ff646bb..fb7c91b 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -24,6 +24,7 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
 import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
 import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
 
@@ -64,17 +65,19 @@ public final class AuthorizationProvider implements Provider {
     private final StoreFactory storeFactory;
     private final List<PolicyProviderFactory> policyProviderFactories;
     private final KeycloakSession keycloakSession;
+    private final RealmModel realm;
 
-    public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory, Executor scheduller) {
+    public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Executor scheduller) {
         this.keycloakSession = session;
+        this.realm = realm;
         this.storeFactory = storeFactory;
         this.scheduller = scheduller;
         this.policyProviderFactories = configurePolicyProviderFactories(session);
         this.policyEvaluator = new DefaultPolicyEvaluator(this, this.policyProviderFactories);
     }
 
-    public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory) {
-        this(session, storeFactory, Runnable::run);
+    public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory) {
+        this(session, realm, storeFactory, Runnable::run);
     }
 
     /**
@@ -120,6 +123,10 @@ public final class AuthorizationProvider implements Provider {
         return this.keycloakSession;
     }
 
+    public RealmModel getRealm() {
+        return realm;
+    }
+
     private List<PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSession session) {
         List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class);
 
diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java
index ae4dcc2..7ca684c 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java
@@ -18,10 +18,14 @@
 
 package org.keycloak.authorization;
 
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
 import org.keycloak.provider.ProviderFactory;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public interface AuthorizationProviderFactory extends ProviderFactory<AuthorizationProvider> {
+
+    AuthorizationProvider create(KeycloakSession session, RealmModel realm);
 }
diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index d5fa9cc..724b655 100644
--- a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -38,6 +38,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -132,12 +133,21 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
             return true;
         }
 
+        Resource resourcePermission = permission.getResource();
+        Set<Resource> policyResources = policy.getResources();
+
+        if (resourcePermission != null && !policyResources.isEmpty()) {
+            if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
+                return false;
+            }
+        }
+
         Set<Scope> scopes = new HashSet<>(policy.getScopes());
 
         if (scopes.isEmpty()) {
             Set<Resource> resources = new HashSet<>();
 
-            resources.addAll(policy.getResources());
+            resources.addAll(policyResources);
 
             for (Resource resource : resources) {
                 scopes.addAll(resource.getScopes());
diff --git a/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java b/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
index 045258c..8b9c27c 100644
--- a/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
+++ b/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
@@ -131,6 +131,11 @@ public enum ResourceType {
     /**
      *
      */
+    , CLIENT_REGISTRATION_TRUSTED_HOST_MODEL
+
+    /**
+     *
+     */
     , CLIENT_ROLE
 
     /**
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientRegistrationTrustedHostModel.java b/server-spi/src/main/java/org/keycloak/models/ClientRegistrationTrustedHostModel.java
new file mode 100644
index 0000000..30b2e6f
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/ClientRegistrationTrustedHostModel.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ClientRegistrationTrustedHostModel {
+
+    RealmModel getRealm();
+
+    String getHostName();
+
+    int getCount();
+    void setCount(int count);
+
+    int getRemainingCount();
+    void setRemainingCount(int remainingCount);
+    void decreaseRemainingCount();
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index 585558c..27ce108 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -82,6 +82,11 @@ public interface UserSessionProvider extends Provider {
     void removeClientInitialAccessModel(RealmModel realm, String id);
     List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm);
 
+    ClientRegistrationTrustedHostModel createClientRegistrationTrustedHostModel(RealmModel realm, String hostName, int count);
+    ClientRegistrationTrustedHostModel getClientRegistrationTrustedHostModel(RealmModel realm, String hostName);
+    void removeClientRegistrationTrustedHostModel(RealmModel realm, String hostName);
+    List<ClientRegistrationTrustedHostModel> listClientRegistrationTrustedHosts(RealmModel realm);
+
     void close();
 
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 73c907b..e2d4799 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -17,6 +17,14 @@
 
 package org.keycloak.models.utils;
 
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.events.Event;
 import org.keycloak.events.admin.AdminEvent;
@@ -31,6 +39,7 @@ import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.OTPPolicy;
 import org.keycloak.models.ProtocolMapperModel;
@@ -72,8 +81,25 @@ import org.keycloak.representations.idm.UserFederationProviderRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
 import org.keycloak.common.util.Time;
-
-import java.util.*;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -767,4 +793,181 @@ public class ModelToRepresentation {
         rep.setConfig(component.getConfig());
         return rep;
     }
+
+    public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
+        ScopeRepresentation scope = new ScopeRepresentation();
+
+        scope.setId(model.getId());
+        scope.setName(model.getName());
+        scope.setIconUri(model.getIconUri());
+
+        StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+
+        scope.setResources(new ArrayList<>());
+
+        storeFactory.getResourceStore().findByScope(model.getId()).forEach(resource -> scope.getResources().add(toRepresentation(resource, resource.getResourceServer(), authorizationProvider)));
+
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+
+        scope.setPolicies(new ArrayList<>());
+
+        policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> {
+            PolicyRepresentation policy = new PolicyRepresentation();
+
+            policy.setId(policyModel.getId());
+            policy.setName(policyModel.getName());
+            policy.setType(policyModel.getType());
+
+            if (!scope.getPolicies().contains(policy)) {
+                scope.getPolicies().add(policy);
+            }
+        });
+
+        return scope;
+    }
+
+    public static ResourceServerRepresentation toRepresentation(ResourceServer model, ClientModel client) {
+        ResourceServerRepresentation server = new ResourceServerRepresentation();
+
+        server.setId(model.getId());
+        server.setClientId(model.getClientId());
+        server.setName(client.getClientId());
+        server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement());
+        server.setPolicyEnforcementMode(model.getPolicyEnforcementMode());
+
+        return server;
+    }
+
+    public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
+        PolicyRepresentation representation = new PolicyRepresentation();
+
+        representation.setId(model.getId());
+        representation.setName(model.getName());
+        representation.setDescription(model.getDescription());
+        representation.setType(model.getType());
+        representation.setDecisionStrategy(model.getDecisionStrategy());
+        representation.setLogic(model.getLogic());
+        representation.setConfig(new HashMap<>(model.getConfig()));
+
+        List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId());
+
+        representation.setDependentPolicies(policies.stream().map(policy -> {
+            PolicyRepresentation representation1 = new PolicyRepresentation();
+
+            representation1.setId(policy.getId());
+            representation1.setName(policy.getName());
+
+            return representation1;
+        }).collect(Collectors.toList()));
+
+        List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
+
+        List<String> obj = model.getAssociatedPolicies().stream().map(policy -> {
+            PolicyRepresentation representation1 = new PolicyRepresentation();
+
+            representation1.setId(policy.getId());
+            representation1.setName(policy.getName());
+            representation1.setType(policy.getType());
+
+            associatedPolicies.add(representation1);
+
+            return policy.getId();
+        }).collect(Collectors.toList());
+
+        representation.setAssociatedPolicies(associatedPolicies);
+
+        try {
+            representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return representation;
+    }
+
+    public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setId(model.getId());
+        resource.setType(model.getType());
+        resource.setName(model.getName());
+        resource.setUri(model.getUri());
+        resource.setIconUri(model.getIconUri());
+
+        ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
+
+        owner.setId(model.getOwner());
+
+        KeycloakSession keycloakSession = authorization.getKeycloakSession();
+        RealmModel realm = authorization.getRealm();
+
+        if (owner.getId().equals(resourceServer.getClientId())) {
+            ClientModel clientModel = realm.getClientById(resourceServer.getClientId());
+            owner.setName(clientModel.getClientId());
+        } else {
+            UserModel userModel = keycloakSession.users().getUserById(owner.getId(), realm);
+
+            if (userModel == null) {
+                throw new RuntimeException("Could not find the user [" + owner.getId() + "] who owns the Resource [" + resource.getId() + "].");
+            }
+
+            owner.setName(userModel.getUsername());
+        }
+
+        resource.setOwner(owner);
+
+        resource.setScopes(model.getScopes().stream().map(model1 -> {
+            ScopeRepresentation scope = new ScopeRepresentation();
+            scope.setId(model1.getId());
+            scope.setName(model1.getName());
+            String iconUri = model1.getIconUri();
+            if (iconUri != null) {
+                scope.setIconUri(iconUri);
+            }
+            return scope;
+        }).collect(Collectors.toSet()));
+
+        resource.setTypedScopes(new ArrayList<>());
+
+        if (resource.getType() != null) {
+            ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+            for (Resource typed : resourceStore.findByType(resource.getType())) {
+                if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
+                    resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
+                        ScopeRepresentation scope = new ScopeRepresentation();
+                        scope.setId(model1.getId());
+                        scope.setName(model1.getName());
+                        String iconUri = model1.getIconUri();
+                        if (iconUri != null) {
+                            scope.setIconUri(iconUri);
+                        }
+                        return scope;
+                    }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
+                }
+            }
+        }
+
+        resource.setPolicies(new ArrayList<>());
+
+        Set<Policy> policies = new HashSet<>();
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+
+        policies.addAll(policyStore.findByResource(resource.getId()));
+        policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId()));
+        policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
+
+        for (Policy policyModel : policies) {
+            PolicyRepresentation policy = new PolicyRepresentation();
+
+            policy.setId(policyModel.getId());
+            policy.setName(policyModel.getName());
+            policy.setType(policyModel.getType());
+
+            if (!resource.getPolicies().contains(policy)) {
+                resource.getPolicies().add(policy);
+            }
+        }
+
+        return resource;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 7e813d6..f36f010 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -18,8 +18,16 @@
 package org.keycloak.models.utils;
 
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationProviderFactory;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.hash.Pbkdf2PasswordHashProvider;
@@ -84,6 +92,12 @@ import org.keycloak.representations.idm.UserFederationMapperRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -95,8 +109,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
-
-import static java.lang.Boolean.TRUE;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 public class RepresentationToModel {
 
@@ -277,6 +291,13 @@ public class RepresentationToModel {
             }
         }
 
+        if (rep.getClients() != null) {
+            rep.getClients().forEach(clientRepresentation -> {
+                ClientModel client = newRealm.getClientByClientId(clientRepresentation.getClientId());
+                importAuthorizationSettings(clientRepresentation, client, session);
+            });
+        }
+
         if (rep.getSmtpServer() != null) {
             newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
         }
@@ -1022,22 +1043,6 @@ public class RepresentationToModel {
         if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
         else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
 
-        boolean createResourceServer = TRUE.equals(resourceRep.getAuthorizationServicesEnabled());
-
-        if (createResourceServer) {
-            AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
-            ResourceServerStore resourceServerStore = provider.getStoreFactory().getResourceServerStore();
-
-            client.setServiceAccountsEnabled(true);
-            client.setBearerOnly(false);
-            client.setPublicClient(false);
-
-            ResourceServer resourceServer = resourceServerStore.create(client.getId());
-
-            resourceServer.setAllowRemoteResourceManagement(true);
-            resourceServer.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING);
-        }
-
         return client;
     }
 
@@ -1656,4 +1661,432 @@ public class RepresentationToModel {
         model.setName(rep.getName());
         return model;
     }
+
+    public static void importAuthorizationSettings(ClientRepresentation clientRepresentation, ClientModel client, KeycloakSession session) {
+        if (Boolean.TRUE.equals(clientRepresentation.getAuthorizationServicesEnabled())) {
+            AuthorizationProviderFactory authorizationFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);
+            AuthorizationProvider authorization = authorizationFactory.create(session, client.getRealm());
+
+            client.setServiceAccountsEnabled(true);
+            client.setBearerOnly(false);
+            client.setPublicClient(false);
+
+            ResourceServerRepresentation rep = clientRepresentation.getAuthorizationSettings();
+
+            if (rep == null) {
+                rep = new ResourceServerRepresentation();
+            }
+
+            rep.setClientId(client.getId());
+
+            toModel(rep, authorization);
+        }
+    }
+
+    public static void toModel(ResourceServerRepresentation rep, AuthorizationProvider authorization) {
+        ResourceServerStore resourceServerStore = authorization.getStoreFactory().getResourceServerStore();
+        ResourceServer resourceServer;
+        ResourceServer existing = resourceServerStore.findByClient(rep.getClientId());
+
+        if (existing == null) {
+            resourceServer = resourceServerStore.create(rep.getClientId());
+            resourceServer.setAllowRemoteResourceManagement(true);
+            resourceServer.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING);
+        } else {
+            resourceServer = existing;
+        }
+
+        resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
+        resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
+
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ScopeStore scopeStore = storeFactory.getScopeStore();
+
+        rep.getScopes().forEach(scope -> {
+            toModel(scope, resourceServer, authorization);
+        });
+
+        KeycloakSession session = authorization.getKeycloakSession();
+        RealmModel realm = authorization.getRealm();
+
+        rep.getResources().forEach(resourceRepresentation -> {
+            ResourceOwnerRepresentation owner = resourceRepresentation.getOwner();
+
+            if (owner == null) {
+                owner = new ResourceOwnerRepresentation();
+                resourceRepresentation.setOwner(owner);
+            }
+
+            owner.setId(resourceServer.getClientId());
+
+            if (owner.getName() != null) {
+                UserModel user = session.users().getUserByUsername(owner.getName(), realm);
+
+                if (user != null) {
+                    owner.setId(user.getId());
+                }
+            }
+
+            toModel(resourceRepresentation, resourceServer, authorization);
+        });
+
+        rep.getPolicies().forEach(policyRepresentation -> {
+            Map<String, String> config = policyRepresentation.getConfig();
+
+            String roles = config.get("roles");
+
+            if (roles != null && !roles.isEmpty()) {
+                try {
+                    List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
+                    config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
+                        String roleName = roleConfig.get("id").toString();
+                        String clientId = null;
+                        int clientIdSeparator = roleName.indexOf("/");
+
+                        if (clientIdSeparator != -1) {
+                            clientId = roleName.substring(0, clientIdSeparator);
+                            roleName = roleName.substring(clientIdSeparator + 1);
+                        }
+
+                        RoleModel role;
+
+                        if (clientId == null) {
+                            role = realm.getRole(roleName);
+                        } else {
+                            role = realm.getClientByClientId(clientId).getRole(roleName);
+                        }
+
+                        // fallback to find any client role with the given name
+                        if (role == null) {
+                            String finalRoleName = roleName;
+                            role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
+                                    .findFirst().orElse(null);
+                        }
+
+                        if (role == null) {
+                            throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
+                        }
+
+                        roleConfig.put("id", role.getId());
+                        return roleConfig;
+                    }).collect(Collectors.toList())));
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+                }
+            }
+
+            String users = config.get("users");
+
+            if (users != null && !users.isEmpty()) {
+                try {
+                    List<String> usersMap = JsonSerialization.readValue(users, List.class);
+                    config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> session.users().getUserByUsername(userName, realm).getId()).collect(Collectors.toList())));
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+                }
+            }
+
+            String scopes = config.get("scopes");
+
+            if (scopes != null && !scopes.isEmpty()) {
+                try {
+                    List<String> scopesMap = JsonSerialization.readValue(scopes, List.class);
+                    config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
+                        Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
+
+                        if (newScope == null) {
+                            throw new RuntimeException("Scope with name [" + scopeName + "] not defined.");
+                        }
+
+                        return newScope.getId();
+                    }).collect(Collectors.toList())));
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+                }
+            }
+
+            String policyResources = config.get("resources");
+
+            if (policyResources != null && !policyResources.isEmpty()) {
+                ResourceStore resourceStore = storeFactory.getResourceStore();
+                try {
+                    List<String> resources = JsonSerialization.readValue(policyResources, List.class);
+                    config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
+                        return resourceStore.findByName(resourceName, resourceServer.getId()).getId();
+                    }).collect(Collectors.toList())));
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+                }
+            }
+
+            String applyPolicies = config.get("applyPolicies");
+
+            if (applyPolicies != null && !applyPolicies.isEmpty()) {
+                PolicyStore policyStore = storeFactory.getPolicyStore();
+                try {
+                    List<String> policies = JsonSerialization.readValue(applyPolicies, List.class);
+                    config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> {
+                        Policy policy = policyStore.findByName(policyName, resourceServer.getId());
+
+                        if (policy == null) {
+                            throw new RuntimeException("Policy with name [" + policyName + "] not defined.");
+                        }
+
+                        return policy.getId();
+                    }).collect(Collectors.toList())));
+                } catch (Exception e) {
+                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
+                }
+            }
+
+            toModel(policyRepresentation, resourceServer, authorization);
+        });
+    }
+
+    public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+        Policy existing;
+
+        if (policy.getId() != null) {
+            existing = policyStore.findById(policy.getId());
+        } else {
+            existing = policyStore.findByName(policy.getName(), resourceServer.getId());
+        }
+
+        if (existing != null) {
+            existing.setName(policy.getName());
+            existing.setDescription(policy.getDescription());
+            existing.setConfig(policy.getConfig());
+            existing.setDecisionStrategy(policy.getDecisionStrategy());
+            existing.setLogic(policy.getLogic());
+
+            updateResources(existing, authorization);
+            updateAssociatedPolicies(existing, resourceServer, authorization);
+            updateScopes(existing, authorization);
+
+            return existing;
+        }
+
+        Policy model = policyStore.create(policy.getName(), policy.getType(), resourceServer);
+
+        model.setDescription(policy.getDescription());
+        model.setDecisionStrategy(policy.getDecisionStrategy());
+        model.setLogic(policy.getLogic());
+        model.setConfig(policy.getConfig());
+
+        updateResources(model, authorization);
+        updateAssociatedPolicies(model, resourceServer, authorization);
+        updateScopes(model, authorization);
+
+        policy.setId(model.getId());
+
+        return model;
+    }
+
+    private static void updateScopes(Policy policy, AuthorizationProvider authorization) {
+        String scopes = policy.getConfig().get("scopes");
+        if (scopes != null) {
+            String[] scopeIds;
+
+            try {
+                scopeIds = JsonSerialization.readValue(scopes, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+
+            for (String scopeId : scopeIds) {
+                boolean hasScope = false;
+
+                for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
+                    if (scopeModel.getId().equals(scopeId)) {
+                        hasScope = true;
+                    }
+                }
+                if (!hasScope) {
+                    policy.addScope(storeFactory.getScopeStore().findById(scopeId));
+                }
+            }
+
+            for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
+                boolean hasScope = false;
+
+                for (String scopeId : scopeIds) {
+                    if (scopeModel.getId().equals(scopeId)) {
+                        hasScope = true;
+                    }
+                }
+                if (!hasScope) {
+                    policy.removeScope(scopeModel);
+                }
+            }
+        }
+    }
+
+    private static void updateAssociatedPolicies(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        String policies = policy.getConfig().get("applyPolicies");
+
+        if (policies != null) {
+            String[] policyIds;
+
+            try {
+                policyIds = JsonSerialization.readValue(policies, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+
+            for (String policyId : policyIds) {
+                boolean hasPolicy = false;
+
+                for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
+                    if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) {
+                        hasPolicy = true;
+                    }
+                }
+
+
+                if (!hasPolicy) {
+                    Policy associatedPolicy = policyStore.findById(policyId);
+
+                    if (associatedPolicy == null) {
+                        associatedPolicy = policyStore.findByName(policyId, resourceServer.getId());
+                    }
+
+                    policy.addAssociatedPolicy(associatedPolicy);
+                }
+            }
+
+            for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
+                boolean hasPolicy = false;
+
+                for (String policyId : policyIds) {
+                    if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) {
+                        hasPolicy = true;
+                    }
+                }
+                if (!hasPolicy) {
+                    policy.removeAssociatedPolicy(policyModel);;
+                }
+            }
+        }
+    }
+
+    private static void updateResources(Policy policy, AuthorizationProvider authorization) {
+        String resources = policy.getConfig().get("resources");
+        if (resources != null) {
+            String[] resourceIds;
+
+            try {
+                resourceIds = JsonSerialization.readValue(resources, String[].class);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
+            StoreFactory storeFactory = authorization.getStoreFactory();
+
+            for (String resourceId : resourceIds) {
+                boolean hasResource = false;
+                for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
+                    if (resourceModel.getId().equals(resourceId)) {
+                        hasResource = true;
+                    }
+                }
+                if (!hasResource && !"".equals(resourceId)) {
+                    policy.addResource(storeFactory.getResourceStore().findById(resourceId));
+                }
+            }
+
+            for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
+                boolean hasResource = false;
+
+                for (String resourceId : resourceIds) {
+                    if (resourceModel.getId().equals(resourceId)) {
+                        hasResource = true;
+                    }
+                }
+
+                if (!hasResource) {
+                    policy.removeResource(resourceModel);
+                }
+            }
+        }
+    }
+
+    public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+        Resource existing;
+
+        if (resource.getId() != null) {
+            existing = resourceStore.findById(resource.getId());
+        } else {
+            existing = resourceStore.findByName(resource.getName(), resourceServer.getId());
+        }
+
+        if (existing != null) {
+            existing.setName(resource.getName());
+            existing.setType(resource.getType());
+            existing.setUri(resource.getUri());
+            existing.setIconUri(resource.getIconUri());
+
+            existing.updateScopes(resource.getScopes().stream()
+                    .map((ScopeRepresentation scope) -> toModel(scope,  resourceServer, authorization))
+                    .collect(Collectors.toSet()));
+            return existing;
+        }
+
+        ResourceOwnerRepresentation owner = resource.getOwner();
+
+        if (owner == null) {
+            owner = new ResourceOwnerRepresentation();
+            owner.setId(resourceServer.getClientId());
+        }
+
+        if (owner.getId() == null) {
+            throw new RuntimeException("No owner specified for resource [" + resource.getName() + "].");
+        }
+
+        Resource model = resourceStore.create(resource.getName(), resourceServer, owner.getId());
+
+        model.setType(resource.getType());
+        model.setUri(resource.getUri());
+        model.setIconUri(resource.getIconUri());
+
+        Set<ScopeRepresentation> scopes = resource.getScopes();
+
+        if (scopes != null) {
+            model.updateScopes(scopes.stream().map((Function<ScopeRepresentation, Scope>) scope -> toModel(scope, resourceServer, authorization)).collect(Collectors.toSet()));
+        }
+
+        resource.setId(model.getId());
+
+        return model;
+    }
+
+    public static Scope toModel(ScopeRepresentation scope, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ScopeStore scopeStore = storeFactory.getScopeStore();
+        Scope existing;
+
+        if (scope.getId() != null) {
+            existing = scopeStore.findById(scope.getId());
+        } else {
+            existing = scopeStore.findByName(scope.getName(), resourceServer.getId());
+        }
+
+        if (existing != null) {
+            existing.setName(scope.getName());
+            existing.setIconUri(scope.getIconUri());
+            return existing;
+        }
+
+        Scope model = scopeStore.create(scope.getName(), resourceServer);
+        model.setIconUri(scope.getIconUri());
+        scope.setId(model.getId());
+
+        return model;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAuthUtil.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAuthUtil.java
index 0d7d911..94cefa3 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAuthUtil.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientAuthUtil.java
@@ -17,13 +17,10 @@
 
 package org.keycloak.authentication.authenticators.client;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
-import org.keycloak.OAuth2Constants;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -32,12 +29,8 @@ public class ClientAuthUtil {
 
 
     public static Response errorResponse(int status, String error, String errorDescription) {
-        Map<String, String> e = new HashMap<String, String>();
-        e.put(OAuth2Constants.ERROR, error);
-        if (errorDescription != null) {
-            e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
-        }
-        return Response.status(status).entity(e).type(MediaType.APPLICATION_JSON_TYPE).build();
+        OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
+        return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
     }
 
 }
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 957e35d..4516de4 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
@@ -19,9 +19,12 @@ package org.keycloak.authentication.authenticators.client;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
@@ -33,6 +36,7 @@ import org.keycloak.authentication.AuthenticationFlowError;
 import org.keycloak.authentication.ClientAuthenticationFlowContext;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.ServicesLogger;
@@ -179,4 +183,16 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator 
     public String getId() {
         return PROVIDER_ID;
     }
+
+    @Override
+    public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
+        if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
+            Set<String> results = new LinkedHashSet<>();
+            results.add(OIDCLoginProtocol.CLIENT_SECRET_BASIC);
+            results.add(OIDCLoginProtocol.CLIENT_SECRET_POST);
+            return results;
+        } else {
+            return Collections.emptySet();
+        }
+    }
 }
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 2ee2fcb..5139e3a 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
@@ -22,9 +22,11 @@ import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -39,10 +41,13 @@ import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.JsonWebToken;
+import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.Urls;
+import org.keycloak.services.util.CertificateInfoHelper;
 
 /**
  * Client authentication based on JWT signed by client private key .
@@ -58,6 +63,7 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
     protected static ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
     public static final String PROVIDER_ID = "client-jwt";
+    public static final String ATTR_PREFIX = "jwt.credential";
     public static final String CERTIFICATE_ATTR = "jwt.credential.certificate";
 
     public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
@@ -116,15 +122,12 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
             }
 
             // Get client key and validate signature
-            String encodedCertificate = client.getAttribute(CERTIFICATE_ATTR);
-            if (encodedCertificate == null) {
-                Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client '" + clientId + "' doesn't have certificate configured");
-                context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
+            PublicKey clientPublicKey = getSignatureValidationKey(client, context);
+            if (clientPublicKey == null) {
+                // Error response already set to context
                 return;
             }
 
-            X509Certificate clientCert = KeycloakModelUtils.getCertificate(encodedCertificate);
-            PublicKey clientPublicKey = clientCert.getPublicKey();
             boolean signatureValid;
             try {
                 signatureValid = RSAProvider.verify(jws, clientPublicKey);
@@ -159,6 +162,33 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
         }
     }
 
+    protected PublicKey getSignatureValidationKey(ClientModel client, ClientAuthenticationFlowContext context) {
+        CertificateRepresentation certInfo = CertificateInfoHelper.getCertificateFromClient(client, ATTR_PREFIX);
+
+        String encodedCertificate = certInfo.getCertificate();
+        String encodedPublicKey = certInfo.getPublicKey();
+
+        if (encodedCertificate == null && encodedPublicKey == null) {
+            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client '" + client.getClientId() + "' doesn't have certificate or publicKey configured");
+            context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
+            return null;
+        }
+
+        if (encodedCertificate != null && encodedPublicKey != null) {
+            Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client '" + client.getClientId() + "' has both publicKey and certificate configured");
+            context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
+            return null;
+        }
+
+        // TODO: Caching of publicKeys / certificates, so it doesn't need to be always computed from pem. For performance reasons...
+        if (encodedCertificate != null) {
+            X509Certificate clientCert = KeycloakModelUtils.getCertificate(encodedCertificate);
+            return clientCert.getPublicKey();
+        } else {
+            return KeycloakModelUtils.getPublicKey(encodedPublicKey);
+        }
+    }
+
     @Override
     public String getDisplayType() {
         return "Signed Jwt";
@@ -209,4 +239,15 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
     public String getId() {
         return PROVIDER_ID;
     }
+
+    @Override
+    public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
+        if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
+            Set<String> results = new HashSet<>();
+            results.add(OIDCLoginProtocol.PRIVATE_KEY_JWT);
+            return results;
+        } else {
+            return Collections.emptySet();
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
index 8de4230..88d0e15 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
@@ -24,6 +24,7 @@ import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorFactory;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -36,12 +37,8 @@ import java.util.Map;
  */
 public abstract class AbstractDirectGrantAuthenticator implements Authenticator, AuthenticatorFactory {
     public Response errorResponse(int status, String error, String errorDescription) {
-        Map<String, String> e = new HashMap<String, String>();
-        e.put(OAuth2Constants.ERROR, error);
-        if (errorDescription != null) {
-            e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
-        }
-        return Response.status(status).entity(e).type(MediaType.APPLICATION_JSON_TYPE).build();
+        OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
+        return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index 4a115ae..4af34ad 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -90,15 +90,15 @@ public class PolicyEvaluationService {
     public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
         KeycloakIdentity identity = createIdentity(evaluationRequest);
         EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
-        authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, identity, asyncResponse));
+        authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse));
     }
 
-    private DecisionResultCollector createDecisionCollector(PolicyEvaluationRequest evaluationRequest, AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) {
+    private DecisionResultCollector createDecisionCollector(AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) {
         return new DecisionResultCollector() {
             @Override
             protected void onComplete(List<Result> results) {
                 try {
-                    asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(evaluationRequest,  results, resourceServer,  authorization, identity)).build());
+                    asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(results, resourceServer,  authorization, identity)).build());
                 } catch (Throwable cause) {
                     asyncResponse.resume(cause);
                 }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index 7c529e9..d7e6dee 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -17,15 +17,11 @@
  */
 package org.keycloak.authorization.admin;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.admin.util.Models;
 import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
 import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
 import org.keycloak.authorization.store.PolicyStore;
@@ -45,11 +41,10 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import java.io.IOException;
-import java.util.HashSet;
 import java.util.stream.Collectors;
 
-import static org.keycloak.authorization.admin.util.Models.toRepresentation;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import static org.keycloak.models.utils.RepresentationToModel.toModel;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -72,12 +67,7 @@ public class PolicyService {
     @NoCache
     public Response create(PolicyRepresentation representation) {
         this.auth.requireManage();
-        Policy policy = Models.toModel(representation, this.resourceServer, authorization);
-
-        updateResources(policy, authorization);
-        updateAssociatedPolicies(policy);
-        updateScopes(policy, authorization);
-
+        Policy policy = toModel(representation, this.resourceServer, authorization);
         PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
 
         if (resource != null) {
@@ -108,15 +98,7 @@ public class PolicyService {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        policy.setName(representation.getName());
-        policy.setDescription(representation.getDescription());
-        policy.setConfig(representation.getConfig());
-        policy.setDecisionStrategy(representation.getDecisionStrategy());
-        policy.setLogic(representation.getLogic());
-
-        updateResources(policy, authorization);
-        updateAssociatedPolicies(policy);
-        updateScopes(policy, authorization);
+        policy = toModel(representation, resourceServer, authorization);
 
         PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
 
@@ -262,137 +244,4 @@ public class PolicyService {
 
         return null;
     }
-
-    private void updateScopes(Policy policy, AuthorizationProvider authorization) {
-        String scopes = policy.getConfig().get("scopes");
-        if (scopes != null) {
-            String[] scopeIds;
-
-            try {
-                scopeIds = new ObjectMapper().readValue(scopes, String[].class);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-
-            StoreFactory storeFactory = authorization.getStoreFactory();
-
-            for (String scopeId : scopeIds) {
-                boolean hasScope = false;
-
-                for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
-                    if (scopeModel.getId().equals(scopeId)) {
-                        hasScope = true;
-                    }
-                }
-                if (!hasScope) {
-                    policy.addScope(storeFactory.getScopeStore().findById(scopeId));
-                }
-            }
-
-            for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
-                boolean hasScope = false;
-
-                for (String scopeId : scopeIds) {
-                    if (scopeModel.getId().equals(scopeId)) {
-                        hasScope = true;
-                    }
-                }
-                if (!hasScope) {
-                    policy.removeScope(scopeModel);
-                }
-            }
-        }
-    }
-
-    private void updateAssociatedPolicies(Policy policy) {
-        String policies = policy.getConfig().get("applyPolicies");
-
-        if (policies != null) {
-            String[] policyIds;
-
-            try {
-                policyIds = new ObjectMapper().readValue(policies, String[].class);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-
-            StoreFactory storeFactory = authorization.getStoreFactory();
-            PolicyStore policyStore = storeFactory.getPolicyStore();
-
-            for (String policyId : policyIds) {
-                boolean hasPolicy = false;
-
-                for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
-                    if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) {
-                        hasPolicy = true;
-                    }
-                }
-
-
-                if (!hasPolicy) {
-                    Policy associatedPolicy = policyStore.findById(policyId);
-
-                    if (associatedPolicy == null) {
-                        associatedPolicy = policyStore.findByName(policyId, this.resourceServer.getId());
-                    }
-
-                    policy.addAssociatedPolicy(associatedPolicy);
-                }
-            }
-
-            for (Policy policyModel : new HashSet<Policy>(policy.getAssociatedPolicies())) {
-                boolean hasPolicy = false;
-
-                for (String policyId : policyIds) {
-                    if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) {
-                        hasPolicy = true;
-                    }
-                }
-                if (!hasPolicy) {
-                    policy.removeAssociatedPolicy(policyModel);;
-                }
-            }
-        }
-    }
-
-    private void updateResources(Policy policy, AuthorizationProvider authorization) {
-        String resources = policy.getConfig().get("resources");
-        if (resources != null) {
-            String[] resourceIds;
-
-            try {
-                resourceIds = new ObjectMapper().readValue(resources, String[].class);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
-
-            StoreFactory storeFactory = authorization.getStoreFactory();
-
-            for (String resourceId : resourceIds) {
-                boolean hasResource = false;
-                for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
-                    if (resourceModel.getId().equals(resourceId)) {
-                        hasResource = true;
-                    }
-                }
-                if (!hasResource && !"".equals(resourceId)) {
-                    policy.addResource(storeFactory.getResourceStore().findById(resourceId));
-                }
-            }
-
-            for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
-                boolean hasResource = false;
-
-                for (String resourceId : resourceIds) {
-                    if (resourceModel.getId().equals(resourceId)) {
-                        hasResource = true;
-                    }
-                }
-
-                if (!hasResource) {
-                    policy.removeResource(resourceModel);
-                }
-            }
-        }
-    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
index ee6661d..d320a64 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java
@@ -20,7 +20,6 @@ package org.keycloak.authorization.admin.representation;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.Decision.Effect;
-import org.keycloak.authorization.admin.util.Models;
 import org.keycloak.authorization.common.KeycloakIdentity;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.ResourceServer;
@@ -28,6 +27,7 @@ import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
 import org.keycloak.authorization.util.Permissions;
+import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@@ -56,7 +56,7 @@ public class PolicyEvaluationResponse {
 
     }
 
-    public static PolicyEvaluationResponse build(PolicyEvaluationRequest evaluationRequest, List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) {
+    public static PolicyEvaluationResponse build(List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) {
         PolicyEvaluationResponse response = new PolicyEvaluationResponse();
         List<EvaluationResultRepresentation> resultsRep = new ArrayList<>();
         AccessToken accessToken = identity.getAccessToken();
@@ -80,21 +80,16 @@ public class PolicyEvaluationResponse {
             resultsRep.add(rep);
 
             if (result.getPermission().getResource() != null) {
-                rep.setResource(Models.toRepresentation(result.getPermission().getResource(), resourceServer, authorization));
+                rep.setResource(ModelToRepresentation.toRepresentation(result.getPermission().getResource(), resourceServer, authorization));
             } else {
                 ResourceRepresentation resource = new ResourceRepresentation();
 
-                resource.setName("Any Resource with Scopes " + result.getPermission().getScopes().stream().map(new Function<Scope, String>() {
-                    @Override
-                    public String apply(Scope scope) {
-                        return scope.getName();
-                    }
-                }).collect(Collectors.toList()));
+                resource.setName("Any Resource with Scopes " + result.getPermission().getScopes().stream().map(Scope::getName).collect(Collectors.toList()));
 
                 rep.setResource(resource);
             }
 
-            rep.setScopes(result.getPermission().getScopes().stream().map(scope -> Models.toRepresentation(scope, authorization)).collect(Collectors.toList()));
+            rep.setScopes(result.getPermission().getScopes().stream().map(scope -> ModelToRepresentation.toRepresentation(scope, authorization)).collect(Collectors.toList()));
 
             List<PolicyResultRepresentation> policies = new ArrayList<>();
 
@@ -163,7 +158,7 @@ public class PolicyEvaluationResponse {
 
                 if (policy.getStatus().equals(Effect.DENY)) {
                     Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId());
-                    for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scope -> Models.toRepresentation(scope, authorization)).collect(Collectors.toList())) {
+                    for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) {
                         if (!policy.getScopes().contains(scope)) {
                             policy.getScopes().add(scope);
                         }
@@ -185,7 +180,7 @@ public class PolicyEvaluationResponse {
     private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) {
         PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation();
 
-        policyResultRep.setPolicy(Models.toRepresentation(policy.getPolicy(), authorization));
+        policyResultRep.setPolicy(ModelToRepresentation.toRepresentation(policy.getPolicy(), authorization));
         policyResultRep.setStatus(policy.getStatus());
         policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
 
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
index 567675f..d02b827 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
@@ -19,33 +19,25 @@ package org.keycloak.authorization.admin;
 
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.admin.util.Models;
-import org.keycloak.authorization.model.Policy;
-import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.ScopeStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.exportimport.util.ExportUtils;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserFederationManager;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 import org.keycloak.representations.idm.authorization.Logic;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
-import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
-import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.services.resources.admin.RealmAuth;
-import org.keycloak.util.JsonSerialization;
 
-import javax.management.relation.Role;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -57,14 +49,9 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
+
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -73,7 +60,6 @@ public class ResourceServerService {
 
     private final AuthorizationProvider authorization;
     private final RealmAuth auth;
-    private final RealmModel realm;
     private final KeycloakSession session;
     private ResourceServer resourceServer;
     private final ClientModel client;
@@ -83,7 +69,6 @@ public class ResourceServerService {
         this.session = authorization.getKeycloakSession();
         this.client = client;
         this.resourceServer = resourceServer;
-        this.realm = client.getRealm();
         this.auth = auth;
     }
 
@@ -128,7 +113,7 @@ public class ResourceServerService {
     @Produces("application/json")
     public Response findById() {
         this.auth.requireView();
-        return Response.ok(Models.toRepresentation(this.resourceServer, this.realm)).build();
+        return Response.ok(toRepresentation(this.resourceServer, this.client)).build();
     }
 
     @Path("/settings")
@@ -136,59 +121,7 @@ public class ResourceServerService {
     @Produces("application/json")
     public Response exportSettings() {
         this.auth.requireManage();
-        StoreFactory storeFactory = authorization.getStoreFactory();
-        ResourceServerRepresentation settings = Models.toRepresentation(resourceServer, this.realm);
-
-        settings.setId(null);
-        settings.setName(null);
-        settings.setClientId(null);
-
-        List<ResourceRepresentation> resources = storeFactory.getResourceStore().findByResourceServer(resourceServer.getId())
-                .stream().map(resource -> {
-                    ResourceRepresentation rep = Models.toRepresentation(resource, resourceServer, authorization);
-
-                    if (rep.getOwner().getId().equals(this.resourceServer.getClientId())) {
-                        rep.setOwner(null);
-                    } else {
-                        rep.getOwner().setId(null);
-                    }
-                    rep.setId(null);
-                    rep.setPolicies(null);
-                    rep.getScopes().forEach(scopeRepresentation -> {
-                        scopeRepresentation.setId(null);
-                        scopeRepresentation.setIconUri(null);
-                    });
-
-                    return rep;
-                }).collect(Collectors.toList());
-
-        settings.setResources(resources);
-
-        List<PolicyRepresentation> policies = new ArrayList<>();
-        PolicyStore policyStore = storeFactory.getPolicyStore();
-
-        policies.addAll(policyStore.findByResourceServer(resourceServer.getId())
-                .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope"))
-                .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList()));
-        policies.addAll(policyStore.findByResourceServer(resourceServer.getId())
-                .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope"))
-                .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList()));
-
-        settings.setPolicies(policies);
-
-        List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(resourceServer.getId()).stream().map(scope -> {
-            ScopeRepresentation rep = Models.toRepresentation(scope, authorization);
-
-            rep.setId(null);
-            rep.setPolicies(null);
-            rep.setResources(null);
-
-            return rep;
-        }).collect(Collectors.toList());
-
-        settings.setScopes(scopes);
-
-        return Response.ok(settings).build();
+        return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build();
     }
 
     @Path("/import")
@@ -197,172 +130,9 @@ public class ResourceServerService {
     public Response importSettings(@Context final UriInfo uriInfo, ResourceServerRepresentation rep) throws IOException {
         this.auth.requireManage();
 
-        resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
-        resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
-
-        StoreFactory storeFactory = authorization.getStoreFactory();
-        ResourceStore resourceStore = storeFactory.getResourceStore();
-        ScopeStore scopeStore = storeFactory.getScopeStore();
-        ScopeService scopeResource = new ScopeService(resourceServer, this.authorization, this.auth);
-
-        ResteasyProviderFactory.getInstance().injectProperties(scopeResource);
-
-        rep.getScopes().forEach(scope -> {
-            Scope existing = scopeStore.findByName(scope.getName(), resourceServer.getId());
-
-            if (existing != null) {
-                scopeResource.update(existing.getId(), scope);
-            } else {
-                scopeResource.create(scope);
-            }
-        });
-
-        ResourceSetService resourceSetResource = new ResourceSetService(resourceServer, this.authorization, this.auth);
-
-        rep.getResources().forEach(resourceRepresentation -> {
-            ResourceOwnerRepresentation owner = resourceRepresentation.getOwner();
-
-            if (owner == null) {
-                owner = new ResourceOwnerRepresentation();
-            }
-
-            owner.setId(resourceServer.getClientId());
-
-            if (owner.getName() != null) {
-                UserModel user = this.session.users().getUserByUsername(owner.getName(), this.realm);
+        rep.setClientId(client.getId());
 
-                if (user != null) {
-                    owner.setId(user.getId());
-                }
-            }
-
-            Resource existing = resourceStore.findByName(resourceRepresentation.getName(), this.resourceServer.getId());
-
-            if (existing != null) {
-                resourceSetResource.update(existing.getId(), resourceRepresentation);
-            } else {
-                resourceSetResource.create(resourceRepresentation);
-            }
-        });
-
-        PolicyStore policyStore = storeFactory.getPolicyStore();
-        PolicyService policyResource = new PolicyService(resourceServer, this.authorization, this.auth);
-
-        ResteasyProviderFactory.getInstance().injectProperties(policyResource);
-
-        rep.getPolicies().forEach(policyRepresentation -> {
-            Map<String, String> config = policyRepresentation.getConfig();
-
-            String roles = config.get("roles");
-
-            if (roles != null && !roles.isEmpty()) {
-                try {
-                    List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
-                    config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
-                        String roleName = roleConfig.get("id").toString();
-                        String clientId = null;
-                        int clientIdSeparator = roleName.indexOf("/");
-
-                        if (clientIdSeparator != -1) {
-                            clientId = roleName.substring(0, clientIdSeparator);
-                            roleName = roleName.substring(clientIdSeparator + 1);
-                        }
-
-                        RoleModel role;
-
-                        if (clientId == null) {
-                            role = realm.getRole(roleName);
-                        } else {
-                            role = realm.getClientByClientId(clientId).getRole(roleName);
-                        }
-
-                        // fallback to find any client role with the given name
-                        if (role == null) {
-                            String finalRoleName = roleName;
-                            role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
-                                    .findFirst().orElse(null);
-                        }
-
-                        if (role == null) {
-                            throw new RuntimeException("Error while importing configuration. Role [" + role + "] could not be found.");
-                        }
-
-                        roleConfig.put("id", role.getId());
-                        return roleConfig;
-                    }).collect(Collectors.toList())));
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
-                }
-            }
-
-            String users = config.get("users");
-
-            if (users != null && !users.isEmpty()) {
-                try {
-                    List<String> usersMap = JsonSerialization.readValue(users, List.class);
-                    config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> this.session.users().getUserByUsername(userName, this.realm).getId()).collect(Collectors.toList())));
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
-                }
-            }
-
-            String scopes = config.get("scopes");
-
-            if (scopes != null && !scopes.isEmpty()) {
-                try {
-                    List<String> scopesMap = JsonSerialization.readValue(scopes, List.class);
-                    config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
-                        Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
-
-                        if (newScope == null) {
-                            throw new RuntimeException("Scope with name [" + scopeName + "] not defined.");
-                        }
-
-                        return newScope.getId();
-                    }).collect(Collectors.toList())));
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
-                }
-            }
-
-            String policyResources = config.get("resources");
-
-            if (policyResources != null && !policyResources.isEmpty()) {
-                try {
-                    List<String> resources = JsonSerialization.readValue(policyResources, List.class);
-                    config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> storeFactory.getResourceStore().findByName(resourceName, resourceServer.getId()).getId()).collect(Collectors.toList())));
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
-                }
-            }
-
-            String applyPolicies = config.get("applyPolicies");
-
-            if (applyPolicies != null && !applyPolicies.isEmpty()) {
-                try {
-                    List<String> policies = JsonSerialization.readValue(applyPolicies, List.class);
-                    config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> {
-                        Policy policy = policyStore.findByName(policyName, resourceServer.getId());
-
-                        if (policy == null) {
-                            throw new RuntimeException("Policy with name [" + policyName + "] not defined.");
-                        }
-
-                        return policy.getId();
-                    }).collect(Collectors.toList())));
-                } catch (Exception e) {
-                    throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
-                }
-            }
-
-            Policy existing = policyStore.findByName(policyRepresentation.getName(), this.resourceServer.getId());
-
-            if (existing != null) {
-                policyResource.update(existing.getId(), policyRepresentation);
-            } else {
-                policyResource.create(policyRepresentation);
-            }
-        });
+        RepresentationToModel.toModel(rep, authorization);
 
         return Response.noContent().build();
     }
@@ -458,61 +228,4 @@ public class ResourceServerService {
             serviceAccount.grantRole(umaProtectionRole);
         }
     }
-
-    private PolicyRepresentation createPolicyRepresentation(StoreFactory storeFactory, Policy policy) {
-        try {
-            PolicyRepresentation rep = Models.toRepresentation(policy, authorization);
-
-            rep.setId(null);
-            rep.setDependentPolicies(null);
-
-            Map<String, String> config = rep.getConfig();
-
-            String roles = config.get("roles");
-
-            if (roles != null && !roles.isEmpty()) {
-                List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
-                config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleMap -> {
-                    roleMap.put("id", realm.getRoleById(roleMap.get("id").toString()).getName());
-                    return roleMap;
-                }).collect(Collectors.toList())));
-            }
-
-            String users = config.get("users");
-
-            if (users != null && !users.isEmpty()) {
-                UserFederationManager userManager = this.session.users();
-                List<String> userIds = JsonSerialization.readValue(users, List.class);
-                config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, this.realm).getUsername()).collect(Collectors.toList())));
-            }
-
-            String scopes = config.get("scopes");
-
-            if (scopes != null && !scopes.isEmpty()) {
-                ScopeStore scopeStore = storeFactory.getScopeStore();
-                List<String> scopeIds = JsonSerialization.readValue(scopes, List.class);
-                config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList())));
-            }
-
-            String policyResources = config.get("resources");
-
-            if (policyResources != null && !policyResources.isEmpty()) {
-                ResourceStore resourceStore = storeFactory.getResourceStore();
-                List<String> resourceIds = JsonSerialization.readValue(policyResources, List.class);
-                config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList())));
-            }
-
-            Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
-
-            if (!associatedPolicies.isEmpty()) {
-                config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
-            }
-
-            rep.setAssociatedPolicies(null);
-
-            return rep;
-        } catch (Exception e) {
-            throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
-        }
-    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index 8bb4a9b..c628a82 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -19,7 +19,6 @@ package org.keycloak.authorization.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.admin.util.Models;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
@@ -27,7 +26,6 @@ import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.services.ErrorResponse;
 import org.keycloak.services.resources.admin.RealmAuth;
 
@@ -45,7 +43,8 @@ import javax.ws.rs.core.Response.Status;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.keycloak.authorization.admin.util.Models.toRepresentation;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import static org.keycloak.models.utils.RepresentationToModel.toModel;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -75,7 +74,7 @@ public class ResourceSetService {
             return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
         }
 
-        Resource model = Models.toModel(resource, this.resourceServer, authorization);
+        Resource model = toModel(resource, this.resourceServer, authorization);
 
         ResourceRepresentation representation = new ResourceRepresentation();
 
@@ -99,14 +98,7 @@ public class ResourceSetService {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        model.setName(resource.getName());
-        model.setType(resource.getType());
-        model.setUri(resource.getUri());
-        model.setIconUri(resource.getIconUri());
-
-        model.updateScopes(resource.getScopes().stream()
-                .map((ScopeRepresentation scope) -> Models.toModel(scope, this.resourceServer, authorization))
-                .collect(Collectors.toSet()));
+        toModel(resource, resourceServer, authorization);
 
         return Response.noContent().build();
     }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index 97b8541..df0e6da 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -44,8 +44,8 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.keycloak.authorization.admin.util.Models.toModel;
-import static org.keycloak.authorization.admin.util.Models.toRepresentation;
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import static org.keycloak.models.utils.RepresentationToModel.toModel;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -88,8 +88,7 @@ public class ScopeService {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        model.setName(scope.getName());
-        model.setIconUri(scope.getIconUri());
+        toModel(scope, resourceServer, authorization);
 
         return Response.noContent().build();
     }
diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
index 5df5a0b..6146594 100644
--- a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
+++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java
@@ -22,6 +22,7 @@ import org.keycloak.Config;
 import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
 import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
 
 import java.util.concurrent.Executor;
@@ -35,13 +36,7 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
 
     @Override
     public AuthorizationProvider create(KeycloakSession session) {
-        StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class);
-
-        if (storeFactory == null) {
-            storeFactory = session.getProvider(StoreFactory.class);
-        }
-
-        return new AuthorizationProvider(session, storeFactory);
+        return create(session, session.getContext().getRealm());
     }
 
     @Override
@@ -70,4 +65,15 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
     public String getId() {
         return "authorization";
     }
-}
+
+    @Override
+    public AuthorizationProvider create(KeycloakSession session, RealmModel realm) {
+        StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class);
+
+        if (storeFactory == null) {
+            storeFactory = session.getProvider(StoreFactory.class);
+        }
+
+        return new AuthorizationProvider(session, realm, storeFactory);
+    }
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
index e45b976..168d62b 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -19,12 +19,12 @@ package org.keycloak.authorization.protection.resource;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.ResourceSetService;
-import org.keycloak.authorization.admin.util.Models;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
 import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -133,25 +133,25 @@ public class ResourceService {
 
                 if ("name".equals(filterType)) {
                     resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getName())).collect(Collectors.toSet()).stream()
-                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
                             .collect(Collectors.toList()));
                 } else if ("type".equals(filterType)) {
                     resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getType())).collect(Collectors.toSet()).stream()
-                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
                             .collect(Collectors.toList()));
                 } else if ("uri".equals(filterType)) {
                     resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getUri())).collect(Collectors.toSet()).stream()
-                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
                             .collect(Collectors.toList()));
                 } else if ("owner".equals(filterType)) {
                     resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getOwner())).collect(Collectors.toSet()).stream()
-                            .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
                             .collect(Collectors.toList()));
                 }
             }
         } else {
             resources = storeFactory.getResourceStore().findByOwner(identity.getId()).stream()
-                    .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization))
+                    .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
                     .collect(Collectors.toSet());
         }
 
diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
index a268ebd..82c2cdd 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -16,14 +16,14 @@
  */
 package org.keycloak.broker.oidc;
 
-import org.keycloak.broker.provider.util.SimpleHttp;
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
 import org.keycloak.jose.jwk.JWK;
-import org.keycloak.jose.jwk.JWKParser;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
+import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.protocol.oidc.utils.JWKSUtils;
+import org.keycloak.services.ServicesLogger;
 import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
@@ -36,6 +36,8 @@ import java.util.Map;
  */
 public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory<OIDCIdentityProvider> {
 
+    private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
+
     public static final String PROVIDER_ID = "oidc";
 
     @Override
@@ -59,7 +61,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
     }
 
     protected static Map<String, String> parseOIDCConfig(InputStream inputStream) {
-        OIDCConfigurationRepresentation rep = null;
+        OIDCConfigurationRepresentation rep;
         try {
             rep = JsonSerialization.readValue(inputStream, OIDCConfigurationRepresentation.class);
         } catch (IOException e) {
@@ -72,31 +74,24 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
         config.setTokenUrl(rep.getTokenEndpoint());
         config.setUserInfoUrl(rep.getUserinfoEndpoint());
         if (rep.getJwksUri() != null) {
-            String uri = rep.getJwksUri();
-            String keySetString = null;
-            try {
-                keySetString = SimpleHttp.doGet(uri).asString();
-                JSONWebKeySet keySet = JsonSerialization.readValue(keySetString, JSONWebKeySet.class);
-                for (JWK jwk : keySet.getKeys()) {
-                    JWKParser parse = JWKParser.create(jwk);
-                    if (parse.getJwk().getPublicKeyUse().equals(JWK.SIG_USE) && keyTypeSupported(jwk.getKeyType())) {
-                        PublicKey key = parse.toPublicKey();
-                        config.setPublicKeySignatureVerifier(KeycloakModelUtils.getPemFromKey(key));
-                        config.setValidateSignature(true);
-                        break;
-                    }
-
-                }
-            } catch (IOException e) {
-                throw new RuntimeException("Failed to query JWKSet from: " + uri, e);
-            }
-
+            sendJwksRequest(rep, config);
         }
         return config.getConfig();
     }
 
-    protected static boolean keyTypeSupported(String type) {
-        return type != null && type.equals("RSA");
+    protected static void sendJwksRequest(OIDCConfigurationRepresentation rep, OIDCIdentityProviderConfig config) {
+        try {
+            JSONWebKeySet keySet = JWKSUtils.sendJwksRequest(rep.getJwksUri());
+            PublicKey key = JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG);
+            if (key == null) {
+                logger.supportedJwkNotFound(JWK.Use.SIG.asString());
+            } else {
+                config.setPublicKeySignatureVerifier(KeycloakModelUtils.getPemFromKey(key));
+                config.setValidateSignature(true);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to query JWKSet from: " + rep.getJwksUri(), e);
+        }
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index 9665a50..1b2caf9 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -22,17 +22,61 @@ import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationProviderFactory;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.PolicyStore;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.common.Version;
 import org.keycloak.common.util.Base64;
 import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.component.ComponentModel;
-import org.keycloak.models.*;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserFederationManager;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.ModelToRepresentation;
-import org.keycloak.representations.idm.*;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ComponentExportRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.RolesRepresentation;
+import org.keycloak.representations.idm.ScopeMappingRepresentation;
+import org.keycloak.representations.idm.UserConsentRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -58,7 +102,7 @@ public class ExportUtils {
         List<ClientModel> clients = realm.getClients();
         List<ClientRepresentation> clientReps = new ArrayList<>();
         for (ClientModel app : clients) {
-            ClientRepresentation clientRep = exportClient(app);
+            ClientRepresentation clientRep = exportClient(session, app);
             clientReps.add(clientRep);
         }
         rep.setClients(clientReps);
@@ -207,12 +251,137 @@ public class ExportUtils {
      * @param client
      * @return full ApplicationRepresentation
      */
-    public static ClientRepresentation exportClient(ClientModel client) {
+    public static ClientRepresentation exportClient(KeycloakSession session, ClientModel client) {
         ClientRepresentation clientRep = ModelToRepresentation.toRepresentation(client);
         clientRep.setSecret(client.getSecret());
+        clientRep.setAuthorizationSettings(exportAuthorizationSettings(session,client));
         return clientRep;
     }
 
+    public static ResourceServerRepresentation exportAuthorizationSettings(KeycloakSession session, ClientModel client) {
+        AuthorizationProviderFactory providerFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);
+        AuthorizationProvider authorization = providerFactory.create(session, client.getRealm());
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        ResourceServer settingsModel = authorization.getStoreFactory().getResourceServerStore().findByClient(client.getId());
+
+        if (settingsModel == null) {
+            return null;
+        }
+
+        ResourceServerRepresentation representation = toRepresentation(settingsModel, client);
+
+        representation.setId(null);
+        representation.setName(null);
+        representation.setClientId(null);
+
+        List<ResourceRepresentation> resources = storeFactory.getResourceStore().findByResourceServer(settingsModel.getId())
+                .stream().map(resource -> {
+                    ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
+
+                    if (rep.getOwner().getId().equals(settingsModel.getClientId())) {
+                        rep.setOwner(null);
+                    } else {
+                        rep.getOwner().setId(null);
+                    }
+                    rep.setId(null);
+                    rep.setPolicies(null);
+                    rep.getScopes().forEach(scopeRepresentation -> {
+                        scopeRepresentation.setId(null);
+                        scopeRepresentation.setIconUri(null);
+                    });
+
+                    return rep;
+                }).collect(Collectors.toList());
+
+        representation.setResources(resources);
+
+        List<PolicyRepresentation> policies = new ArrayList<>();
+        PolicyStore policyStore = storeFactory.getPolicyStore();
+
+        policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
+                .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope"))
+                .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
+        policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
+                .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope"))
+                .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
+
+        representation.setPolicies(policies);
+
+        List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
+            ScopeRepresentation rep = toRepresentation(scope, authorization);
+
+            rep.setId(null);
+            rep.setPolicies(null);
+            rep.setResources(null);
+
+            return rep;
+        }).collect(Collectors.toList());
+
+        representation.setScopes(scopes);
+
+        return representation;
+    }
+
+    private static PolicyRepresentation createPolicyRepresentation(AuthorizationProvider authorizationProvider, Policy policy) {
+        KeycloakSession session = authorizationProvider.getKeycloakSession();
+        RealmModel realm = authorizationProvider.getRealm();
+        StoreFactory storeFactory = authorizationProvider.getStoreFactory();
+        try {
+            PolicyRepresentation rep = toRepresentation(policy, authorizationProvider);
+
+            rep.setId(null);
+            rep.setDependentPolicies(null);
+
+            Map<String, String> config = rep.getConfig();
+
+            String roles = config.get("roles");
+
+            if (roles != null && !roles.isEmpty()) {
+                List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
+                config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleMap -> {
+                    roleMap.put("id", realm.getRoleById(roleMap.get("id").toString()).getName());
+                    return roleMap;
+                }).collect(Collectors.toList())));
+            }
+
+            String users = config.get("users");
+
+            if (users != null && !users.isEmpty()) {
+                UserFederationManager userManager = session.users();
+                List<String> userIds = JsonSerialization.readValue(users, List.class);
+                config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, realm).getUsername()).collect(Collectors.toList())));
+            }
+
+            String scopes = config.get("scopes");
+
+            if (scopes != null && !scopes.isEmpty()) {
+                ScopeStore scopeStore = storeFactory.getScopeStore();
+                List<String> scopeIds = JsonSerialization.readValue(scopes, List.class);
+                config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList())));
+            }
+
+            String policyResources = config.get("resources");
+
+            if (policyResources != null && !policyResources.isEmpty()) {
+                ResourceStore resourceStore = storeFactory.getResourceStore();
+                List<String> resourceIds = JsonSerialization.readValue(policyResources, List.class);
+                config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList())));
+            }
+
+            Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+
+            if (!associatedPolicies.isEmpty()) {
+                config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
+            }
+
+            rep.setAssociatedPolicies(null);
+
+            return rep;
+        } catch (Exception e) {
+            throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
+        }
+    }
+
     public static List<RoleRepresentation> exportRoles(Collection<RoleModel> roles) {
         List<RoleRepresentation> roleReps = new ArrayList<RoleRepresentation>();
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
index c55b482..392b3ce 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
@@ -17,11 +17,8 @@
 
 package org.keycloak.protocol.oidc;
 
-import org.keycloak.Config;
 import org.keycloak.exportimport.ClientDescriptionConverter;
-import org.keycloak.exportimport.ClientDescriptionConverterFactory;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.representations.oidc.OIDCClientRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.clientregistration.oidc.DescriptionConverter;
@@ -32,46 +29,28 @@ import java.io.IOException;
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class OIDCClientDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
+public class OIDCClientDescriptionConverter implements ClientDescriptionConverter {
 
-    public static final String ID = "openid-connect";
+    private final KeycloakSession session;
 
-    @Override
-    public boolean isSupported(String description) {
-        description = description.trim();
-        return (description.startsWith("{") && description.endsWith("}") && description.contains("\"redirect_uris\""));
+    public OIDCClientDescriptionConverter(KeycloakSession session) {
+        this.session = session;
     }
 
+
     @Override
     public ClientRepresentation convertToInternal(String description) {
         try {
             OIDCClientRepresentation clientOIDC = JsonSerialization.readValue(description, OIDCClientRepresentation.class);
-            return DescriptionConverter.toInternal(clientOIDC);
+            return DescriptionConverter.toInternal(session, clientOIDC);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    public ClientDescriptionConverter create(KeycloakSession session) {
-        return this;
-    }
-
-    @Override
-    public void init(Config.Scope config) {
-    }
-
-    @Override
-    public void postInit(KeycloakSessionFactory factory) {
-    }
-
-    @Override
     public void close() {
     }
 
-    @Override
-    public String getId() {
-        return ID;
-    }
 
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverterFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverterFactory.java
new file mode 100644
index 0000000..fe29f50
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverterFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.exportimport.ClientDescriptionConverterFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCClientDescriptionConverterFactory implements ClientDescriptionConverterFactory {
+
+    public static final String ID = "openid-connect";
+
+    @Override
+    public boolean isSupported(String description) {
+        description = description.trim();
+        return (description.startsWith("{") && description.endsWith("}") && description.contains("\"redirect_uris\""));
+    }
+
+    @Override
+    public ClientDescriptionConverter create(KeycloakSession session) {
+        return new OIDCClientDescriptionConverter(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 7ad2354..3c4c3aa 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -77,6 +77,12 @@ public class OIDCLoginProtocol implements LoginProtocol {
     public static final String PROMPT_VALUE_CONSENT = "consent";
     public static final String PROMPT_VALUE_SELECT_ACCOUNT = "select_account";
 
+    // Client authentication methods
+    public static final String CLIENT_SECRET_BASIC = "client_secret_basic";
+    public static final String CLIENT_SECRET_POST = "client_secret_post";
+    public static final String CLIENT_SECRET_JWT = "client_secret_jwt";
+    public static final String PRIVATE_KEY_JWT = "private_key_jwt";
+
     private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
     protected KeycloakSession session;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
index c5e86ea..7afec32 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
@@ -19,7 +19,6 @@ package org.keycloak.protocol.oidc;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import org.keycloak.OAuth2Constants;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.jose.jwk.JWKBuilder;
@@ -31,7 +30,7 @@ import org.keycloak.protocol.oidc.endpoints.LoginStatusIframeEndpoint;
 import org.keycloak.protocol.oidc.endpoints.LogoutEndpoint;
 import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
 import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint;
-import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
+import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.resources.RealmsResource;
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index 050795a..eb5b5b4 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -18,12 +18,15 @@
 package org.keycloak.protocol.oidc;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.ClientAuthenticator;
+import org.keycloak.authentication.ClientAuthenticatorFactory;
 import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
 import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.IDToken;
 import org.keycloak.services.clientregistration.ClientRegistrationService;
 import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
@@ -33,6 +36,8 @@ import org.keycloak.wellknown.WellKnownProvider;
 
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
+
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -51,9 +56,6 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
 
     public static final List<String> DEFAULT_RESPONSE_MODES_SUPPORTED = list("query", "fragment", "form_post");
 
-    // Should be rather retrieved dynamically based on available ClientAuthenticator providers?
-    public static final List<String> DEFAULT_CLIENT_AUTH_METHODS_SUPPORTED = list("client_secret_basic", "client_secret_post", "private_key_jwt");
-
     public static final List<String> DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED = list(Algorithm.RS256.toString());
 
     // The exact list depends on protocolMappers
@@ -93,7 +95,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
         config.setResponseModesSupported(DEFAULT_RESPONSE_MODES_SUPPORTED);
         config.setGrantTypesSupported(DEFAULT_GRANT_TYPES_SUPPORTED);
 
-        config.setTokenEndpointAuthMethodsSupported(DEFAULT_CLIENT_AUTH_METHODS_SUPPORTED);
+        config.setTokenEndpointAuthMethodsSupported(getClientAuthMethodsSupported());
         config.setTokenEndpointAuthSigningAlgValuesSupported(DEFAULT_CLIENT_AUTH_SIGNING_ALG_VALUES_SUPPORTED);
 
         config.setClaimsSupported(DEFAULT_CLAIMS_SUPPORTED);
@@ -120,4 +122,16 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
         return s;
     }
 
+    private List<String> getClientAuthMethodsSupported() {
+        List<String> result = new ArrayList<>();
+
+        List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
+        for (ProviderFactory factory : providerFactories) {
+            ClientAuthenticatorFactory clientAuthFactory = (ClientAuthenticatorFactory) factory;
+            result.addAll(clientAuthFactory.getProtocolAuthenticatorMethods(OIDCLoginProtocol.LOGIN_PROTOCOL));
+        }
+
+        return result;
+    }
+
 }
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 6e5a309..a7ba7c3 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
@@ -17,15 +17,20 @@
 
 package org.keycloak.protocol.oidc.utils;
 
+import java.util.List;
 import java.util.Map;
 
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.ClientAuthenticator;
+import org.keycloak.authentication.ClientAuthenticatorFactory;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.services.ErrorResponseException;
 
 import javax.ws.rs.WebApplicationException;
@@ -70,6 +75,18 @@ public class AuthorizeClientUtil {
         return processor;
     }
 
+    public static ClientAuthenticatorFactory findClientAuthenticatorForOIDCAuthMethod(KeycloakSession session, String oidcAuthMethod) {
+        List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
+        for (ProviderFactory factory : providerFactories) {
+            ClientAuthenticatorFactory clientAuthFactory = (ClientAuthenticatorFactory) factory;
+            if (clientAuthFactory.getProtocolAuthenticatorMethods(OIDCLoginProtocol.LOGIN_PROTOCOL).contains(oidcAuthMethod)) {
+                return clientAuthFactory;
+            }
+        }
+
+        return null;
+    }
+
     public static class ClientAuthResult {
 
         private final ClientModel client;
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
new file mode 100644
index 0000000..c856a81
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/JWKSUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.protocol.oidc.utils;
+
+import java.io.IOException;
+import java.security.PublicKey;
+
+import org.keycloak.broker.provider.util.SimpleHttp;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
+import org.keycloak.jose.jwk.JWKParser;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JWKSUtils {
+
+    public static JSONWebKeySet sendJwksRequest(String jwksURI) throws IOException {
+        String keySetString = SimpleHttp.doGet(jwksURI).asString();
+        return JsonSerialization.readValue(keySetString, JSONWebKeySet.class);
+    }
+
+
+    public static PublicKey getKeyForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
+        for (JWK jwk : keySet.getKeys()) {
+            JWKParser parser = JWKParser.create(jwk);
+            if (parser.getJwk().getPublicKeyUse().equals(requestedUse.asString()) && parser.isAlgorithmSupported(jwk.getKeyType())) {
+                return parser.toPublicKey();
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
index cacd211..3356c31 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
@@ -17,7 +17,7 @@
 
 package org.keycloak.protocol.saml;
 
-import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
+import org.keycloak.services.util.CertificateInfoHelper;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -35,7 +35,7 @@ public interface SamlConfigAttributes {
     String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
     String SAML_ENCRYPT = "saml.encrypt";
     String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
-    String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
-    String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
-    String SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.PRIVATE_KEY;
+    String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + CertificateInfoHelper.X509CERTIFICATE;
+    String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + CertificateInfoHelper.X509CERTIFICATE;
+    String SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE = "saml.encryption." + CertificateInfoHelper.PRIVATE_KEY;
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
index ffda262..88b5a34 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -21,6 +21,7 @@ import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.utils.ModelToRepresentation;
@@ -54,8 +55,9 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
 
             client = ModelToRepresentation.toRepresentation(clientModel);
 
-            String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, clientModel);
+            client.setSecret(clientModel.getSecret());
 
+            String registrationAccessToken = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, clientModel);
             client.setRegistrationAccessToken(registrationAccessToken);
 
             if (auth.isInitialAccessToken()) {
@@ -63,6 +65,11 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
                 initialAccessModel.decreaseRemainingCount();
             }
 
+            if (auth.isRegistrationHostTrusted()) {
+                ClientRegistrationTrustedHostModel trustedHost = auth.getTrustedHostModel();
+                trustedHost.decreaseRemainingCount();
+            }
+
             event.client(client.getClientId()).success();
             return client;
         } catch (ModelDuplicateException e) {
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
index 5564ef2..93c2648 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
@@ -18,22 +18,23 @@
 package org.keycloak.services.clientregistration;
 
 import org.jboss.resteasy.spi.Failure;
-import org.jboss.resteasy.spi.NotFoundException;
-import org.jboss.resteasy.spi.UnauthorizedException;
 import org.keycloak.Config;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.authentication.AuthenticationProcessor;
 import org.keycloak.common.util.Time;
+import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.AdminRoles;
 import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
 import org.keycloak.representations.JsonWebToken;
-import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.ErrorResponseException;
 import org.keycloak.util.TokenUtil;
 
 import javax.ws.rs.core.HttpHeaders;
@@ -54,6 +55,8 @@ public class ClientRegistrationAuth {
     private JsonWebToken jwt;
     private ClientInitialAccessModel initialAccessModel;
 
+    private ClientRegistrationTrustedHostModel trustedHostModel;
+
     public ClientRegistrationAuth(KeycloakSession session, EventBuilder event) {
         this.session = session;
         this.event = event;
@@ -65,6 +68,9 @@ public class ClientRegistrationAuth {
 
         String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
         if (authorizationHeader == null) {
+
+            // Try trusted hosts
+            trustedHostModel = ClientRegistrationHostUtils.getTrustedHost(session.getContext().getConnection().getRemoteAddr(), session, realm);
             return;
         }
 
@@ -73,19 +79,24 @@ public class ClientRegistrationAuth {
             return;
         }
 
-        jwt = ClientRegistrationTokenUtils.verifyToken(realm, uri, split[1]);
-        if (jwt == null) {
-            throw unauthorized();
+        ClientRegistrationTokenUtils.TokenVerification tokenVerification = ClientRegistrationTokenUtils.verifyToken(realm, uri, split[1]);
+        if (tokenVerification.getError() != null) {
+            throw unauthorized(tokenVerification.getError().getMessage());
         }
+        jwt = tokenVerification.getJwt();
 
         if (isInitialAccessToken()) {
             initialAccessModel = session.sessions().getClientInitialAccessModel(session.getContext().getRealm(), jwt.getId());
             if (initialAccessModel == null) {
-                throw unauthorized();
+                throw unauthorized("Initial Access Token not found");
             }
         }
     }
 
+    public boolean isRegistrationHostTrusted() {
+        return trustedHostModel != null;
+    }
+
     private boolean isBearerToken() {
         return jwt != null && TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType());
     }
@@ -101,7 +112,10 @@ public class ClientRegistrationAuth {
     public void requireCreate() {
         init();
 
-        if (isBearerToken()) {
+        if (isRegistrationHostTrusted()) {
+            // Client registrations from trusted hosts
+            return;
+        } else if (isBearerToken()) {
             if (hasRole(AdminRoles.MANAGE_CLIENTS, AdminRoles.CREATE_CLIENT)) {
                 return;
             } else {
@@ -115,7 +129,7 @@ public class ClientRegistrationAuth {
             }
         }
 
-        throw unauthorized();
+        throw unauthorized("Not authenticated to view client. Host not trusted and Token is missing or invalid.");
     }
 
     public void requireView(ClientModel client) {
@@ -131,18 +145,18 @@ public class ClientRegistrationAuth {
                 throw forbidden();
             }
         } else if (isRegistrationAccessToken()) {
-            if (client.getRegistrationToken() != null && client != null && client.getRegistrationToken().equals(jwt.getId())) {
+            if (client.getRegistrationToken() != null && client.getRegistrationToken().equals(jwt.getId())) {
                 return;
             }
         } else if (isInitialAccessToken()) {
-            throw unauthorized();
+            throw unauthorized("Not initial access token");
         } else {
             if (authenticateClient(client)) {
                 return;
             }
         }
 
-        throw unauthorized();
+        throw unauthorized("Not authorized to view client. Missing or invalid token or bad client credentials.");
     }
 
     public void requireUpdate(ClientModel client) {
@@ -163,13 +177,17 @@ public class ClientRegistrationAuth {
             }
         }
 
-        throw unauthorized();
+        throw unauthorized("Not authorized to update client. Missing or invalid token.");
     }
 
     public ClientInitialAccessModel getInitialAccessModel() {
         return initialAccessModel;
     }
 
+    public ClientRegistrationTrustedHostModel getTrustedHostModel() {
+        return trustedHostModel;
+    }
+
     private boolean hasRole(String... role) {
         try {
             Map<String, Object> otherClaims = jwt.getOtherClaims();
@@ -218,36 +236,36 @@ public class ClientRegistrationAuth {
         Response response = processor.authenticateClient();
         if (response != null) {
             event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw unauthorized();
+            throw unauthorized("Failed to authenticate client");
         }
 
         ClientModel authClient = processor.getClient();
-        if (client == null) {
+        if (authClient == null) {
             event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw unauthorized();
+            throw unauthorized("No client authenticated");
         }
 
         if (!authClient.getClientId().equals(client.getClientId())) {
             event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
-            throw unauthorized();
+            throw unauthorized("Different client authenticated");
         }
 
         return true;
     }
 
-    private Failure unauthorized() {
-        event.error(Errors.NOT_ALLOWED);
-        return new UnauthorizedException();
+    private Failure unauthorized(String errorDescription) {
+        event.detail(Details.REASON, errorDescription).error(Errors.INVALID_TOKEN);
+        throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, errorDescription, Response.Status.UNAUTHORIZED);
     }
 
     private Failure forbidden() {
         event.error(Errors.NOT_ALLOWED);
-        return new ForbiddenException();
+        throw new ErrorResponseException(OAuthErrorException.INSUFFICIENT_SCOPE, "Forbidden", Response.Status.FORBIDDEN);
     }
 
     private Failure notFound() {
-        event.error(Errors.NOT_ALLOWED);
-        return new NotFoundException("Client not found");
+        event.error(Errors.CLIENT_NOT_FOUND);
+        throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client not found", Response.Status.NOT_FOUND);
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationHostUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationHostUtils.java
new file mode 100644
index 0000000..93cd984
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationHostUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.clientregistration;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationHostUtils {
+
+    private static final Logger logger = Logger.getLogger(ClientRegistrationHostUtils.class);
+
+    /**
+     * @return null if host from request is not trusted. Otherwise return trusted host model
+     */
+    public static ClientRegistrationTrustedHostModel getTrustedHost(String hostAddress, KeycloakSession session, RealmModel realm) {
+        logger.debugf("Verifying remote host : %s", hostAddress);
+
+        List<ClientRegistrationTrustedHostModel> trustedHosts = session.sessions().listClientRegistrationTrustedHosts(realm);
+
+        for (ClientRegistrationTrustedHostModel realmTrustedHost : trustedHosts) {
+            try {
+                if (realmTrustedHost.getRemainingCount() <= 0) {
+                    continue;
+                }
+
+                String realmHostIPAddress = InetAddress.getByName(realmTrustedHost.getHostName()).getHostAddress();
+                logger.debugf("Trying host '%s' of address '%s'", realmTrustedHost.getHostName(), realmHostIPAddress);
+                if (realmHostIPAddress.equals(hostAddress)) {
+                    logger.debugf("Successfully verified host : %s", realmTrustedHost.getHostName());
+                    return realmTrustedHost;
+                }
+            } catch (UnknownHostException uhe) {
+                logger.debugf("Unknown host from realm configuration: %s", realmTrustedHost.getHostName());
+            }
+        }
+
+        logger.debugf("Failed to verify remote host : %s", hostAddress);
+        return null;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
index 2fe65cc..2f33e5d 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationTokenUtils.java
@@ -56,40 +56,44 @@ public class ClientRegistrationTokenUtils {
         return createToken(realm, uri, model.getId(), TYPE_INITIAL_ACCESS_TOKEN, model.getExpiration() > 0 ? model.getTimestamp() + model.getExpiration() : 0);
     }
 
-    public static JsonWebToken verifyToken(RealmModel realm, UriInfo uri, String token) {
+    public static TokenVerification verifyToken(RealmModel realm, UriInfo uri, String token) {
+        if (token == null) {
+            return TokenVerification.error(new RuntimeException("Missing token"));
+        }
+
         JWSInput input;
         try {
             input = new JWSInput(token);
         } catch (JWSInputException e) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Invalid token", e));
         }
 
         if (!RSAProvider.verify(input, realm.getPublicKey())) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Failed verify token"));
         }
 
         JsonWebToken jwt;
         try {
             jwt = input.readJsonContent(JsonWebToken.class);
         } catch (JWSInputException e) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Token is not JWT", e));
         }
 
         if (!getIssuer(realm, uri).equals(jwt.getIssuer())) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Issuer from token don't match with the realm issuer."));
         }
 
         if (!jwt.isActive()) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Token not active."));
         }
 
         if (!(TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType()) ||
                 TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType()) ||
                 TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType()))) {
-            return null;
+            return TokenVerification.error(new RuntimeException("Invalid type of token"));
         }
 
-        return jwt;
+        return TokenVerification.success(jwt);
     }
 
     private static String createToken(RealmModel realm, UriInfo uri, String id, String type, int expiration) {
@@ -112,4 +116,31 @@ public class ClientRegistrationTokenUtils {
         return Urls.realmIssuer(uri.getBaseUri(), realm.getName());
     }
 
+    protected static class TokenVerification {
+
+        private final JsonWebToken jwt;
+        private final RuntimeException error;
+
+        public static TokenVerification success(JsonWebToken jwt) {
+            return new TokenVerification(jwt, null);
+        }
+
+        public static TokenVerification error(RuntimeException error) {
+            return new TokenVerification(null, error);
+        }
+
+        private TokenVerification(JsonWebToken jwt, RuntimeException error) {
+            this.jwt = jwt;
+            this.error = error;
+        }
+
+        public JsonWebToken getJwt() {
+            return jwt;
+        }
+
+        public RuntimeException getError() {
+            return error;
+        }
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
index cfbf42d..3ddcc59 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
@@ -18,24 +18,38 @@
 package org.keycloak.services.clientregistration.oidc;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.ClientAuthenticator;
+import org.keycloak.authentication.ClientAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
+import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.protocol.oidc.utils.JWKSUtils;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
+import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.oidc.OIDCClientRepresentation;
 import org.keycloak.services.clientregistration.ClientRegistrationException;
+import org.keycloak.services.util.CertificateInfoHelper;
 
+import java.io.IOException;
 import java.net.URI;
+import java.security.PublicKey;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class DescriptionConverter {
 
-    public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) throws ClientRegistrationException {
+    public static ClientRepresentation toInternal(KeycloakSession session, OIDCClientRepresentation clientOIDC) throws ClientRegistrationException {
         ClientRepresentation client = new ClientRepresentation();
         client.setClientId(clientOIDC.getClientId());
         client.setName(clientOIDC.getClientName());
@@ -60,15 +74,79 @@ public class DescriptionConverter {
             throw new ClientRegistrationException(iae.getMessage(), iae);
         }
 
+        String authMethod = clientOIDC.getTokenEndpointAuthMethod();
+        ClientAuthenticatorFactory clientAuthFactory;
+        if (authMethod == null) {
+            clientAuthFactory = (ClientAuthenticatorFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, KeycloakModelUtils.getDefaultClientAuthenticatorType());
+        } else {
+            clientAuthFactory = AuthorizeClientUtil.findClientAuthenticatorForOIDCAuthMethod(session, authMethod);
+        }
+
+        if (clientAuthFactory == null) {
+            throw new ClientRegistrationException("Not found clientAuthenticator for requested token_endpoint_auth_method");
+        }
+        client.setClientAuthenticatorType(clientAuthFactory.getId());
+
+        // Externalize to ClientAuthenticator itself?
+        if (authMethod != null && authMethod.equals(OIDCLoginProtocol.PRIVATE_KEY_JWT)) {
+
+            PublicKey publicKey = retrievePublicKey(clientOIDC);
+            if (publicKey == null) {
+                throw new ClientRegistrationException("Didn't find key of supported keyType for use " + JWK.Use.SIG.asString());
+            }
+
+            String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+
+            CertificateRepresentation rep = new CertificateRepresentation();
+            rep.setPublicKey(publicKeyPem);
+            CertificateInfoHelper.updateClientRepresentationCertificateInfo(client, rep, JWTClientAuthenticator.ATTR_PREFIX);
+        }
+
         return client;
     }
 
-    public static OIDCClientRepresentation toExternalResponse(ClientRepresentation client, URI uri) {
+
+    private static PublicKey retrievePublicKey(OIDCClientRepresentation clientOIDC) {
+        if (clientOIDC.getJwksUri() == null && clientOIDC.getJwks() == null) {
+            throw new ClientRegistrationException("Requested client authentication method '%s' but jwks_uri nor jwks were available in config");
+        }
+
+        if (clientOIDC.getJwksUri() != null && clientOIDC.getJwks() != null) {
+            throw new ClientRegistrationException("Illegal to use both jwks_uri and jwks");
+        }
+
+        JSONWebKeySet keySet;
+        if (clientOIDC.getJwks() != null) {
+            keySet = clientOIDC.getJwks();
+        } else {
+            try {
+                keySet = JWKSUtils.sendJwksRequest(clientOIDC.getJwksUri());
+            } catch (IOException ioe) {
+                throw new ClientRegistrationException("Failed to send JWKS request to specified jwks_uri", ioe);
+            }
+        }
+
+        return JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG);
+    }
+
+
+    public static OIDCClientRepresentation toExternalResponse(KeycloakSession session, ClientRepresentation client, URI uri) {
         OIDCClientRepresentation response = new OIDCClientRepresentation();
         response.setClientId(client.getClientId());
+
+        ClientAuthenticatorFactory clientAuth = (ClientAuthenticatorFactory) session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, client.getClientAuthenticatorType());
+        Set<String> oidcClientAuthMethods = clientAuth.getProtocolAuthenticatorMethods(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        if (oidcClientAuthMethods != null && !oidcClientAuthMethods.isEmpty()) {
+            response.setTokenEndpointAuthMethod(oidcClientAuthMethods.iterator().next());
+        }
+
+        if (client.getClientAuthenticatorType().equals(ClientIdAndSecretAuthenticator.PROVIDER_ID)) {
+            response.setClientSecret(client.getSecret());
+            response.setClientSecretExpiresAt(0);
+        }
+
         response.setClientName(client.getName());
         response.setClientUri(client.getBaseUrl());
-        response.setClientSecret(client.getSecret());
         response.setRedirectUris(client.getRedirectUris());
         response.setRegistrationAccessToken(client.getRegistrationAccessToken());
         response.setRegistrationClientUri(uri.toString());
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
index 84dfc78..8bc1ecb 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
@@ -53,10 +53,10 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
         }
 
         try {
-            ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+            ClientRepresentation client = DescriptionConverter.toInternal(session, clientOIDC);
             client = create(client);
             URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
-            clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
+            clientOIDC = DescriptionConverter.toExternalResponse(session, client, uri);
             clientOIDC.setClientIdIssuedAt(Time.currentTime());
             return Response.created(uri).entity(clientOIDC).build();
         } catch (ClientRegistrationException cre) {
@@ -70,7 +70,7 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
     @Produces(MediaType.APPLICATION_JSON)
     public Response getOIDC(@PathParam("clientId") String clientId) {
         ClientRepresentation client = get(clientId);
-        OIDCClientRepresentation clientOIDC = DescriptionConverter.toExternalResponse(client, session.getContext().getUri().getRequestUri());
+        OIDCClientRepresentation clientOIDC = DescriptionConverter.toExternalResponse(session, client, session.getContext().getUri().getRequestUri());
         return Response.ok(clientOIDC).build();
     }
 
@@ -79,10 +79,10 @@ public class OIDCClientRegistrationProvider extends AbstractClientRegistrationPr
     @Consumes(MediaType.APPLICATION_JSON)
     public Response updateOIDC(@PathParam("clientId") String clientId, OIDCClientRepresentation clientOIDC) {
         try {
-            ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+            ClientRepresentation client = DescriptionConverter.toInternal(session, clientOIDC);
             client = update(clientId, client);
             URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(client.getClientId()).build();
-            clientOIDC = DescriptionConverter.toExternalResponse(client, uri);
+            clientOIDC = DescriptionConverter.toExternalResponse(session, client, uri);
             return Response.ok(clientOIDC).build();
         } catch (ClientRegistrationException cre) {
             logger.clientRegistrationException(cre.getMessage());
diff --git a/services/src/main/java/org/keycloak/services/ErrorResponseException.java b/services/src/main/java/org/keycloak/services/ErrorResponseException.java
index c29f0f3..538374c 100644
--- a/services/src/main/java/org/keycloak/services/ErrorResponseException.java
+++ b/services/src/main/java/org/keycloak/services/ErrorResponseException.java
@@ -18,6 +18,7 @@
 package org.keycloak.services;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
@@ -42,12 +43,8 @@ public class ErrorResponseException extends WebApplicationException {
 
     @Override
     public Response getResponse() {
-        Map<String, String> e = new HashMap<String, String>();
-        e.put(OAuth2Constants.ERROR, error);
-        if (errorDescription != null) {
-            e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
-        }
-        return Response.status(status).entity(e).type(MediaType.APPLICATION_JSON_TYPE).build();
+        OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
+        return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
index f72a3d9..f4f2c89 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminEventBuilder.java
@@ -184,12 +184,6 @@ public class AdminEventBuilder {
         return path.substring(path.indexOf(realmRelative) + realmRelative.length());
     }
 
-    public void error(String error) {
-        adminEvent.setOperationType(OperationType.valueOf(adminEvent.getOperationType().name() + "_ERROR"));
-        adminEvent.setError(error);
-        send();
-    }
-
     public AdminEventBuilder representation(Object value) {
         if (value == null || value.equals("")) {
             return this;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index af0367a..f29ed8c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -25,14 +25,19 @@ import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.common.util.StreamUtil;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.jose.jwk.JWK;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.utils.JWKSUtils;
 import org.keycloak.representations.KeyStoreConfig;
 import org.keycloak.representations.idm.CertificateRepresentation;
 import org.keycloak.services.ErrorResponseException;
 import org.keycloak.common.util.PemUtils;
+import org.keycloak.services.util.CertificateInfoHelper;
+import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -49,6 +54,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyStore;
 import java.security.PrivateKey;
+import java.security.PublicKey;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.List;
@@ -60,17 +66,12 @@ import java.util.Map;
  */
 public class ClientAttributeCertificateResource {
 
-    public static final String PRIVATE_KEY = "private.key";
-    public static final String X509CERTIFICATE = "certificate";
-
     protected RealmModel realm;
     private RealmAuth auth;
     protected ClientModel client;
     protected KeycloakSession session;
     protected AdminEventBuilder adminEvent;
     protected String attributePrefix;
-    protected String privateAttribute;
-    protected String certificateAttribute;
 
     public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) {
         this.realm = realm;
@@ -78,8 +79,6 @@ public class ClientAttributeCertificateResource {
         this.client = client;
         this.session = session;
         this.attributePrefix = attributePrefix;
-        this.privateAttribute = attributePrefix + "." + PRIVATE_KEY;
-        this.certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
         this.adminEvent = adminEvent.resource(ResourceType.CLIENT);
     }
 
@@ -98,9 +97,7 @@ public class ClientAttributeCertificateResource {
             throw new NotFoundException("Could not find client");
         }
 
-        CertificateRepresentation info = new CertificateRepresentation();
-        info.setCertificate(client.getAttribute(certificateAttribute));
-        info.setPrivateKey(client.getAttribute(privateAttribute));
+        CertificateRepresentation info = CertificateInfoHelper.getCertificateFromClient(client, attributePrefix);
         return info;
     }
 
@@ -122,8 +119,7 @@ public class ClientAttributeCertificateResource {
 
         CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(client.getClientId());
 
-        client.setAttribute(privateAttribute, info.getPrivateKey());
-        client.setAttribute(certificateAttribute, info.getCertificate());
+        CertificateInfoHelper.updateClientModelCertificateInfo(client, info, attributePrefix);
 
         adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).representation(info).success();
 
@@ -151,18 +147,12 @@ public class ClientAttributeCertificateResource {
 
         CertificateRepresentation info = getCertFromRequest(uriInfo, input);
 
-        if (info.getPrivateKey() != null) {
-            client.setAttribute(privateAttribute, info.getPrivateKey());
-        } else if (info.getCertificate() != null) {
-            client.removeAttribute(privateAttribute);
-        } else {
+        try {
+            CertificateInfoHelper.updateClientModelCertificateInfo(client, info, attributePrefix);
+        } catch (IllegalStateException ise) {
             throw new ErrorResponseException("certificate-not-found", "Certificate or key with given alias not found in the keystore", Response.Status.BAD_REQUEST);
         }
 
-        if (info.getCertificate() != null) {
-            client.setAttribute(certificateAttribute, info.getCertificate());
-        }
-
         adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).representation(info).success();
         return info;
     }
@@ -187,12 +177,12 @@ public class ClientAttributeCertificateResource {
         }
 
         CertificateRepresentation info = getCertFromRequest(uriInfo, input);
+        info.setPrivateKey(null);
 
-        if (info.getCertificate() != null) {
-            client.setAttribute(certificateAttribute, info.getCertificate());
-            client.removeAttribute(privateAttribute);
-        } else {
-            throw new ErrorResponseException("certificate-not-found", "Certificate with given alias not found in the keystore", Response.Status.BAD_REQUEST);
+        try {
+            CertificateInfoHelper.updateClientModelCertificateInfo(client, info, attributePrefix);
+        } catch (IllegalStateException ise) {
+            throw new ErrorResponseException("certificate-not-found", "Certificate or key with given alias not found in the keystore", Response.Status.BAD_REQUEST);
         }
 
         adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).representation(info).success();
@@ -210,10 +200,16 @@ public class ClientAttributeCertificateResource {
             info.setCertificate(pem);
             return info;
 
+        } else if (keystoreFormat.equals("JSON Web Key Set (JWK)")) {
+            InputStream stream = inputParts.get(0).getBody(InputStream.class, null);
+            JSONWebKeySet keySet = JsonSerialization.readValue(stream, JSONWebKeySet.class);
+            PublicKey publicKey = JWKSUtils.getKeyForUse(keySet, JWK.Use.SIG);
+            String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+            info.setPublicKey(publicKeyPem);
+            return info;
         }
 
 
-
         String keyAlias = uploadForm.get("keyAlias").get(0).getBodyAsString();
         List<InputPart> keyPasswordPart = uploadForm.get("keyPassword");
         char[] keyPassword = keyPasswordPart != null ? keyPasswordPart.get(0).getBodyAsString().toCharArray() : null;
@@ -272,8 +268,10 @@ public class ClientAttributeCertificateResource {
             throw new NotAcceptableException("Only support jks or pkcs12 format.");
         }
 
-        String privatePem = client.getAttribute(privateAttribute);
-        String certPem = client.getAttribute(certificateAttribute);
+        CertificateRepresentation info = CertificateInfoHelper.getCertificateFromClient(client, attributePrefix);
+        String privatePem = info.getPrivateKey();
+        String certPem = info.getCertificate();
+
         if (privatePem == null && certPem == null) {
             throw new NotFoundException("keypair not generated for client");
         }
@@ -322,7 +320,10 @@ public class ClientAttributeCertificateResource {
         CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(client.getClientId());
         byte[] rtn = getKeystore(config, info.getPrivateKey(), info.getCertificate());
 
-        client.setAttribute(certificateAttribute, info.getCertificate());
+        info.setPrivateKey(null);
+
+        CertificateInfoHelper.updateClientModelCertificateInfo(client, info, attributePrefix);
+
         adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).representation(info).success();
         return rtn;
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationTrustedHostResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationTrustedHostResource.java
new file mode 100644
index 0000000..000885f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationTrustedHostResource.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resources.admin;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.events.admin.ResourceType;
+import org.keycloak.models.ClientRegistrationTrustedHostModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
+import org.keycloak.services.ErrorResponse;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegistrationTrustedHostResource {
+
+    private final RealmAuth auth;
+    private final RealmModel realm;
+    private final AdminEventBuilder adminEvent;
+
+    @Context
+    protected KeycloakSession session;
+
+    @Context
+    protected UriInfo uriInfo;
+
+    public ClientRegistrationTrustedHostResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+        this.auth = auth;
+        this.realm = realm;
+        this.adminEvent = adminEvent.resource(ResourceType.CLIENT_REGISTRATION_TRUSTED_HOST_MODEL);
+
+        auth.init(RealmAuth.Resource.CLIENT);
+    }
+
+    /**
+     * Create a new initial access token.
+     *
+     * @param config
+     * @return
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response create(ClientRegistrationTrustedHostRepresentation config) {
+        auth.requireManage();
+
+        if (config.getHostName() == null) {
+            return ErrorResponse.error("hostName not provided in config", Response.Status.BAD_REQUEST);
+        }
+
+        int count = config.getCount() != null ? config.getCount() : 1;
+
+        try {
+            ClientRegistrationTrustedHostModel clientRegTrustedHostModel = session.sessions().createClientRegistrationTrustedHostModel(realm, config.getHostName(), count);
+
+            adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientRegTrustedHostModel.getHostName()).representation(config).success();
+
+            return Response.created(uriInfo.getAbsolutePathBuilder().path(clientRegTrustedHostModel.getHostName()).build()).build();
+        } catch (ModelDuplicateException mde) {
+            return ErrorResponse.exists(mde.getMessage());
+        }
+    }
+
+    /**
+     * Update a new initial access token.
+     *
+     * @param config
+     * @return
+     */
+    @PUT
+    @Path("{hostname}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response update(final @PathParam("hostname") String hostName, ClientRegistrationTrustedHostRepresentation config) {
+        auth.requireManage();
+
+        if (config.getHostName() == null || !hostName.equals(config.getHostName())) {
+            return ErrorResponse.error("hostName not provided in config or not compatible", Response.Status.BAD_REQUEST);
+        }
+
+        if (config.getCount() == null) {
+            return ErrorResponse.error("count needs to be available", Response.Status.BAD_REQUEST);
+        }
+
+        if (config.getRemainingCount() != null && config.getRemainingCount() > config.getCount()) {
+            return ErrorResponse.error("remainingCount can't be bigger than count", Response.Status.BAD_REQUEST);
+        }
+
+        ClientRegistrationTrustedHostModel hostModel = session.sessions().getClientRegistrationTrustedHostModel(realm, config.getHostName());
+        if (hostModel == null) {
+            return ErrorResponse.error("hostName record not found", Response.Status.NOT_FOUND);
+        }
+
+        hostModel.setCount(config.getCount());
+        hostModel.setRemainingCount(config.getRemainingCount());
+
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(config).success();
+        return Response.noContent().build();
+    }
+
+    /**
+     * Get an initial access token.
+     *
+     * @param hostName
+     * @return
+     */
+    @GET
+    @Path("{hostname}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public ClientRegistrationTrustedHostRepresentation getConfig(final @PathParam("hostname") String hostName) {
+        auth.requireView();
+
+        ClientRegistrationTrustedHostModel hostModel = session.sessions().getClientRegistrationTrustedHostModel(realm, hostName);
+        if (hostModel == null) {
+            throw new NotFoundException("hostName record not found");
+        }
+
+        return wrap(hostModel);
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<ClientRegistrationTrustedHostRepresentation> list() {
+        auth.requireView();
+
+        List<ClientRegistrationTrustedHostModel> models = session.sessions().listClientRegistrationTrustedHosts(realm);
+        List<ClientRegistrationTrustedHostRepresentation> reps = new LinkedList<>();
+        for (ClientRegistrationTrustedHostModel m : models) {
+            ClientRegistrationTrustedHostRepresentation r = wrap(m);
+            reps.add(r);
+        }
+        return reps;
+    }
+
+    @DELETE
+    @Path("{hostname}")
+    public void delete(final @PathParam("hostname") String hostName) {
+        auth.requireManage();
+
+        session.sessions().removeClientRegistrationTrustedHostModel(realm, hostName);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
+    }
+
+    private ClientRegistrationTrustedHostRepresentation wrap(ClientRegistrationTrustedHostModel model) {
+        return ClientRegistrationTrustedHostRepresentation.create(model.getHostName(), model.getCount(), model.getRemainingCount());
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 22441ad..4fd941b 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -207,6 +207,19 @@ public class RealmAdminResource {
         return resource;
     }
 
+
+    /**
+     * Base path for managing client initial access tokens
+     *
+     * @return
+     */
+    @Path("clients-trusted-hosts")
+    public ClientRegistrationTrustedHostResource getClientRegistrationTrustedHost() {
+        ClientRegistrationTrustedHostResource resource = new ClientRegistrationTrustedHostResource(realm, auth, adminEvent);
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+        return resource;
+    }
+
     /**
      * Base path for managing components under this realm.
      *
diff --git a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
index c633587..898ad3c 100755
--- a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
+++ b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
@@ -19,6 +19,7 @@ package org.keycloak.services.resources;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.UnauthorizedException;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.constants.AdapterConstants;
@@ -30,6 +31,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.common.util.Time;
@@ -172,11 +174,9 @@ public class ClientsManagementService {
         ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
 
         if (client.isPublicClient()) {
-            Map<String, String> error = new HashMap<String, String>();
-            error.put(OAuth2Constants.ERROR, "invalid_client");
-            error.put(OAuth2Constants.ERROR_DESCRIPTION, "Public clients not allowed");
+            OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(OAuthErrorException.INVALID_CLIENT, "Public clients not allowed");
             event.error(Errors.INVALID_CLIENT);
-            throw new BadRequestException("Public clients not allowed", javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST).entity(error).type(MediaType.APPLICATION_JSON_TYPE).build());
+            throw new BadRequestException("Public clients not allowed", javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build());
         }
 
         return client;
@@ -185,11 +185,9 @@ public class ClientsManagementService {
     protected String getClientClusterHost(MultivaluedMap<String, String> formData) {
         String clientClusterHost = formData.getFirst(AdapterConstants.CLIENT_CLUSTER_HOST);
         if (clientClusterHost == null || clientClusterHost.length() == 0) {
-            Map<String, String> error = new HashMap<String, String>();
-            error.put(OAuth2Constants.ERROR, "invalid_request");
-            error.put(OAuth2Constants.ERROR_DESCRIPTION, "Client cluster host not specified");
+            OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation( OAuthErrorException.INVALID_REQUEST, "Client cluster host not specified");
             event.error(Errors.INVALID_CODE);
-            throw new BadRequestException("Cluster host not specified", javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST).entity(error).type(MediaType.APPLICATION_JSON_TYPE).build());
+            throw new BadRequestException("Cluster host not specified", javax.ws.rs.core.Response.status(javax.ws.rs.core.Response.Status.BAD_REQUEST).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build());
         }
 
         return clientClusterHost;
diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java
index 8aa4961..de7eef7 100644
--- a/services/src/main/java/org/keycloak/services/ServicesLogger.java
+++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java
@@ -425,4 +425,9 @@ public interface ServicesLogger extends BasicLogger {
     @LogMessage(level = ERROR)
     @Message(id=95, value="Client is not allowed to initiate browser login with given response_type. %s flow is disabled for the client.")
     void flowNotAllowed(String flowName);
+
+    @LogMessage(level = WARN)
+    @Message(id=96, value="Not found JWK of supported keyType under jwks_uri for usage: %s")
+    void supportedJwkNotFound(String usage);
+
 }
diff --git a/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java b/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java
new file mode 100644
index 0000000..b309927
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/util/CertificateInfoHelper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.util;
+
+import java.util.HashMap;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.representations.idm.CertificateRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CertificateInfoHelper {
+
+
+    public static final String PRIVATE_KEY = "private.key";
+    public static final String X509CERTIFICATE = "certificate";
+    public static final String PUBLIC_KEY = "public.key";
+
+
+    public static CertificateRepresentation getCertificateFromClient(ClientModel client, String attributePrefix) {
+        String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY;
+        String certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
+        String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY;
+
+        CertificateRepresentation rep = new CertificateRepresentation();
+        rep.setCertificate(client.getAttribute(certificateAttribute));
+        rep.setPublicKey(client.getAttribute(publicKeyAttribute));
+        rep.setPrivateKey(client.getAttribute(privateKeyAttribute));
+
+        return rep;
+    }
+
+
+    public static void updateClientModelCertificateInfo(ClientModel client, CertificateRepresentation rep, String attributePrefix) {
+        String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY;
+        String certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
+        String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY;
+
+        if (rep.getPublicKey() == null && rep.getCertificate() == null) {
+            throw new IllegalStateException("Both certificate and publicKey are null!");
+        }
+
+        if (rep.getPublicKey() != null && rep.getCertificate() != null) {
+            throw new IllegalStateException("Both certificate and publicKey are not null!");
+        }
+
+        setOrRemoveAttr(client, privateKeyAttribute, rep.getPrivateKey());
+        setOrRemoveAttr(client, publicKeyAttribute, rep.getPublicKey());
+        setOrRemoveAttr(client, certificateAttribute, rep.getCertificate());
+    }
+
+    private static void setOrRemoveAttr(ClientModel client, String attrName, String attrValue) {
+        if (attrValue != null) {
+            client.setAttribute(attrName, attrValue);
+        } else {
+            client.removeAttribute(attrName);
+        }
+    }
+
+
+    public static void updateClientRepresentationCertificateInfo(ClientRepresentation client, CertificateRepresentation rep, String attributePrefix) {
+        String privateKeyAttribute = attributePrefix + "." + PRIVATE_KEY;
+        String certificateAttribute = attributePrefix + "." + X509CERTIFICATE;
+        String publicKeyAttribute = attributePrefix + "." + PUBLIC_KEY;
+
+        if (rep.getPublicKey() == null && rep.getCertificate() == null) {
+            throw new IllegalStateException("Both certificate and publicKey are null!");
+        }
+
+        if (rep.getPublicKey() != null && rep.getCertificate() != null) {
+            throw new IllegalStateException("Both certificate and publicKey are not null!");
+        }
+
+        setOrRemoveAttr(client, privateKeyAttribute, rep.getPrivateKey());
+        setOrRemoveAttr(client, publicKeyAttribute, rep.getPublicKey());
+        setOrRemoveAttr(client, certificateAttribute, rep.getCertificate());
+    }
+
+    private static void setOrRemoveAttr(ClientRepresentation client, String attrName, String attrValue) {
+        if (attrValue != null) {
+            if (client.getAttributes() == null) {
+                client.setAttributes(new HashMap<>());
+            }
+            client.getAttributes().put(attrName, attrValue);
+        } else {
+            if (client.getAttributes() != null) {
+                client.getAttributes().remove(attrName);
+            }
+        }
+    }
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory b/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
index c9a536f..1ad9238 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
@@ -16,5 +16,5 @@
 #
 
 org.keycloak.exportimport.KeycloakClientDescriptionConverter
-org.keycloak.protocol.oidc.OIDCClientDescriptionConverter
+org.keycloak.protocol.oidc.OIDCClientDescriptionConverterFactory
 org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
index 2f8824d..83ee504 100755
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/forms/PassThroughClientAuthenticator.java
@@ -18,10 +18,12 @@
 package org.keycloak.testsuite.forms;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.keycloak.authentication.AuthenticationFlowError;
 import org.keycloak.authentication.ClientAuthenticationFlowContext;
@@ -119,4 +121,9 @@ public class PassThroughClientAuthenticator extends AbstractClientAuthenticator 
     public String getId() {
         return PROVIDER_ID;
     }
+
+    @Override
+    public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
+        return Collections.emptySet();
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index a964e07..586351d 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -40,7 +40,7 @@ import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
-import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
+import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 5d12db4..e4dfe0e 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -733,7 +733,7 @@ public class AccountTest extends TestRealmKeycloakTest {
         Assert.assertTrue(applicationsPage.isCurrent());
 
         Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
-        Assert.assertEquals(3, apps.size());
+        Assert.assertEquals(4, apps.size());
 
         AccountApplicationsPage.AppEntry accountEntry = apps.get("Account");
         Assert.assertEquals(2, accountEntry.getRolesAvailable().size());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java
index a6167eb..87c0024 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java
@@ -21,12 +21,16 @@ import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.test.api.ArquillianResource;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -67,6 +71,21 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam
 
             assertTrue(this.driver.getPageSource().contains("Your permissions are"));
             assertTrue(this.driver.getPageSource().contains("Default Resource"));
+
+            boolean hasDefaultPermission = false;
+            boolean hasDefaultPolicy = false;
+
+            for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
+                if ("Default Policy".equals(policy.getName())) {
+                    hasDefaultPolicy = true;
+                }
+                if ("Default Permission".equals(policy.getName())) {
+                    hasDefaultPermission = true;
+                }
+            }
+
+            assertTrue(hasDefaultPermission);
+            assertTrue(hasDefaultPolicy);
         } finally {
             this.deployer.undeploy(RESOURCE_SERVER_ID);
         }
@@ -95,4 +114,14 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam
         // enable authorization services in order to generate the default config and continue with tests
         clients.get(client.getId()).update(client);
     }
+
+    private AuthorizationResource getAuthorizationResource() throws FileNotFoundException {
+        return getClientResource(RESOURCE_SERVER_ID).authorization();
+    }
+
+    private ClientResource getClientResource(String clientId) {
+        ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
+        ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
+        return clients.get(resourceServer.getId());
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
index d7946ce..006dc92 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -17,6 +17,7 @@
 package org.keycloak.testsuite.admin;
 
 import org.jboss.logging.Logger;
+import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.admin.client.resource.RoleResource;
@@ -192,4 +193,13 @@ public class ApiUtil {
         }
         return contains;
     }
+
+    public static AuthorizationResource findAuthorizationSettings(RealmResource realm, String clientId) {
+        for (ClientRepresentation c : realm.clients().findAll()) {
+            if (c.getClientId().equals(clientId)) {
+                return realm.clients().get(c.getId()).authorization();
+            }
+        }
+        return null;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientRegTrustedHostTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientRegTrustedHostTest.java
new file mode 100644
index 0000000..f430e6a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientRegTrustedHostTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin;
+
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientRegistrationTrustedHostResource;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.events.admin.ResourceType;
+import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.util.AdminEventPaths;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientRegTrustedHostTest extends AbstractAdminTest {
+
+
+    private ClientRegistrationTrustedHostResource resource;
+
+    @Before
+    public void before() {
+        resource = realm.clientRegistrationTrustedHost();
+    }
+
+    @Test
+    public void testInitialAccessTokens() {
+
+        // Successfully create "localhost1" rep
+        ClientRegistrationTrustedHostRepresentation rep = new ClientRegistrationTrustedHostRepresentation();
+        rep.setHostName("localhost1");
+        rep.setCount(5);
+
+        Response res = resource.create(rep);
+        assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientRegistrationTrustedHostPath("localhost1"), rep, ResourceType.CLIENT_REGISTRATION_TRUSTED_HOST_MODEL);
+        res.close();
+
+        // Failed to create conflicting rep "localhost1" again
+        res = resource.create(rep);
+        Assert.assertEquals(409, res.getStatus());
+        assertAdminEvents.assertEmpty();
+        res.close();
+
+        // Successfully create "localhost2" rep
+        rep = new ClientRegistrationTrustedHostRepresentation();
+        rep.setHostName("localhost2");
+        rep.setCount(10);
+
+        res = resource.create(rep);
+        assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientRegistrationTrustedHostPath("localhost2"), rep, ResourceType.CLIENT_REGISTRATION_TRUSTED_HOST_MODEL);
+        res.close();
+
+        // Get "localhost1"
+        rep = resource.get("localhost1");
+        assertRep(rep, "localhost1", 5, 5);
+
+        // Update "localhost1"
+        rep.setCount(7);
+        rep.setRemainingCount(7);
+        resource.update("localhost1", rep);
+        assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientRegistrationTrustedHostPath("localhost1"), rep, ResourceType.CLIENT_REGISTRATION_TRUSTED_HOST_MODEL);
+
+        // Get all
+        List<ClientRegistrationTrustedHostRepresentation> alls = resource.list();
+        Assert.assertEquals(2, alls.size());
+        assertRep(findByHost(alls, "localhost1"), "localhost1", 7, 7);
+        assertRep(findByHost(alls, "localhost2"), "localhost2", 10, 10);
+
+        // Delete "localhost1"
+        resource.delete("localhost1");
+        assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientRegistrationTrustedHostPath("localhost1"), ResourceType.CLIENT_REGISTRATION_TRUSTED_HOST_MODEL);
+
+        // Get all and check just "localhost2" available
+        alls = resource.list();
+        Assert.assertEquals(1, alls.size());
+        assertRep(alls.get(0), "localhost2", 10, 10);
+    }
+
+    private ClientRegistrationTrustedHostRepresentation findByHost(List<ClientRegistrationTrustedHostRepresentation> list, String hostName) {
+        for (ClientRegistrationTrustedHostRepresentation rep : list) {
+            if (hostName.equals(rep.getHostName())) {
+                return rep;
+            }
+        }
+        return null;
+    }
+
+    private void assertRep(ClientRegistrationTrustedHostRepresentation rep, String expectedHost, int expectedCount, int expectedRemaining) {
+        Assert.assertEquals(expectedHost, rep.getHostName());
+        Assert.assertEquals(expectedCount, rep.getCount().intValue());
+        Assert.assertEquals(expectedRemaining, rep.getRemainingCount().intValue());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
index f455303..0543238 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java
@@ -432,7 +432,7 @@ public class GroupTest extends AbstractGroupTest {
 
         // List realm roles
         assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite");
-        assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user");
+        assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium");
         assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child");
 
         // List client roles
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index dd5729a..c87bdf1 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -814,7 +814,7 @@ public class UserTest extends AbstractAdminTest {
 
         // List realm roles
         assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
-        assertNames(roles.realmLevel().listAvailable(), "admin");
+        assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium");
         assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
 
         // List client roles
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
index ab587c3..5d7d2c1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
@@ -17,27 +17,61 @@
 
 package org.keycloak.testsuite.client;
 
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
 import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
 import org.keycloak.client.registration.Auth;
 import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
 import org.keycloak.common.util.CollectionUtil;
+import org.keycloak.common.util.KeycloakUriBuilder;
+import org.keycloak.constants.ServiceUrlConstants;
+import org.keycloak.jose.jwk.JSONWebKeySet;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.utils.OIDCResponseType;
+import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
 import org.keycloak.representations.idm.ClientInitialAccessPresentation;
+import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.oidc.OIDCClientRepresentation;
-
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.util.OAuthClient;
+import java.security.PrivateKey;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
 
 import static org.junit.Assert.*;
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
 
+    private static final String PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
+    private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+        testRealms.get(0).setPrivateKey(PRIVATE_KEY);
+        testRealms.get(0).setPublicKey(PUBLIC_KEY);
+    }
+
     @Before
     public void before() throws Exception {
         super.before();
@@ -46,11 +80,16 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         reg.auth(Auth.token(token));
     }
 
-    public OIDCClientRepresentation create() throws ClientRegistrationException {
+    private OIDCClientRepresentation createRep() {
         OIDCClientRepresentation client = new OIDCClientRepresentation();
         client.setClientName("RegistrationAccessTokenTest");
         client.setClientUri("http://root");
         client.setRedirectUris(Collections.singletonList("http://redirect"));
+        return client;
+    }
+
+    public OIDCClientRepresentation create() throws ClientRegistrationException {
+        OIDCClientRepresentation client = createRep();
 
         OIDCClientRepresentation response = reg.oidc().create(client);
 
@@ -58,13 +97,53 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
     }
 
     @Test
+    public void testCreateWithTrustedHost() throws Exception {
+        reg.auth(null);
+
+        OIDCClientRepresentation client = createRep();
+
+        // Failed to create client
+        try {
+            reg.oidc().create(client);
+            Assert.fail("Not expected to successfuly register client");
+        } catch (ClientRegistrationException expected) {
+            HttpErrorException httpEx = (HttpErrorException) expected.getCause();
+            Assert.assertEquals(401, httpEx.getStatusLine().getStatusCode());
+        }
+
+        // Create trusted host entry
+        Response response = adminClient.realm(REALM_NAME).clientRegistrationTrustedHost().create(ClientRegistrationTrustedHostRepresentation.create("localhost", 2, 2));
+        Assert.assertEquals(201, response.getStatus());
+
+        // Successfully register client
+        reg.oidc().create(client);
+
+        // Just one remaining available
+        ClientRegistrationTrustedHostRepresentation rep = adminClient.realm(REALM_NAME).clientRegistrationTrustedHost().get("localhost");
+        Assert.assertEquals(1, rep.getRemainingCount().intValue());
+
+        // Successfully register client2
+        reg.oidc().create(client);
+
+        // Failed to create 3rd client
+        try {
+            reg.oidc().create(client);
+            Assert.fail("Not expected to successfuly register client");
+        } catch (ClientRegistrationException expected) {
+            HttpErrorException httpEx = (HttpErrorException) expected.getCause();
+            Assert.assertEquals(401, httpEx.getStatusLine().getStatusCode());
+        }
+    }
+
+    @Test
     public void createClient() throws ClientRegistrationException {
         OIDCClientRepresentation response = create();
 
         assertNotNull(response.getRegistrationAccessToken());
         assertNotNull(response.getClientIdIssuedAt());
         assertNotNull(response.getClientId());
-        assertNull(response.getClientSecretExpiresAt());
+        assertNotNull(response.getClientSecret());
+        assertEquals(0, response.getClientSecretExpiresAt().intValue());
         assertNotNull(response.getRegistrationClientUri());
         assertEquals("RegistrationAccessTokenTest", response.getClientName());
         assertEquals("http://root", response.getClientUri());
@@ -72,6 +151,7 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         assertEquals("http://redirect", response.getRedirectUris().get(0));
         assertEquals(Arrays.asList("code", "none"), response.getResponseTypes());
         assertEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN), response.getGrantTypes());
+        assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, response.getTokenEndpointAuthMethod());
     }
 
     @Test
@@ -84,6 +164,9 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         assertNotEquals(response.getRegistrationAccessToken(), rep.getRegistrationAccessToken());
         assertTrue(CollectionUtil.collectionEquals(Arrays.asList("code", "none"), response.getResponseTypes()));
         assertTrue(CollectionUtil.collectionEquals(Arrays.asList(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN), response.getGrantTypes()));
+        assertNotNull(response.getClientSecret());
+        assertEquals(0, response.getClientSecretExpiresAt().intValue());
+        assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, response.getTokenEndpointAuthMethod());
     }
 
     @Test
@@ -122,4 +205,89 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
         reg.oidc().delete(response);
     }
 
+    @Test
+    public void createClientWithJWKS() throws Exception {
+        OIDCClientRepresentation clientRep = createRep();
+
+        clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
+        clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.PRIVATE_KEY_JWT);
+
+        // Corresponds to PRIVATE_KEY
+        JSONWebKeySet keySet = loadJson(getClass().getResourceAsStream("/clientreg-test/jwks.json"), JSONWebKeySet.class);
+        clientRep.setJwks(keySet);
+
+        OIDCClientRepresentation response = reg.oidc().create(clientRep);
+        Assert.assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, response.getTokenEndpointAuthMethod());
+        Assert.assertNull(response.getClientSecret());
+        Assert.assertNull(response.getClientSecretExpiresAt());
+
+        // Tries to authenticate client with privateKey JWT
+        String signedJwt = getClientSignedJWT(response.getClientId());
+        OAuthClient.AccessTokenResponse accessTokenResponse = doClientCredentialsGrantRequest(signedJwt);
+        Assert.assertEquals(200, accessTokenResponse.getStatusCode());
+        AccessToken accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken());
+        Assert.assertEquals(response.getClientId(), accessToken.getAudience()[0]);
+    }
+
+    @Test
+    public void createClientWithJWKSURI() throws Exception {
+        OIDCClientRepresentation clientRep = createRep();
+
+        clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
+        clientRep.setTokenEndpointAuthMethod(OIDCLoginProtocol.PRIVATE_KEY_JWT);
+
+        // Use the realmKey for client authentication too
+        clientRep.setJwksUri(oauth.getCertsUrl(REALM_NAME));
+
+        OIDCClientRepresentation response = reg.oidc().create(clientRep);
+        Assert.assertEquals(OIDCLoginProtocol.PRIVATE_KEY_JWT, response.getTokenEndpointAuthMethod());
+        Assert.assertNull(response.getClientSecret());
+        Assert.assertNull(response.getClientSecretExpiresAt());
+
+        // Tries to authenticate client with privateKey JWT
+        String signedJwt = getClientSignedJWT(response.getClientId());
+        OAuthClient.AccessTokenResponse accessTokenResponse = doClientCredentialsGrantRequest(signedJwt);
+        Assert.assertEquals(200, accessTokenResponse.getStatusCode());
+        AccessToken accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken());
+        Assert.assertEquals(response.getClientId(), accessToken.getAudience()[0]);
+    }
+
+
+    // Client auth with signedJWT - helper methods
+
+    private String getClientSignedJWT(String clientId) {
+        String realmInfoUrl = KeycloakUriBuilder.fromUri(getAuthServerRoot()).path(ServiceUrlConstants.REALM_INFO_PATH).build(REALM_NAME).toString();
+
+        PrivateKey privateKey = KeycloakModelUtils.getPrivateKey(PRIVATE_KEY);
+
+        JWTClientCredentialsProvider jwtProvider = new JWTClientCredentialsProvider();
+        jwtProvider.setPrivateKey(privateKey);
+        jwtProvider.setTokenTimeout(10);
+        return jwtProvider.createSignedRequestToken(clientId, realmInfoUrl);
+
+    }
+
+
+    private OAuthClient.AccessTokenResponse doClientCredentialsGrantRequest(String signedJwt) throws Exception {
+        List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+        parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
+        parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ASSERTION_TYPE, OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT));
+        parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ASSERTION, signedJwt));
+
+        HttpResponse response = sendRequest(oauth.getServiceAccountUrl(), parameters);
+        return new OAuthClient.AccessTokenResponse(response);
+    }
+
+    private HttpResponse sendRequest(String requestUrl, List<NameValuePair> parameters) throws Exception {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(requestUrl);
+            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            post.setEntity(formEntity);
+            return client.execute(post);
+        } finally {
+            oauth.closeClient(client);
+        }
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index e1c3eb6..6abaf97 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -158,12 +158,12 @@ public class ExportImportTest extends AbstractExportImportTest {
         realm.components().add(component);
 
 
-        ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
-        String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
-        ExportImportConfig.setFile(targetFilePath);
+        testingClient.testing().setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
 
-        ExportImportConfig.setAction(ExportImportConfig.ACTION_EXPORT);
-        ExportImportConfig.setRealmName("component-realm");
+        String targetFilePath = testingClient.testing().getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
+        testingClient.testing().setFile(targetFilePath);
+        testingClient.testing().setAction(ExportImportConfig.ACTION_EXPORT);
+        testingClient.testing().setRealmName("component-realm");
 
         testingClient.testing().runExport();
 
@@ -173,7 +173,7 @@ public class ExportImportTest extends AbstractExportImportTest {
         Assert.assertEquals(3, adminClient.realms().findAll().size());
 
         // Configure import
-        ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
+        testingClient.testing().setAction(ExportImportConfig.ACTION_IMPORT);
 
         testingClient.testing().runImport();
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
index 95b327f..19aee3f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
@@ -17,14 +17,18 @@
 
 package org.keycloak.testsuite.exportimport;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
+
 import org.junit.Assert;
 import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.ClientTemplateResource;
 import org.keycloak.admin.client.resource.RealmResource;
@@ -50,6 +54,10 @@ import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserFederationMapperRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.client.KeycloakTestingClient;
 import org.keycloak.testsuite.util.RealmRepUtil;
@@ -86,7 +94,7 @@ public class ExportImportUtil {
         Assert.assertEquals(0, userRsc.getFederatedIdentity().size());
 
         List<ClientRepresentation> resources = realmRsc.clients().findAll();
-        Assert.assertEquals(8, resources.size());
+        Assert.assertEquals(9, resources.size());
 
         // Test applications imported
         ClientRepresentation application = ApiUtil.findClientByClientId(realmRsc, "Application").toRepresentation();
@@ -97,7 +105,7 @@ public class ExportImportUtil {
         Assert.assertNotNull(otherApp);
         Assert.assertNull(nonExisting);
         List<ClientRepresentation> clients = realmRsc.clients().findAll();
-        Assert.assertEquals(8, clients.size());
+        Assert.assertEquals(9, clients.size());
         Assert.assertTrue(hasClient(clients, application));
         Assert.assertTrue(hasClient(clients, otherApp));
         Assert.assertTrue(hasClient(clients, accountApp));
@@ -366,6 +374,8 @@ public class ExportImportUtil {
         UserRepresentation linked = testingClient.testing().getUserByServiceAccountClient(realm.getRealm(), otherApp.getClientId());//session.users().getUserByServiceAccountClient(otherApp);
         Assert.assertNotNull(linked);
         Assert.assertEquals("my-service-user", linked.getUsername());
+
+        assertAuthorizationSettings(realmRsc);
     }
 
     private static boolean isProtocolMapperGranted(Map<String, Object> consent, ProtocolMapperRepresentation mapperRep) {
@@ -544,4 +554,89 @@ public class ExportImportUtil {
         return false;
     }
 
+    private static void assertAuthorizationSettings(RealmResource realmRsc) {
+        AuthorizationResource authzResource = ApiUtil.findAuthorizationSettings(realmRsc, "test-app-authz");
+
+        Assert.assertNotNull(authzResource);
+
+        List<ResourceRepresentation> resources = authzResource.resources().resources();
+        Assert.assertEquals(4, resources.size());
+        ResourceServerRepresentation authzSettings = authzResource.getSettings();
+        List<Predicate<ResourceRepresentation>> resourcePredicates = new ArrayList<>();
+        resourcePredicates.add(resourceRep -> {
+            if ("Admin Resource".equals(resourceRep.getName())) {
+                Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId());
+                Assert.assertEquals("/protected/admin/*", resourceRep.getUri());
+                Assert.assertEquals("http://test-app-authz/protected/admin", resourceRep.getType());
+                Assert.assertEquals("http://icons.com/icon-admin", resourceRep.getIconUri());
+                Assert.assertEquals(1, resourceRep.getScopes().size());
+                return true;
+            }
+            return false;
+        });
+        resourcePredicates.add(resourceRep -> {
+            if ("Protected Resource".equals(resourceRep.getName())) {
+                Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId());
+                Assert.assertEquals("/*", resourceRep.getUri());
+                Assert.assertEquals("http://test-app-authz/protected/resource", resourceRep.getType());
+                Assert.assertEquals("http://icons.com/icon-resource", resourceRep.getIconUri());
+                Assert.assertEquals(1, resourceRep.getScopes().size());
+                return true;
+            }
+            return false;
+        });
+        resourcePredicates.add(resourceRep -> {
+            if ("Premium Resource".equals(resourceRep.getName())) {
+                Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId());
+                Assert.assertEquals("/protected/premium/*", resourceRep.getUri());
+                Assert.assertEquals("urn:test-app-authz:protected:resource", resourceRep.getType());
+                Assert.assertEquals("http://icons.com/icon-premium", resourceRep.getIconUri());
+                Assert.assertEquals(1, resourceRep.getScopes().size());
+                return true;
+            }
+            return false;
+        });
+        resourcePredicates.add(resourceRep -> {
+            if ("Main Page".equals(resourceRep.getName())) {
+                Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId());
+                Assert.assertNull(resourceRep.getUri());
+                Assert.assertEquals("urn:test-app-authz:protected:resource", resourceRep.getType());
+                Assert.assertEquals("http://icons.com/icon-main-page", resourceRep.getIconUri());
+                Assert.assertEquals(3, resourceRep.getScopes().size());
+                return true;
+            }
+            return false;
+        });
+        assertPredicate(resources, resourcePredicates);
+
+        List<ScopeRepresentation> scopes = authzResource.scopes().scopes();
+        Assert.assertEquals(6, scopes.size());
+        List<Predicate<ScopeRepresentation>> scopePredicates = new ArrayList<>();
+        scopePredicates.add(scopeRepresentation -> "admin-access".equals(scopeRepresentation.getName()));
+        scopePredicates.add(scopeRepresentation -> "resource-access".equals(scopeRepresentation.getName()));
+        scopePredicates.add(scopeRepresentation -> "premium-access".equals(scopeRepresentation.getName()));
+        scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForAdmin".equals(scopeRepresentation.getName()));
+        scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForUser".equals(scopeRepresentation.getName()));
+        scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForPremiumUser".equals(scopeRepresentation.getName()));
+        assertPredicate(scopes, scopePredicates);
+
+        List<PolicyRepresentation> policies = authzResource.policies().policies();
+        Assert.assertEquals(10, policies.size());
+        List<Predicate<PolicyRepresentation>> policyPredicates = new ArrayList<>();
+        policyPredicates.add(policyRepresentation -> "Any Admin Policy".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Any User Policy".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Only Premium User Policy".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "All Users Policy".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Premium Resource Permission".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Administrative Resource Permission".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Protected Resource Permission".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Action 1 on Main Page Resource Permission".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Action 2 on Main Page Resource Permission".equals(policyRepresentation.getName()));
+        policyPredicates.add(policyRepresentation -> "Action 3 on Main Page Resource Permission".equals(policyRepresentation.getName()));
+        assertPredicate(policies, policyPredicates);
+    }
+
+    private static <D> void assertPredicate(List<D> source, List<Predicate<D>> predicate) {
+        Assert.assertTrue(!source.stream().filter(object -> !predicate.stream().filter(predicate1 -> predicate1.test(object)).findFirst().isPresent()).findAny().isPresent());
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
index 900767f..ec800ed 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
@@ -21,12 +21,15 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.oidc.TokenMetadataRepresentation;
+import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.TestRealmKeycloakTest;
 import org.keycloak.testsuite.util.KeycloakModelUtils;
@@ -112,7 +115,9 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
         AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
         String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "bad_credential", accessTokenResponse.getAccessToken());
 
-        assertEquals("{\"error_description\":\"Authentication failed.\",\"error\":\"invalid_request\"}", tokenResponse);
+        OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(tokenResponse, OAuth2ErrorRepresentation.class);
+        Assert.assertEquals("Authentication failed.", errorRep.getErrorDescription());
+        Assert.assertEquals(OAuthErrorException.INVALID_REQUEST, errorRep.getError());
     }
 
     @Test
@@ -157,7 +162,9 @@ public class TokenIntrospectionTest extends TestRealmKeycloakTest {
         AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
         String tokenResponse = oauth.introspectAccessTokenWithClientCredential("public-cli", "it_doesnt_matter", accessTokenResponse.getAccessToken());
 
-        assertEquals("{\"error_description\":\"Client not allowed.\",\"error\":\"invalid_request\"}", tokenResponse);
+        OAuth2ErrorRepresentation errorRep = JsonSerialization.readValue(tokenResponse, OAuth2ErrorRepresentation.class);
+        Assert.assertEquals("Client not allowed.", errorRep.getErrorDescription());
+        Assert.assertEquals(OAuthErrorException.INVALID_REQUEST, errorRep.getError());
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
index dddc621..eddd171 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
@@ -317,6 +317,15 @@ public class AdminEventPaths {
         return uri.toString();
     }
 
+    // CLIENT REGISTRATION TRUSTED HOSTS
+
+    public static String clientRegistrationTrustedHostPath(String hostName) {
+        URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientRegistrationTrustedHost")
+                .path(ClientInitialAccessResource.class, "delete")
+                .build(hostName);
+        return uri.toString();
+    }
+
     // GROUPS
 
     public static String groupsPath() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem
new file mode 100644
index 0000000..906ff55
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/client-auth-test/publickey.pem
@@ -0,0 +1 @@
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/jwks.json b/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/jwks.json
new file mode 100644
index 0000000..25ba0dd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/clientreg-test/jwks.json
@@ -0,0 +1,32 @@
+{"keys": [
+  {
+    "use": "enc",
+    "n": "tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q",
+    "e": "AQAB",
+    "kty": "RSA",
+    "kid": "a0"
+  },
+  {
+    "use":"sig",
+    "n":"q1awrk7QK24Gmcy9Yb4dMbS-ZnO6NDaj1Z2F5C74HMIgtwYyxsNbRhBqCWlw7kmkZZaG5udyQYY8d91Db_uc_1DBuJMrQVsYXjVSpy-hoKpTWmzGhXzyzwhfJAICp7Iu_TTKPp-ip0mPGHlJnnP6dr4ztjY7EgFXFhEDFYSd9S8",
+    "e":"AQAB",
+    "kty":"RSA",
+    "kid":"FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE"
+  },
+  {
+    "use": "sig",
+    "crv": "P-256",
+    "kty": "EC",
+    "y": "HtxLgYFXpJSomE8cN7qCEHXvKuLGZMWbK1FiJLCRCW8",
+    "x": "PMtxvxd-owwLzE_cUlA4_nT_bWcdcfnlhFF3wh8Gl5o",
+    "kid": "a2"
+  },
+  {
+    "use": "enc",
+    "crv": "P-256",
+    "kty": "EC",
+    "y": "xJd7r3N8WSjTW7ebZySfYzJtWYHeWjF34u3-BxoPfs4",
+    "x": "KIWYBJU45adk20B99K_93qvVGaqumQKGauW_RTQPazY",
+    "kid": "a3"
+  }
+]}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
index 23be719..6cd3ef6 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
@@ -204,6 +204,168 @@
                     }
                 }
             ]
+        },
+        {
+            "clientId": "test-app-authz",
+            "enabled": true,
+            "baseUrl": "/test-app-authz",
+            "adminUrl": "/test-app-authz",
+            "bearerOnly": false,
+            "authorizationSettings": {
+                "allowRemoteResourceManagement": true,
+                "policyEnforcementMode": "ENFORCING",
+                "resources": [
+                    {
+                        "name": "Admin Resource",
+                        "uri": "/protected/admin/*",
+                        "type": "http://test-app-authz/protected/admin",
+                        "icon_uri" : "http://icons.com/icon-admin",
+                        "scopes": [
+                            {
+                                "name": "admin-access"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "Protected Resource",
+                        "uri": "/*",
+                        "type": "http://test-app-authz/protected/resource",
+                        "icon_uri" : "http://icons.com/icon-resource",
+                        "scopes": [
+                            {
+                                "name": "resource-access"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "Premium Resource",
+                        "uri": "/protected/premium/*",
+                        "type": "urn:test-app-authz:protected:resource",
+                        "icon_uri" : "http://icons.com/icon-premium",
+                        "scopes": [
+                            {
+                                "name": "premium-access"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "Main Page",
+                        "type": "urn:test-app-authz:protected:resource",
+                        "icon_uri" : "http://icons.com/icon-main-page",
+                        "scopes": [
+                            {
+                                "name": "urn:test-app-authz:page:main:actionForAdmin"
+                            },
+                            {
+                                "name": "urn:test-app-authz:page:main:actionForUser"
+                            },
+                            {
+                                "name": "urn:test-app-authz:page:main:actionForPremiumUser"
+                            }
+                        ]
+                    }
+                ],
+                "policies": [
+                    {
+                        "name": "Any Admin Policy",
+                        "description": "Defines that adminsitrators can do something",
+                        "type": "role",
+                        "config": {
+                            "roles": "[{\"id\":\"admin\"}]"
+                        }
+                    },
+                    {
+                        "name": "Any User Policy",
+                        "description": "Defines that any user can do something",
+                        "type": "role",
+                        "config": {
+                            "roles": "[{\"id\":\"user\"}]"
+                        }
+                    },
+                    {
+                        "name": "Only Premium User Policy",
+                        "description": "Defines that only premium users can do something",
+                        "type": "role",
+                        "logic": "POSITIVE",
+                        "config": {
+                            "roles": "[{\"id\":\"customer-user-premium\"}]"
+                        }
+                    },
+                    {
+                        "name": "All Users Policy",
+                        "description": "Defines that all users can do something",
+                        "type": "aggregate",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Premium Resource Permission",
+                        "description": "A policy that defines access to premium resources",
+                        "type": "resource",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Premium Resource\"]",
+                            "applyPolicies": "[\"Only Premium User Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Administrative Resource Permission",
+                        "description": "A policy that defines access to administrative resources",
+                        "type": "resource",
+                        "decisionStrategy": "UNANIMOUS",
+                        "config": {
+                            "resources": "[\"Admin Resource\"]",
+                            "applyPolicies": "[\"Any Admin Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Protected Resource Permission",
+                        "description": "A policy that defines access to any protected resource",
+                        "type": "resource",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "resources": "[\"Protected Resource\"]",
+                            "applyPolicies": "[\"All Users Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Action 1 on Main Page Resource Permission",
+                        "description": "A policy that defines access to action 1 on the main page",
+                        "type": "scope",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "scopes": "[\"urn:test-app-authz:page:main:actionForAdmin\"]",
+                            "applyPolicies": "[\"Any Admin Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Action 2 on Main Page Resource Permission",
+                        "description": "A policy that defines access to action 2 on the main page",
+                        "type": "scope",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "scopes": "[\"urn:test-app-authz:page:main:actionForUser\"]",
+                            "applyPolicies": "[\"Any User Policy\"]"
+                        }
+                    },
+                    {
+                        "name": "Action 3 on Main Page Resource Permission",
+                        "description": "A policy that defines access to action 3 on the main page",
+                        "type": "scope",
+                        "decisionStrategy": "AFFIRMATIVE",
+                        "config": {
+                            "scopes": "[\"urn:test-app-authz:page:main:actionForPremiumUser\"]",
+                            "applyPolicies": "[\"Only Premium User Policy\"]"
+                        }
+                    }
+                ]
+            },
+            "redirectUris": [
+                "/test-app-authz/*"
+            ],
+            "secret": "secret"
         }
     ],
     "oauthClients" : [
@@ -239,6 +401,13 @@
         "realm" : [
             {
                 "name": "admin"
+            },
+            {
+                "name": "user"
+            },
+            {
+                "name": "customer-user-premium",
+                "description": "Have User Premium privileges"
             }
         ],
         "application" : {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
index 11e25d3..c0b2b6c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
@@ -117,6 +117,164 @@
         "http://localhost:8180/auth/realms/master/app/*"
       ],
       "secret": "password"
+    },
+    {
+      "clientId": "test-app-authz",
+      "enabled": true,
+      "baseUrl": "/test-app-authz",
+      "adminUrl": "/test-app-authz",
+      "bearerOnly": false,
+      "authorizationSettings": {
+        "allowRemoteResourceManagement": true,
+        "policyEnforcementMode": "ENFORCING",
+        "resources": [
+          {
+            "name": "Admin Resource",
+            "uri": "/protected/admin/*",
+            "type": "http://test-app-authz/protected/admin",
+            "scopes": [
+              {
+                "name": "admin-access"
+              }
+            ]
+          },
+          {
+            "name": "Protected Resource",
+            "uri": "/*",
+            "type": "http://test-app-authz/protected/resource",
+            "scopes": [
+              {
+                "name": "resource-access"
+              }
+            ]
+          },
+          {
+            "name": "Premium Resource",
+            "uri": "/protected/premium/*",
+            "type": "urn:test-app-authz:protected:resource",
+            "scopes": [
+              {
+                "name": "premium-access"
+              }
+            ]
+          },
+          {
+            "name": "Main Page",
+            "type": "urn:test-app-authz:protected:resource",
+            "scopes": [
+              {
+                "name": "urn:test-app-authz:page:main:actionForAdmin"
+              },
+              {
+                "name": "urn:test-app-authz:page:main:actionForUser"
+              },
+              {
+                "name": "urn:test-app-authz:page:main:actionForPremiumUser"
+              }
+            ]
+          }
+        ],
+        "policies": [
+          {
+            "name": "Any Admin Policy",
+            "description": "Defines that adminsitrators can do something",
+            "type": "role",
+            "config": {
+              "roles": "[{\"id\":\"admin\"}]"
+            }
+          },
+          {
+            "name": "Any User Policy",
+            "description": "Defines that any user can do something",
+            "type": "role",
+            "config": {
+              "roles": "[{\"id\":\"user\"}]"
+            }
+          },
+          {
+            "name": "Only Premium User Policy",
+            "description": "Defines that only premium users can do something",
+            "type": "role",
+            "logic": "POSITIVE",
+            "config": {
+              "roles": "[{\"id\":\"customer-user-premium\"}]"
+            }
+          },
+          {
+            "name": "All Users Policy",
+            "description": "Defines that all users can do something",
+            "type": "aggregate",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
+            }
+          },
+          {
+            "name": "Premium Resource Permission",
+            "description": "A policy that defines access to premium resources",
+            "type": "resource",
+            "decisionStrategy": "UNANIMOUS",
+            "config": {
+              "resources": "[\"Premium Resource\"]",
+              "applyPolicies": "[\"Only Premium User Policy\"]"
+            }
+          },
+          {
+            "name": "Administrative Resource Permission",
+            "description": "A policy that defines access to administrative resources",
+            "type": "resource",
+            "decisionStrategy": "UNANIMOUS",
+            "config": {
+              "resources": "[\"Admin Resource\"]",
+              "applyPolicies": "[\"Any Admin Policy\"]"
+            }
+          },
+          {
+            "name": "Protected Resource Permission",
+            "description": "A policy that defines access to any protected resource",
+            "type": "resource",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "resources": "[\"Protected Resource\"]",
+              "applyPolicies": "[\"All Users Policy\"]"
+            }
+          },
+          {
+            "name": "Action 1 on Main Page Resource Permission",
+            "description": "A policy that defines access to action 1 on the main page",
+            "type": "scope",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "scopes": "[\"urn:test-app-authz:page:main:actionForAdmin\"]",
+              "applyPolicies": "[\"Any Admin Policy\"]"
+            }
+          },
+          {
+            "name": "Action 2 on Main Page Resource Permission",
+            "description": "A policy that defines access to action 2 on the main page",
+            "type": "scope",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "scopes": "[\"urn:test-app-authz:page:main:actionForUser\"]",
+              "applyPolicies": "[\"Any User Policy\"]"
+            }
+          },
+          {
+            "name": "Action 3 on Main Page Resource Permission",
+            "description": "A policy that defines access to action 3 on the main page",
+            "type": "scope",
+            "decisionStrategy": "AFFIRMATIVE",
+            "config": {
+              "scopes": "[\"urn:test-app-authz:page:main:actionForPremiumUser\"]",
+              "applyPolicies": "[\"Only Premium User Policy\"]"
+            }
+          }
+        ]
+      },
+      "redirectUris": [
+        "/test-app-authz/*"
+      ],
+      "secret": "secret"
     }
   ],
   "roles" : {
@@ -128,6 +286,10 @@
       {
         "name": "admin",
         "description": "Have Administrator privileges"
+      },
+      {
+        "name": "customer-user-premium",
+        "description": "Have User Premium privileges"
       }
     ],
     "client" : {
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 930cc36..02c7a70 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -275,6 +275,7 @@ service-account-roles.tooltip=Allows you to authenticate role mappings for the s
 client-authenticator=Client Authenticator
 client-authenticator.tooltip=Client Authenticator used for authentication this client against Keycloak server
 certificate.tooltip=Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.
+publicKey.tooltip=Public Key for validate JWT issued by client and signed by Client private key.
 no-client-certificate-configured=No client certificate configured
 gen-new-keys-and-cert=Generate new keys and certificate
 import-certificate=Import Certificate
@@ -511,12 +512,25 @@ remainingCount=Remaining Count
 created=Created
 back=Back
 initial-access-tokens=Initial Access Tokens
+initial-access-tokens.tooltip=Initial Access Tokens for dynamic registrations of clients. Request with those tokens can be sent from any host.
 add-initial-access-tokens=Add Initial Access Token
 initial-access-token=Initial Access Token
 initial-access.copyPaste.tooltip=Copy/paste the initial access token before navigating away from this page as it's not posible to retrieve later
 continue=Continue
 initial-access-token.confirm.title=Copy Initial Access Token
 initial-access-token.confirm.text=Please copy and paste the initial access token before confirming as it can't be retrieved later
+no-initial-access-available=No Initial Access Tokens available
+
+trusted-hosts-legend=Trusted Hosts For Client Registrations
+trusted-hosts-legend.tooltip=Hosts, which are trusted for client registrations. Client registration requests from those hosts can be sent even without initial access token. The amount of client registrations from particular host can be limited to specified count.
+no-client-trusted-hosts-available=No Trusted Hosts available
+add-client-reg-trusted-host=Add Trusted Host
+hostname=Hostname
+client-reg-hostname.tooltip=Fully-Qualified Hostname or IP Address. Client registration requests from this host/address will be trusted and allowed to register new client.
+client-reg-count.tooltip=Allowed count of client registration requests from particular host. You need to restart this once the limit is reached.
+client-reg-remainingCount.tooltip=Remaining count of client registration requests from this host. You need to restart this once the limit is reached.
+reset-remaining-count=Reset Remaining Count
+
 
 client-templates=Client Templates
 client-templates.tooltip=Client templates allow you to define common configuration that is shared between multiple clients
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index d85c019..c3b25ec 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -208,6 +208,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 clientInitialAccess : function(ClientInitialAccessLoader) {
                     return ClientInitialAccessLoader();
+                },
+                clientRegTrustedHosts : function(ClientRegistrationTrustedHostListLoader) {
+                    return ClientRegistrationTrustedHostListLoader();
                 }
             },
             controller : 'ClientInitialAccessCtrl'
@@ -221,6 +224,30 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'ClientInitialAccessCreateCtrl'
         })
+        .when('/realms/:realm/client-reg-trusted-hosts/create', {
+            templateUrl : resourceUrl + '/partials/client-reg-trusted-host-create.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                clientRegTrustedHost : function() {
+                    return {};
+                }
+            },
+            controller : 'ClientRegistrationTrustedHostDetailCtrl'
+        })
+        .when('/realms/:realm/client-reg-trusted-hosts/:hostname', {
+            templateUrl : resourceUrl + '/partials/client-reg-trusted-host-detail.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                clientRegTrustedHost : function(ClientRegistrationTrustedHostLoader) {
+                    return ClientRegistrationTrustedHostLoader();
+                }
+            },
+            controller : 'ClientRegistrationTrustedHostDetailCtrl'
+        })
         .when('/realms/:realm/keys-settings', {
             templateUrl : resourceUrl + '/partials/realm-keys.html',
             resolve : {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index ac88ae6..d1041c7 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -364,8 +364,12 @@ module.controller('ClientCertificateImportCtrl', function($scope, $location, $ht
         "Certificate PEM"
     ];
 
+    if (callingContext == 'jwt-credentials') {
+        $scope.keyFormats.push('JSON Web Key Set (JWK)');
+    }
+
     $scope.hideKeystoreSettings = function() {
-        return $scope.uploadKeyFormat == 'Certificate PEM';
+        return $scope.uploadKeyFormat == 'Certificate PEM' || $scope.uploadKeyFormat == 'JSON Web Key Set (JWK)';
     }
 
     $scope.uploadKeyFormat = $scope.keyFormats[0];
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 317b376..f42ce55 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -2149,9 +2149,23 @@ module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, flow
 
 });
 
-module.controller('ClientInitialAccessCtrl', function($scope, realm, clientInitialAccess, ClientInitialAccess, Dialog, Notifications, $route) {
+module.controller('ClientInitialAccessCtrl', function($scope, realm, clientInitialAccess, clientRegTrustedHosts, ClientInitialAccess, ClientRegistrationTrustedHost, Dialog, Notifications, $route, $location) {
     $scope.realm = realm;
     $scope.clientInitialAccess = clientInitialAccess;
+    $scope.clientRegTrustedHosts = clientRegTrustedHosts;
+
+    $scope.updateHost = function(hostname) {
+        $location.url('/realms/' + realm.realm + '/client-reg-trusted-hosts/' + hostname);
+    };
+
+    $scope.removeHost = function(hostname) {
+        Dialog.confirmDelete(hostname, 'trusted host for client registration', function() {
+            ClientRegistrationTrustedHost.remove({ realm: realm.realm, hostname: hostname }, function() {
+                Notifications.success("The trusted host for client registration was deleted.");
+                $route.reload();
+            });
+        });
+    };
 
     $scope.remove = function(id) {
         Dialog.confirmDelete(id, 'initial access token', function() {
@@ -2163,6 +2177,57 @@ module.controller('ClientInitialAccessCtrl', function($scope, realm, clientIniti
     }
 });
 
+module.controller('ClientRegistrationTrustedHostDetailCtrl', function($scope, realm, clientRegTrustedHost, ClientRegistrationTrustedHost, Dialog, Notifications, $route, $location) {
+    $scope.realm = realm;
+
+    $scope.create = !clientRegTrustedHost.hostName;
+    $scope.changed = false;
+
+    if ($scope.create) {
+        $scope.count = 5;
+    } else {
+        $scope.hostName = clientRegTrustedHost.hostName;
+        $scope.count = clientRegTrustedHost.count;
+        $scope.remainingCount = clientRegTrustedHost.remainingCount;
+    }
+
+    $scope.save = function() {
+        if ($scope.create) {
+            ClientRegistrationTrustedHost.save({
+                realm: realm.realm
+            }, { hostName: $scope.hostName, count: $scope.count, remainingCount: $scope.count }, function (data) {
+                Notifications.success("The trusted host was created.");
+                $location.url('/realms/' + realm.realm + '/client-reg-trusted-hosts/' + $scope.hostName);
+            });
+        } else {
+            ClientRegistrationTrustedHost.update({
+                realm: realm.realm, hostname: $scope.hostName
+            }, { hostName: $scope.hostName, count: $scope.count, remainingCount: $scope.count }, function (data) {
+                Notifications.success("The trusted host was updated.");
+                $route.reload();
+            });
+        }
+    };
+
+    $scope.cancel = function() {
+        $location.url('/realms/' + realm.realm + '/client-initial-access');
+    };
+
+    $scope.resetRemainingCount = function() {
+        $scope.save();
+    }
+
+    $scope.$watch('count', function(newVal, oldVal) {
+        if (oldVal == newVal) {
+            return;
+        }
+
+        $scope.changed = true;
+    });
+
+});
+
+
 module.controller('ClientInitialAccessCreateCtrl', function($scope, realm, ClientInitialAccess, TimeUnit, Dialog, $location, $translate) {
     $scope.expirationUnit = 'Days';
     $scope.expiration = TimeUnit.toUnit(0, $scope.expirationUnit);
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index 33fb7ac..4b76863 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -510,6 +510,23 @@ module.factory('ClientInitialAccessLoader', function(Loader, ClientInitialAccess
     });
 });
 
+module.factory('ClientRegistrationTrustedHostListLoader', function(Loader, ClientRegistrationTrustedHost, $route) {
+    return Loader.query(ClientRegistrationTrustedHost, function() {
+        return {
+            realm: $route.current.params.realm
+        }
+    });
+});
+
+module.factory('ClientRegistrationTrustedHostLoader', function(Loader, ClientRegistrationTrustedHost, $route) {
+    return Loader.get(ClientRegistrationTrustedHost, function() {
+        return {
+            realm: $route.current.params.realm,
+            hostname : $route.current.params.hostname
+        }
+    });
+});
+
 
 
 
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js
index 7e4df38..6d8fdcd 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -298,6 +298,18 @@ module.factory('ClientInitialAccess', function($resource) {
     });
 });
 
+module.factory('ClientRegistrationTrustedHost', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/clients-trusted-hosts/:hostname', {
+        realm : '@realm',
+        hostname : '@hostname'
+    }, {
+         update : {
+             method : 'PUT'
+         }
+       }
+    );
+});
+
 
 module.factory('ClientProtocolMapper', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/clients/:client/protocol-mappers/models/:id', {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
index 8c581d7..0cb1999 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
@@ -1,13 +1,26 @@
 <div>
     <form class="form-horizontal no-margin-top" name="keyForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSignedJWTCtrl">
         <div class="form-group">
-            <label class="col-md-2 control-label" for="signingCert">{{:: 'certificate' | translate}}</label>
-            <kc-tooltip>{{:: 'certificate.tooltip' | translate}}</kc-tooltip>
 
-            <div class="col-sm-10" data-ng-show="signingKeyInfo.certificate">
-                <textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5" kc-select-action="click" readonly>{{signingKeyInfo.certificate}}</textarea>
+            <div data-ng-show="signingKeyInfo.certificate">
+                <label class="col-md-2 control-label" for="signingCert">{{:: 'certificate' | translate}}</label>
+                <kc-tooltip>{{:: 'certificate.tooltip' | translate}}</kc-tooltip>
+
+                <div class="col-sm-10" data-ng-show="signingKeyInfo.certificate">
+                    <textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5" kc-select-action="click" readonly>{{signingKeyInfo.certificate}}</textarea>
+                </div>
             </div>
-            <div class="col-sm-10" data-ng-hide="signingKeyInfo.certificate">
+
+            <div data-ng-show="signingKeyInfo.publicKey">
+                <label class="col-md-2 control-label" for="publicKey">{{:: 'publicKey' | translate}}</label>
+                <kc-tooltip>{{:: 'publicKey.tooltip' | translate}}</kc-tooltip>
+
+                <div class="col-sm-10" data-ng-show="signingKeyInfo.publicKey">
+                    <textarea type="text" id="publicKey" name="publicKey" class="form-control" rows="5" kc-select-action="click" readonly>{{signingKeyInfo.publicKey}}</textarea>
+                </div>
+            </div>
+
+            <div class="col-sm-10" data-ng-hide="signingKeyInfo.certificate || signingKeyInfo.publicKey">
                 {{:: 'no-client-certificate-configured' | translate}}
             </div>
         </div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
index 1e8d6a9..9968a31 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
@@ -26,14 +26,14 @@
                 </div>
                 <kc-tooltip>{{:: 'archive-format.tooltip' | translate}}</kc-tooltip>
             </div>
-            <div class="form-group">
+            <div class="form-group" data-ng-hide="hideKeystoreSettings()">
                 <label class="col-md-2 control-label" for="uploadKeyAlias">{{:: 'key-alias' | translate}}</label>
                 <div class="col-md-6">
                     <input class="form-control" type="text" id="uploadKeyAlias" name="uploadKeyAlias" data-ng-model="uploadKeyAlias" autofocus required>
                 </div>
                 <kc-tooltip>{{:: 'jwt-import.key-alias.tooltip' | translate}}</kc-tooltip>
             </div>
-            <div class="form-group">
+            <div class="form-group" data-ng-hide="hideKeystoreSettings()">
                 <label class="col-md-2 control-label" for="uploadStorePassword">{{:: 'store-password' | translate}}</label>
                 <div class="col-md-6">
                     <input class="form-control" type="password" id="uploadStorePassword" name="uploadStorePassword" data-ng-model="uploadStorePassword" autofocus required>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
index 11b3dcf..4bc9d68 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
@@ -1,6 +1,59 @@
 <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
     <kc-tabs-realm></kc-tabs-realm>
 
+    <div class="form-group">
+        <legend>{{:: 'trusted-hosts-legend' | translate}}</legend>
+        <kc-tooltip>{{:: 'trusted-hosts-legend.tooltip' | translate}}</kc-tooltip>
+    </div>
+
+    <table class="table table-striped table-bordered">
+        <thead>
+        <tr>
+            <th class="kc-table-actions" colspan="6">
+                <div class="form-inline">
+
+                    <div class="form-group">
+                        <div class="input-group">
+                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search1.host" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                            <div class="input-group-addon">
+                                <i class="fa fa-search" type="submit"></i>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="pull-right" data-ng-show="access.manageClients">
+                        <a id="createClientTrustedHost" class="btn btn-default" href="#/realms/{{realm.realm}}/client-reg-trusted-hosts/create">{{:: 'create' | translate}}</a>
+                    </div>
+                </div>
+            </th>
+        </tr>
+        <tr data-ng-hide="clientRegTrustedHosts.length == 0">
+            <th>{{:: 'hostname' | translate}}</th>
+            <th>{{:: 'count' | translate}}</th>
+            <th>{{:: 'remainingCount' | translate}}</th>
+            <th colspan="2">{{:: 'actions' | translate}}</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr ng-repeat="host in clientRegTrustedHosts | filter:search1 | orderBy:'timestamp'">
+            <td>{{host.hostName}}</td>
+            <td>{{host.count}}</td>
+            <td>{{host.remainingCount}}</td>
+            <td class="kc-action-cell" data-ng-click="updateHost(host.hostName)">{{:: 'update' | translate}}</td>
+            <td class="kc-action-cell" data-ng-click="removeHost(host.hostName)">{{:: 'delete' | translate}}</td>
+        </tr>
+        <tr data-ng-show="(clientRegTrustedHosts | filter:search1).length == 0">
+            <td class="text-muted" colspan="3" data-ng-show="search1.host">{{:: 'no-results' | translate}}</td>
+            <td class="text-muted" colspan="3" data-ng-hide="search1.host">{{:: 'no-client-trusted-hosts-available' | translate}}</td>
+        </tr>
+        </tbody>
+    </table>
+
+
+    <div class="form-group">
+        <legend>{{:: 'initial-access-tokens' | translate}}</legend>
+        <kc-tooltip>{{:: 'initial-access-tokens.tooltip' | translate}}</kc-tooltip>
+    </div>
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
@@ -8,7 +61,7 @@
                 <div class="form-inline">
                     <div class="form-group">
                         <div class="input-group">
-                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.id" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search2.id" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
                             <div class="input-group-addon">
                                 <i class="fa fa-search" type="submit"></i>
                             </div>
@@ -21,7 +74,7 @@
                 </div>
             </th>
         </tr>
-        <tr data-ng-hide="clients.length == 0">
+        <tr data-ng-hide="clientInitialAccess.length == 0">
             <th>{{:: 'id' | translate}}</th>
             <th>{{:: 'created' | translate}}</th>
             <th>{{:: 'expires' | translate}}</th>
@@ -31,7 +84,7 @@
         </tr>
         </thead>
         <tbody>
-        <tr ng-repeat="ia in clientInitialAccess | filter:search | orderBy:'timestamp'">
+        <tr ng-repeat="ia in clientInitialAccess | filter:search2 | orderBy:'timestamp'">
             <td>{{ia.id}}</td>
             <td>{{(ia.timestamp * 1000)|date:'shortDate'}}&nbsp;{{(ia.timestamp * 1000)|date:'mediumTime'}}</td>
             <td><span data-ng-show="ia.expiration > 0">{{((ia.timestamp + ia.expiration) * 1000)|date:'shortDate'}}&nbsp;{{((ia.timestamp + ia.expiration) * 1000)|date:'mediumTime'}}</span></td>
@@ -39,9 +92,9 @@
             <td>{{ia.remainingCount}}</td>
             <td class="kc-action-cell" data-ng-click="remove(ia.id)">{{:: 'delete' | translate}}</td>
         </tr>
-        <tr data-ng-show="(clients | filter:search).length == 0">
-            <td class="text-muted" colspan="3" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
-            <td class="text-muted" colspan="3" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
+        <tr data-ng-show="(clientInitialAccess | filter:search2).length == 0">
+            <td class="text-muted" colspan="3" data-ng-show="search2.id">{{:: 'no-results' | translate}}</td>
+            <td class="text-muted" colspan="3" data-ng-hide="search2.id">{{:: 'no-initial-access-available' | translate}}</td>
         </tr>
         </tbody>
     </table>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-create.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-create.html
new file mode 100644
index 0000000..cb07613
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-create.html
@@ -0,0 +1,55 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/client-initial-access">{{:: 'initial-access-tokens' | translate}}</a></li>
+        <li>{{:: 'add-client-reg-trusted-host' | translate}}</li>
+    </ol>
+
+    <h1>{{:: 'add-client-reg-trusted-host' | translate}}</h1>
+
+    <form class="form-horizontal" name="createForm" novalidate kc-read-only="!access.manageRealm">
+
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="hostName">{{:: 'hostname' | translate}} </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="hostName" name="hostName" data-ng-model="hostName">
+            </div>
+            <kc-tooltip>{{:: 'client-reg-hostname.tooltip' | translate}}</kc-tooltip>
+        </div>
+
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="count">{{:: 'count' | translate}} </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="count" name="count" required min="1" max="10000" data-ng-model="count">
+            </div>
+            <kc-tooltip>{{:: 'client-reg-count.tooltip' | translate}}</kc-tooltip>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <button kc-save>{{:: 'save' | translate}}</button>
+                <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
+            </div>
+        </div>
+    </form>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-detail.html
new file mode 100644
index 0000000..5644fc1
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-reg-trusted-host-detail.html
@@ -0,0 +1,64 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/client-initial-access">{{:: 'initial-access-tokens' | translate}}</a></li>
+        <li>{{hostName}}</li>
+    </ol>
+
+    <h1>{{hostName}}</h1>
+
+    <form class="form-horizontal" name="createForm" novalidate kc-read-only="!access.manageRealm">
+
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="hostName">{{:: 'hostname' | translate}} </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="hostName" name="hostName" data-ng-model="hostName" readonly>
+            </div>
+            <kc-tooltip>{{:: 'client-reg-hostname.tooltip' | translate}}</kc-tooltip>
+        </div>
+
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="count">{{:: 'count' | translate}} </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="count" name="count" required min="1" max="10000" data-ng-model="count">
+            </div>
+            <kc-tooltip>{{:: 'client-reg-count.tooltip' | translate}}</kc-tooltip>
+        </div>
+
+        <div class="form-group">
+            <label class="col-md-2 control-label" for="remainingCount">{{:: 'remainingCount' | translate}} </label>
+            <div class="col-sm-6">
+                <input class="form-control" type="text" id="remainingCount" name="remainingCount" data-ng-model="remainingCount" readonly>
+            </div>
+            <kc-tooltip>{{:: 'client-reg-remainingCount.tooltip' | translate}}</kc-tooltip>
+        </div>
+
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+                <button class="btn btn-primary" data-ng-click="resetRemainingCount()" data-ng-hide="changed">{{:: 'reset-remaining-count' | translate}}</button>
+                <button data-ng-show="changed" kc-save>{{:: 'save' | translate}}</button>
+                <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
+            </div>
+        </div>
+    </form>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index e766bcb..96fb247 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -25,6 +25,7 @@
     || path[2] == 'theme-settings'
     || path[2] == 'token-settings'
     || path[2] == 'cache-settings'
+    || path[2] == 'client-initial-access'
     || path[2] == 'defense'
     || path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings' || path[2] == 'auth-settings') && path[3] != 'clients') && 'active'">
                 <a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> {{:: 'realm-settings' | translate}}</a>
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
index 20b54ea..f99cab3 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
@@ -63,6 +63,7 @@ public class KeycloakServerDeploymentProcessor implements DeploymentUnitProcesso
             st.addDependency(cacheContainerService.append("offlineSessions"));
             st.addDependency(cacheContainerService.append("loginFailures"));
             st.addDependency(cacheContainerService.append("work"));
+            st.addDependency(cacheContainerService.append("authorization"));;
         }
     }
 
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
index 35b4229..bbc1a83 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-infinispan.xml
@@ -95,6 +95,7 @@
                 <distributed-cache name="sessions" mode="SYNC" owners="1"/>
                 <distributed-cache name="offlineSessions" mode="SYNC" owners="1"/>
                 <distributed-cache name="loginFailures" mode="SYNC" owners="1"/>
+                <distributed-cache name="authorization" mode="SYNC" owners="1"/>
                 <replicated-cache name="work" mode="SYNC" />
             </cache-container>
             <cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">