keycloak-aplcache

Merge pull request #1650 from stianst/client-reg2 KEYCLOAK-1868

9/29/2015 9:12:59 AM

Changes

saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java 21(+0 -21)

saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientImporterFactory 1(+0 -1)

Details

diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml
index 24902d5..9d6e545 100644
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml
@@ -8,6 +8,10 @@
             </column>
         </addColumn>
 
+        <addColumn tableName="CLIENT">
+            <column name="ROOT_URL" type="VARCHAR(255)"/>
+        </addColumn>
+
         <createTable tableName="OFFLINE_USER_SESSION">
             <column name="USER_ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
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 020445e..7ad8c6e 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -11,6 +11,7 @@ public class ClientRepresentation {
     protected String id;
     protected String clientId;
     protected String name;
+    protected String rootUrl;
     protected String adminUrl;
     protected String baseUrl;
     protected Boolean surrogateAuthRequired;
@@ -74,6 +75,14 @@ public class ClientRepresentation {
         this.surrogateAuthRequired = surrogateAuthRequired;
     }
 
+    public String getRootUrl() {
+        return rootUrl;
+    }
+
+    public void setRootUrl(String rootUrl) {
+        this.rootUrl = rootUrl;
+    }
+
     public String getAdminUrl() {
         return adminUrl;
     }
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index ae334e7..3cb3e60 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -564,8 +564,6 @@ module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role, 
 module.controller('ClientImportCtrl', function($scope, $location, $upload, realm, serverInfo, Notifications) {
 
     $scope.realm = realm;
-    $scope.configFormats = serverInfo.clientImporters;
-    $scope.configFormat = null;
 
     $scope.files = [];
 
@@ -614,7 +612,6 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm
 module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications) {
     $scope.realm = realm;
     $scope.clients = clients;
-    $scope.importButton = serverInfo.clientImporters.length > 0;
 
     $scope.removeClient = function(client) {
         Dialog.confirmDelete(client.clientId, 'client', function() {
@@ -670,7 +667,7 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, Clie
     }
 });
 
-module.controller('ClientDetailCtrl', function($scope, realm, client, $route, serverInfo, Client, $location, Dialog, Notifications) {
+module.controller('ClientDetailCtrl', function($scope, realm, client, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
     $scope.accessTypes = [
         "confidential",
         "public",
@@ -709,40 +706,45 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
     $scope.samlEncrypt = false;
     $scope.samlForcePostBinding = false;
     $scope.samlForceNameIdFormat = false;
-    if (!$scope.create) {
-        if (!client.attributes) {
-            client.attributes = {};
+
+    function updateProperties() {
+        if (!$scope.client.attributes) {
+            $scope.client.attributes = {};
         }
-        $scope.client= angular.copy(client);
         $scope.accessType = $scope.accessTypes[0];
-        if (client.bearerOnly) {
+        if ($scope.client.bearerOnly) {
             $scope.accessType = $scope.accessTypes[2];
-        } else if (client.publicClient) {
+        } else if ($scope.client.publicClient) {
             $scope.accessType = $scope.accessTypes[1];
         }
-        if (client.protocol) {
-            $scope.protocol = $scope.protocols[$scope.protocols.indexOf(client.protocol)];
+        if ($scope.client.protocol) {
+            $scope.protocol = $scope.protocols[$scope.protocols.indexOf($scope.client.protocol)];
         } else {
             $scope.protocol = $scope.protocols[0];
         }
-        if (client.attributes['saml.signature.algorithm'] == 'RSA_SHA1') {
+        if ($scope.client.attributes['saml.signature.algorithm'] == 'RSA_SHA1') {
             $scope.signatureAlgorithm = $scope.signatureAlgorithms[0];
-        } else if (client.attributes['saml.signature.algorithm'] == 'RSA_SHA256') {
+        } else if ($scope.client.attributes['saml.signature.algorithm'] == 'RSA_SHA256') {
             $scope.signatureAlgorithm = $scope.signatureAlgorithms[1];
-        } else if (client.attributes['saml.signature.algorithm'] == 'RSA_SHA512') {
+        } else if ($scope.client.attributes['saml.signature.algorithm'] == 'RSA_SHA512') {
             $scope.signatureAlgorithm = $scope.signatureAlgorithms[2];
-        } else if (client.attributes['saml.signature.algorithm'] == 'DSA_SHA1') {
+        } else if ($scope.client.attributes['saml.signature.algorithm'] == 'DSA_SHA1') {
             $scope.signatureAlgorithm = $scope.signatureAlgorithms[3];
         }
-        if (client.attributes['saml_name_id_format'] == 'unspecified') {
+        if ($scope.client.attributes['saml_name_id_format'] == 'unspecified') {
             $scope.nameIdFormat = $scope.nameIdFormats[0];
-        } else if (client.attributes['saml_name_id_format'] == 'email') {
+        } else if ($scope.client.attributes['saml_name_id_format'] == 'email') {
             $scope.nameIdFormat = $scope.nameIdFormats[1];
-        } else if (client.attributes['saml_name_id_format'] == 'transient') {
+        } else if ($scope.client.attributes['saml_name_id_format'] == 'transient') {
             $scope.nameIdFormat = $scope.nameIdFormats[2];
-        } else if (client.attributes['saml_name_id_format'] == 'persistent') {
+        } else if ($scope.client.attributes['saml_name_id_format'] == 'persistent') {
             $scope.nameIdFormat = $scope.nameIdFormats[3];
         }
+    }
+
+    if (!$scope.create) {
+        $scope.client = angular.copy(client);
+        updateProperties();
     } else {
         $scope.client = { enabled: true, attributes: {}};
         $scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value;
@@ -813,6 +815,29 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
         }
     }
 
+    $scope.importFile = function(fileContent){
+        console.debug(fileContent);
+        ClientDescriptionConverter.save({
+            realm: realm.realm
+        }, fileContent, function (data) {
+            $scope.client = data;
+            updateProperties();
+            $scope.importing = true;
+        });
+    };
+
+    $scope.viewImportDetails = function() {
+        $modal.open({
+            templateUrl: resourceUrl + '/partials/modal/view-object.html',
+            controller: 'JsonModalCtrl',
+            resolve: {
+                object: function () {
+                    return $scope.client;
+                }
+            }
+        })
+    };
+
     $scope.switchChange = function() {
         $scope.changed = true;
     }
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index d1abe26..8d56c90 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -927,6 +927,13 @@ module.factory('Client', function($resource) {
     });
 });
 
+module.factory('ClientDescriptionConverter', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/client-description-converter', {
+        realm : '@realm'
+    });
+});
+
+
 module.factory('ClientInstallation', function($resource) {
     var url = authUrl + '/admin/realms/:realm/clients/:client/installation/json';
     return {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 06c939b..cc4ea82 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -10,6 +10,20 @@
 
     <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
         <fieldset class="border-top">
+            <div class="form-group" data-ng-show="create">
+                <label for="name" class="col-sm-2 control-label">Import</label>
+
+                <div class="col-md-6" data-ng-hide="importing">
+                    <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+                    <input id="import-file" type="file" class="hidden" kc-on-read-file="importFile($fileContent)">
+                </div>
+
+                <div class="col-md-6" data-ng-show="importing">
+                    <button class="btn btn-default" data-ng-click="viewImportDetails()">View details</button>
+                    <button class="btn btn-default" data-ng-click="reset()">Clear import</button>
+                </div>
+            </div>
+
             <div class="form-group">
                 <label class="col-md-2 control-label" for="clientId">Client ID <span class="required" data-ng-show="create">*</span></label>
                 <div class="col-sm-6">
@@ -174,6 +188,14 @@
                 <kc-tooltip>The name ID format to use for the subject.</kc-tooltip>
             </div>
 
+            <div class="form-group" data-ng-show="!client.bearerOnly">
+                <label class="col-md-2 control-label" for="rootUrl">Root URL</label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" name="rootUrl" id="rootUrl" data-ng-model="client.rootUrl">
+                </div>
+                <kc-tooltip>Root URL appended to relative URLs</kc-tooltip>
+            </div>
+
             <div class="form-group clearfix block" data-ng-hide="client.bearerOnly || client.directGrantsOnly">
                 <label class="col-md-2 control-label" for="newRedirectUri"><span class="required" data-ng-show="protocol != 'saml'">*</span> Valid Redirect URIs</label>
 
@@ -252,7 +274,7 @@
                     </div>
                 </div>
 
-                <kc-tooltip>Allowed CORS origins.</kc-tooltip>
+                <kc-tooltip>Allowed CORS origins. To permit all origins of Valid Redirect URIs add '+'. To permit all origins add '*'.</kc-tooltip>
             </div>
         </fieldset>
         <fieldset data-ng-show="protocol == 'saml'">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
index 1b194bb..dfa3e00 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
@@ -37,7 +37,7 @@
             <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
             <td>{{client.enabled}}</td>
             <td ng-class="{'text-muted': !client.baseUrl}">
-                <a href="{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.baseUrl}}</a>
+                <a href="{{client.rootUrl}}{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.rootUrl}}{{client.baseUrl}}</a>
                 <span data-ng-hide="client.baseUrl">Not defined</span>
             </td>
             <td class="kc-action-cell">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
index 4ac36fd..6de9285 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
@@ -1,8 +1,9 @@
 <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
 
+    <h1>Add Realm</h1>
+
     <form class="form-horizontal" name="realmForm" novalidate>
         <fieldset>
-            <legend><span class="text">Create Realm</span></legend>
             <div class="form-group">
                 <label for="name" class="col-sm-2 control-label">Import</label>
 
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 1de1c7e..89e896c 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -241,7 +241,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
         }
 
         if (client != null) {
-            attributes.put("client", new ClientBean(client));
+            attributes.put("client", new ClientBean(client, baseUri));
         }
 
         attributes.put("login", new LoginBean(formData));
@@ -322,7 +322,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
             logger.warn("Failed to load properties", e);
         }
         if (client != null) {
-            attributes.put("client", new ClientBean(client));
+            attributes.put("client", new ClientBean(client, baseUri));
         }
 
         Properties messagesBundle;
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
index 0404ea7..f10d031 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ClientBean.java
@@ -1,6 +1,9 @@
 package org.keycloak.login.freemarker.model;
 
 import org.keycloak.models.ClientModel;
+import org.keycloak.services.util.ResolveRelative;
+
+import java.net.URI;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -10,8 +13,11 @@ public class ClientBean {
 
     protected ClientModel client;
 
-    public ClientBean(ClientModel client) {
+    private URI requestUri;
+
+    public ClientBean(ClientModel client, URI requestUri) {
         this.client = client;
+        this.requestUri = requestUri;
     }
 
     public String getClientId() {
@@ -23,7 +29,7 @@ public class ClientBean {
     }
 
     public String getBaseUrl() {
-        return client.getBaseUrl();
+        return ResolveRelative.resolveRelativeUri(requestUri, client.getRootUrl(), client.getBaseUrl());
     }
 
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
index ad5315b..87513ec 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
@@ -1,13 +1,9 @@
 package org.keycloak.admin.client.resource;
 
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import java.util.List;
 import java.util.Map;
@@ -28,6 +24,12 @@ public interface RealmResource {
     @Path("clients")
     ClientsResource clients();
 
+    @Path("client-description-converter")
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
+    @Produces(MediaType.APPLICATION_JSON)
+    ClientRepresentation convertClientDescription(String description);
+
     @Path("users")
     UsersResource users();
 
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 daccf8e..ee4317c 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -56,6 +56,10 @@ public interface ClientModel extends RoleContainerModel {
 
     void setManagementUrl(String url);
 
+    String getRootUrl();
+
+    void setRootUrl(String url);
+
     String getBaseUrl();
 
     void setBaseUrl(String url);
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 52e9721..4c742ab 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
@@ -24,6 +24,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
 
     private boolean surrogateAuthRequired;
     private String managementUrl;
+    private String rootUrl;
     private String baseUrl;
     private boolean bearerOnly;
     private boolean consentRequired;
@@ -196,6 +197,14 @@ public class ClientEntity extends AbstractIdentifiableEntity {
         this.managementUrl = managementUrl;
     }
 
+    public String getRootUrl() {
+        return rootUrl;
+    }
+
+    public void setRootUrl(String rootUrl) {
+        this.rootUrl = rootUrl;
+    }
+
     public String getBaseUrl() {
         return baseUrl;
     }
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 b71fce9..9906ab2 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
@@ -306,6 +306,7 @@ public class ModelToRepresentation {
         rep.setServiceAccountsEnabled(clientModel.isServiceAccountsEnabled());
         rep.setDirectGrantsOnly(clientModel.isDirectGrantsOnly());
         rep.setSurrogateAuthRequired(clientModel.isSurrogateAuthRequired());
+        rep.setRootUrl(clientModel.getRootUrl());
         rep.setBaseUrl(clientModel.getBaseUrl());
         rep.setNotBefore(clientModel.getNotBefore());
         rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
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 8a79f01..ffb9673 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -692,6 +692,7 @@ public class RepresentationToModel {
         client.setManagementUrl(resourceRep.getAdminUrl());
         if (resourceRep.isSurrogateAuthRequired() != null)
             client.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
+        if (resourceRep.getRootUrl() != null) client.setRootUrl(resourceRep.getRootUrl());
         if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl());
         if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
         if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
@@ -796,6 +797,7 @@ public class RepresentationToModel {
         if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
         if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
         if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout());
+        if (rep.getRootUrl() != null) resource.setRootUrl(rep.getRootUrl());
         if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl());
         if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
         if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
index 8003b70..1ddc364 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java
@@ -422,6 +422,16 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public void setRootUrl(String url) {
+        entity.setRootUrl(url);
+    }
+
+    @Override
+    public String getRootUrl() {
+        return entity.getRootUrl();
+    }
+
+    @Override
     public void setBaseUrl(String url) {
         entity.setBaseUrl(url);
     }
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 9f79b15..e181978 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
@@ -346,6 +346,18 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public String getRootUrl() {
+        if (updated != null) return updated.getRootUrl();
+        return cached.getRootUrl();
+    }
+
+    @Override
+    public void setRootUrl(String url) {
+        getDelegateForUpdate();
+        updated.setRootUrl(url);
+    }
+
+    @Override
     public String getBaseUrl() {
         if (updated != null) return updated.getBaseUrl();
         return cached.getBaseUrl();
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 11447d0..3015acf 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
@@ -42,6 +42,7 @@ public class CachedClient implements Serializable {
     private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
     private boolean surrogateAuthRequired;
     private String managementUrl;
+    private String rootUrl;
     private String baseUrl;
     private List<String> defaultRoles = new LinkedList<String>();
     private boolean bearerOnly;
@@ -76,6 +77,7 @@ public class CachedClient implements Serializable {
         }
         surrogateAuthRequired = model.isSurrogateAuthRequired();
         managementUrl = model.getManagementUrl();
+        rootUrl = model.getRootUrl();
         baseUrl = model.getBaseUrl();
         defaultRoles.addAll(model.getDefaultRoles());
         bearerOnly = model.isBearerOnly();
@@ -169,6 +171,10 @@ public class CachedClient implements Serializable {
         return managementUrl;
     }
 
+    public String getRootUrl() {
+        return rootUrl;
+    }
+
     public String getBaseUrl() {
         return baseUrl;
     }
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 b0acc41..1e0c21e 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
@@ -442,6 +442,16 @@ public class ClientAdapter implements ClientModel {
     }
 
     @Override
+    public String getRootUrl() {
+        return entity.getRootUrl();
+    }
+
+    @Override
+    public void setRootUrl(String url) {
+        entity.setRootUrl(url);
+    }
+
+    @Override
     public String getBaseUrl() {
         return entity.getBaseUrl();
     }
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 1f6ac25..b0a30cd 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
@@ -82,6 +82,9 @@ public class ClientEntity {
     @Column(name="SURROGATE_AUTH_REQUIRED")
     private boolean surrogateAuthRequired;
 
+    @Column(name="ROOT_URL")
+    private String rootUrl;
+
     @Column(name="BASE_URL")
     private String baseUrl;
 
@@ -260,6 +263,14 @@ public class ClientEntity {
         this.surrogateAuthRequired = surrogateAuthRequired;
     }
 
+    public String getRootUrl() {
+        return rootUrl;
+    }
+
+    public void setRootUrl(String rootUrl) {
+        this.rootUrl = rootUrl;
+    }
+
     public String getBaseUrl() {
         return baseUrl;
     }
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 26effca..4e1b4fa 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
@@ -440,6 +440,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
     }
 
     @Override
+    public void setRootUrl(String url) {
+        getMongoEntity().setRootUrl(url);
+        updateMongoEntity();
+    }
+
+    @Override
+    public String getRootUrl() {
+        return getMongoEntity().getRootUrl();
+    }
+
+    @Override
     public void setBaseUrl(String url) {
         getMongoEntity().setBaseUrl(url);
         updateMongoEntity();
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index d6b745b..8d6fa15 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -485,7 +485,7 @@ public class SamlProtocol implements LoginProtocol {
         }
         if (logoutServiceUrl == null && client instanceof ClientModel) logoutServiceUrl = ((ClientModel)client).getManagementUrl();
         if (logoutServiceUrl == null || logoutServiceUrl.trim().equals("")) return null;
-        return ResourceAdminManager.resolveUri(uriInfo.getRequestUri(), logoutServiceUrl);
+        return ResourceAdminManager.resolveUri(uriInfo.getRequestUri(), client.getRootUrl(), logoutServiceUrl);
 
     }
 
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
new file mode 100755
index 0000000..b874ead
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.EntityDescriptorDescriptionConverter
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
index 156ee44..17c3bdb 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LoginStatusIframeEndpoint.java
@@ -58,7 +58,7 @@ public class LoginStatusIframeEndpoint {
             }
         }
 
-        for (String r : RedirectUtils.resolveValidRedirects(uriInfo, client.getRedirectUris())) {
+        for (String r : RedirectUtils.resolveValidRedirects(uriInfo, client.getRootUrl(), client.getRedirectUris())) {
             int i = r.indexOf('/', 8);
             if (i != -1) {
                 r = r.substring(0, i);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
new file mode 100644
index 0000000..56b47bb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCClientDescriptionConverter.java
@@ -0,0 +1,65 @@
+package org.keycloak.protocol.oidc;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.exportimport.ClientDescriptionConverterFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.representations.OIDCClientRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientDescriptionConverter implements ClientDescriptionConverter, ClientDescriptionConverterFactory {
+
+    @Override
+    public boolean isSupported(String description) {
+        description = description.trim();
+        return (description.startsWith("{") && description.endsWith("}") && description.contains("\"redirect_uris\""));
+    }
+
+    @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;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ClientDescriptionConverter create(KeycloakSession session) {
+        return this;
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getId() {
+        return "openid-connect";
+    }
+
+}
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
new file mode 100644
index 0000000..7de415c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCClientRepresentation.java
@@ -0,0 +1,45 @@
+package org.keycloak.protocol.oidc.representations;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRepresentation {
+
+    @JsonProperty("redirect_uris")
+    private List<String> redirectUris;
+
+    @JsonProperty("client_name")
+    private String clientName;
+
+    @JsonProperty("client_uri")
+    private String clientUri;
+
+    public List<String> getRedirectUris() {
+        return redirectUris;
+    }
+
+    public void setRedirectUris(List<String> redirectUris) {
+        this.redirectUris = redirectUris;
+    }
+
+    public String getClientName() {
+        return clientName;
+    }
+
+    public void setClientName(String clientName) {
+        this.clientName = clientName;
+    }
+
+    public String getClientUri() {
+        return clientUri;
+    }
+
+    public void setClientUri(String clientUri) {
+        this.clientUri = clientUri;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index c43140a..c166565 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -24,6 +24,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.ProtocolMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
+import org.keycloak.protocol.oidc.utils.WebOriginsUtils;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
@@ -217,7 +218,7 @@ public class TokenManager {
     }
 
     public AccessToken createClientAccessToken(KeycloakSession session, Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession, ClientSessionModel clientSession) {
-        AccessToken token = initToken(realm, client, user, userSession, clientSession);
+        AccessToken token = initToken(realm, client, user, userSession, clientSession, session.getContext().getUri());
         for (RoleModel role : requestedRoles) {
             addComposites(token, role);
         }
@@ -380,7 +381,7 @@ public class TokenManager {
     }
 
 
-    protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientSessionModel clientSession) {
+    protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientSessionModel clientSession, UriInfo uriInfo) {
         AccessToken token = new AccessToken();
         if (clientSession != null) token.clientSession(clientSession.getId());
         token.id(KeycloakModelUtils.generateId());
@@ -398,7 +399,7 @@ public class TokenManager {
         }
         Set<String> allowedOrigins = client.getWebOrigins();
         if (allowedOrigins != null) {
-            token.setAllowedOrigins(allowedOrigins);
+            token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(uriInfo, client));
         }
         return token;
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
index df650f6..ff4601f 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java
@@ -19,22 +19,22 @@ public class RedirectUtils {
     private static final Logger logger = Logger.getLogger(RedirectUtils.class);
 
     public static String verifyRealmRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm) {
-        Set<String> validRedirects = getValidateRedirectUris(realm);
-        return verifyRedirectUri(uriInfo, redirectUri, realm, validRedirects);
+        Set<String> validRedirects = getValidateRedirectUris(uriInfo, realm);
+        return verifyRedirectUri(uriInfo, null, redirectUri, realm, validRedirects);
     }
 
     public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, ClientModel client) {
         Set<String> validRedirects = client.getRedirectUris();
-        return verifyRedirectUri(uriInfo, redirectUri, realm, validRedirects);
+        return verifyRedirectUri(uriInfo, client.getRootUrl(), redirectUri, realm, validRedirects);
     }
 
-    public static Set<String> resolveValidRedirects(UriInfo uriInfo, Set<String> validRedirects) {
+    public static Set<String> resolveValidRedirects(UriInfo uriInfo, String rootUrl, Set<String> validRedirects) {
         // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
         Set<String> resolveValidRedirects = new HashSet<String>();
         for (String validRedirect : validRedirects) {
             resolveValidRedirects.add(validRedirect); // add even relative urls.
             if (validRedirect.startsWith("/")) {
-                validRedirect = relativeToAbsoluteURI(uriInfo, validRedirect);
+                validRedirect = relativeToAbsoluteURI(uriInfo, rootUrl, validRedirect);
                 logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
                 resolveValidRedirects.add(validRedirect);
             }
@@ -42,17 +42,15 @@ public class RedirectUtils {
         return resolveValidRedirects;
     }
 
-    private static Set<String> getValidateRedirectUris(RealmModel realm) {
-        Set<String> redirects = new HashSet<String>();
+    private static Set<String> getValidateRedirectUris(UriInfo uriInfo, RealmModel realm) {
+        Set<String> redirects = new HashSet<>();
         for (ClientModel client : realm.getClients()) {
-            for (String redirect : client.getRedirectUris()) {
-                redirects.add(redirect);
-            }
+            redirects.addAll(resolveValidRedirects(uriInfo, client.getRootUrl(), client.getRedirectUris()));
         }
         return redirects;
     }
 
-    private static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, Set<String> validRedirects) {
+    private static String verifyRedirectUri(UriInfo uriInfo, String rootUrl, String redirectUri, RealmModel realm, Set<String> validRedirects) {
         if (redirectUri == null) {
             if (validRedirects.size() != 1) return null;
             String validRedirect = validRedirects.iterator().next();
@@ -66,7 +64,7 @@ public class RedirectUtils {
             redirectUri = null;
         } else {
             String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
-            Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, validRedirects);
+            Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, rootUrl, validRedirects);
 
             boolean valid = matchesRedirects(resolveValidRedirects, r);
 
@@ -86,7 +84,7 @@ public class RedirectUtils {
                 valid = matchesRedirects(resolveValidRedirects, r);
             }
             if (valid && redirectUri.startsWith("/")) {
-                redirectUri = relativeToAbsoluteURI(uriInfo, redirectUri);
+                redirectUri = relativeToAbsoluteURI(uriInfo, rootUrl, redirectUri);
             }
             redirectUri = valid ? redirectUri : null;
         }
@@ -98,13 +96,16 @@ public class RedirectUtils {
         }
     }
 
-    private static String relativeToAbsoluteURI(UriInfo uriInfo, String relative) {
-        URI baseUri = uriInfo.getBaseUri();
-        String uri = baseUri.getScheme() + "://" + baseUri.getHost();
-        if (baseUri.getPort() != -1) {
-            uri += ":" + baseUri.getPort();
+    private static String relativeToAbsoluteURI(UriInfo uriInfo, String rootUrl, String relative) {
+        if (rootUrl == null) {
+            URI baseUri = uriInfo.getBaseUri();
+            String uri = baseUri.getScheme() + "://" + baseUri.getHost();
+            if (baseUri.getPort() != -1) {
+                uri += ":" + baseUri.getPort();
+            }
+            rootUrl = uri;
         }
-        relative = uri + relative;
+        relative = rootUrl + relative;
         return relative;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/WebOriginsUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/WebOriginsUtils.java
new file mode 100644
index 0000000..5684ef6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/WebOriginsUtils.java
@@ -0,0 +1,30 @@
+package org.keycloak.protocol.oidc.utils;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.util.UriUtils;
+
+import javax.ws.rs.core.UriInfo;
+import java.util.Set;
+
+/**
+ * Created by st on 22.09.15.
+ */
+public class WebOriginsUtils {
+
+    public static final String INCLUDE_REDIRECTS = "+";
+
+    public static Set<String> resolveValidWebOrigins(UriInfo uriInfo, ClientModel client) {
+        Set<String> webOrigins = client.getWebOrigins();
+        if (webOrigins != null && webOrigins.contains("+")) {
+            webOrigins.remove(INCLUDE_REDIRECTS);
+            client.getRedirectUris();
+            for (String redirectUri : RedirectUtils.resolveValidRedirects(uriInfo, client.getRootUrl(), client.getRedirectUris())) {
+                if (redirectUri.startsWith("http://") || redirectUri.startsWith("https://")) {
+                    webOrigins.add(UriUtils.getOrigin(redirectUri));
+                }
+            }
+        }
+        return webOrigins;
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index 8a94b6e..d2014d5 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -46,8 +46,8 @@ public class ResourceAdminManager {
         this.session = session;
     }
 
-    public static String resolveUri(URI requestUri, String uri) {
-        String absoluteURI = ResolveRelative.resolveRelativeUri(requestUri, uri);
+    public static String resolveUri(URI requestUri, String rootUrl, String uri) {
+        String absoluteURI = ResolveRelative.resolveRelativeUri(requestUri, rootUrl, uri);
         return StringPropertyReplacer.replaceProperties(absoluteURI);
 
    }
@@ -58,8 +58,7 @@ public class ResourceAdminManager {
             return null;
         }
 
-        // this is to support relative admin urls when keycloak and clients are deployed on the same machine
-        String absoluteURI = ResolveRelative.resolveRelativeUri(requestUri, mgmtUrl);
+        String absoluteURI = ResolveRelative.resolveRelativeUri(requestUri, client.getRootUrl(), mgmtUrl);
 
         // this is for resolving URI like "http://${jboss.host.name}:8080/..." in order to send request to same machine and avoid request to LB in cluster environment
         return StringPropertyReplacer.replaceProperties(absoluteURI);
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 3b6edea..20e7b6e 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -779,7 +779,7 @@ public class AccountService extends AbstractSecuredLocalService {
             if (referrerUri != null) {
                 referrerUri = RedirectUtils.verifyRedirectUri(uriInfo, referrerUri, realm, referrerClient);
             } else {
-                referrerUri = ResolveRelative.resolveRelativeUri(uriInfo.getRequestUri(), referrerClient.getBaseUrl());
+                referrerUri = ResolveRelative.resolveRelativeUri(uriInfo.getRequestUri(), client.getRootUrl(), referrerClient.getBaseUrl());
             }
 
             if (referrerUri != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 70022c0..4ef2a63 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -4,8 +4,6 @@ import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
 import org.keycloak.events.EventType;
 import org.keycloak.events.admin.OperationType;
-import org.keycloak.exportimport.ClientImporter;
-import org.keycloak.exportimport.ClientImporterFactory;
 import org.keycloak.freemarker.Theme;
 import org.keycloak.freemarker.ThemeProvider;
 import org.keycloak.models.KeycloakSession;
@@ -51,7 +49,6 @@ public class ServerInfoAdminResource {
         setSocialProviders(info);
         setIdentityProviders(info);
         setThemes(info);
-        setClientImporters(info);
         setProviders(info);
         setProtocolMapperTypes(info);
         setBuiltinProtocolMappers(info);
@@ -144,7 +141,7 @@ public class ServerInfoAdminResource {
             ProtocolMapper mapper = (ProtocolMapper)p;
             List<ProtocolMapperTypeRepresentation> types = info.getProtocolMapperTypes().get(mapper.getProtocol());
             if (types == null) {
-                types = new LinkedList<ProtocolMapperTypeRepresentation>();
+                types = new LinkedList<>();
                 info.getProtocolMapperTypes().put(mapper.getProtocol(), types);
             }
             ProtocolMapperTypeRepresentation rep = new ProtocolMapperTypeRepresentation();
@@ -179,17 +176,6 @@ public class ServerInfoAdminResource {
         }
     }
 
-    private void setClientImporters(ServerInfoRepresentation info) {
-        info.setClientImporters(new LinkedList<Map<String, String>>());
-        for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ClientImporter.class)) {
-            ClientImporterFactory factory = (ClientImporterFactory)p;
-            Map<String, String> data = new HashMap<String, String>();
-            data.put("id", factory.getId());
-            data.put("name", factory.getDisplayName());
-            info.getClientImporters().add(data);
-        }
-    }
-
     private static Map<String, List<String>> createEnumsMap(Class... enums) {
         Map<String, List<String>> m = new HashMap<>();
         for (Class e : enums) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index fda0ee3..82dc0bd 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -13,7 +13,8 @@ import org.keycloak.events.EventType;
 import org.keycloak.events.admin.AdminEvent;
 import org.keycloak.events.admin.AdminEventQuery;
 import org.keycloak.events.admin.OperationType;
-import org.keycloak.exportimport.ClientImporter;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.exportimport.ClientDescriptionConverterFactory;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -25,7 +26,9 @@ import org.keycloak.models.cache.CacheUserProvider;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.adapters.action.GlobalRequestResult;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
@@ -99,10 +102,18 @@ public class RealmAdminResource {
      *
      * @return
      */
-    @Path("client-importers/{formatId}")
-    public Object getClientImporter(@PathParam("formatId") String formatId) {
-        ClientImporter importer = session.getProvider(ClientImporter.class, formatId);
-        return importer.createJaxrsService(realm, auth);
+    @Path("client-description-converter")
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
+    @POST
+    @Produces(MediaType.APPLICATION_JSON)
+    public ClientRepresentation convertClientDescription(String description) {
+        for (ProviderFactory<ClientDescriptionConverter> factory : session.getKeycloakSessionFactory().getProviderFactories(ClientDescriptionConverter.class)) {
+            if (((ClientDescriptionConverterFactory) factory).isSupported(description)) {
+                return factory.create(session).convertToInternal(description);
+            }
+        }
+
+        throw new BadRequestException("Unsupported format");
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/services/resources/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/resources/ClientRegistrationService.java
new file mode 100644
index 0000000..87e55b8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/ClientRegistrationService.java
@@ -0,0 +1,100 @@
+package org.keycloak.services.resources;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.BadRequestException;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.exportimport.ClientDescriptionConverter;
+import org.keycloak.exportimport.KeycloakClientDescriptionConverter;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+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 ClientRegistrationService {
+
+    protected static final Logger logger = Logger.getLogger(ClientRegistrationService.class);
+
+    private RealmModel realm;
+
+    private EventBuilder event;
+
+    @Context
+    private KeycloakSession session;
+
+    public ClientRegistrationService(RealmModel realm, EventBuilder event) {
+        this.realm = realm;
+        this.event = event;
+    }
+
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
+    public Response create(String description, @QueryParam("format") String format) {
+        if (format == null) {
+            format = KeycloakClientDescriptionConverter.ID;
+        }
+
+        ClientDescriptionConverter converter = session.getProvider(ClientDescriptionConverter.class, format);
+        if (converter == null) {
+            throw new BadRequestException("Invalid format");
+        }
+        ClientRepresentation rep = converter.convertToInternal(description);
+
+        try {
+            ClientModel clientModel = RepresentationToModel.createClient(session, realm, rep, true);
+            rep = ModelToRepresentation.toRepresentation(clientModel);
+            URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+            return Response.created(uri).entity(rep).build();
+        } catch (ModelDuplicateException e) {
+            return ErrorResponse.exists("Client " + rep.getClientId() + " already exists");
+        }
+    }
+
+    @GET
+    @Path("{clientId}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public ClientRepresentation get(@PathParam("clientId") String clientId) {
+        AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
+        ClientModel client = clientAuth.getClient();
+        if (client == null) {
+            throw new NotFoundException("Client not found");
+        }
+        return ModelToRepresentation.toRepresentation(client);
+    }
+
+    @PUT
+    @Path("{clientId}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
+        ClientModel client = realm.getClientByClientId(clientId);
+        if (client == null) {
+            throw new NotFoundException("Client not found");
+        }
+        RepresentationToModel.updateClient(rep, client);
+    }
+
+    @DELETE
+    @Path("{clientId}")
+    public void delete(@PathParam("clientId") String clientId) {
+        ClientModel client = realm.getClientByClientId(clientId);
+        if (client == null) {
+            throw new NotFoundException("Client not found");
+        }
+        realm.removeClient(client.getId());
+    }
+
+}
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 083eb9a..52f49df 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -112,6 +112,15 @@ public class RealmsResource {
         return service;
     }
 
+//    @Path("{realm}/client-registration")
+//    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);
+//        ResteasyProviderFactory.getInstance().injectProperties(service);
+//        return service;
+//    }
+
     @Path("{realm}/clients-managements")
     public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
         RealmModel realm = init(name);
diff --git a/services/src/main/java/org/keycloak/services/util/ResolveRelative.java b/services/src/main/java/org/keycloak/services/util/ResolveRelative.java
index 0954f73..757ff58 100755
--- a/services/src/main/java/org/keycloak/services/util/ResolveRelative.java
+++ b/services/src/main/java/org/keycloak/services/util/ResolveRelative.java
@@ -8,13 +8,17 @@ import java.net.URI;
  * @version $Revision: 1 $
  */
 public class ResolveRelative {
-    public static String resolveRelativeUri(URI requestUri, String url) {
+    public static String resolveRelativeUri(URI requestUri, String rootUrl, String url) {
         if (url == null || !url.startsWith("/")) return url;
-        UriBuilder builder = UriBuilder.fromPath(url).host(requestUri.getHost());
-        builder.scheme(requestUri.getScheme());
-        if (requestUri.getPort() != -1) {
-            builder.port(requestUri.getPort());
+        if (rootUrl != null) {
+            return rootUrl + url;
+        } else {
+            UriBuilder builder = UriBuilder.fromPath(url).host(requestUri.getHost());
+            builder.scheme(requestUri.getScheme());
+            if (requestUri.getPort() != -1) {
+                builder.port(requestUri.getPort());
+            }
+            return builder.build().toString();
         }
-        return builder.build().toString();
     }
 }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory b/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
new file mode 100644
index 0000000..139c7b1
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.exportimport.ClientDescriptionConverterFactory
@@ -0,0 +1,2 @@
+org.keycloak.exportimport.KeycloakClientDescriptionConverter
+org.keycloak.protocol.oidc.OIDCClientDescriptionConverter
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 0a9b272..6d88f97 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1,6 +1,6 @@
 org.keycloak.protocol.LoginProtocolSpi
 org.keycloak.protocol.ProtocolMapperSpi
-org.keycloak.exportimport.ClientImportSpi
+org.keycloak.exportimport.ClientDescriptionConverterSpi
 org.keycloak.wellknown.WellKnownSpi
 org.keycloak.messages.MessagesSpi
 org.keycloak.authentication.AuthenticatorSpi
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index aaed86e..deda8f8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -177,6 +177,7 @@ public class AdminAPITest {
         if (appRep.isBearerOnly() != null) Assert.assertEquals(appRep.isBearerOnly(), storedApp.isBearerOnly());
         if (appRep.isPublicClient() != null) Assert.assertEquals(appRep.isPublicClient(), storedApp.isPublicClient());
         if (appRep.isFullScopeAllowed() != null) Assert.assertEquals(appRep.isFullScopeAllowed(), storedApp.isFullScopeAllowed());
+        if (appRep.getRootUrl() != null) Assert.assertEquals(appRep.getRootUrl(), storedApp.getRootUrl());
         if (appRep.getAdminUrl() != null) Assert.assertEquals(appRep.getAdminUrl(), storedApp.getAdminUrl());
         if (appRep.getBaseUrl() != null) Assert.assertEquals(appRep.getBaseUrl(), storedApp.getBaseUrl());
         if (appRep.isSurrogateAuthRequired() != null) Assert.assertEquals(appRep.isSurrogateAuthRequired(), storedApp.isSurrogateAuthRequired());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
index 8208aa2..8c41dfa 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -1,13 +1,18 @@
 package org.keycloak.testsuite.admin;
 
+import org.apache.commons.io.IOUtils;
 import org.junit.Test;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.services.managers.RealmManager;
+import org.keycloak.util.JsonSerialization;
 
 import javax.ws.rs.NotFoundException;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -132,4 +137,35 @@ public class RealmTest extends AbstractClientTest {
         }
     }
 
+    @Test
+    public void convertKeycloakClientDescription() throws IOException {
+        ClientRepresentation description = new ClientRepresentation();
+        description.setClientId("client-id");
+        description.setRedirectUris(Collections.singletonList("http://localhost"));
+
+        ClientRepresentation converted = realm.convertClientDescription(JsonSerialization.writeValueAsString(description));
+        assertEquals("client-id", converted.getClientId());
+        assertEquals("http://localhost", converted.getRedirectUris().get(0));
+    }
+
+    @Test
+    public void convertOIDCClientDescription() throws IOException {
+        String description = IOUtils.toString(getClass().getResourceAsStream("/client-descriptions/client-oidc.json"));
+
+        ClientRepresentation converted = realm.convertClientDescription(description);
+        assertEquals(36, converted.getClientId().length());
+        assertEquals(1, converted.getRedirectUris().size());
+        assertEquals("http://localhost", converted.getRedirectUris().get(0));
+    }
+
+    @Test
+    public void convertSAMLClientDescription() throws IOException {
+        String description = IOUtils.toString(getClass().getResourceAsStream("/client-descriptions/saml-entity-descriptor.xml"));
+
+        ClientRepresentation converted = realm.convertClientDescription(description);
+        assertEquals("loadbalancer-9.siroe.com", converted.getClientId());
+        assertEquals(1, converted.getRedirectUris().size());
+        assertEquals("https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp", converted.getRedirectUris().get(0));
+    }
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
index 358fd23..f97a05e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTest.java
@@ -30,7 +30,7 @@ public class SamlAdapterTest {
             initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig",  "bad-client-post-sig.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig",  "bad-realm-post-sig.war", classLoader);
             initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc",  "post-enc.war", classLoader);
-            SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth", this);
+            SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth");
             server.getServer().deploy(createDeploymentInfo("employee.war", "/employee", SamlSPFacade.class));
 
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
index 6e0817b..2c32d51 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SamlAdapterTestStrategy.java
@@ -1,5 +1,6 @@
 package org.keycloak.testsuite.keycloaksaml;
 
+import org.apache.commons.io.IOUtils;
 import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
 import org.junit.Assert;
 import org.junit.ClassRule;
@@ -8,6 +9,8 @@ import org.junit.Test;
 import org.junit.rules.ExternalResource;
 import org.keycloak.Config;
 import org.keycloak.adapters.saml.SamlPrincipal;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
@@ -24,6 +27,7 @@ import org.keycloak.protocol.saml.mappers.HardcodedRole;
 import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.RoleNameMapper;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 import org.keycloak.services.managers.RealmManager;
@@ -34,6 +38,7 @@ import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.WebDriver;
 
 import javax.ws.rs.client.Client;
@@ -51,6 +56,8 @@ import java.io.InputStream;
 import java.util.LinkedList;
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -106,9 +113,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     public void testPostSimpleLoginLogout() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
         System.out.println(driver.getPageSource());
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post?GLO=true");
@@ -117,9 +124,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     public void testPostSimpleUnauthorized(CheckAuthError error) {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("unauthorized", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
         System.out.println(driver.getPageSource());
         error.check(driver);
     }
@@ -127,7 +134,7 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
     public void testPostSimpleLoginLogoutIdpInitiated() {
         driver.navigate().to(AUTH_SERVER_URL + "/realms/demo/protocol/saml/clients/sales-post");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post/");
         System.out.println(driver.getPageSource());
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post?GLO=true");
@@ -136,9 +143,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     public void testPostSignedLoginLogout() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig?GLO=true");
         checkLoggedOut(APP_SERVER_BASE_URL + "/sales-post-sig/");
@@ -146,9 +153,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
     }
     public void testPostSignedLoginLogoutTransientNameID() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig-transient/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-transient/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-transient/");
         System.out.println(driver.getPageSource());
         Assert.assertFalse(driver.getPageSource().contains("bburke"));
         Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
@@ -158,9 +165,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
     }
     public void testPostSignedLoginLogoutPersistentNameID() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig-persistent/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-persistent/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-persistent/");
         System.out.println(driver.getPageSource());
         Assert.assertFalse(driver.getPageSource().contains("bburke"));
         Assert.assertTrue(driver.getPageSource().contains("principal=G-"));
@@ -170,9 +177,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
     }
     public void testPostSignedLoginLogoutEmailNameID() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig-email/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-email/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig-email/");
         System.out.println(driver.getPageSource());
         Assert.assertTrue(driver.getPageSource().contains("principal=bburke@redhat.com"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig-email?GLO=true");
@@ -188,8 +195,8 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         Assert.assertTrue(driver.getCurrentUrl().startsWith(AUTH_SERVER_URL + "/realms/demo/protocol/saml"));
         System.out.println(driver.getCurrentUrl());
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee/");
-        Assert.assertEquals(SamlSPFacade.sentRelayState, SamlSPFacade.RELAY_STATE);
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee/");
+        assertEquals(SamlSPFacade.sentRelayState, SamlSPFacade.RELAY_STATE);
         Assert.assertNotNull(SamlSPFacade.samlResponse);
 
     }
@@ -206,13 +213,13 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
             requiredRoles.add("user");
             SendUsernameServlet.checkRoles = requiredRoles;
             loginPage.login("bburke", "password");
-            Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee2/");
+            assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee2/");
             SendUsernameServlet.checkRoles = null;
             SamlPrincipal principal = (SamlPrincipal) SendUsernameServlet.sentPrincipal;
             Assert.assertNotNull(principal);
-            Assert.assertEquals("bburke@redhat.com", principal.getAttribute(X500SAMLProfileConstants.EMAIL.get()));
-            Assert.assertEquals("bburke@redhat.com", principal.getFriendlyAttribute("email"));
-            Assert.assertEquals("617", principal.getAttribute("phone"));
+            assertEquals("bburke@redhat.com", principal.getAttribute(X500SAMLProfileConstants.EMAIL.get()));
+            assertEquals("bburke@redhat.com", principal.getFriendlyAttribute("email"));
+            assertEquals("617", principal.getAttribute("phone"));
             Assert.assertNull(principal.getFriendlyAttribute("phone"));
             driver.navigate().to(APP_SERVER_BASE_URL + "/employee2/?GLO=true");
             checkLoggedOut(APP_SERVER_BASE_URL + "/employee2/");
@@ -252,11 +259,11 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
             requiredRoles.add("pee-on");
             SendUsernameServlet.checkRoles = requiredRoles;
             loginPage.login("bburke", "password");
-            Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee2/");
+            assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee2/");
             SendUsernameServlet.checkRoles = null;
             SamlPrincipal principal = (SamlPrincipal) SendUsernameServlet.sentPrincipal;
             Assert.assertNotNull(principal);
-            Assert.assertEquals("hard", principal.getAttribute("hardcoded-attribute"));
+            assertEquals("hard", principal.getAttribute("hardcoded-attribute"));
 
 
         }
@@ -266,7 +273,7 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         driver.navigate().to(APP_SERVER_BASE_URL + "/employee-sig/");
         Assert.assertTrue(driver.getCurrentUrl().startsWith(AUTH_SERVER_URL + "/realms/demo/protocol/saml"));
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/employee-sig?GLO=true");
         checkLoggedOut(APP_SERVER_BASE_URL + "/employee-sig/");
@@ -277,7 +284,7 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         driver.navigate().to(APP_SERVER_BASE_URL + "/employee-sig-front/");
         Assert.assertTrue(driver.getCurrentUrl().startsWith(AUTH_SERVER_URL + "/realms/demo/protocol/saml"));
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig-front/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig-front/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/employee-sig-front?GLO=true");
         checkLoggedOut(APP_SERVER_BASE_URL + "/employee-sig-front/");
@@ -291,19 +298,19 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
         Assert.assertTrue(driver.getCurrentUrl().startsWith(AUTH_SERVER_URL + "/realms/demo/protocol/saml"));
         System.out.println("login to form");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
 
         // visit 2nd app
         System.out.println("visit 2nd app ");
         driver.navigate().to(APP_SERVER_BASE_URL + "/employee-sig-front/");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig-front/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/employee-sig-front/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
 
         // visit 3rd app
         System.out.println("visit 3rd app ");
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-sig/");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-sig/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
 
         // logout of first app
@@ -320,9 +327,9 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     public void testPostEncryptedLoginLogout() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-enc/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-enc/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-post-enc/");
         Assert.assertTrue(driver.getPageSource().contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-post-enc?GLO=true");
         checkLoggedOut(APP_SERVER_BASE_URL + "/sales-post-enc/");
@@ -330,8 +337,8 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
     }
     public void testPostBadClientSignature() {
         driver.navigate().to(APP_SERVER_BASE_URL + "/bad-client-sales-post-sig/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
-        Assert.assertEquals(driver.getTitle(), "We're sorry...");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getTitle(), "We're sorry...");
 
     }
     public static interface CheckAuthError {
@@ -340,39 +347,19 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     public void testPostBadRealmSignature(CheckAuthError error) {
         driver.navigate().to(APP_SERVER_BASE_URL + "/bad-realm-sales-post-sig/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/bad-realm-sales-post-sig/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/bad-realm-sales-post-sig/");
         System.out.println(driver.getPageSource());
         error.check(driver);
     }
 
-    private static String createToken(String AUTH_SERVER_URL, AbstractKeycloakRule keycloakRule) {
-        KeycloakSession session = keycloakRule.startSession();
-        try {
-            RealmManager manager = new RealmManager(session);
-
-            RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
-            ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
-            TokenManager tm = new TokenManager();
-            UserModel admin = session.users().getUserByUsername("admin", adminRealm);
-            ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
-            clientSession.setNote(OIDCLoginProtocol.ISSUER, AUTH_SERVER_URL + "/realms/master");
-            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
-            AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
-            return tm.encodeToken(adminRealm, token);
-        } finally {
-            keycloakRule.stopSession(session, true);
-        }
-    }
-
-
     public void testMetadataPostSignedLoginLogout() throws Exception {
 
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-metadata/");
-        Assert.assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
+        assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
         loginPage.login("bburke", "password");
-        Assert.assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-metadata/");
+        assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/sales-metadata/");
         String pageSource = driver.getPageSource();
         Assert.assertTrue(pageSource.contains("bburke"));
         driver.navigate().to(APP_SERVER_BASE_URL + "/sales-metadata?GLO=true");
@@ -380,30 +367,21 @@ public class SamlAdapterTestStrategy  extends ExternalResource {
 
     }
 
-    public static void uploadSP(String AUTH_SERVER_URL, AbstractKeycloakRule keycloakRule) {
-        String token = createToken(AUTH_SERVER_URL, keycloakRule);
-        final String authHeader = "Bearer " + token;
-        ClientRequestFilter authFilter = new ClientRequestFilter() {
-            @Override
-            public void filter(ClientRequestContext requestContext) throws IOException {
-                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
-            }
-        };
-        Client client = ClientBuilder.newBuilder().register(authFilter).build();
-        UriBuilder authBase = UriBuilder.fromUri(AUTH_SERVER_URL + "");
-        WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
-
-
-        MultipartFormDataOutput formData = new MultipartFormDataOutput();
-        InputStream is = SamlAdapterTestStrategy.class.getResourceAsStream("/keycloak-saml/sp-metadata.xml");
-        Assert.assertNotNull(is);
-        formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
-
-        WebTarget upload = adminRealms.path("demo/client-importers/saml2-entity-descriptor/upload");
-        System.out.println(upload.getUri());
-        Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
-        Assert.assertEquals(204, response.getStatus());
-        response.close();
-        client.close();
+    public static void uploadSP(String AUTH_SERVER_URL) {
+        try {
+            Keycloak keycloak = Keycloak.getInstance(AUTH_SERVER_URL, "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID, null);
+            RealmResource admin = keycloak.realm("demo");
+
+            admin.toRepresentation();
+
+            ClientRepresentation clientRep = admin.convertClientDescription(IOUtils.toString(SamlAdapterTestStrategy.class.getResourceAsStream("/keycloak-saml/sp-metadata.xml")));
+            Response response = admin.clients().create(clientRep);
+
+            assertEquals(201, response.getStatus());
+
+            keycloak.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index 2d2ab44..019bfea 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -1,11 +1,14 @@
 package org.keycloak.testsuite.saml;
 
+import org.apache.commons.io.IOUtils;
 import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
 import org.junit.Assert;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.Config;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
@@ -22,9 +25,11 @@ import org.keycloak.protocol.saml.mappers.HardcodedRole;
 import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.RoleNameMapper;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.resources.admin.AdminRoot;
 import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
@@ -56,6 +61,8 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import static org.junit.Assert.assertEquals;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -480,30 +487,21 @@ public class SamlBindingTest {
     }
 
     public static void uploadSP() {
-        String token = createToken();
-        final String authHeader = "Bearer " + token;
-        ClientRequestFilter authFilter = new ClientRequestFilter() {
-            @Override
-            public void filter(ClientRequestContext requestContext) throws IOException {
-                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
-            }
-        };
-        Client client = ClientBuilder.newBuilder().register(authFilter).build();
-        UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
-        WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
-
-
-        MultipartFormDataOutput formData = new MultipartFormDataOutput();
-        InputStream is = SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml");
-        Assert.assertNotNull(is);
-        formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
-
-        WebTarget upload = adminRealms.path("demo/client-importers/saml2-entity-descriptor/upload");
-        System.out.println(upload.getUri());
-        Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
-        Assert.assertEquals(204, response.getStatus());
-        response.close();
-        client.close();
+        try {
+            Keycloak keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CONSOLE_CLIENT_ID, null);
+            RealmResource admin = keycloak.realm("demo");
+
+            admin.toRepresentation();
+
+            ClientRepresentation clientRep = admin.convertClientDescription(IOUtils.toString(SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml")));
+            Response response = admin.clients().create(clientRep);
+
+            assertEquals(201, response.getStatus());
+
+            keycloak.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
 
 
diff --git a/testsuite/integration/src/test/resources/client-descriptions/client-oidc.json b/testsuite/integration/src/test/resources/client-descriptions/client-oidc.json
new file mode 100644
index 0000000..51562fd
--- /dev/null
+++ b/testsuite/integration/src/test/resources/client-descriptions/client-oidc.json
@@ -0,0 +1,6 @@
+{
+  "client_name": "Name",
+  "redirect_uris": [
+    "http://localhost"
+  ]
+}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/client-descriptions/saml-entity-descriptor.xml b/testsuite/integration/src/test/resources/client-descriptions/saml-entity-descriptor.xml
new file mode 100644
index 0000000..b00ab25
--- /dev/null
+++ b/testsuite/integration/src/test/resources/client-descriptions/saml-entity-descriptor.xml
@@ -0,0 +1,82 @@
+<EntityDescriptor
+    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+    entityID="loadbalancer-9.siroe.com">
+    <SPSSODescriptor
+        AuthnRequestsSigned="false"
+        WantAssertionsSigned="false"
+        protocolSupportEnumeration=
+            "urn:oasis:names:tc:SAML:2.0:protocol">
+        <KeyDescriptor use="signing">
+            <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+                <X509Data>
+                    <X509Certificate>
+MIICYDCCAgqgAwIBAgICBoowDQYJKoZIhvcNAQEEBQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+EwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEeMBwGA1UEChMVU3VuIE1pY3Jvc3lz
+dGVtcyBJbmMuMRowGAYDVQQLExFJZGVudGl0eSBTZXJ2aWNlczEcMBoGA1UEAxMTQ2VydGlmaWNh
+dGUgTWFuYWdlcjAeFw0wNjExMDIxOTExMzRaFw0xMDA3MjkxOTExMzRaMDcxEjAQBgNVBAoTCXNp
+cm9lLmNvbTEhMB8GA1UEAxMYbG9hZGJhbGFuY2VyLTkuc2lyb2UuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCjOwa5qoaUuVnknqf5pdgAJSEoWlvx/jnUYbkSDpXLzraEiy2UhvwpoBgB
+EeTSUaPPBvboCItchakPI6Z/aFdH3Wmjuij9XD8r1C+q//7sUO0IGn0ORycddHhoo0aSdnnxGf9V
+tREaqKm9dJ7Yn7kQHjo2eryMgYxtr/Z5Il5F+wIDAQABo2AwXjARBglghkgBhvhCAQEEBAMCBkAw
+DgYDVR0PAQH/BAQDAgTwMB8GA1UdIwQYMBaAFDugITflTCfsWyNLTXDl7cMDUKuuMBgGA1UdEQQR
+MA+BDW1hbGxhQHN1bi5jb20wDQYJKoZIhvcNAQEEBQADQQB/6DOB6sRqCZu2OenM9eQR0gube85e
+nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
+                    </X509Certificate>
+                </X509Data>
+            </KeyInfo>
+        </KeyDescriptor>
+        <KeyDescriptor use="encryption">
+            <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
+                <X509Data>
+                    <X509Certificate>
+MIICTDCCAfagAwIBAgICBo8wDQYJKoZIhvcNAQEEBQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+EwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEeMBwGA1UEChMVU3VuIE1pY3Jvc3lz
+dGVtcyBJbmMuMRowGAYDVQQLExFJZGVudGl0eSBTZXJ2aWNlczEcMBoGA1UEAxMTQ2VydGlmaWNh
+dGUgTWFuYWdlcjAeFw0wNjExMDcyMzU2MTdaFw0xMDA4MDMyMzU2MTdaMCMxITAfBgNVBAMTGGxv
+YWRiYWxhbmNlci05LnNpcm9lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAw574iRU6
+HsSO4LXW/OGTXyfsbGv6XRVOoy3v+J1pZ51KKejcDjDJXNkKGn3/356AwIaqbcymWd59T0zSqYfR
+Hn+45uyjYxRBmVJseLpVnOXLub9jsjULfGx0yjH4w+KsZSZCXatoCHbj/RJtkzuZY6V9to/hkH3S
+InQB4a3UAgMCAwEAAaNgMF4wEQYJYIZIAYb4QgEBBAQDAgZAMA4GA1UdDwEB/wQEAwIE8DAfBgNV
+HSMEGDAWgBQ7oCE35Uwn7FsjS01w5e3DA1CrrjAYBgNVHREEETAPgQ1tYWxsYUBzdW4uY29tMA0G
+CSqGSIb3DQEBBAUAA0EAMlbfBg/ff0Xkv4DOR5LEqmfTZKqgdlD81cXynfzlF7XfnOqI6hPIA90I
+x5Ql0ejivIJAYcMGUyA+/YwJg2FGoA==
+                    </X509Certificate>
+                </X509Data>
+            </KeyInfo>
+            <EncryptionMethod Algorithm=
+                "https://www.w3.org/2001/04/xmlenc#aes128-cbc">
+                <KeySize xmlns="https://www.w3.org/2001/04/xmlenc#">128</KeySize>
+            </EncryptionMethod>
+        </KeyDescriptor>
+        <SingleLogoutService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/SPSloRedirect/metaAlias/sp"
+            ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPSloRedirect/metaAlias/sp"/>
+        <SingleLogoutService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/SPSloSoap/metaAlias/sp"/>
+       <ManageNameIDService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/SPMniRedirect/metaAlias/sp"
+            ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPMniRedirect/metaAlias/sp"/>
+        <ManageNameIDService
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/SPMniSoap/metaAlias/sp"
+            ResponseLocation="https://LoadBalancer-9.siroe.com:3443/federation/SPMniSoap/metaAlias/sp"/>
+        <NameIDFormat>
+            urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+        </NameIDFormat>
+        <NameIDFormat>
+            urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+        </NameIDFormat>
+        <AssertionConsumerService
+            isDefault="true"
+            index="0"
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp"/>
+        <AssertionConsumerService
+            index="1"
+            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+            Location="https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp"/>
+    </SPSSODescriptor>
+</EntityDescriptor>
diff --git a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
index 98eec5a..b16f954 100755
--- a/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
+++ b/testsuite/tomcat6/src/test/java/org/keycloak/testsuite/TomcatSamlTest.java
@@ -30,7 +30,6 @@ import org.junit.Test;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.testsuite.adapter.AdapterTestStrategy;
 import org.keycloak.testsuite.keycloaksaml.SamlAdapterTestStrategy;
 import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.openqa.selenium.WebDriver;
@@ -73,7 +72,7 @@ public class TomcatSamlTest {
         tomcat.deploySaml("/bad-client-sales-post-sig", "bad-client-signed-post");
         tomcat.deploySaml("/bad-realm-sales-post-sig", "bad-realm-signed-post");
         tomcat.deploySaml("/sales-post-enc", "encrypted-post");
-        SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth", keycloakRule);
+        SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth");
 
 
         tomcat.start();