keycloak-aplcache

KEYCLOAK-1749 Client registration service Changed endpoints

11/11/2015 11:31:39 AM

Changes

Details

diff --git a/client-api/src/main/java/org/keycloak/client/registration/Auth.java b/client-api/src/main/java/org/keycloak/client/registration/Auth.java
new file mode 100644
index 0000000..5b0e85f
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/Auth.java
@@ -0,0 +1,58 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpRequest;
+import org.keycloak.common.util.Base64;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class Auth {
+
+    public abstract void addAuth(HttpRequest request);
+
+    public static Auth token(String token) {
+        return new BearerTokenAuth(token);
+    }
+
+    public static Auth token(ClientRepresentation client) {
+        return new BearerTokenAuth(client.getRegistrationAccessToken());
+    }
+
+    public static Auth client(String clientId, String clientSecret) {
+        return new BasicAuth(clientId, clientSecret);
+    }
+
+    private static class BearerTokenAuth extends Auth {
+
+        private String token;
+
+        public BearerTokenAuth(String token) {
+            this.token = token;
+        }
+
+        @Override
+        public void addAuth(HttpRequest request) {
+            request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
+        }
+    }
+
+    private static class BasicAuth extends Auth {
+
+        private String username;
+        private String password;
+
+        public BasicAuth(String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+
+        @Override
+        public void addAuth(HttpRequest request) {
+            String val = Base64.encodeBytes((username + ":" + password).getBytes());
+            request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + val);
+        }
+    }
+
+}
diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
index 82a3b37..e59de76 100644
--- a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
+++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
@@ -1,18 +1,9 @@
 package org.keycloak.client.registration;
 
-import org.apache.http.HttpHeaders;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.common.util.Base64;
 import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
@@ -23,160 +14,58 @@ import java.io.InputStream;
  */
 public class ClientRegistration {
 
-    private String clientRegistrationUrl;
-    private HttpClient httpClient;
-    private Auth auth;
+    private final String DEFAULT = "default";
+    private final String INSTALLATION = "install";
 
-    public static ClientRegistrationBuilder create() {
-        return new ClientRegistrationBuilder();
-    }
+    private HttpUtil httpUtil;
 
-    private ClientRegistration() {
+    public ClientRegistration(String authServerUrl, String realm) {
+        httpUtil = new HttpUtil(HttpClients.createDefault(), HttpUtil.getUrl(authServerUrl, "realms", realm, "clients"));
     }
 
-    public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
-        String content = serialize(client);
-        InputStream resultStream = doPost(content);
-        return deserialize(resultStream, ClientRepresentation.class);
+    public ClientRegistration(String authServerUrl, String realm, HttpClient httpClient) {
+        httpUtil = new HttpUtil(httpClient, HttpUtil.getUrl(authServerUrl, "realms", realm, "clients"));
     }
 
-    public ClientRepresentation get() throws ClientRegistrationException {
-        if (auth instanceof ClientIdSecretAuth) {
-            String clientId = ((ClientIdSecretAuth) auth).clientId;
-            return get(clientId);
-        } else {
-            throw new ClientRegistrationException("Requires client authentication");
+    public void close() throws ClientRegistrationException {
+        if (httpUtil != null) {
+            httpUtil.close();
         }
+        httpUtil = null;
     }
 
-    public ClientRepresentation get(String clientId) throws ClientRegistrationException {
-        InputStream resultStream = doGet(clientId);
-        return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
+    public ClientRegistration auth(Auth auth) {
+        httpUtil.setAuth(auth);
+        return this;
     }
 
-    public void update(ClientRepresentation client) throws ClientRegistrationException {
+    public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
         String content = serialize(client);
-        doPut(content, client.getClientId());
-    }
-
-    public void delete() throws ClientRegistrationException {
-        if (auth instanceof ClientIdSecretAuth) {
-            String clientId = ((ClientIdSecretAuth) auth).clientId;
-            delete(clientId);
-        } else {
-            throw new ClientRegistrationException("Requires client authentication");
-        }
+        InputStream resultStream = httpUtil.doPost(content, DEFAULT);
+        return deserialize(resultStream, ClientRepresentation.class);
     }
 
-    public void delete(String clientId) throws ClientRegistrationException {
-        doDelete(clientId);
+    public ClientRepresentation get(String clientId) throws ClientRegistrationException {
+        InputStream resultStream = httpUtil.doGet(DEFAULT, clientId);
+        return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
     }
 
-    public void close() throws ClientRegistrationException {
-        if (httpClient instanceof CloseableHttpClient) {
-            try {
-                ((CloseableHttpClient) httpClient).close();
-            } catch (IOException e) {
-                throw new ClientRegistrationException("Failed to close http client", e);
-            }
-        }
+    public AdapterConfig getAdapterConfig(String clientId) throws ClientRegistrationException {
+        InputStream resultStream = httpUtil.doGet(INSTALLATION, clientId);
+        return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null;
     }
 
-    private InputStream doPost(String content) throws ClientRegistrationException {
-        try {
-            HttpPost request = new HttpPost(clientRegistrationUrl);
-
-            request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
-            request.setHeader(HttpHeaders.ACCEPT, "application/json");
-            request.setEntity(new StringEntity(content));
-
-            auth.addAuth(request);
-
-            HttpResponse response = httpClient.execute(request);
-            InputStream responseStream = null;
-            if (response.getEntity() != null) {
-                responseStream = response.getEntity().getContent();
-            }
-
-            if (response.getStatusLine().getStatusCode() == 201) {
-                return responseStream;
-            } else {
-                responseStream.close();
-                throw new HttpErrorException(response.getStatusLine());
-            }
-        } catch (IOException e) {
-            throw new ClientRegistrationException("Failed to send request", e);
-        }
-    }
-
-    private InputStream doGet(String endpoint) throws ClientRegistrationException {
-        try {
-            HttpGet request = new HttpGet(clientRegistrationUrl + "/" + endpoint);
-
-            request.setHeader(HttpHeaders.ACCEPT, "application/json");
-
-            auth.addAuth(request);
-
-            HttpResponse response = httpClient.execute(request);
-            InputStream responseStream = null;
-            if (response.getEntity() != null) {
-                responseStream = response.getEntity().getContent();
-            }
-
-            if (response.getStatusLine().getStatusCode() == 200) {
-                return responseStream;
-            } else if (response.getStatusLine().getStatusCode() == 404) {
-                responseStream.close();
-                return null;
-            } else {
-                responseStream.close();
-                throw new HttpErrorException(response.getStatusLine());
-            }
-        } catch (IOException e) {
-            throw new ClientRegistrationException("Failed to send request", e);
-        }
+    public void update(ClientRepresentation client) throws ClientRegistrationException {
+        String content = serialize(client);
+        httpUtil.doPut(content, DEFAULT, client.getClientId());
     }
 
-    private void doPut(String content, String endpoint) throws ClientRegistrationException {
-        try {
-            HttpPut request = new HttpPut(clientRegistrationUrl + "/" + endpoint);
-
-            request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
-            request.setHeader(HttpHeaders.ACCEPT, "application/json");
-            request.setEntity(new StringEntity(content));
-
-            auth.addAuth(request);
-
-            HttpResponse response = httpClient.execute(request);
-            if (response.getEntity() != null) {
-                response.getEntity().getContent().close();
-            }
-
-            if (response.getStatusLine().getStatusCode() != 200) {
-                throw new HttpErrorException(response.getStatusLine());
-            }
-        } catch (IOException e) {
-            throw new ClientRegistrationException("Failed to send request", e);
-        }
+    public void delete(ClientRepresentation client) throws ClientRegistrationException {
+        delete(client.getClientId());
     }
 
-    private void doDelete(String endpoint) throws ClientRegistrationException {
-        try {
-            HttpDelete request = new HttpDelete(clientRegistrationUrl + "/" + endpoint);
-
-            auth.addAuth(request);
-
-            HttpResponse response = httpClient.execute(request);
-            if (response.getEntity() != null) {
-                response.getEntity().getContent().close();
-            }
-
-            if (response.getStatusLine().getStatusCode() != 200) {
-                throw new HttpErrorException(response.getStatusLine());
-            }
-        } catch (IOException e) {
-            throw new ClientRegistrationException("Failed to send request", e);
-        }
+    public void delete(String clientId) throws ClientRegistrationException {
+        httpUtil.doDelete(DEFAULT, clientId);
     }
 
     private String serialize(ClientRepresentation client) throws ClientRegistrationException {
@@ -195,81 +84,4 @@ public class ClientRegistration {
         }
     }
 
-    public static class ClientRegistrationBuilder {
-
-        private String realm;
-
-        private String authServerUrl;
-
-        private Auth auth;
-
-        private HttpClient httpClient;
-
-        public ClientRegistrationBuilder realm(String realm) {
-            this.realm = realm;
-            return this;
-        }
-        public ClientRegistrationBuilder authServerUrl(String authServerUrl) {
-            this.authServerUrl = authServerUrl;
-            return this;
-        }
-
-        public ClientRegistrationBuilder auth(String token) {
-            this.auth = new TokenAuth(token);
-            return this;
-        }
-
-        public ClientRegistrationBuilder auth(String clientId, String clientSecret) {
-            this.auth = new ClientIdSecretAuth(clientId, clientSecret);
-            return this;
-        }
-
-        public ClientRegistrationBuilder httpClient(HttpClient httpClient) {
-            this.httpClient = httpClient;
-            return this;
-        }
-
-        public ClientRegistration build() {
-            ClientRegistration clientRegistration = new ClientRegistration();
-            clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration/default";
-
-            clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
-            clientRegistration.auth = auth;
-
-            return clientRegistration;
-        }
-
-    }
-
-    public interface Auth {
-        void addAuth(HttpRequest httpRequest);
-    }
-
-    public static class AuthorizationHeaderAuth implements Auth {
-        private String credentials;
-
-        public AuthorizationHeaderAuth(String credentials) {
-            this.credentials = credentials;
-        }
-
-        public void addAuth(HttpRequest httpRequest) {
-            httpRequest.setHeader(HttpHeaders.AUTHORIZATION, credentials);
-        }
-    }
-
-    public static class TokenAuth extends AuthorizationHeaderAuth {
-        public TokenAuth(String token) {
-            super("Bearer " + token);
-        }
-    }
-
-    public static class ClientIdSecretAuth extends AuthorizationHeaderAuth {
-        private String clientId;
-
-        public ClientIdSecretAuth(String clientId, String clientSecret) {
-            super("Basic " + Base64.encodeBytes((clientId + ":" + clientSecret).getBytes()));
-            this.clientId = clientId;
-        }
-    }
-
 }
diff --git a/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java
new file mode 100644
index 0000000..699d378
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/HttpUtil.java
@@ -0,0 +1,159 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+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 java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+class HttpUtil {
+
+    private HttpClient httpClient;
+
+    private String baseUri;
+
+    private Auth auth;
+
+    HttpUtil(HttpClient httpClient, String baseUri) {
+        this.httpClient = httpClient;
+        this.baseUri = baseUri;
+    }
+
+    void setAuth(Auth auth) {
+        this.auth = auth;
+    }
+
+    InputStream doPost(String content, String... path) throws ClientRegistrationException {
+        try {
+            HttpPost request = new HttpPost(getUrl(baseUri, path));
+
+            request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+            request.setHeader(HttpHeaders.ACCEPT, "application/json");
+            request.setEntity(new StringEntity(content));
+
+            addAuth(request);
+
+            HttpResponse response = httpClient.execute(request);
+            InputStream responseStream = null;
+            if (response.getEntity() != null) {
+                responseStream = response.getEntity().getContent();
+            }
+
+            if (response.getStatusLine().getStatusCode() == 201) {
+                return responseStream;
+            } else {
+                responseStream.close();
+                throw new HttpErrorException(response.getStatusLine());
+            }
+        } catch (IOException e) {
+            throw new ClientRegistrationException("Failed to send request", e);
+        }
+    }
+
+    InputStream doGet(String... path) throws ClientRegistrationException {
+        try {
+            HttpGet request = new HttpGet(getUrl(baseUri, path));
+
+            request.setHeader(HttpHeaders.ACCEPT, "application/json");
+
+            addAuth(request);
+
+            HttpResponse response = httpClient.execute(request);
+            InputStream responseStream = null;
+            if (response.getEntity() != null) {
+                responseStream = response.getEntity().getContent();
+            }
+
+            if (response.getStatusLine().getStatusCode() == 200) {
+                return responseStream;
+            } else if (response.getStatusLine().getStatusCode() == 404) {
+                responseStream.close();
+                return null;
+            } else {
+                responseStream.close();
+                throw new HttpErrorException(response.getStatusLine());
+            }
+        } catch (IOException e) {
+            throw new ClientRegistrationException("Failed to send request", e);
+        }
+    }
+
+    void doPut(String content, String... path) throws ClientRegistrationException {
+        try {
+            HttpPut request = new HttpPut(getUrl(baseUri, path));
+
+            request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+            request.setHeader(HttpHeaders.ACCEPT, "application/json");
+            request.setEntity(new StringEntity(content));
+
+            addAuth(request);
+
+            HttpResponse response = httpClient.execute(request);
+            if (response.getEntity() != null) {
+                response.getEntity().getContent().close();
+            }
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new HttpErrorException(response.getStatusLine());
+            }
+        } catch (IOException e) {
+            throw new ClientRegistrationException("Failed to send request", e);
+        }
+    }
+
+    void doDelete(String... path) throws ClientRegistrationException {
+        try {
+            HttpDelete request = new HttpDelete(getUrl(baseUri, path));
+
+            addAuth(request);
+
+            HttpResponse response = httpClient.execute(request);
+            if (response.getEntity() != null) {
+                response.getEntity().getContent().close();
+            }
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new HttpErrorException(response.getStatusLine());
+            }
+        } catch (IOException e) {
+            throw new ClientRegistrationException("Failed to send request", e);
+        }
+    }
+
+    void close() throws ClientRegistrationException {
+        if (httpClient instanceof CloseableHttpClient) {
+            try {
+                ((CloseableHttpClient) httpClient).close();
+            } catch (IOException e) {
+                throw new ClientRegistrationException("Failed to close http client", e);
+            }
+        }
+    }
+
+    static String getUrl(String baseUri, String... path) {
+        StringBuilder s = new StringBuilder();
+        s.append(baseUri);
+        for (String p : path) {
+            s.append('/');
+            s.append(p);
+        }
+        return s.toString();
+    }
+
+    private void addAuth(HttpRequestBase request) {
+        if (auth != null) {
+            auth.addAuth(request);
+        }
+    }
+
+}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
index 1120252..aed99fc 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml
@@ -58,6 +58,9 @@
         <addForeignKeyConstraint baseColumnNames="GROUP_ID" baseTableName="GROUP_ROLE_MAPPING" constraintName="FK_GROUP_ROLE_GROUP" referencedColumnNames="ID" referencedTableName="KEYCLOAK_GROUP"/>
         <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="GROUP_ROLE_MAPPING" constraintName="FK_GROUP_ROLE_ROLE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
 
+        <addColumn tableName="CLIENT">
+            <column name="REGISTRATION_SECRET" type="VARCHAR(255)"/>
+        </addColumn>
 
     </changeSet>
 </databaseChangeLog>
\ No newline at end of file
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 0999505..514c0fb 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -19,6 +19,7 @@ public class ClientRepresentation {
     protected Boolean enabled;
     protected String clientAuthenticatorType;
     protected String secret;
+    protected String registrationAccessToken;
     protected String[] defaultRoles;
     protected List<String> redirectUris;
     protected List<String> webOrigins;
@@ -124,6 +125,14 @@ public class ClientRepresentation {
         this.secret = secret;
     }
 
+    public String getRegistrationAccessToken() {
+        return registrationAccessToken;
+    }
+
+    public void setRegistrationAccessToken(String registrationAccessToken) {
+        this.registrationAccessToken = registrationAccessToken;
+    }
+
     public List<String> getRedirectUris() {
         return redirectUris;
     }
@@ -251,4 +260,5 @@ public class ClientRepresentation {
     public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
         this.protocolMappers = protocolMappers;
     }
+
 }
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index 8753f45..c421aea 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -90,6 +90,9 @@ public interface ClientModel extends RoleContainerModel {
     String getSecret();
     public void setSecret(String secret);
 
+    String getRegistrationSecret();
+    void setRegistrationSecret(String registrationSecret);
+
     boolean isFullScopeAllowed();
     void setFullScopeAllowed(boolean value);
 
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
index aa4f725..f15614f 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java
@@ -17,6 +17,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
     private boolean enabled;
     private String clientAuthenticatorType;
     private String secret;
+    private String registrationSecret;
     private String protocol;
     private int notBefore;
     private boolean publicClient;
@@ -90,6 +91,14 @@ public class ClientEntity extends AbstractIdentifiableEntity {
         this.secret = secret;
     }
 
+    public String getRegistrationSecret() {
+        return registrationSecret;
+    }
+
+    public void setRegistrationSecret(String registrationSecret) {
+        this.registrationSecret = registrationSecret;
+    }
+
     public int getNotBefore() {
         return notBefore;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
index cf8bc6d..f430e5f 100755
--- a/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
+++ b/model/api/src/main/java/org/keycloak/models/KeycloakContext.java
@@ -5,6 +5,7 @@ import org.keycloak.models.utils.RealmImporter;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
+import java.net.URI;
 import java.util.Locale;
 
 /**
@@ -12,6 +13,8 @@ import java.util.Locale;
  */
 public interface KeycloakContext {
 
+    URI getAuthServerUrl();
+
     String getContextPath();
 
     UriInfo getUri();
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 59c640c..0df3516 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -11,8 +11,6 @@ import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.OTPPolicy;
-import org.keycloak.models.session.PersistentClientSessionModel;
-import org.keycloak.models.session.PersistentUserSessionModel;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredActionProviderModel;
@@ -47,7 +45,6 @@ import org.keycloak.representations.idm.UserSessionRepresentation;
 import org.keycloak.common.util.Time;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -390,6 +387,7 @@ public class ModelToRepresentation {
         rep.setNotBefore(clientModel.getNotBefore());
         rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
         rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType());
+        rep.setRegistrationAccessToken(clientModel.getRegistrationSecret());
 
         Set<String> redirectUris = clientModel.getRedirectUris();
         if (redirectUris != null) {
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index a31d355..a46ee57 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -737,6 +737,8 @@ public class RepresentationToModel {
             KeycloakModelUtils.generateSecret(client);
         }
 
+        client.setRegistrationSecret(resourceRep.getRegistrationAccessToken());
+
         if (resourceRep.getAttributes() != null) {
             for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) {
                 client.setAttribute(entry.getKey(), entry.getValue());
@@ -813,6 +815,7 @@ public class RepresentationToModel {
         if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
         if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout());
         if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType());
+        if (rep.getRegistrationAccessToken() != null) resource.setRegistrationSecret(rep.getRegistrationAccessToken());
         resource.updateClient();
 
         if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 477c5d6..7a87373 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -120,6 +120,15 @@ public class ClientAdapter implements ClientModel {
         getDelegateForUpdate();
         updated.setSecret(secret);
     }
+    public String getRegistrationSecret() {
+        if (updated != null) return updated.getRegistrationSecret();
+        return cached.getRegistrationSecret();
+    }
+
+    public void setRegistrationSecret(String registrationsecret) {
+        getDelegateForUpdate();
+        updated.setRegistrationSecret(registrationsecret);
+    }
 
     public boolean isPublicClient() {
         if (updated != null) return updated.isPublicClient();
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index 2d58222..1c04b9d 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -31,6 +31,7 @@ public class CachedClient implements Serializable {
     private boolean enabled;
     private String clientAuthenticatorType;
     private String secret;
+    private String registrationSecret;
     private String protocol;
     private Map<String, String> attributes = new HashMap<String, String>();
     private boolean publicClient;
@@ -57,6 +58,7 @@ public class CachedClient implements Serializable {
         id = model.getId();
         clientAuthenticatorType = model.getClientAuthenticatorType();
         secret = model.getSecret();
+        registrationSecret = model.getRegistrationSecret();
         clientId = model.getClientId();
         name = model.getName();
         description = model.getDescription();
@@ -129,6 +131,10 @@ public class CachedClient implements Serializable {
         return secret;
     }
 
+    public String getRegistrationSecret() {
+        return registrationSecret;
+    }
+
     public boolean isPublicClient() {
         return publicClient;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 3baddd1..5ea0b11 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -178,6 +178,16 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public String getRegistrationSecret() {
+        return entity.getRegistrationSecret();
+    }
+
+    @Override
+    public void setRegistrationSecret(String registrationSecret) {
+        entity.setRegistrationSecret(registrationSecret);
+    }
+
+    @Override
     public boolean validateSecret(String secret) {
         return secret.equals(entity.getSecret());
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index f26f651..881b129 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -42,6 +42,8 @@ public class ClientEntity {
     private boolean enabled;
     @Column(name="SECRET")
     private String secret;
+    @Column(name="REGISTRATION_SECRET")
+    private String registrationSecret;
     @Column(name="CLIENT_AUTHENTICATOR_TYPE")
     private String clientAuthenticatorType;
     @Column(name="NOT_BEFORE")
@@ -201,6 +203,14 @@ public class ClientEntity {
         this.secret = secret;
     }
 
+    public String getRegistrationSecret() {
+        return registrationSecret;
+    }
+
+    public void setRegistrationSecret(String registrationSecret) {
+        this.registrationSecret = registrationSecret;
+    }
+
     public int getNotBefore() {
         return notBefore;
     }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index e99f142..cbacd09 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -178,6 +178,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
+    public String getRegistrationSecret() {
+        return getMongoEntity().getRegistrationSecret();
+    }
+
+    @Override
+    public void setRegistrationSecret(String registrationSecretsecret) {
+        getMongoEntity().setRegistrationSecret(registrationSecretsecret);
+        updateMongoEntity();
+    }
+
+    @Override
     public boolean isPublicClient() {
         return getMongoEntity().isPublicClient();
     }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java
new file mode 100644
index 0000000..298623d
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProvider.java
@@ -0,0 +1,79 @@
+package org.keycloak.protocol.saml.clientregistration;
+
+import org.jboss.logging.Logger;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.clientregistration.ClientRegAuth;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class EntityDescriptorClientRegistrationProvider implements ClientRegistrationProvider {
+
+    private static final Logger logger = Logger.getLogger(EntityDescriptorClientRegistrationProvider.class);
+
+    private KeycloakSession session;
+    private EventBuilder event;
+    private ClientRegAuth auth;
+
+    public EntityDescriptorClientRegistrationProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+//    @POST
+//    @Consumes(MediaType.APPLICATION_XML)
+//    @Produces(MediaType.APPLICATION_JSON)
+//    public Response create(String descriptor) {
+//        event.event(EventType.CLIENT_REGISTER);
+//
+//        auth.requireCreate();
+//
+//        ClientRepresentation client = session.getProvider(ClientDescriptionConverter.class, EntityDescriptorDescriptionConverter.ID).convertToInternal(descriptor);
+//
+//        try {
+//            ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+//            client = ModelToRepresentation.toRepresentation(clientModel);
+//            URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+//
+//            logger.infov("Created client {0}", client.getClientId());
+//
+//            event.client(client.getClientId()).success();
+//
+//            return Response.created(uri).entity(client).build();
+//        } catch (ModelDuplicateException e) {
+//            return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
+//        }
+//    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public void setAuth(ClientRegAuth auth) {
+        this.auth = auth;
+    }
+
+    @Override
+    public void setEvent(EventBuilder event) {
+        this.event = event;
+    }
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java
new file mode 100644
index 0000000..393ff36
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/clientregistration/EntityDescriptorClientRegistrationProviderFactory.java
@@ -0,0 +1,38 @@
+package org.keycloak.protocol.saml.clientregistration;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.ClientRegistrationProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class EntityDescriptorClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
+
+    public static final String ID = "saml2-entity-descriptor";
+
+    @Override
+    public ClientRegistrationProvider create(KeycloakSession session) {
+        return new EntityDescriptorClientRegistrationProvider(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/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
index da99613..42ff3ee 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
@@ -33,6 +33,8 @@ import java.util.Map;
  */
 public class EntityDescriptorDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
 
+    public static final String ID = "saml2-entity-descriptor";
+
     @Override
     public boolean isSupported(String description) {
         description = description.trim();
@@ -161,7 +163,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
 
     @Override
     public String getId() {
-        return "saml2-entity-descriptor";
+        return ID;
     }
 
 }
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
new file mode 100644
index 0000000..e4f8117
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.clientregistration.EntityDescriptorClientRegistrationProviderFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 9b862ef..2250dd0 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -190,7 +190,7 @@ public class LogoutEndpoint {
     }
 
     private ClientModel authorizeClient() {
-        ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient();
+        ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
 
         if (client.isBearerOnly()) {
             throw new ErrorResponseException("invalid_client", "Bearer-only not allowed", Response.Status.BAD_REQUEST);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 1fb3e4a..34161d8 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -145,7 +145,7 @@ public class TokenEndpoint {
     }
 
     private void checkClient() {
-        AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
+        AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event);
         client = clientAuth.getClient();
         clientAuthAttributes = clientAuth.getClientAuthAttributes();
 
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 56b47bb..955dfe4 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
@@ -8,6 +8,7 @@ import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.clientregistration.oidc.DescriptionConverter;
 import org.keycloak.util.JsonSerialization;
 
 import java.io.IOException;
@@ -17,6 +18,8 @@ import java.io.IOException;
  */
 public class OIDCClientDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
 
+    public static final String ID = "openid-connect";
+
     @Override
     public boolean isSupported(String description) {
         description = description.trim();
@@ -26,15 +29,8 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte
     @Override
     public ClientRepresentation convertToInternal(String description) {
         try {
-            OIDCClientRepresentation oidcRep = JsonSerialization.readValue(description, OIDCClientRepresentation.class);
-
-            ClientRepresentation client = new ClientRepresentation();
-            client.setClientId(KeycloakModelUtils.generateId());
-            client.setName(oidcRep.getClientName());
-            client.setRedirectUris(oidcRep.getRedirectUris());
-            client.setBaseUrl(oidcRep.getClientUri());
-
-            return client;
+            OIDCClientRepresentation clientOIDC = JsonSerialization.readValue(description, OIDCClientRepresentation.class);
+            return DescriptionConverter.toInternal(clientOIDC);
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
@@ -59,7 +55,7 @@ public class OIDCClientDescriptionConverter implements ClientDescriptionConverte
 
     @Override
     public String getId() {
-        return "openid-connect";
+        return ID;
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
index 7de415c..4a83ebd 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
@@ -12,12 +12,48 @@ public class OIDCClientRepresentation {
     @JsonProperty("redirect_uris")
     private List<String> redirectUris;
 
+    @JsonProperty("token_endpoint_auth_method")
+    private String tokenEndpointAuthMethod;
+
+    @JsonProperty("grant_types")
+    private String grantTypes;
+
+    @JsonProperty("response_types")
+    private String responseTypes;
+
     @JsonProperty("client_name")
     private String clientName;
 
     @JsonProperty("client_uri")
     private String clientUri;
 
+    @JsonProperty("logo_uri")
+    private String logoUri;
+
+    @JsonProperty("scope")
+    private String scope;
+
+    @JsonProperty("contacts")
+    private String contacts;
+
+    @JsonProperty("tos_uri")
+    private String tos_uri;
+
+    @JsonProperty("policy_uri")
+    private String policy_uri;
+
+    @JsonProperty("jwks_uri")
+    private String jwks_uri;
+
+    @JsonProperty("jwks")
+    private String jwks;
+
+    @JsonProperty("software_id")
+    private String softwareId;
+
+    @JsonProperty("software_version")
+    private String softwareVersion;
+
     public List<String> getRedirectUris() {
         return redirectUris;
     }
@@ -26,6 +62,30 @@ public class OIDCClientRepresentation {
         this.redirectUris = redirectUris;
     }
 
+    public String getTokenEndpointAuthMethod() {
+        return tokenEndpointAuthMethod;
+    }
+
+    public void setTokenEndpointAuthMethod(String tokenEndpointAuthMethod) {
+        this.tokenEndpointAuthMethod = tokenEndpointAuthMethod;
+    }
+
+    public String getGrantTypes() {
+        return grantTypes;
+    }
+
+    public void setGrantTypes(String grantTypes) {
+        this.grantTypes = grantTypes;
+    }
+
+    public String getResponseTypes() {
+        return responseTypes;
+    }
+
+    public void setResponseTypes(String responseTypes) {
+        this.responseTypes = responseTypes;
+    }
+
     public String getClientName() {
         return clientName;
     }
@@ -42,4 +102,76 @@ public class OIDCClientRepresentation {
         this.clientUri = clientUri;
     }
 
+    public String getLogoUri() {
+        return logoUri;
+    }
+
+    public void setLogoUri(String logoUri) {
+        this.logoUri = logoUri;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getContacts() {
+        return contacts;
+    }
+
+    public void setContacts(String contacts) {
+        this.contacts = contacts;
+    }
+
+    public String getTos_uri() {
+        return tos_uri;
+    }
+
+    public void setTos_uri(String tos_uri) {
+        this.tos_uri = tos_uri;
+    }
+
+    public String getPolicy_uri() {
+        return policy_uri;
+    }
+
+    public void setPolicy_uri(String policy_uri) {
+        this.policy_uri = policy_uri;
+    }
+
+    public String getJwks_uri() {
+        return jwks_uri;
+    }
+
+    public void setJwks_uri(String jwks_uri) {
+        this.jwks_uri = jwks_uri;
+    }
+
+    public String getJwks() {
+        return jwks;
+    }
+
+    public void setJwks(String jwks) {
+        this.jwks = jwks;
+    }
+
+    public String getSoftwareId() {
+        return softwareId;
+    }
+
+    public void setSoftwareId(String softwareId) {
+        this.softwareId = softwareId;
+    }
+
+    public String getSoftwareVersion() {
+        return softwareVersion;
+    }
+
+    public void setSoftwareVersion(String softwareVersion) {
+        this.softwareVersion = softwareVersion;
+    }
+
 }
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 c017ba3..a2d0d6c 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
@@ -19,18 +19,8 @@ import javax.ws.rs.core.Response;
  */
 public class AuthorizeClientUtil {
 
-    public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event, RealmModel realm) {
-        AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow();
-        String flowId = clientAuthFlow.getId();
-
-        AuthenticationProcessor processor = new AuthenticationProcessor();
-        processor.setFlowId(flowId)
-                .setConnection(session.getContext().getConnection())
-                .setEventBuilder(event)
-                .setRealm(realm)
-                .setSession(session)
-                .setUriInfo(session.getContext().getUri())
-                .setRequest(session.getContext().getContextObject(HttpRequest.class));
+    public static ClientAuthResult authorizeClient(KeycloakSession session, EventBuilder event) {
+        AuthenticationProcessor processor = getAuthenticationProcessor(session, event);
 
         Response response = processor.authenticateClient();
         if (response != null) {
@@ -45,6 +35,24 @@ public class AuthorizeClientUtil {
         return new ClientAuthResult(client, processor.getClientAuthAttributes());
     }
 
+    public static AuthenticationProcessor getAuthenticationProcessor(KeycloakSession session, EventBuilder event) {
+        RealmModel realm = session.getContext().getRealm();
+
+        AuthenticationFlowModel clientAuthFlow = realm.getClientAuthenticationFlow();
+        String flowId = clientAuthFlow.getId();
+
+        AuthenticationProcessor processor = new AuthenticationProcessor();
+        processor.setFlowId(flowId)
+                .setConnection(session.getContext().getConnection())
+                .setEventBuilder(event)
+                .setRealm(realm)
+                .setSession(session)
+                .setUriInfo(session.getContext().getUri())
+                .setRequest(session.getContext().getContextObject(HttpRequest.class));
+
+        return processor;
+    }
+
     public static class ClientAuthResult {
 
         private final ClientModel client;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
new file mode 100644
index 0000000..feffc5f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
@@ -0,0 +1,92 @@
+package org.keycloak.services.clientregistration;
+
+import org.jboss.resteasy.spi.UnauthorizedException;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AdapterInstallationClientRegistrationProvider implements ClientRegistrationProvider {
+
+    private KeycloakSession session;
+    private EventBuilder event;
+    private ClientRegAuth auth;
+
+    public AdapterInstallationClientRegistrationProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @GET
+    @Path("{clientId}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response get(@PathParam("clientId") String clientId) {
+        event.event(EventType.CLIENT_INFO);
+
+        ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+
+        if (auth.isAuthenticated()) {
+            auth.requireView(client);
+        } else {
+            authenticateClient(client);
+        }
+
+        ClientManager clientManager = new ClientManager(new RealmManager(session));
+        Object rep = clientManager.toInstallationRepresentation(session.getContext().getRealm(), client, session.getContext().getAuthServerUrl());
+
+        event.client(client.getClientId()).success();
+        return Response.ok(rep).build();
+    }
+
+    @Override
+    public void setAuth(ClientRegAuth auth) {
+        this.auth = auth;
+    }
+
+    @Override
+    public void setEvent(EventBuilder event) {
+        this.event = event;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    private void authenticateClient(ClientModel client) {
+        if (client.isPublicClient()) {
+            return;
+        }
+
+        AuthenticationProcessor processor = AuthorizeClientUtil.getAuthenticationProcessor(session, event);
+
+        Response response = processor.authenticateClient();
+        if (response != null) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw new ForbiddenException();
+        }
+
+        ClientModel authClient = processor.getClient();
+        if (client == null) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw new ForbiddenException();
+        }
+
+        if (!authClient.getClientId().equals(client.getClientId())) {
+            event.client(client.getClientId()).error(Errors.NOT_ALLOWED);
+            throw new ForbiddenException();
+        }
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java
new file mode 100644
index 0000000..4964349
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegAuth.java
@@ -0,0 +1,128 @@
+package org.keycloak.services.clientregistration;
+
+import org.jboss.resteasy.spi.UnauthorizedException;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.*;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+
+import javax.ws.rs.core.HttpHeaders;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegAuth {
+
+    private KeycloakSession session;
+    private EventBuilder event;
+
+    private String token;
+    private AccessToken.Access bearerRealmAccess;
+
+    private boolean authenticated = false;
+
+    public ClientRegAuth(KeycloakSession session, EventBuilder event) {
+        this.session = session;
+        this.event = event;
+
+        init();
+    }
+
+    private void init() {
+        RealmModel realm = session.getContext().getRealm();
+
+        String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+        if (authorizationHeader == null) {
+            return;
+        }
+
+        String[] split = authorizationHeader.split(" ");
+        if (!split[0].equalsIgnoreCase("bearer")) {
+            return;
+        }
+
+        if (split[1].indexOf('.') == -1) {
+            token = split[1];
+            authenticated = true;
+        } else {
+            AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm);
+            bearerRealmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
+            authenticated = true;
+        }
+    }
+
+    public boolean isAuthenticated() {
+        return authenticated;
+    }
+
+    public void requireCreate() {
+        if (!authenticated) {
+            event.error(Errors.NOT_ALLOWED);
+            throw new UnauthorizedException();
+        }
+
+        if (bearerRealmAccess != null) {
+            if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
+                return;
+            }
+        }
+
+        event.error(Errors.NOT_ALLOWED);
+        throw new ForbiddenException();
+    }
+
+    public void requireView(ClientModel client) {
+        if (!authenticated) {
+            event.error(Errors.NOT_ALLOWED);
+            throw new UnauthorizedException();
+        }
+
+        if (client == null) {
+            event.error(Errors.NOT_ALLOWED);
+            throw new ForbiddenException();
+        }
+
+        if (bearerRealmAccess != null) {
+            if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) {
+                return;
+            }
+        } else if (token != null) {
+            if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) {
+                return;
+            }
+        }
+
+        event.error(Errors.NOT_ALLOWED);
+        throw new ForbiddenException();
+    }
+
+    public void requireUpdate(ClientModel client) {
+        if (!authenticated) {
+            event.error(Errors.NOT_ALLOWED);
+            throw new UnauthorizedException();
+        }
+
+        if (client == null) {
+            event.error(Errors.NOT_ALLOWED);
+            throw new ForbiddenException();
+        }
+
+        if (bearerRealmAccess != null) {
+            if (bearerRealmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS) || bearerRealmAccess.isUserInRole(AdminRoles.VIEW_CLIENTS)) {
+                return;
+            }
+        } else if (token != null) {
+            if (client.getRegistrationSecret() != null && client.getRegistrationSecret().equals(token)) {
+                return;
+            }
+        }
+
+        event.error(Errors.NOT_ALLOWED);
+        throw new ForbiddenException();
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
index d1d6648..0c6bc4e 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
@@ -9,7 +9,7 @@ import org.keycloak.provider.Provider;
  */
 public interface ClientRegistrationProvider extends Provider {
 
-    void setRealm(RealmModel realm);
+    void setAuth(ClientRegAuth auth);
 
     void setEvent(EventBuilder event);
 
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
index 8b215fa..ea508de 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
@@ -2,36 +2,43 @@ package org.keycloak.services.clientregistration;
 
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.ErrorResponseException;
 
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class ClientRegistrationService {
 
-    private RealmModel realm;
-
     private EventBuilder event;
 
     @Context
     private KeycloakSession session;
 
-    public ClientRegistrationService(RealmModel realm, EventBuilder event) {
-        this.realm = realm;
+    public ClientRegistrationService(EventBuilder event) {
         this.event = event;
     }
 
     @Path("{provider}")
     public Object getProvider(@PathParam("provider") String providerId) {
+        checkSsl();
+
         ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId);
-        provider.setRealm(realm);
         provider.setEvent(event);
+        provider.setAuth(new ClientRegAuth(session, event));
         return provider;
     }
 
+    private void checkSsl() {
+        if (!session.getContext().getUri().getBaseUri().getScheme().equals("https")) {
+            if (session.getContext().getRealm().getSslRequired().isRequired(session.getContext().getConnection())) {
+                throw new ErrorResponseException("invalid_request", "HTTPS required", Response.Status.FORBIDDEN);
+            }
+        }
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
index 04cb46a..603ed08 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
@@ -1,22 +1,16 @@
 package org.keycloak.services.clientregistration;
 
-import org.jboss.logging.Logger;
-import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
-import org.keycloak.models.*;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
-import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
-import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.ErrorResponse;
-import org.keycloak.services.ForbiddenException;
-import org.keycloak.services.managers.AppAuthManager;
-import org.keycloak.services.managers.AuthenticationManager;
 
 import javax.ws.rs.*;
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.net.URI;
@@ -26,31 +20,29 @@ import java.net.URI;
  */
 public class DefaultClientRegistrationProvider implements ClientRegistrationProvider {
 
-    private static final Logger logger = Logger.getLogger(DefaultClientRegistrationProvider.class);
-
     private KeycloakSession session;
     private EventBuilder event;
-    private RealmModel realm;
+    private ClientRegAuth auth;
 
     public DefaultClientRegistrationProvider(KeycloakSession session) {
         this.session = session;
     }
 
-
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
     public Response create(ClientRepresentation client) {
         event.event(EventType.CLIENT_REGISTER);
 
-        authenticate(true, null);
+        auth.requireCreate();
 
         try {
-            ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true);
+            ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+            clientModel.setRegistrationSecret(TokenGenerator.createRegistrationAccessToken());
+
             client = ModelToRepresentation.toRepresentation(clientModel);
             URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
 
-            logger.infov("Created client {0}", client.getClientId());
-
             event.client(client.getClientId()).success();
             return Response.created(uri).entity(client).build();
         } catch (ModelDuplicateException e) {
@@ -64,10 +56,10 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
     public Response get(@PathParam("clientId") String clientId) {
         event.event(EventType.CLIENT_INFO);
 
-        ClientModel client = authenticate(false, clientId);
-        if (client == null) {
-            return Response.status(Response.Status.NOT_FOUND).build();
-        }
+        ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+        auth.requireView(client);
+
+        event.client(client.getClientId()).success();
         return Response.ok(ModelToRepresentation.toRepresentation(client)).build();
     }
 
@@ -77,12 +69,12 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
     public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
         event.event(EventType.CLIENT_UPDATE).client(clientId);
 
-        ClientModel client = authenticate(false, clientId);
-        RepresentationToModel.updateClient(rep, client);
+        ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+        auth.requireUpdate(client);
 
-        logger.infov("Updated client {0}", rep.getClientId());
+        RepresentationToModel.updateClient(rep, client);
 
-        event.success();
+        event.client(client.getClientId()).success();
         return Response.status(Response.Status.OK).build();
     }
 
@@ -91,9 +83,11 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
     public Response delete(@PathParam("clientId") String clientId) {
         event.event(EventType.CLIENT_DELETE).client(clientId);
 
-        ClientModel client = authenticate(false, clientId);
-        if (realm.removeClient(client.getId())) {
-            event.success();
+        ClientModel client = session.getContext().getRealm().getClientByClientId(clientId);
+        auth.requireUpdate(client);
+
+        if (session.getContext().getRealm().removeClient(client.getId())) {
+            event.client(client.getClientId()).success();
             return Response.ok().build();
         } else {
             return Response.status(Response.Status.NOT_FOUND).build();
@@ -101,55 +95,17 @@ public class DefaultClientRegistrationProvider implements ClientRegistrationProv
     }
 
     @Override
-    public void close() {
-
-    }
-
-
-    private ClientModel authenticate(boolean create, String clientId) {
-        String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
-
-        boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
-
-        if (bearer) {
-            AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm);
-            AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
-            if (realmAccess != null) {
-                if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
-                    return create ? null : realm.getClientByClientId(clientId);
-                }
-
-                if (create && realmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
-                    return create ? null : realm.getClientByClientId(clientId);
-                }
-            }
-        } else if (!create) {
-            ClientModel client;
-
-            try {
-                AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
-                client = clientAuth.getClient();
-
-                if (client != null && !client.isPublicClient() && client.getClientId().equals(clientId)) {
-                    return client;
-                }
-            } catch (Throwable t) {
-            }
-        }
-
-        event.error(Errors.NOT_ALLOWED);
-
-        throw new ForbiddenException();
+    public void setAuth(ClientRegAuth auth) {
+        this.auth = auth;
     }
 
     @Override
-    public void setRealm(RealmModel realm) {
-this.realm = realm;
+    public void setEvent(EventBuilder event) {
+        this.event = event;
     }
 
     @Override
-    public void setEvent(EventBuilder event) {
-        this.event = event;
+    public void close() {
     }
 
 }
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
new file mode 100644
index 0000000..1e0784c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/DescriptionConverter.java
@@ -0,0 +1,38 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DescriptionConverter {
+
+    public static ClientRepresentation toInternal(OIDCClientRepresentation clientOIDC) {
+        ClientRepresentation client = new ClientRepresentation();
+        client.setClientId(KeycloakModelUtils.generateId());
+        client.setName(clientOIDC.getClientName());
+        client.setRedirectUris(clientOIDC.getRedirectUris());
+        client.setBaseUrl(clientOIDC.getClientUri());
+        return client;
+    }
+
+    public static OIDCClientResponseRepresentation toExternalResponse(ClientRepresentation client) {
+        OIDCClientResponseRepresentation response = new OIDCClientResponseRepresentation();
+        response.setClientId(client.getClientId());
+
+        response.setClientName(client.getName());
+        response.setClientUri(client.getBaseUrl());
+
+        response.setClientSecret(client.getSecret());
+        response.setClientSecretExpiresAt(0);
+
+        response.setRedirectUris(client.getRedirectUris());
+
+        response.setRegistrationAccessToken(client.getRegistrationAccessToken());
+
+        return response;
+    }
+
+}
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
new file mode 100644
index 0000000..4d131cc
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientRegistrationProvider.java
@@ -0,0 +1,100 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.jboss.logging.Logger;
+import org.keycloak.common.util.Time;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.OIDCClientDescriptionConverter;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.clientregistration.ClientRegAuth;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.TokenGenerator;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRegistrationProvider implements ClientRegistrationProvider {
+
+    private static final Logger logger = Logger.getLogger(OIDCClientRegistrationProvider.class);
+
+    private KeycloakSession session;
+    private EventBuilder event;
+    private ClientRegAuth auth;
+
+    public OIDCClientRegistrationProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+//    @POST
+//    @Consumes(MediaType.APPLICATION_JSON)
+//    @Produces(MediaType.APPLICATION_JSON)
+//    public Response create(OIDCClientRepresentation clientOIDC) {
+//        event.event(EventType.CLIENT_REGISTER);
+//
+//        auth.requireCreate();
+//
+//        ClientRepresentation client = DescriptionConverter.toInternal(clientOIDC);
+//
+//        try {
+//            ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+//
+//            client = ModelToRepresentation.toRepresentation(clientModel);
+//
+//            String registrationAccessToken = TokenGenerator.createRegistrationAccessToken();
+//
+//            clientModel.setRegistrationSecret(registrationAccessToken);
+//
+//            URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+//
+//            logger.infov("Created client {0}", client.getClientId());
+//
+//            event.client(client.getClientId()).success();
+//
+//            OIDCClientResponseRepresentation response = DescriptionConverter.toExternalResponse(client);
+//
+//            response.setClientName(client.getName());
+//            response.setClientUri(client.getBaseUrl());
+//
+//            response.setClientSecret(client.getSecret());
+//            response.setClientSecretExpiresAt(0);
+//
+//            response.setRedirectUris(client.getRedirectUris());
+//
+//            response.setRegistrationAccessToken(registrationAccessToken);
+//            response.setRegistrationClientUri(uri.toString());
+//
+//            return Response.created(uri).entity(response).build();
+//        } catch (ModelDuplicateException e) {
+//            return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
+//        }
+//    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public void setAuth(ClientRegAuth auth) {
+        this.auth = auth;
+    }
+
+    @Override
+    public void setEvent(EventBuilder event) {
+        this.event = event;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java
new file mode 100644
index 0000000..cd4eea4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/oidc/OIDCClientResponseRepresentation.java
@@ -0,0 +1,77 @@
+package org.keycloak.services.clientregistration.oidc;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientResponseRepresentation extends OIDCClientRepresentation {
+
+    @JsonProperty("client_id")
+    private String clientId;
+
+    @JsonProperty("client_secret")
+    private String clientSecret;
+
+    @JsonProperty("client_id_issued_at")
+    private int clientIdIssuedAt;
+
+    @JsonProperty("client_secret_expires_at")
+    private int clientSecretExpiresAt;
+
+    @JsonProperty("registration_client_uri")
+    private String registrationClientUri;
+
+    @JsonProperty("registration_access_token")
+    private String registrationAccessToken;
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public void setClientId(String clientId) {
+        this.clientId = clientId;
+    }
+
+    public String getClientSecret() {
+        return clientSecret;
+    }
+
+    public void setClientSecret(String clientSecret) {
+        this.clientSecret = clientSecret;
+    }
+
+    public int getClientIdIssuedAt() {
+        return clientIdIssuedAt;
+    }
+
+    public void setClientIdIssuedAt(int clientIdIssuedAt) {
+        this.clientIdIssuedAt = clientIdIssuedAt;
+    }
+
+    public int getClientSecretExpiresAt() {
+        return clientSecretExpiresAt;
+    }
+
+    public void setClientSecretExpiresAt(int clientSecretExpiresAt) {
+        this.clientSecretExpiresAt = clientSecretExpiresAt;
+    }
+
+    public String getRegistrationClientUri() {
+        return registrationClientUri;
+    }
+
+    public void setRegistrationClientUri(String registrationClientUri) {
+        this.registrationClientUri = registrationClientUri;
+    }
+
+    public String getRegistrationAccessToken() {
+        return registrationAccessToken;
+    }
+
+    public void setRegistrationAccessToken(String registrationAccessToken) {
+        this.registrationAccessToken = registrationAccessToken;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java
new file mode 100644
index 0000000..3c49d0f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/TokenGenerator.java
@@ -0,0 +1,27 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.common.util.Base64Url;
+
+import java.security.SecureRandom;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TokenGenerator {
+
+    private static final int REGISTRATION_ACCESS_TOKEN_BYTES = 32;
+
+    private TokenGenerator() {
+    }
+
+    public String createInitialAccessToken() {
+        return null;
+    }
+
+    public static String createRegistrationAccessToken() {
+        byte[] buf = new byte[REGISTRATION_ACCESS_TOKEN_BYTES];
+        new SecureRandom().nextBytes(buf);
+        return Base64Url.encode(buf);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
index a78f99e..26aba4b 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java
@@ -10,6 +10,7 @@ import org.keycloak.services.util.LocaleHelper;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.UriInfo;
+import java.net.URI;
 import java.util.Locale;
 
 /**
@@ -30,6 +31,13 @@ public class DefaultKeycloakContext implements KeycloakContext {
     }
 
     @Override
+    public URI getAuthServerUrl() {
+        UriInfo uri = getUri();
+        KeycloakApplication keycloakApplication = getContextObject(KeycloakApplication.class);
+        return keycloakApplication.getBaseUri(uri);
+    }
+
+    @Override
     public String getContextPath() {
         KeycloakApplication app = getContextObject(KeycloakApplication.class);
         return app.getContextPath();
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 ddb301d..a933712 100755
--- a/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
+++ b/services/src/main/java/org/keycloak/services/resources/ClientsManagementService.java
@@ -153,7 +153,7 @@ public class ClientsManagementService {
     }
 
     protected ClientModel authorizeClient() {
-        ClientModel client = AuthorizeClientUtil.authorizeClient(session, event, realm).getClient();
+        ClientModel client = AuthorizeClientUtil.authorizeClient(session, event).getClient();
 
         if (client.isPublicClient()) {
             Map<String, String> error = new HashMap<String, String>();
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index f18f000..4b5e4f2 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -113,11 +113,11 @@ public class RealmsResource {
         return service;
     }
 
-    @Path("{realm}/client-registration")
+    @Path("{realm}/clients")
     public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
         RealmModel realm = init(name);
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
-        ClientRegistrationService service = new ClientRegistrationService(realm, event);
+        ClientRegistrationService service = new ClientRegistrationService(event);
         ResteasyProviderFactory.getInstance().injectProperties(service);
         return service;
     }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
index 3e8773a..d9b8c41 100644
--- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
@@ -1 +1,3 @@
-org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
\ No newline at end of file
+org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
+org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory
+org.keycloak.services.clientregistration.AdapterInstallationClientRegistrationProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
new file mode 100644
index 0000000..8b8dfad
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AbstractClientRegistrationTest.java
@@ -0,0 +1,96 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.After;
+import org.junit.Before;
+import org.keycloak.client.registration.ClientRegistration;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.Constants;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class AbstractClientRegistrationTest extends AbstractKeycloakTest {
+
+    static final String REALM_NAME = "test";
+
+    ClientRegistration reg;
+
+    @Before
+    public void before() throws Exception {
+        reg = new ClientRegistration(testContext.getAuthServerContextRoot() + "/auth", "test");
+    }
+
+    @After
+    public void after() throws Exception {
+        reg.close();
+    }
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation rep = new RealmRepresentation();
+        rep.setEnabled(true);
+        rep.setRealm(REALM_NAME);
+        rep.setUsers(new LinkedList<UserRepresentation>());
+
+        LinkedList<CredentialRepresentation> credentials = new LinkedList<>();
+        CredentialRepresentation password = new CredentialRepresentation();
+        password.setType(CredentialRepresentation.PASSWORD);
+        password.setValue("password");
+        credentials.add(password);
+
+        UserRepresentation user = new UserRepresentation();
+        user.setEnabled(true);
+        user.setUsername("manage-clients");
+        user.setCredentials(credentials);
+        user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS)));
+
+        rep.getUsers().add(user);
+
+        UserRepresentation user2 = new UserRepresentation();
+        user2.setEnabled(true);
+        user2.setUsername("create-clients");
+        user2.setCredentials(credentials);
+        user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT)));
+
+        rep.getUsers().add(user2);
+
+        UserRepresentation user3 = new UserRepresentation();
+        user3.setEnabled(true);
+        user3.setUsername("no-access");
+        user3.setCredentials(credentials);
+
+        rep.getUsers().add(user3);
+
+        testRealms.add(rep);
+    }
+
+    public ClientRepresentation createClient(ClientRepresentation client) {
+        Response response = adminClient.realm(REALM_NAME).clients().create(client);
+        String id = response.getLocation().toString();
+        id = id.substring(id.lastIndexOf('/') + 1);
+        client.setId(id);
+        response.close();
+        return client;
+    }
+
+    public ClientRepresentation getClient(String clientId) {
+        try {
+            return adminClient.realm(REALM_NAME).clients().get(clientId).toRepresentation();
+        } catch (NotFoundException e) {
+            return null;
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
new file mode 100644
index 0000000..b8bdb41
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/AdapterInstallationConfigTest.java
@@ -0,0 +1,103 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AdapterInstallationConfigTest extends AbstractClientRegistrationTest {
+
+    private ClientRepresentation client;
+    private ClientRepresentation client2;
+    private ClientRepresentation clientPublic;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        client = new ClientRepresentation();
+        client.setEnabled(true);
+        client.setClientId("RegistrationAccessTokenTest");
+        client.setSecret("RegistrationAccessTokenTestClientSecret");
+        client.setPublicClient(false);
+        client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+        client.setRootUrl("http://root");
+        client = createClient(client);
+
+        client2 = new ClientRepresentation();
+        client2.setEnabled(true);
+        client2.setClientId("RegistrationAccessTokenTest2");
+        client2.setSecret("RegistrationAccessTokenTestClientSecret");
+        client2.setPublicClient(false);
+        client2.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+        client2.setRootUrl("http://root");
+        client2 = createClient(client2);
+
+        clientPublic = new ClientRepresentation();
+        clientPublic.setEnabled(true);
+        clientPublic.setClientId("RegistrationAccessTokenTestPublic");
+        clientPublic.setPublicClient(true);
+        clientPublic.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessTokenPublic");
+        clientPublic.setRootUrl("http://root");
+        clientPublic = createClient(clientPublic);
+    }
+
+    @Test
+    public void getConfigWithRegistrationAccessToken() throws ClientRegistrationException {
+        reg.auth(Auth.token(client.getRegistrationAccessToken()));
+
+        AdapterConfig config = reg.getAdapterConfig(client.getClientId());
+        assertNotNull(config);
+    }
+
+    @Test
+    public void getConfig() throws ClientRegistrationException {
+        reg.auth(Auth.client(client.getClientId(), client.getSecret()));
+
+        AdapterConfig config = reg.getAdapterConfig(client.getClientId());
+        assertNotNull(config);
+    }
+
+    @Test
+    public void getConfigMissingSecret() throws ClientRegistrationException {
+        reg.auth(null);
+
+        try {
+            reg.getAdapterConfig(client.getClientId());
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
+    }
+
+    @Test
+    public void getConfigWrongClient() throws ClientRegistrationException {
+        reg.auth(Auth.client(client.getClientId(), client.getSecret()));
+
+        try {
+            reg.getAdapterConfig(client2.getClientId());
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
+    }
+
+    @Test
+    public void getConfigPublicClient() throws ClientRegistrationException {
+        reg.auth(null);
+
+        AdapterConfig config = reg.getAdapterConfig(clientPublic.getClientId());
+        assertNotNull(config);
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
index 1d4b011..8b84b10 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
@@ -1,302 +1,211 @@
 package org.keycloak.testsuite.client;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
-import org.keycloak.client.registration.ClientRegistration;
+import org.keycloak.client.registration.Auth;
 import org.keycloak.client.registration.ClientRegistrationException;
 import org.keycloak.client.registration.HttpErrorException;
-import org.keycloak.models.AdminRoles;
-import org.keycloak.models.Constants;
-import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 
+import javax.ws.rs.NotFoundException;
 import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
 
 import static org.junit.Assert.*;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public class ClientRegistrationTest extends AbstractKeycloakTest {
+public class ClientRegistrationTest extends AbstractClientRegistrationTest {
 
-    private static final String REALM_NAME = "test";
     private static final String CLIENT_ID = "test-client";
     private static final String CLIENT_SECRET = "test-client-secret";
 
-    private ClientRegistration clientRegistrationAsAdmin;
-    private ClientRegistration clientRegistrationAsClient;
-
-    @Before
-    public void before() throws ClientRegistrationException {
-        clientRegistrationAsAdmin = clientBuilder().auth(getToken("manage-clients", "password")).build();
-        clientRegistrationAsClient = clientBuilder().auth(CLIENT_ID, CLIENT_SECRET).build();
-    }
-
-    @After
-    public void after() throws ClientRegistrationException {
-        clientRegistrationAsAdmin.close();
-        clientRegistrationAsClient.close();
-    }
-
-    @Override
-    public void addTestRealms(List<RealmRepresentation> testRealms) {
-        RealmRepresentation rep = new RealmRepresentation();
-        rep.setEnabled(true);
-        rep.setRealm(REALM_NAME);
-        rep.setUsers(new LinkedList<UserRepresentation>());
-
-        LinkedList<CredentialRepresentation> credentials = new LinkedList<>();
-        CredentialRepresentation password = new CredentialRepresentation();
-        password.setType(CredentialRepresentation.PASSWORD);
-        password.setValue("password");
-        credentials.add(password);
-
-        UserRepresentation user = new UserRepresentation();
-        user.setEnabled(true);
-        user.setUsername("manage-clients");
-        user.setCredentials(credentials);
-        user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS)));
-
-        rep.getUsers().add(user);
-
-        UserRepresentation user2 = new UserRepresentation();
-        user2.setEnabled(true);
-        user2.setUsername("create-clients");
-        user2.setCredentials(credentials);
-        user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT)));
-
-        rep.getUsers().add(user2);
-
-        UserRepresentation user3 = new UserRepresentation();
-        user3.setEnabled(true);
-        user3.setUsername("no-access");
-        user3.setCredentials(credentials);
-
-        rep.getUsers().add(user3);
-
-        testRealms.add(rep);
-    }
-
-    private void registerClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
+    private ClientRepresentation registerClient() throws ClientRegistrationException {
         ClientRepresentation client = new ClientRepresentation();
         client.setClientId(CLIENT_ID);
         client.setSecret(CLIENT_SECRET);
 
-        ClientRepresentation createdClient = clientRegistration.create(client);
+        ClientRepresentation createdClient = reg.create(client);
         assertEquals(CLIENT_ID, createdClient.getClientId());
 
         client = adminClient.realm(REALM_NAME).clients().get(createdClient.getId()).toRepresentation();
         assertEquals(CLIENT_ID, client.getClientId());
 
-        AccessTokenResponse token2 = oauthClient.getToken(REALM_NAME, CLIENT_ID, CLIENT_SECRET, "manage-clients", "password");
-        assertNotNull(token2.getToken());
+        return client;
     }
 
     @Test
     public void registerClientAsAdmin() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
+        authManageClients();
+        registerClient();
     }
 
     @Test
     public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
-        try {
-            registerClient(clientRegistration);
-        } finally {
-            clientRegistration.close();
-        }
+        authCreateClients();
+        registerClient();
     }
 
     @Test
     public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+        authNoAccess();
         try {
-            registerClient(clientRegistration);
+            registerClient();
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
     @Test
+    public void getClientAsAdmin() throws ClientRegistrationException {
+        registerClientAsAdmin();
+        ClientRepresentation rep = reg.get(CLIENT_ID);
+        assertNotNull(rep);
+    }
+
+    @Test
     public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+        registerClientAsAdmin();
+        authCreateClients();
         try {
-            clientRegistration.get(CLIENT_ID);
+            reg.get(CLIENT_ID);
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
     @Test
-    public void wrongClient() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-
-        ClientRepresentation client = new ClientRepresentation();
-        client.setClientId("test-client-2");
-        client.setSecret("test-client-2-secret");
-
-        clientRegistrationAsAdmin.create(client);
-
-        ClientRegistration clientRegistration = clientBuilder().auth("test-client-2", "test-client-2-secret").build();
-
-        client = clientRegistration.get("test-client-2");
-        assertNotNull(client);
-        assertEquals("test-client-2", client.getClientId());
-
+    public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
+        registerClientAsAdmin();
+        authNoAccess();
         try {
-            try {
-                clientRegistration.get(CLIENT_ID);
-                fail("Expected 403");
-            } catch (ClientRegistrationException e) {
-                assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-            }
-
-            client = clientRegistrationAsAdmin.get(CLIENT_ID);
-            try {
-                clientRegistration.update(client);
-                fail("Expected 403");
-            } catch (ClientRegistrationException e) {
-                assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-            }
-
-            try {
-                clientRegistration.delete(CLIENT_ID);
-                fail("Expected 403");
-            } catch (ClientRegistrationException e) {
-                assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-            }
-        }
-        finally {
-            clientRegistration.close();
+            reg.get(CLIENT_ID);
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
         }
     }
 
     @Test
-    public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+    public void getClientNotFound() throws ClientRegistrationException {
+        authManageClients();
         try {
-            clientRegistration.get(CLIENT_ID);
+            reg.get(CLIENT_ID);
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
-    private void updateClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
-        ClientRepresentation client = clientRegistration.get(CLIENT_ID);
+    private void updateClient() throws ClientRegistrationException {
+        ClientRepresentation client = reg.get(CLIENT_ID);
         client.setRedirectUris(Collections.singletonList("http://localhost:8080/app"));
 
-        clientRegistration.update(client);
+        reg.update(client);
 
-        ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID);
+        ClientRepresentation updatedClient = reg.get(CLIENT_ID);
 
         assertEquals(1, updatedClient.getRedirectUris().size());
         assertEquals("http://localhost:8080/app", updatedClient.getRedirectUris().get(0));
     }
 
+
     @Test
     public void updateClientAsAdmin() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        updateClient(clientRegistrationAsAdmin);
+        registerClientAsAdmin();
+
+        authManageClients();
+        updateClient();
     }
 
     @Test
     public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+        authCreateClients();
         try {
-            updateClient(clientRegistration);
+            updateClient();
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
     @Test
     public void updateClientAsAdminWithNoAccess() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+        authNoAccess();
         try {
-            updateClient(clientRegistration);
+            updateClient();
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
     @Test
-    public void updateClientAsClient() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        updateClient(clientRegistrationAsClient);
+    public void updateClientNotFound() throws ClientRegistrationException {
+        authManageClients();
+        try {
+            updateClient();
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
     }
 
-    private void deleteClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
-        clientRegistration.delete(CLIENT_ID);
-
-        // Can't authenticate as client after client is deleted
-        ClientRepresentation client = clientRegistrationAsAdmin.get(CLIENT_ID);
-        assertNull(client);
+    private void deleteClient(ClientRepresentation client) throws ClientRegistrationException {
+        reg.delete(CLIENT_ID);
+        try {
+            adminClient.realm("test").clients().get(client.getId()).toRepresentation();
+            fail("Expected 403");
+        } catch (NotFoundException e) {
+        }
     }
 
     @Test
     public void deleteClientAsAdmin() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        deleteClient(clientRegistrationAsAdmin);
+        authCreateClients();
+        ClientRepresentation client = registerClient();
+
+        authManageClients();
+        deleteClient(client);
     }
 
     @Test
     public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+        authManageClients();
+        ClientRepresentation client = registerClient();
         try {
-            deleteClient(clientRegistration);
+            authCreateClients();
+            deleteClient(client);
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
     @Test
     public void deleteClientAsAdminWithNoAccess() throws ClientRegistrationException {
-        ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+        authManageClients();
+        ClientRepresentation client = registerClient();
         try {
-            deleteClient(clientRegistration);
+            authNoAccess();
+            deleteClient(client);
             fail("Expected 403");
         } catch (ClientRegistrationException e) {
             assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
-        } finally {
-            clientRegistration.close();
         }
     }
 
-    @Test
-    public void deleteClientAsClient() throws ClientRegistrationException {
-        registerClient(clientRegistrationAsAdmin);
-        deleteClient(clientRegistrationAsClient);
+    private void authCreateClients() {
+        reg.auth(Auth.token(getToken("create-clients", "password")));
+    }
+
+    private void authManageClients() {
+        reg.auth(Auth.token(getToken("manage-clients", "password")));
     }
 
-    private ClientRegistration.ClientRegistrationBuilder clientBuilder() {
-        return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth");
+    private void authNoAccess() {
+        reg.auth(Auth.token(getToken("no-access", "password")));
     }
 
     private String getToken(String username, String password) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
new file mode 100644
index 0000000..d76da19
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/RegistrationAccessTokenTest.java
@@ -0,0 +1,94 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.Auth;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class RegistrationAccessTokenTest extends AbstractClientRegistrationTest {
+
+    private ClientRepresentation client;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        client = new ClientRepresentation();
+        client.setEnabled(true);
+        client.setClientId("RegistrationAccessTokenTest");
+        client.setSecret("RegistrationAccessTokenTestClientSecret");
+        client.setRegistrationAccessToken("RegistrationAccessTokenTestRegistrationAccessToken");
+        client.setRootUrl("http://root");
+        client = createClient(client);
+
+        reg.auth(Auth.token(client.getRegistrationAccessToken()));
+    }
+
+    @Test
+    public void getClientWithRegistrationToken() throws ClientRegistrationException {
+        ClientRepresentation rep = reg.get(client.getClientId());
+        assertNotNull(rep);
+    }
+
+    @Test
+    public void getClientWithBadRegistrationToken() throws ClientRegistrationException {
+        reg.auth(Auth.token("invalid"));
+        try {
+            reg.get(client.getClientId());
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
+    }
+
+    @Test
+    public void updateClientWithRegistrationToken() throws ClientRegistrationException {
+        client.setRootUrl("http://newroot");
+        reg.update(client);
+
+        assertEquals("http://newroot", getClient(client.getId()).getRootUrl());
+    }
+
+    @Test
+    public void updateClientWithBadRegistrationToken() throws ClientRegistrationException {
+        client.setRootUrl("http://newroot");
+
+        reg.auth(Auth.token("invalid"));
+        try {
+            reg.update(client);
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
+
+        assertEquals("http://root", getClient(client.getId()).getRootUrl());
+    }
+
+    @Test
+    public void deleteClientWithRegistrationToken() throws ClientRegistrationException {
+        reg.delete(client);
+        assertNull(getClient(client.getId()));
+    }
+
+    @Test
+    public void deleteClientWithBadRegistrationToken() throws ClientRegistrationException {
+        reg.auth(Auth.token("invalid"));
+        try {
+            reg.delete(client);
+            fail("Expected 403");
+        } catch (ClientRegistrationException e) {
+            assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+        }
+        assertNotNull(getClient(client.getId()));
+    }
+
+}