You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/03/25 11:18:55 UTC

[syncope] branch SYNCOPE-163-1 updated: [SYNCOPE-160] Refactoring ClientAppService

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch SYNCOPE-163-1
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/SYNCOPE-163-1 by this push:
     new 8ed0f7a  [SYNCOPE-160] Refactoring ClientAppService
8ed0f7a is described below

commit 8ed0f7a0a6e8e10d86df0422721600afbf2bb317
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Mar 25 12:18:42 2020 +0100

    [SYNCOPE-160] Refactoring ClientAppService
---
 .../module/AbstractAuthModuleConf.java             |   6 +-
 .../lib/authentication/module/AuthModuleConf.java  |   4 +-
 .../syncope/common/lib/to/client/ClientAppTO.java  |   4 +-
 .../{OIDCRelyingPartyTO.java => OIDCRPTO.java}     |   8 +-
 ...{SAML2ServiceProviderTO.java => SAML2SPTO.java} |  16 +-
 .../syncope/common/lib/types/AMEntitlement.java    |  28 +--
 .../ClientAppType.java}                            |   9 +-
 ...rviceProviderNameId.java => SAML2SPNameId.java} |   5 +-
 .../common/rest/api/service/ClientAppService.java  |  65 ++---
 .../org/apache/syncope/common/lib/to/AnyTO.java    |   3 -
 .../common/lib/types/IdRepoImplementationType.java |   2 +-
 .../syncope/common/lib/types/PolicyType.java       |   2 +-
 .../apache/syncope/core/logic/ClientAppLogic.java  | 204 ++++++++++++++++
 .../core/logic/oidc/OIDCRelyingPartyLogic.java     | 101 --------
 .../core/logic/saml/SAML2ServiceProviderLogic.java |  98 --------
 .../rest/cxf/service/ClientAppServiceImpl.java     |  68 ++----
 .../org/apache/syncope/core/logic/PolicyLogic.java |   3 +
 .../syncope/core/logic/cocoon/FopSerializer.java   |  26 +-
 .../syncope/core/logic/cocoon/TextSerializer.java  |   2 +-
 .../syncope/core/logic/cocoon/XSLTTransformer.java |  26 +-
 .../{OIDCRelyingPartyDAO.java => OIDCRPDAO.java}   |  17 +-
 ...AML2ServiceProviderDAO.java => SAML2SPDAO.java} |  17 +-
 .../api/entity/authentication/ClientAppUtils.java  |  12 +-
 .../authentication/ClientAppUtilsFactory.java}     |  16 +-
 .../{OIDCRelyingParty.java => OIDCRP.java}         |   6 +-
 .../{SAML2ServiceProvider.java => SAML2SP.java}    |  12 +-
 .../core/persistence/jpa/dao/AbstractDAO.java      |   5 +-
 .../core/persistence/jpa/dao/JPAPolicyDAO.java     |   9 +-
 ...AOIDCRelyingPartyDAO.java => JPAOIDCRPDAO.java} |  59 +++--
 .../jpa/dao/authentication/JPASAML2SPDAO.java      |  52 ++--
 .../persistence/jpa/entity/JPAEntityFactory.java   |  20 +-
 .../core/persistence/jpa/entity/JPARealm.java      |  42 ++--
 .../entity/authentication/JPAClientAppUtils.java   |  51 ++++
 .../authentication/JPAClientAppUtilsFactory.java   |  72 ++++++
 .../{JPAOIDCRelyingParty.java => JPAOIDCRP.java}   |  19 +-
 ...PASAML2ServiceProvider.java => JPASAML2SP.java} |  16 +-
 .../jpa/entity/policy/JPAPolicyUtils.java          |   5 +-
 .../jpa/entity/policy/JPAPolicyUtilsFactory.java   |   4 +-
 .../core/persistence/jpa/inner/AuthModuleTest.java |   8 +-
 .../{OIDCRelyingPartyTest.java => OIDCRPTest.java} |  34 ++-
 ...L2ServiceProviderTest.java => SAML2SPTest.java} |  38 ++-
 .../core/persistence/jpa/outer/PolicyTest.java     |  11 +-
 ...rtyDataBinder.java => ClientAppDataBinder.java} |  12 +-
 .../java/data/ClientAppDataBinderImpl.java         | 264 +++++++++++++++++++++
 .../java/data/OIDCRelyingPartyDataBinderImpl.java  | 141 -----------
 .../data/SAML2ServiceProviderDataBinderImpl.java   | 150 ------------
 .../org/apache/syncope/fit/AbstractITCase.java     |  24 --
 .../apache/syncope/fit/core/ClientAppITCase.java   | 229 ++++++++++++++++++
 .../syncope/fit/core/OIDCRelyingPartyITCase.java   | 124 ----------
 .../org/apache/syncope/fit/core/PolicyITCase.java  |  16 +-
 .../org/apache/syncope/fit/core/RealmITCase.java   |   4 +-
 .../fit/core/SAML2ServiceProviderITCase.java       | 125 ----------
 fit/wa-reference/src/main/resources/wa.properties  |   8 +
 pom.xml                                            |  11 +-
 wa/bootstrap/pom.xml                               |   4 +-
 .../java/org/apache/syncope/wa/WARestClient.java   |  16 +-
 .../RestfulCloudConfigBootstrapConfiguration.java  |  27 +--
 .../syncope/wa/starter/SyncopeWAConfiguration.java |  14 +-
 .../wa/starter/rest/SyncopeServiceRegistry.java    |  18 +-
 wa/starter/src/main/resources/wa.properties        |   2 -
 60 files changed, 1207 insertions(+), 1187 deletions(-)

diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AbstractAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AbstractAuthModuleConf.java
index 5ca2010..bfc90c3 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AbstractAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AbstractAuthModuleConf.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
 import java.util.List;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
-import org.apache.syncope.common.lib.to.ProfileItemTO;
+import org.apache.syncope.common.lib.to.ItemTO;
 
 @XmlType
 @XmlSeeAlso({ JaasAuthModuleConf.class, StaticAuthModuleConf.class, LDAPAuthModuleConf.class, OIDCAuthModuleConf.class,
@@ -39,7 +39,7 @@ public abstract class AbstractAuthModuleConf implements Serializable, AuthModule
 
     private int order;
 
-    private List<ProfileItemTO> profileItems = new ArrayList<>();
+    private List<ItemTO> profileItems = new ArrayList<>();
 
     public AbstractAuthModuleConf() {
         setName(getClass().getName());
@@ -67,7 +67,7 @@ public abstract class AbstractAuthModuleConf implements Serializable, AuthModule
     @XmlElement(name = "profileItem")
     @JsonProperty("profileItems")
     @Override
-    public List<ProfileItemTO> getProfileItems() {
+    public List<ItemTO> getProfileItems() {
         return profileItems;
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AuthModuleConf.java
index 0858313..7eb13a9 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/authentication/module/AuthModuleConf.java
@@ -21,7 +21,7 @@ package org.apache.syncope.common.lib.authentication.module;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import java.io.Serializable;
 import java.util.List;
-import org.apache.syncope.common.lib.to.ProfileItemTO;
+import org.apache.syncope.common.lib.to.ItemTO;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface AuthModuleConf extends Serializable {
@@ -45,5 +45,5 @@ public interface AuthModuleConf extends Serializable {
      *
      * @return list of mapping items
      */
-    List<ProfileItemTO> getProfileItems();
+    List<ItemTO> getProfileItems();
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
index c141629..5a7b234 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
@@ -31,11 +31,11 @@ import javax.xml.bind.annotation.XmlType;
 
 @XmlRootElement(name = "clientApp")
 @XmlType
-@XmlSeeAlso({ OIDCRelyingPartyTO.class, SAML2ServiceProviderTO.class })
+@XmlSeeAlso({ OIDCRPTO.class, SAML2SPTO.class })
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@class")
 @JsonPropertyOrder(value = { "@class", "key", "name", "description",
     "authPolicy", "accessPolicy", "attReleasePolicy" })
-@Schema(subTypes = { OIDCRelyingPartyTO.class, SAML2ServiceProviderTO.class }, discriminatorProperty = "@class")
+@Schema(subTypes = { OIDCRPTO.class, SAML2SPTO.class }, discriminatorProperty = "@class")
 public abstract class ClientAppTO extends BaseBean implements EntityTO {
 
     private static final long serialVersionUID = 6577639976115661357L;
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRelyingPartyTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
similarity index 96%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRelyingPartyTO.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
index e8f5f57..987c534 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRelyingPartyTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
@@ -33,10 +33,10 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
 
-@XmlRootElement(name = "oidcRelyingParty")
+@XmlRootElement(name = "oidcrp")
 @XmlType
 @Schema(allOf = { ClientAppTO.class })
-public class OIDCRelyingPartyTO extends ClientAppTO {
+public class OIDCRPTO extends ClientAppTO {
 
     private static final long serialVersionUID = -6370888503924521351L;
 
@@ -59,7 +59,7 @@ public class OIDCRelyingPartyTO extends ClientAppTO {
     @XmlTransient
     @JsonProperty("@class")
     @Schema(name = "@class", required = true,
-            example = "org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO")
+            example = "org.apache.syncope.common.lib.to.client.OIDCRPTO")
     @Override
     public String getDiscriminator() {
         return getClass().getName();
@@ -137,7 +137,7 @@ public class OIDCRelyingPartyTO extends ClientAppTO {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        OIDCRelyingPartyTO rhs = (OIDCRelyingPartyTO) obj;
+        OIDCRPTO rhs = (OIDCRPTO) obj;
         return new EqualsBuilder()
                 .appendSuper(super.equals(obj))
                 .append(this.clientId, rhs.clientId)
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2ServiceProviderTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
similarity index 93%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2ServiceProviderTO.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
index 4ef2d59..d2cfac5 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2ServiceProviderTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
@@ -25,12 +25,12 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.types.SAML2ServiceProviderNameId;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
 
-@XmlRootElement(name = "saml2ServiceProvider")
+@XmlRootElement(name = "saml2SP")
 @XmlType
 @Schema(allOf = { ClientAppTO.class })
-public class SAML2ServiceProviderTO extends ClientAppTO {
+public class SAML2SPTO extends ClientAppTO {
 
     private static final long serialVersionUID = -6370888503924521351L;
 
@@ -50,7 +50,7 @@ public class SAML2ServiceProviderTO extends ClientAppTO {
 
     private String requiredAuthenticationContextClass;
 
-    private SAML2ServiceProviderNameId requiredNameIdFormat;
+    private SAML2SPNameId requiredNameIdFormat;
 
     private Integer skewAllowance;
 
@@ -63,7 +63,7 @@ public class SAML2ServiceProviderTO extends ClientAppTO {
     @XmlTransient
     @JsonProperty("@class")
     @Schema(name = "@class", required = true,
-            example = "org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO")
+            example = "org.apache.syncope.common.lib.to.client.SAML2SPTO")
     @Override
     public String getDiscriminator() {
         return getClass().getName();
@@ -133,11 +133,11 @@ public class SAML2ServiceProviderTO extends ClientAppTO {
         this.requiredAuthenticationContextClass = requiredAuthenticationContextClass;
     }
 
-    public SAML2ServiceProviderNameId getRequiredNameIdFormat() {
+    public SAML2SPNameId getRequiredNameIdFormat() {
         return requiredNameIdFormat;
     }
 
-    public void setRequiredNameIdFormat(final SAML2ServiceProviderNameId requiredNameIdFormat) {
+    public void setRequiredNameIdFormat(final SAML2SPNameId requiredNameIdFormat) {
         this.requiredNameIdFormat = requiredNameIdFormat;
     }
 
@@ -184,7 +184,7 @@ public class SAML2ServiceProviderTO extends ClientAppTO {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        SAML2ServiceProviderTO rhs = (SAML2ServiceProviderTO) obj;
+        SAML2SPTO rhs = (SAML2SPTO) obj;
         return new EqualsBuilder()
                 .appendSuper(super.equals(obj))
                 .append(this.entityId, rhs.entityId)
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index 83e1b47..046f61b 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -34,33 +34,27 @@ public final class AMEntitlement {
 
     public static final String GATEWAY_ROUTE_PUSH = "GATEWAY_ROUTE_PUSH";
 
-    public static final String AUTHENTICATION_MODULE_READ = "AUTHENTICATION_MODULE_READ";
+    public static final String AUTHMODULE_READ = "AUTHMODULE_READ";
 
-    public static final String AUTHENTICATION_MODULE_LIST = "AUTHENTICATION_MODULE_LIST";
+    public static final String AUTHMODULE_LIST = "AUTHMODULE_LIST";
 
-    public static final String AUTHENTICATION_MODULE_CREATE = "AUTHENTICATION_MODULE_CREATE";
+    public static final String AUTHMODULE_CREATE = "AUTHMODULE_CREATE";
 
-    public static final String AUTHENTICATION_MODULE_UPDATE = "AUTHENTICATION_MODULE_UPDATE";
+    public static final String AUTHMODULE_UPDATE = "AUTHMODULE_CREATE";
 
-    public static final String AUTHENTICATION_MODULE_DELETE = "AUTHENTICATION_MODULE_DELETE";
+    public static final String AUTHMODULE_DELETE = "AUTHMODULE_DELETE";
 
-    private static final Set<String> VALUES;
-
-    public static final String SAML2_SERVICE_PROVIDER_READ = "OIDC_RELYING_PARTY_READ";
-
-    public static final String OIDC_RELYING_PARTY_DELETE = "OIDC_RELYING_PARTY_DELETE";
+    public static final String CLIENTAPP_READ = "CLIENTAPP_READ";
 
-    public static final String OIDC_RELYING_PARTY_READ = "OIDC_RELYING_PARTY_READ";
+    public static final String CLIENTAPP_LIST = "CLIENTAPP_LIST";
 
-    public static final String SAML2_SERVICE_PROVIDER_LIST = "SAML2_SERVICE_PROVIDER_LIST";
+    public static final String CLIENTAPP_CREATE = "CLIENTAPP_CREATE";
 
-    public static final String OIDC_RELYING_PARTY_CREATE = "OIDC_RELYING_PARTY_CREATE";
+    public static final String CLIENTAPP_UPDATE = "CLIENTAPP_CREATE";
 
-    public static final String SAML2_SERVICE_PROVIDER_DELETE = "SAML2_SERVICE_PROVIDER_DELETE";
+    public static final String CLIENTAPP_DELETE = "CLIENTAPP_DELETE";
 
-    public static final String OIDC_RELYING_PARTY_LIST = "OIDC_RELYING_PARTY_LIST";
-
-    public static final String SAML2_SERVICE_PROVIDER_CREATE = "SAML2_SERVICE_PROVIDER_CREATE";
+    private static final Set<String> VALUES;
 
     static {
         Set<String> values = new TreeSet<>();
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ProfileItemTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/ClientAppType.java
similarity index 83%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ProfileItemTO.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/ClientAppType.java
index f0768de..7f90159 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/ProfileItemTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/ClientAppType.java
@@ -16,10 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.to;
+package org.apache.syncope.common.lib.types;
 
-public class ProfileItemTO extends ItemTO {
+import javax.xml.bind.annotation.XmlEnum;
 
-    private static final long serialVersionUID = 5308969043827855713L;
+@XmlEnum
+public enum ClientAppType {
+    SAML2SP,
+    OIDCRP;
 
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2ServiceProviderNameId.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2SPNameId.java
similarity index 93%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2ServiceProviderNameId.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2SPNameId.java
index 1cdad24..d75a68d 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2ServiceProviderNameId.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/SAML2SPNameId.java
@@ -21,7 +21,7 @@ package org.apache.syncope.common.lib.types;
 import javax.xml.bind.annotation.XmlEnum;
 
 @XmlEnum
-public enum SAML2ServiceProviderNameId {
+public enum SAML2SPNameId {
 
     EMAIL_ADDRESS("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"),
     UNSPECIFIED("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"),
@@ -32,12 +32,11 @@ public enum SAML2ServiceProviderNameId {
 
     private final String nameId;
 
-    SAML2ServiceProviderNameId(final String nameId) {
+    SAML2SPNameId(final String nameId) {
         this.nameId = nameId;
     }
 
     public String getNameId() {
         return nameId;
     }
-
 }
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ClientAppService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ClientAppService.java
index 8fd178a..002c5b5 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ClientAppService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ClientAppService.java
@@ -27,8 +27,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.security.SecurityRequirements;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import org.apache.syncope.common.lib.to.client.ClientAppTO;
-import org.apache.syncope.common.rest.api.RESTHeaders;
+import java.util.List;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -41,8 +40,9 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-
-import java.util.List;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
  * REST operations for applications.
@@ -55,69 +55,80 @@ import java.util.List;
 public interface ClientAppService extends JAXRSService {
 
     /**
-     * Returns a list of all applications.
+     * Returns the client app matching the given key.
      *
-     * @return list of all applications.
+     * @param type client app type
+     * @param key key of requested client app
+     * @param <T> response type (extending ClientAppTO)
+     * @return client app with matching id
      */
     @GET
+    @Path("{type}/{key}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    List<ClientAppTO> list();
+    <T extends ClientAppTO> T read(
+            @NotNull @PathParam("type") ClientAppType type,
+            @NotNull @PathParam("key") String key);
 
     /**
-     * Returns application with matching key.
+     * Returns a list of policies of the matching type.
      *
-     * @param key application key to be read
-     * @return application with matching key
+     * @param type Type selector for requested policies
+     * @param <T> response type (extending ClientAppTO)
+     * @return list of policies with matching type
      */
     @GET
-    @Path("{key}")
+    @Path("{type}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    ClientAppTO read(@NotNull @PathParam("key") String key);
+    <T extends ClientAppTO> List<T> list(@NotNull @PathParam("type") ClientAppType type);
 
     /**
-     * Creates a new application.
+     * Create a new client app.
      *
-     * @param applicationTO application to be created
-     * @return Response object featuring Location header of created application
+     * @param type client app type
+     * @param clientAppTO ClientApp to be created (needs to match type)
+     * @return Response object featuring Location header of created client app
      */
     @ApiResponses(
             @ApiResponse(responseCode = "201",
-                    description = "Application successfully created", headers = {
+                    description = "ClientApp successfully created", headers = {
                 @Header(name = RESTHeaders.RESOURCE_KEY, schema =
                         @Schema(type = "string"),
-                        description = "Key value for the entity created"),
+                        description = "UUID generated for the entity created"),
                 @Header(name = HttpHeaders.LOCATION, schema =
                         @Schema(type = "string"),
                         description = "URL of the entity created") }))
     @POST
+    @Path("{type}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    Response create(@NotNull ClientAppTO applicationTO);
+    Response create(@NotNull @PathParam("type") ClientAppType type, @NotNull ClientAppTO clientAppTO);
 
     /**
-     * Updates the application matching the provided key.
+     * Updates client app matching the given key.
      *
-     * @param applicationTO application to be stored
+     * @param type client app type
+     * @param clientAppTO ClientApp to replace existing client app
      */
-    @Parameter(name = "key", description = "Application's key", in = ParameterIn.PATH, schema =
+    @Parameter(name = "key", description = "ClientApp's key", in = ParameterIn.PATH, schema =
             @Schema(type = "string"))
     @ApiResponses(
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
-    @Path("{key}")
+    @Path("{type}/{key}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void update(@NotNull ClientAppTO applicationTO);
+    void update(@NotNull @PathParam("type") ClientAppType type, @NotNull ClientAppTO clientAppTO);
 
     /**
-     * Deletes the application matching the provided key.
+     * Delete client app matching the given key.
      *
-     * @param key application key to be deleted
+     * @param type client app type
+     * @param key key of client app to be deleted
      */
     @ApiResponses(
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
-    @Path("{key}")
+    @Path("{type}/{key}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void delete(@NotNull @PathParam("key") String key);
+    void delete(@NotNull @PathParam("type") ClientAppType type, @NotNull @PathParam("key") String key);
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
index 7d0c740..662c601 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AnyTO.java
@@ -71,9 +71,6 @@ public abstract class AnyTO extends AbstractAnnotatedBean implements EntityTO, R
 
     private final Set<String> resources = new HashSet<>();
 
-    protected AnyTO() {
-    }
-
     @Schema(name = "@class", required = true)
     public abstract String getDiscriminator();
 
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
index 037bffa..013cb06 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
@@ -40,7 +40,7 @@ public final class IdRepoImplementationType {
     public static final String RECIPIENTS_PROVIDER = "RECIPIENTS_PROVIDER";
 
     public static final String AUDIT_APPENDER = "AUDIT_APPENDER";
-    
+
     private static final Map<String, String> VALUES = Map.ofEntries(
             Pair.of(JWT_SSO_PROVIDER, "org.apache.syncope.core.spring.security.JWTSSOProvider"),
             Pair.of(REPORTLET, "org.apache.syncope.core.persistence.api.dao.Reportlet"),
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
index 108e8d6..44b2667 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
@@ -34,7 +34,7 @@ public enum PolicyType {
     /**
      * How authentication policies should look like.
      */
-    AUTHENTICATION,
+    AUTH,
     /**
      * How attribute release policies should look like.
      */
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java
new file mode 100644
index 0000000..ebe9f14
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.dao.authentication.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
+import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class ClientAppLogic extends AbstractTransactionalLogic<ClientAppTO> {
+
+    @Autowired
+    private ClientAppUtilsFactory clientAppUtilsFactory;
+
+    @Autowired
+    private ClientAppDataBinder binder;
+
+    @Autowired
+    private SAML2SPDAO saml2spDAO;
+
+    @Autowired
+    private OIDCRPDAO oidcrpDAO;
+
+    @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_LIST + "')")
+    public <T extends ClientAppTO> List<T> list(final ClientAppType type) {
+        Stream<T> stream;
+
+        switch (type) {
+            case OIDCRP:
+                stream = oidcrpDAO.findAll().stream().map(binder::getClientAppTO);
+                break;
+
+            case SAML2SP:
+            default:
+                stream = saml2spDAO.findAll().stream().map(binder::getClientAppTO);
+        }
+
+        return stream.collect(Collectors.toList());
+    }
+
+    private void checkType(final ClientAppType type, final ClientAppUtils clientAppUtils) {
+        if (clientAppUtils.getType() != type) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
+            sce.getElements().add("Found " + type + ", expected " + clientAppUtils.getType());
+            throw sce;
+        }
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_READ + "')")
+    @Transactional(readOnly = true)
+    public <T extends ClientAppTO> T read(final ClientAppType type, final String key) {
+        switch (type) {
+            case OIDCRP:
+                OIDCRP oidcrp = oidcrpDAO.find(key);
+                if (oidcrp == null) {
+                    throw new NotFoundException("Client app " + key + " not found");
+                }
+
+                checkType(type, clientAppUtilsFactory.getInstance(oidcrp));
+
+                return binder.getClientAppTO(oidcrp);
+
+            case SAML2SP:
+            default:
+                SAML2SP saml2sp = saml2spDAO.find(key);
+                if (saml2sp == null) {
+                    throw new NotFoundException("Client app " + key + " not found");
+                }
+
+                checkType(type, clientAppUtilsFactory.getInstance(saml2sp));
+
+                return binder.getClientAppTO(saml2sp);
+        }
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_CREATE + "')")
+    public ClientAppTO create(final ClientAppType type, final ClientAppTO clientAppTO) {
+        checkType(type, clientAppUtilsFactory.getInstance(clientAppTO));
+
+        switch (type) {
+            case OIDCRP:
+                return binder.getClientAppTO(oidcrpDAO.save(binder.create(clientAppTO)));
+
+            case SAML2SP:
+            default:
+                return binder.getClientAppTO(saml2spDAO.save(binder.create(clientAppTO)));
+        }
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_UPDATE + "')")
+    public void update(final ClientAppType type, final ClientAppTO clientAppTO) {
+        checkType(type, clientAppUtilsFactory.getInstance(clientAppTO));
+
+        switch (type) {
+            case OIDCRP:
+                OIDCRP oidcrp = oidcrpDAO.find(clientAppTO.getKey());
+                if (oidcrp == null) {
+                    throw new NotFoundException("Client app " + clientAppTO.getKey() + " not found");
+                }
+                binder.update(oidcrp, clientAppTO);
+                oidcrpDAO.save(oidcrp);
+                break;
+
+            case SAML2SP:
+            default:
+                SAML2SP saml2sp = saml2spDAO.find(clientAppTO.getKey());
+                if (saml2sp == null) {
+                    throw new NotFoundException("Client app " + clientAppTO.getKey() + " not found");
+                }
+                binder.update(saml2sp, clientAppTO);
+                saml2spDAO.save(saml2sp);
+        }
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_DELETE + "')")
+    public void delete(final ClientAppType type, final String key) {
+        switch (type) {
+            case OIDCRP:
+                OIDCRP oidcrp = oidcrpDAO.find(key);
+                if (oidcrp == null) {
+                    throw new NotFoundException("Client app " + key + " not found");
+                }
+                oidcrpDAO.delete(oidcrp);
+                break;
+
+            case SAML2SP:
+            default:
+                SAML2SP saml2sp = saml2spDAO.find(key);
+                if (saml2sp == null) {
+                    throw new NotFoundException("Client app " + key + " not found");
+                }
+                saml2spDAO.delete(saml2sp);
+        }
+    }
+
+    @Override
+    protected ClientAppTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof ClientAppTO) {
+                    key = ((ClientAppTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                ClientApp clientApp = saml2spDAO.find(key);
+                if (clientApp == null) {
+                    clientApp = oidcrpDAO.find(key);
+                }
+
+                return binder.getClientAppTO(clientApp);
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCRelyingPartyLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCRelyingPartyLogic.java
deleted file mode 100644
index 00f4516..0000000
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCRelyingPartyLogic.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.oidc;
-
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.syncope.core.logic.AbstractClientAppLogic;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRelyingPartyDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
-import org.apache.syncope.core.provisioning.api.data.OIDCRelyingPartyDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.types.AMEntitlement;
-
-@Component
-public class OIDCRelyingPartyLogic extends AbstractClientAppLogic<OIDCRelyingPartyTO> {
-
-    @Autowired
-    private OIDCRelyingPartyDAO oidcRelyingPartyDAO;
-
-    @Autowired
-    private OIDCRelyingPartyDataBinder binder;
-
-    @PreAuthorize("hasRole('" + AMEntitlement.OIDC_RELYING_PARTY_READ + "')")
-    @Transactional(readOnly = true)
-    @Override
-    public OIDCRelyingPartyTO read(final String key) {
-        OIDCRelyingParty application = oidcRelyingPartyDAO.find(key);
-        if (application == null) {
-            LOG.error("Could not find application '" + key + '\'');
-
-            throw new NotFoundException(key);
-        }
-
-        return binder.getClientApplicationTO(application);
-    }
-
-    @PreAuthorize("hasRole('" + AMEntitlement.OIDC_RELYING_PARTY_LIST + "')")
-    @Transactional(readOnly = true)
-    @Override
-    public List<OIDCRelyingPartyTO> list() {
-        return oidcRelyingPartyDAO.findAll()
-                .stream().map(binder::getClientApplicationTO).collect(Collectors.toList());
-    }
-
-    @PreAuthorize("hasRole('" + AMEntitlement.OIDC_RELYING_PARTY_CREATE + "')")
-    @Override
-    public OIDCRelyingPartyTO create(final OIDCRelyingPartyTO applicationTO) {
-        return binder.getClientApplicationTO(oidcRelyingPartyDAO.save(binder.create(applicationTO)));
-    }
-
-    @Override
-    @PreAuthorize("hasRole('" + IdRepoEntitlement.APPLICATION_UPDATE + "')")
-    public OIDCRelyingPartyTO update(final OIDCRelyingPartyTO applicationTO) {
-        OIDCRelyingParty application = oidcRelyingPartyDAO.find(applicationTO.getKey());
-        if (application == null) {
-            LOG.error("Could not find application '" + applicationTO.getKey() + '\'');
-            throw new NotFoundException(applicationTO.getKey());
-        }
-        return binder.getClientApplicationTO(
-                oidcRelyingPartyDAO.save(binder.update(application, applicationTO)));
-    }
-
-    @Override
-    @PreAuthorize("hasRole('" + AMEntitlement.OIDC_RELYING_PARTY_DELETE + "')")
-    public OIDCRelyingPartyTO delete(final String key) {
-        OIDCRelyingParty application = oidcRelyingPartyDAO.find(key);
-        if (application == null) {
-            LOG.error("Could not find application '" + key + '\'');
-
-            throw new NotFoundException(key);
-        }
-
-        OIDCRelyingPartyTO deleted = binder.getClientApplicationTO(application);
-        oidcRelyingPartyDAO.delete(key);
-        return deleted;
-    }
-
-}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/saml/SAML2ServiceProviderLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/saml/SAML2ServiceProviderLogic.java
deleted file mode 100644
index 886144b..0000000
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/saml/SAML2ServiceProviderLogic.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.saml;
-
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.syncope.core.logic.AbstractClientAppLogic;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.authentication.SAML2ServiceProviderDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
-import org.apache.syncope.core.provisioning.api.data.SAML2ServiceProviderDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.types.AMEntitlement;
-
-@Component
-public class SAML2ServiceProviderLogic extends AbstractClientAppLogic<SAML2ServiceProviderTO> {
-
-    @Autowired
-    private SAML2ServiceProviderDAO saml2ServiceProviderDAO;
-
-    @Autowired
-    private SAML2ServiceProviderDataBinder binder;
-
-    @Override
-    @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SERVICE_PROVIDER_DELETE + "')")
-    public SAML2ServiceProviderTO delete(final String key) {
-        SAML2ServiceProvider application = saml2ServiceProviderDAO.find(key);
-        if (application == null) {
-            LOG.error("Could not find application '" + key + '\'');
-
-            throw new NotFoundException(key);
-        }
-
-        SAML2ServiceProviderTO deleted = binder.getClientApplicationTO(application);
-        saml2ServiceProviderDAO.delete(key);
-        return deleted;
-    }
-
-    @Override
-    @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SERVICE_PROVIDER_LIST + "')")
-    @Transactional(readOnly = true)
-    public List<SAML2ServiceProviderTO> list() {
-        return saml2ServiceProviderDAO.findAll().stream()
-                .map(binder::getClientApplicationTO).collect(Collectors.toList());
-    }
-
-    @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SERVICE_PROVIDER_READ + "')")
-    @Transactional(readOnly = true)
-    @Override
-    public SAML2ServiceProviderTO read(final String key) {
-        SAML2ServiceProvider application = saml2ServiceProviderDAO.find(key);
-        if (application == null) {
-            LOG.error("Could not find application '" + key + '\'');
-            throw new NotFoundException(key);
-        }
-
-        return binder.getClientApplicationTO(application);
-    }
-
-    @Override
-    @PreAuthorize("hasRole('" + AMEntitlement.SAML2_SERVICE_PROVIDER_CREATE + "')")
-    public SAML2ServiceProviderTO create(final SAML2ServiceProviderTO applicationTO) {
-        return binder.getClientApplicationTO(saml2ServiceProviderDAO.save(binder.create(applicationTO)));
-    }
-
-    @Override
-    @PreAuthorize("hasRole('" + IdRepoEntitlement.APPLICATION_UPDATE + "')")
-    public SAML2ServiceProviderTO update(final SAML2ServiceProviderTO applicationTO) {
-        SAML2ServiceProvider application = saml2ServiceProviderDAO.find(applicationTO.getKey());
-        if (application == null) {
-            LOG.error("Could not find application '" + applicationTO.getKey() + '\'');
-            throw new NotFoundException(applicationTO.getKey());
-        }
-
-        return binder.getClientApplicationTO(saml2ServiceProviderDAO.save(binder.update(application, applicationTO)));
-    }
-}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
index 011bc1c..cf6988e 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
@@ -18,77 +18,49 @@
  */
 package org.apache.syncope.core.rest.cxf.service;
 
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.client.ClientAppTO;
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.service.ClientAppService;
-import org.apache.syncope.core.logic.AbstractClientAppLogic;
-import org.apache.syncope.core.logic.oidc.OIDCRelyingPartyLogic;
-import org.apache.syncope.core.logic.saml.SAML2ServiceProviderLogic;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.logic.ClientAppLogic;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import javax.ws.rs.core.Response;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-
 @Service
 public class ClientAppServiceImpl extends AbstractServiceImpl implements ClientAppService {
 
     @Autowired
-    private SAML2ServiceProviderLogic saml2Logic;
-
-    @Autowired
-    private OIDCRelyingPartyLogic oidcLogic;
-
-    private AbstractClientAppLogic<ClientAppTO> getLogicFor(final ClientAppTO clientApp) {
-        if (clientApp instanceof SAML2ServiceProviderTO) {
-            return (AbstractClientAppLogic) this.saml2Logic;
-        }
-        if (clientApp instanceof OIDCRelyingPartyTO) {
-            return (AbstractClientAppLogic) this.oidcLogic;
-        }
-        throw new IllegalArgumentException("Unable to determine type for " + clientApp.getName());
-    }
+    private ClientAppLogic logic;
 
     @Override
-    public List<ClientAppTO> list() {
-        List<ClientAppTO> applications = new ArrayList<>(saml2Logic.list());
-        applications.addAll(oidcLogic.list());
-        return applications;
+    public Response create(final ClientAppType type, final ClientAppTO clientAppTO) {
+        ClientAppTO policy = logic.create(type, clientAppTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(policy.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, policy.getKey()).
+                build();
     }
 
     @Override
-    public ClientAppTO read(final String key) {
-        try {
-            return this.saml2Logic.read(key);
-        } catch (NotFoundException e) {
-            return this.oidcLogic.read(key);
-        }
+    public <T extends ClientAppTO> List<T> list(final ClientAppType type) {
+        return logic.list(type);
     }
 
     @Override
-    public Response create(final ClientAppTO applicationTO) {
-        ClientAppTO created = getLogicFor(applicationTO).create(applicationTO);
-        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
-        return Response.created(location).
-            header(RESTHeaders.RESOURCE_KEY, created.getKey()).
-            build();
+    public <T extends ClientAppTO> T read(final ClientAppType type, final String key) {
+        return logic.read(type, key);
     }
 
     @Override
-    public void update(final ClientAppTO applicationTO) {
-        getLogicFor(applicationTO).update(applicationTO);
+    public void update(final ClientAppType type, final ClientAppTO clientAppTO) {
+        logic.update(type, clientAppTO);
     }
 
     @Override
-    public void delete(final String key) {
-        ClientAppTO app = read(key);
-        getLogicFor(app).delete(key);
+    public void delete(final ClientAppType type, final String key) {
+        logic.delete(type, key);
     }
-
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
index b643057..d391d71 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
@@ -65,6 +65,9 @@ public class PolicyLogic extends AbstractTransactionalLogic<PolicyTO> {
     @PreAuthorize("hasRole('" + IdRepoEntitlement.POLICY_UPDATE + "')")
     public PolicyTO update(final PolicyType type, final PolicyTO policyTO) {
         Policy policy = policyDAO.find(policyTO.getKey());
+        if (policy == null) {
+            throw new NotFoundException("Policy " + policyTO.getKey() + " not found");
+        }
 
         PolicyUtils policyUtils = policyUtilsFactory.getInstance(policy);
         if (policyUtils.getType() != type) {
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
index bacec1f..5af1669 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/FopSerializer.java
@@ -1,18 +1,20 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
 package org.apache.syncope.core.logic.cocoon;
 
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
index ce4ffb4..8d60855 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/TextSerializer.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
index 271f84a..eb5dab6 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/cocoon/XSLTTransformer.java
@@ -1,18 +1,20 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
 package org.apache.syncope.core.logic.cocoon;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRelyingPartyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRPDAO.java
similarity index 74%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRelyingPartyDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRPDAO.java
index 0845602..f1789f2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRelyingPartyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/OIDCRPDAO.java
@@ -19,26 +19,25 @@
 package org.apache.syncope.core.persistence.api.dao.authentication;
 
 import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
 
 import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
 
-public interface OIDCRelyingPartyDAO extends DAO<OIDCRelyingParty> {
+public interface OIDCRPDAO extends DAO<OIDCRP> {
 
-    OIDCRelyingParty find(String key);
+    OIDCRP find(String key);
 
-    OIDCRelyingParty findByName(String name);
+    OIDCRP findByName(String name);
 
-    OIDCRelyingParty findByClientId(String clientId);
+    OIDCRP findByClientId(String clientId);
 
-    List<OIDCRelyingParty> findAll();
+    List<OIDCRP> findAll();
 
-    OIDCRelyingParty save(OIDCRelyingParty application);
+    OIDCRP save(OIDCRP clientApp);
 
     void delete(String key);
 
     void deleteByClientId(String clientId);
 
-    void delete(OIDCRelyingParty application);
-
+    void delete(OIDCRP clientApp);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2ServiceProviderDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2SPDAO.java
similarity index 72%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2ServiceProviderDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2SPDAO.java
index 64de553..46a0dad 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2ServiceProviderDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/authentication/SAML2SPDAO.java
@@ -19,26 +19,25 @@
 package org.apache.syncope.core.persistence.api.dao.authentication;
 
 import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
 
 import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
 
-public interface SAML2ServiceProviderDAO extends DAO<SAML2ServiceProvider> {
+public interface SAML2SPDAO extends DAO<SAML2SP> {
 
-    SAML2ServiceProvider find(String key);
+    SAML2SP find(String key);
 
-    SAML2ServiceProvider findByName(String name);
+    SAML2SP findByName(String name);
 
-    SAML2ServiceProvider findByEntityId(String clientId);
+    SAML2SP findByEntityId(String clientId);
 
-    List<SAML2ServiceProvider> findAll();
+    List<SAML2SP> findAll();
 
-    SAML2ServiceProvider save(SAML2ServiceProvider application);
+    SAML2SP save(SAML2SP clientApp);
 
     void delete(String key);
 
     void deleteByEntityId(String entityId);
 
-    void delete(SAML2ServiceProvider application);
-
+    void delete(SAML2SP clientApp);
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthPolicyType.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtils.java
similarity index 69%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthPolicyType.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtils.java
index 3835bd8..2474325 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthPolicyType.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtils.java
@@ -16,15 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.types;
+package org.apache.syncope.core.persistence.api.entity.authentication;
 
-public final class AuthPolicyType {
+import org.apache.syncope.common.lib.types.ClientAppType;
 
-    public static final String AUTHENTICATION_POLICY_BPM = "AUTHENTICATION_POLICY_BPM";
+public interface ClientAppUtils {
 
-    public static final String AUTHENTICATION_POLICY_CHAIN = "AUTHENTICATION_POLICY_CHAIN";
+    ClientAppType getType();
 
-    private AuthPolicyType() {
-        // private constructor for static utility class
-    }
+    Class<? extends ClientApp> clientAppClass();
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2ServiceProviderDataBinder.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtilsFactory.java
similarity index 60%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2ServiceProviderDataBinder.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtilsFactory.java
index fc36a9b..debdd75 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2ServiceProviderDataBinder.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/ClientAppUtilsFactory.java
@@ -16,16 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.data;
+package org.apache.syncope.core.persistence.api.entity.authentication;
 
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
 
-public interface SAML2ServiceProviderDataBinder {
+public interface ClientAppUtilsFactory {
 
-    SAML2ServiceProvider create(SAML2ServiceProviderTO applicationTO);
+    ClientAppUtils getInstance(ClientAppType type);
 
-    SAML2ServiceProvider update(SAML2ServiceProvider application, SAML2ServiceProviderTO applicationTO);
+    ClientAppUtils getInstance(ClientApp clientApp);
 
-    SAML2ServiceProviderTO getClientApplicationTO(SAML2ServiceProvider application);
+    ClientAppUtils getInstance(Class<? extends ClientAppTO> clientAppClass);
+
+    ClientAppUtils getInstance(ClientAppTO clientAppTO);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRelyingParty.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRP.java
similarity index 93%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRelyingParty.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRP.java
index cffe89c..fe5dbbe 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRelyingParty.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/OIDCRP.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *    http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -15,7 +15,6 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- *
  */
 package org.apache.syncope.core.persistence.api.entity.authentication;
 
@@ -23,7 +22,7 @@ import java.util.List;
 import java.util.Set;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
 
-public interface OIDCRelyingParty extends ClientApp {
+public interface OIDCRP extends ClientApp {
 
     void setClientId(String id);
 
@@ -50,5 +49,4 @@ public interface OIDCRelyingParty extends ClientApp {
     OIDCSubjectType getSubjectType();
 
     void setSubjectType(OIDCSubjectType subjectType);
-
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2ServiceProvider.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2SP.java
similarity index 85%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2ServiceProvider.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2SP.java
index 99834fd..0199b64 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2ServiceProvider.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/authentication/SAML2SP.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *    http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -15,13 +15,12 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- *
  */
 package org.apache.syncope.core.persistence.api.entity.authentication;
 
-import org.apache.syncope.common.lib.types.SAML2ServiceProviderNameId;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
 
-public interface SAML2ServiceProvider extends ClientApp {
+public interface SAML2SP extends ClientApp {
 
     String getEntityId();
 
@@ -55,9 +54,9 @@ public interface SAML2ServiceProvider extends ClientApp {
 
     String getRequiredAuthenticationContextClass();
 
-    void setRequiredNameIdFormat(SAML2ServiceProviderNameId location);
+    void setRequiredNameIdFormat(SAML2SPNameId location);
 
-    SAML2ServiceProviderNameId getRequiredNameIdFormat();
+    SAML2SPNameId getRequiredNameIdFormat();
 
     void setSkewAllowance(Integer location);
 
@@ -74,5 +73,4 @@ public interface SAML2ServiceProvider extends ClientApp {
     void setServiceProviderNameIdQualifier(String location);
 
     String getServiceProviderNameIdQualifier();
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
index 5810fc4..899c3a5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
@@ -19,11 +19,10 @@
 package org.apache.syncope.core.persistence.jpa.dao;
 
 import javax.persistence.EntityManager;
-
-import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.DAO;
 import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Configurable;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
index cb7effa..e305918 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.util.List;
+import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
@@ -25,6 +27,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
@@ -44,11 +47,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Repository;
 
-import javax.persistence.TypedQuery;
-
-import java.util.List;
-import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
-
 @Repository
 public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
 
@@ -183,7 +181,6 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
 
     @Override
     public <T extends Policy> void delete(final T policy) {
-
         realmDAO.findByPolicy(policy).forEach(realm -> {
             if (policy instanceof AccountPolicy) {
                 realm.setAccountPolicy(null);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRelyingPartyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRPDAO.java
similarity index 57%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRelyingPartyDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRPDAO.java
index e01808e..498f5ed 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRelyingPartyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPAOIDCRPDAO.java
@@ -18,53 +18,51 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao.authentication;
 
-import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRelyingPartyDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
+import java.util.List;
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.authentication.JPAOIDCRelyingParty;
+import org.apache.syncope.core.persistence.jpa.entity.authentication.JPAOIDCRP;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
-import javax.persistence.NoResultException;
-import javax.persistence.TypedQuery;
-import java.util.List;
+import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
 
 @Repository
-public class JPAOIDCRelyingPartyDAO extends AbstractDAO<OIDCRelyingParty> implements OIDCRelyingPartyDAO {
+public class JPAOIDCRPDAO extends AbstractDAO<OIDCRP> implements OIDCRPDAO {
 
     @Override
-    public OIDCRelyingParty find(final String key) {
-        return entityManager().find(JPAOIDCRelyingParty.class, key);
+    public OIDCRP find(final String key) {
+        return entityManager().find(JPAOIDCRP.class, key);
     }
 
     @Override
-    public OIDCRelyingParty findByName(final String name) {
-        TypedQuery<OIDCRelyingParty> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAOIDCRelyingParty.class.getSimpleName()
-                + " e WHERE e.name=:name", OIDCRelyingParty.class);
+    public OIDCRP findByName(final String name) {
+        TypedQuery<OIDCRP> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCRP.class.getSimpleName() + " e WHERE e.name=:name", OIDCRP.class);
         query.setParameter("name", name);
 
-        OIDCRelyingParty result = null;
+        OIDCRP result = null;
         try {
             result = query.getSingleResult();
         } catch (NoResultException e) {
-            LOG.debug("No OIDCRelyingParty found with name {}", name, e);
+            LOG.debug("No OIDCRP found with name {}", name, e);
         }
 
         return result;
     }
 
     @Override
-    public OIDCRelyingParty findByClientId(final String clientId) {
-        TypedQuery<OIDCRelyingParty> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAOIDCRelyingParty.class.getSimpleName()
-                + " e WHERE e.clientId=:clientId", OIDCRelyingParty.class);
+    public OIDCRP findByClientId(final String clientId) {
+        TypedQuery<OIDCRP> query = entityManager().createQuery("SELECT e FROM " + JPAOIDCRP.class.getSimpleName()
+                + " e WHERE e.clientId=:clientId", OIDCRP.class);
         query.setParameter("clientId", clientId);
 
-        OIDCRelyingParty result = null;
+        OIDCRP result = null;
         try {
             result = query.getSingleResult();
         } catch (NoResultException e) {
-            LOG.debug("No OIDCRelyingParty found with clientId {}", clientId, e);
+            LOG.debug("No OIDCRP found with clientId {}", clientId, e);
         }
 
         return result;
@@ -72,22 +70,21 @@ public class JPAOIDCRelyingPartyDAO extends AbstractDAO<OIDCRelyingParty> implem
 
     @Transactional(readOnly = true)
     @Override
-    public List<OIDCRelyingParty> findAll() {
-        TypedQuery<OIDCRelyingParty> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAOIDCRelyingParty.class.getSimpleName()
-                + " e", OIDCRelyingParty.class);
+    public List<OIDCRP> findAll() {
+        TypedQuery<OIDCRP> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAOIDCRP.class.getSimpleName() + " e", OIDCRP.class);
 
         return query.getResultList();
     }
 
     @Override
-    public OIDCRelyingParty save(final OIDCRelyingParty rp) {
-        return entityManager().merge(rp);
+    public OIDCRP save(final OIDCRP clientApp) {
+        return entityManager().merge(clientApp);
     }
 
     @Override
     public void delete(final String key) {
-        OIDCRelyingParty rpTO = find(key);
+        OIDCRP rpTO = find(key);
         if (rpTO == null) {
             return;
         }
@@ -97,7 +94,7 @@ public class JPAOIDCRelyingPartyDAO extends AbstractDAO<OIDCRelyingParty> implem
 
     @Override
     public void deleteByClientId(final String clientId) {
-        OIDCRelyingParty rpTO = findByClientId(clientId);
+        OIDCRP rpTO = findByClientId(clientId);
         if (rpTO == null) {
             return;
         }
@@ -105,7 +102,7 @@ public class JPAOIDCRelyingPartyDAO extends AbstractDAO<OIDCRelyingParty> implem
     }
 
     @Override
-    public void delete(final OIDCRelyingParty rpTO) {
-        entityManager().remove(rpTO);
+    public void delete(final OIDCRP clientApp) {
+        entityManager().remove(clientApp);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPASAML2SPDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPASAML2SPDAO.java
index dc56d2e..d37a067 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPASAML2SPDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/authentication/JPASAML2SPDAO.java
@@ -18,53 +18,51 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao.authentication;
 
-import org.apache.syncope.core.persistence.api.dao.authentication.SAML2ServiceProviderDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
 import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.authentication.JPASAML2ServiceProvider;
+import org.apache.syncope.core.persistence.jpa.entity.authentication.JPASAML2SP;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 import java.util.List;
+import org.apache.syncope.core.persistence.api.dao.authentication.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
 
 @Repository
-public class JPASAML2SPDAO extends AbstractDAO<SAML2ServiceProvider> implements SAML2ServiceProviderDAO {
+public class JPASAML2SPDAO extends AbstractDAO<SAML2SP> implements SAML2SPDAO {
 
     @Override
-    public SAML2ServiceProvider find(final String key) {
-        return entityManager().find(JPASAML2ServiceProvider.class, key);
+    public SAML2SP find(final String key) {
+        return entityManager().find(JPASAML2SP.class, key);
     }
 
     @Override
-    public SAML2ServiceProvider findByName(final String name) {
-        final TypedQuery<SAML2ServiceProvider> query = entityManager().createQuery(
-                "SELECT e FROM " + JPASAML2ServiceProvider.class.getSimpleName() + " e WHERE e.name=:name",
-                SAML2ServiceProvider.class);
+    public SAML2SP findByName(final String name) {
+        TypedQuery<SAML2SP> query = entityManager().createQuery(
+                "SELECT e FROM " + JPASAML2SP.class.getSimpleName() + " e WHERE e.name=:name", SAML2SP.class);
         query.setParameter("name", name);
 
-        SAML2ServiceProvider result = null;
+        SAML2SP result = null;
         try {
             result = query.getSingleResult();
         } catch (final NoResultException e) {
-            LOG.debug("No SAML2ServiceProvider found with name {}", name, e);
+            LOG.debug("No SAML2SP found with name {}", name, e);
         }
 
         return result;
     }
 
     @Override
-    public SAML2ServiceProvider findByEntityId(final String entityId) {
-        final TypedQuery<SAML2ServiceProvider> query = entityManager().createQuery(
-                "SELECT e FROM " + JPASAML2ServiceProvider.class.getSimpleName() + " e WHERE e.entityId=:entityId",
-                SAML2ServiceProvider.class);
+    public SAML2SP findByEntityId(final String entityId) {
+        TypedQuery<SAML2SP> query = entityManager().createQuery(
+                "SELECT e FROM " + JPASAML2SP.class.getSimpleName() + " e WHERE e.entityId=:entityId", SAML2SP.class);
         query.setParameter("entityId", entityId);
 
-        SAML2ServiceProvider result = null;
+        SAML2SP result = null;
         try {
             result = query.getSingleResult();
         } catch (final NoResultException e) {
-            LOG.debug("No SAML2ServiceProvider found with clientId {}", entityId, e);
+            LOG.debug("No SAML2SP found with clientId {}", entityId, e);
         }
 
         return result;
@@ -72,21 +70,21 @@ public class JPASAML2SPDAO extends AbstractDAO<SAML2ServiceProvider> implements
 
     @Transactional(readOnly = true)
     @Override
-    public List<SAML2ServiceProvider> findAll() {
-        final TypedQuery<SAML2ServiceProvider> query = entityManager().createQuery(
-                "SELECT e FROM " + JPASAML2ServiceProvider.class.getSimpleName() + " e", SAML2ServiceProvider.class);
+    public List<SAML2SP> findAll() {
+        TypedQuery<SAML2SP> query = entityManager().createQuery(
+                "SELECT e FROM " + JPASAML2SP.class.getSimpleName() + " e", SAML2SP.class);
 
         return query.getResultList();
     }
 
     @Override
-    public SAML2ServiceProvider save(final SAML2ServiceProvider policy) {
-        return entityManager().merge(policy);
+    public SAML2SP save(final SAML2SP clientApp) {
+        return entityManager().merge(clientApp);
     }
 
     @Override
     public void delete(final String key) {
-        final SAML2ServiceProvider policy = find(key);
+        SAML2SP policy = find(key);
         if (policy == null) {
             return;
         }
@@ -96,7 +94,7 @@ public class JPASAML2SPDAO extends AbstractDAO<SAML2ServiceProvider> implements
 
     @Override
     public void deleteByEntityId(final String entityId) {
-        final SAML2ServiceProvider app = findByEntityId(entityId);
+        SAML2SP app = findByEntityId(entityId);
         if (app == null) {
             return;
         }
@@ -104,7 +102,7 @@ public class JPASAML2SPDAO extends AbstractDAO<SAML2ServiceProvider> implements
     }
 
     @Override
-    public void delete(final SAML2ServiceProvider policy) {
-        entityManager().remove(policy);
+    public void delete(final SAML2SP clientApp) {
+        entityManager().remove(clientApp);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 258485d..986c716 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -56,8 +56,9 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrUnique
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
+import org.apache.syncope.core.persistence.api.entity.authentication.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
@@ -66,6 +67,7 @@ import org.apache.syncope.core.persistence.api.entity.group.TypeExtension;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
@@ -107,8 +109,8 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrVal
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.authentication.JPAAuthModule;
-import org.apache.syncope.core.persistence.jpa.entity.authentication.JPAOIDCRelyingParty;
-import org.apache.syncope.core.persistence.jpa.entity.authentication.JPASAML2ServiceProvider;
+import org.apache.syncope.core.persistence.jpa.entity.authentication.JPAOIDCRP;
+import org.apache.syncope.core.persistence.jpa.entity.authentication.JPASAML2SP;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrValue;
@@ -151,8 +153,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
-import org.apache.syncope.core.persistence.api.entity.authentication.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 
 public class JPAEntityFactory implements EntityFactory {
 
@@ -313,10 +313,10 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAAccessPolicy();
         } else if (reference.equals(AttrReleasePolicy.class)) {
             result = (E) new JPAAttrReleasePolicy();
-        } else if (reference.equals(OIDCRelyingParty.class)) {
-            result = (E) new JPAOIDCRelyingParty();
-        } else if (reference.equals(SAML2ServiceProvider.class)) {
-            result = (E) new JPASAML2ServiceProvider();
+        } else if (reference.equals(OIDCRP.class)) {
+            result = (E) new JPAOIDCRP();
+        } else if (reference.equals(SAML2SP.class)) {
+            result = (E) new JPASAML2SP();
         } else {
             throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName());
         }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index f520d5e..abe881c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -18,6 +18,22 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.persistence.Cacheable;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.Size;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
@@ -25,38 +41,20 @@ import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RealmCheck;
 
-import javax.persistence.Cacheable;
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
-import javax.persistence.OneToMany;
-import javax.persistence.Table;
-import javax.persistence.UniqueConstraint;
-import javax.validation.constraints.Size;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
-
 @Entity
 @Table(name = JPARealm.TABLE, uniqueConstraints =
         @UniqueConstraint(columnNames = { "name", "parent_id" }))
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtils.java
new file mode 100644
index 0000000..b6160f3
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.authentication;
+
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
+
+public class JPAClientAppUtils implements ClientAppUtils {
+
+    private final ClientAppType type;
+
+    protected JPAClientAppUtils(final ClientAppType type) {
+        this.type = type;
+    }
+
+    @Override
+    public ClientAppType getType() {
+        return type;
+    }
+
+    @Override
+    public Class<? extends ClientApp> clientAppClass() {
+        switch (type) {
+            case OIDCRP:
+                return OIDCRP.class;
+
+            case SAML2SP:
+            default:
+                return SAML2SP.class;
+        }
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtilsFactory.java
new file mode 100644
index 0000000..f19691d
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAClientAppUtilsFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.authentication;
+
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.to.client.OIDCRPTO;
+import org.apache.syncope.common.lib.to.client.SAML2SPTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JPAClientAppUtilsFactory implements ClientAppUtilsFactory {
+
+    @Override
+    public ClientAppUtils getInstance(final ClientAppType type) {
+        return new JPAClientAppUtils(type);
+    }
+
+    @Override
+    public ClientAppUtils getInstance(final ClientApp clientApp) {
+        ClientAppType type;
+        if (clientApp instanceof SAML2SP) {
+            type = ClientAppType.SAML2SP;
+        } else if (clientApp instanceof OIDCRP) {
+            type = ClientAppType.OIDCRP;
+        } else {
+            throw new IllegalArgumentException("Invalid client app: " + clientApp);
+        }
+
+        return getInstance(type);
+    }
+
+    @Override
+    public ClientAppUtils getInstance(final Class<? extends ClientAppTO> clientAppClass) {
+        ClientAppType type;
+        if (clientAppClass == SAML2SPTO.class) {
+            type = ClientAppType.SAML2SP;
+        } else if (clientAppClass == OIDCRPTO.class) {
+            type = ClientAppType.OIDCRP;
+        } else {
+            throw new IllegalArgumentException("Invalid ClientAppTO app: " + clientAppClass.getName());
+        }
+
+        return getInstance(type);
+    }
+
+    @Override
+    public ClientAppUtils getInstance(final ClientAppTO clientAppTO) {
+        return getInstance(clientAppTO.getClass());
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRelyingParty.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRP.java
similarity index 89%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRelyingParty.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRP.java
index 37c40fb..58797a0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRelyingParty.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPAOIDCRP.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.authentication;
 
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
-
 import javax.persistence.CollectionTable;
 import javax.persistence.Column;
 import javax.persistence.ElementCollection;
@@ -27,21 +25,21 @@ import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.Table;
-
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
 
 @Entity
-@Table(name = JPAOIDCRelyingParty.TABLE)
-public class JPAOIDCRelyingParty extends AbstractClientApp implements OIDCRelyingParty {
-
-    public static final String TABLE = "OIDCRelyingParty";
+@Table(name = JPAOIDCRP.TABLE)
+public class JPAOIDCRP extends AbstractClientApp implements OIDCRP {
 
     private static final long serialVersionUID = 7422422526695279794L;
 
+    public static final String TABLE = "OIDCRP";
+
     @Column(unique = true, nullable = false)
     private String clientId;
 
@@ -59,21 +57,21 @@ public class JPAOIDCRelyingParty extends AbstractClientApp implements OIDCRelyin
 
     @ElementCollection(fetch = FetchType.EAGER)
     @Column
-    @CollectionTable(name = "OIDCRelyingParty_RedirectUris",
+    @CollectionTable(name = "OIDCRP_RedirectUris",
             joinColumns =
             @JoinColumn(name = "client_id", referencedColumnName = "id"))
     private List<String> redirectUris = new ArrayList<>();
 
     @ElementCollection(fetch = FetchType.EAGER)
     @Column
-    @CollectionTable(name = "OIDCRelyingParty_SupportedGrantTypes",
+    @CollectionTable(name = "OIDCRP_SupportedGrantTypes",
             joinColumns =
             @JoinColumn(name = "client_id", referencedColumnName = "id"))
     private Set<String> supportedGrantTypes = new HashSet<>();
 
     @ElementCollection(fetch = FetchType.EAGER)
     @Column(name = "supportedResponseType")
-    @CollectionTable(name = "OIDCRelyingParty_SupportedResponseTypes",
+    @CollectionTable(name = "OIDCRP_SupportedResponseTypes",
             joinColumns =
             @JoinColumn(name = "client_id", referencedColumnName = "id"))
     private Set<String> supportedResponseTypes = new HashSet<>();
@@ -142,5 +140,4 @@ public class JPAOIDCRelyingParty extends AbstractClientApp implements OIDCRelyin
     public Set<String> getSupportedResponseTypes() {
         return supportedResponseTypes;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2ServiceProvider.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2SP.java
similarity index 90%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2ServiceProvider.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2SP.java
index c40234c..f573b8e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2ServiceProvider.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/authentication/JPASAML2SP.java
@@ -18,17 +18,17 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.authentication;
 
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Table;
-import org.apache.syncope.common.lib.types.SAML2ServiceProviderNameId;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
 
 @Entity
-@Table(name = JPASAML2ServiceProvider.TABLE)
-public class JPASAML2ServiceProvider extends AbstractClientApp implements SAML2ServiceProvider {
+@Table(name = JPASAML2SP.TABLE)
+public class JPASAML2SP extends AbstractClientApp implements SAML2SP {
 
-    public static final String TABLE = "SAML2ServiceProvider";
+    public static final String TABLE = "SAML2SP";
 
     private static final long serialVersionUID = 6422422526695279794L;
 
@@ -57,7 +57,7 @@ public class JPASAML2ServiceProvider extends AbstractClientApp implements SAML2S
     private String requiredAuthenticationContextClass;
 
     @Column
-    private SAML2ServiceProviderNameId requiredNameIdFormat;
+    private SAML2SPNameId requiredNameIdFormat;
 
     @Column
     private Integer skewAllowance;
@@ -152,12 +152,12 @@ public class JPASAML2ServiceProvider extends AbstractClientApp implements SAML2S
     }
 
     @Override
-    public SAML2ServiceProviderNameId getRequiredNameIdFormat() {
+    public SAML2SPNameId getRequiredNameIdFormat() {
         return requiredNameIdFormat;
     }
 
     @Override
-    public void setRequiredNameIdFormat(final SAML2ServiceProviderNameId requiredNameIdFormat) {
+    public void setRequiredNameIdFormat(final SAML2SPNameId requiredNameIdFormat) {
         this.requiredNameIdFormat = requiredNameIdFormat;
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
index 3e0c7b3..c694eca 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java
@@ -53,8 +53,8 @@ public class JPAPolicyUtils implements PolicyUtils {
 
             case PULL:
                 return PullPolicy.class;
-                
-            case AUTHENTICATION:
+
+            case AUTH:
                 return AuthPolicy.class;
 
             case ATTR_RELEASE:
@@ -68,5 +68,4 @@ public class JPAPolicyUtils implements PolicyUtils {
                 return PushPolicy.class;
         }
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
index 711b6d1..1db66e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
@@ -59,7 +59,7 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
         } else if (policy instanceof PushPolicy) {
             type = PolicyType.PUSH;
         } else if (policy instanceof AuthPolicy) {
-            type = PolicyType.AUTHENTICATION;
+            type = PolicyType.AUTH;
         } else if (policy instanceof AccessPolicy) {
             type = PolicyType.ACCESS;
         } else if (policy instanceof AttrReleasePolicy) {
@@ -83,7 +83,7 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
         } else if (policyClass == PushPolicyTO.class) {
             type = PolicyType.PUSH;
         } else if (policyClass == AuthPolicyTO.class) {
-            type = PolicyType.AUTHENTICATION;
+            type = PolicyType.AUTH;
         } else if (policyClass == AccessPolicyTO.class) {
             type = PolicyType.ACCESS;
         } else if (policyClass == AttrReleasePolicyTO.class) {
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
index 95ad9a4..3bbbf10 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
@@ -41,7 +41,7 @@ import java.util.UUID;
 import org.apache.syncope.core.persistence.api.entity.authentication.AuthModule;
 import org.apache.syncope.common.lib.authentication.module.AuthModuleConf;
 import org.apache.syncope.common.lib.authentication.module.OIDCAuthModuleConf;
-import org.apache.syncope.common.lib.to.ProfileItemTO;
+import org.apache.syncope.common.lib.to.ItemTO;
 import org.apache.syncope.core.persistence.api.dao.authentication.AuthModuleDAO;
 
 @Transactional("Master")
@@ -113,10 +113,10 @@ public class AuthModuleTest extends AbstractTest {
         conf.setUserIdAttribute("uid");
         conf.setBaseDn("cn=Directory Manager,dc=example,dc=org");
         conf.setBindCredential("Password");
-        ProfileItemTO keyMapping = new ProfileItemTO();
+        ItemTO keyMapping = new ItemTO();
         keyMapping.setIntAttrName("uid");
         keyMapping.setExtAttrName("username");
-        ProfileItemTO fullnameMapping = new ProfileItemTO();
+        ItemTO fullnameMapping = new ItemTO();
         fullnameMapping.setIntAttrName("cn");
         fullnameMapping.setExtAttrName("fullname");
         conf.getProfileItems().addAll(List.of(fullnameMapping, keyMapping));
@@ -156,7 +156,7 @@ public class AuthModuleTest extends AbstractTest {
         conf.setUserIdAttribute("username");
         conf.setResponseType("code");
         conf.setScope("openid email profile");
-        ProfileItemTO keyMapping = new ProfileItemTO();
+        ItemTO keyMapping = new ItemTO();
         keyMapping.setIntAttrName("uid");
         keyMapping.setExtAttrName("username");
         conf.getProfileItems().add(keyMapping);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRelyingPartyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
similarity index 78%
rename from core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRelyingPartyTest.java
rename to core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
index 4e24459..263c2a4 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRelyingPartyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
@@ -18,31 +18,30 @@
  */
 package org.apache.syncope.core.persistence.jpa.inner;
 
-import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRelyingPartyDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
-import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
+import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 
 @Transactional("Master")
-public class OIDCRelyingPartyTest extends AbstractClientAppTest {
+public class OIDCRPTest extends AbstractClientAppTest {
 
     @Autowired
-    private OIDCRelyingPartyDAO oidcRelyingPartyDAO;
+    private OIDCRPDAO oidcrpDAO;
 
     @Test
     public void find() {
-        int beforeCount = oidcRelyingPartyDAO.findAll().size();
+        int beforeCount = oidcrpDAO.findAll().size();
 
-        OIDCRelyingParty rp = entityFactory.newEntity(OIDCRelyingParty.class);
+        OIDCRP rp = entityFactory.newEntity(OIDCRP.class);
         rp.setName("OIDC");
         rp.setDescription("This is a sample OIDC RP");
         rp.setClientId("clientid");
@@ -57,23 +56,22 @@ public class OIDCRelyingPartyTest extends AbstractClientAppTest {
         AuthPolicy authPolicy = buildAndSaveAuthPolicy();
         rp.setAuthPolicy(authPolicy);
 
-        oidcRelyingPartyDAO.save(rp);
+        oidcrpDAO.save(rp);
 
         assertNotNull(rp);
         assertNotNull(rp.getKey());
 
-        int afterCount = oidcRelyingPartyDAO.findAll().size();
+        int afterCount = oidcrpDAO.findAll().size();
         assertEquals(afterCount, beforeCount + 1);
 
-        rp = oidcRelyingPartyDAO.findByClientId("clientid");
+        rp = oidcrpDAO.findByClientId("clientid");
         assertNotNull(rp);
         assertNotNull(rp.getAuthPolicy());
 
-        rp = oidcRelyingPartyDAO.findByName("OIDC");
+        rp = oidcrpDAO.findByName("OIDC");
         assertNotNull(rp);
 
-        oidcRelyingPartyDAO.deleteByClientId("clientid");
-        assertNull(oidcRelyingPartyDAO.findByName("OIDC"));
+        oidcrpDAO.deleteByClientId("clientid");
+        assertNull(oidcrpDAO.findByName("OIDC"));
     }
-
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2ServiceProviderTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
similarity index 71%
rename from core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2ServiceProviderTest.java
rename to core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
index 4e55b9c..8639e9a 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2ServiceProviderTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
@@ -18,35 +18,34 @@
  */
 package org.apache.syncope.core.persistence.jpa.inner;
 
-import org.apache.syncope.core.persistence.api.dao.authentication.SAML2ServiceProviderDAO;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
-import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
-import org.apache.syncope.common.lib.types.SAML2ServiceProviderNameId;
+import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.dao.authentication.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
 
 @Transactional("Master")
-public class SAML2ServiceProviderTest extends AbstractClientAppTest {
+public class SAML2SPTest extends AbstractClientAppTest {
 
     @Autowired
-    private SAML2ServiceProviderDAO saml2ServiceProviderDAO;
+    private SAML2SPDAO saml2spDAO;
 
     @Test
     public void find() {
-        int beforeCount = saml2ServiceProviderDAO.findAll().size();
-        SAML2ServiceProvider rp = entityFactory.newEntity(SAML2ServiceProvider.class);
+        int beforeCount = saml2spDAO.findAll().size();
+        SAML2SP rp = entityFactory.newEntity(SAML2SP.class);
         rp.setName("SAML2");
         rp.setDescription("This is a sample SAML2 SP");
         rp.setEntityId("urn:example:saml2:sp");
         rp.setMetadataLocation("https://example.org/metadata.xml");
-        rp.setRequiredNameIdFormat(SAML2ServiceProviderNameId.EMAIL_ADDRESS);
+        rp.setRequiredNameIdFormat(SAML2SPNameId.EMAIL_ADDRESS);
         rp.setEncryptionOptional(true);
         rp.setEncryptAssertions(true);
 
@@ -56,22 +55,21 @@ public class SAML2ServiceProviderTest extends AbstractClientAppTest {
         AuthPolicy authnPolicy = buildAndSaveAuthPolicy();
         rp.setAuthPolicy(authnPolicy);
 
-        saml2ServiceProviderDAO.save(rp);
+        saml2spDAO.save(rp);
 
         assertNotNull(rp);
         assertNotNull(rp.getKey());
 
-        int afterCount = saml2ServiceProviderDAO.findAll().size();
+        int afterCount = saml2spDAO.findAll().size();
         assertEquals(afterCount, beforeCount + 1);
 
-        rp = saml2ServiceProviderDAO.findByEntityId(rp.getEntityId());
+        rp = saml2spDAO.findByEntityId(rp.getEntityId());
         assertNotNull(rp);
 
-        rp = saml2ServiceProviderDAO.findByName(rp.getName());
+        rp = saml2spDAO.findByName(rp.getName());
         assertNotNull(rp);
 
-        saml2ServiceProviderDAO.deleteByEntityId(rp.getEntityId());
-        assertNull(saml2ServiceProviderDAO.findByName(rp.getName()));
+        saml2spDAO.deleteByEntityId(rp.getEntityId());
+        assertNull(saml2spDAO.findByName(rp.getName()));
     }
-
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
index d5342c3..fe6e99e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
@@ -20,9 +20,7 @@ package org.apache.syncope.core.persistence.jpa.outer;
 
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRelyingPartyDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
@@ -40,11 +38,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+
 @Transactional("Master")
 public class PolicyTest extends AbstractClientAppTest {
 
     @Autowired
-    private OIDCRelyingPartyDAO oidcRelyingPartyDAO;
+    private OIDCRPDAO oidcRelyingPartyDAO;
 
     @Autowired
     private RealmDAO realmDAO;
@@ -56,7 +57,7 @@ public class PolicyTest extends AbstractClientAppTest {
         AuthPolicy authPolicy = buildAndSaveAuthPolicy();
 
         // Create new client app and assign policy
-        OIDCRelyingParty rp = entityFactory.newEntity(OIDCRelyingParty.class);
+        OIDCRP rp = entityFactory.newEntity(OIDCRP.class);
         rp.setName("OIDC");
         rp.setDescription("This is a sample OIDC RP");
         rp.setClientId(UUID.randomUUID().toString());
@@ -83,7 +84,7 @@ public class PolicyTest extends AbstractClientAppTest {
         assertNotNull(realm);
 
         // Create new client app and assign policy
-        OIDCRelyingParty rp = entityFactory.newEntity(OIDCRelyingParty.class);
+        OIDCRP rp = entityFactory.newEntity(OIDCRP.class);
         rp.setName("OIDC");
         rp.setDescription("This is a sample OIDC RP");
         rp.setClientId(UUID.randomUUID().toString());
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCRelyingPartyDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
similarity index 71%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCRelyingPartyDataBinder.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
index 6829f41..308543d 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCRelyingPartyDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
@@ -18,14 +18,14 @@
  */
 package org.apache.syncope.core.provisioning.api.data;
 
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientApp;
 
-public interface OIDCRelyingPartyDataBinder {
+public interface ClientAppDataBinder {
 
-    OIDCRelyingParty create(OIDCRelyingPartyTO applicationTO);
+    <T extends ClientApp> T create(ClientAppTO clientAppTO);
 
-    OIDCRelyingParty update(OIDCRelyingParty application, OIDCRelyingPartyTO applicationTO);
+    <T extends ClientApp> void update(T clientApp, ClientAppTO clientAppTO);
 
-    OIDCRelyingPartyTO getClientApplicationTO(OIDCRelyingParty application);
+    <T extends ClientAppTO> T getClientAppTO(ClientApp clientApp);
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
new file mode 100644
index 0000000..a915513
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.to.client.OIDCRPTO;
+import org.apache.syncope.common.lib.to.client.SAML2SPTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.authentication.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.authentication.SAML2SP;
+import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRP;
+
+@Component
+public class ClientAppDataBinderImpl implements ClientAppDataBinder {
+
+    @Autowired
+    private PolicyDAO policyDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends ClientApp> T create(final ClientAppTO clientAppTO) {
+        if (clientAppTO instanceof SAML2SPTO) {
+            return (T) doCreate((SAML2SPTO) clientAppTO);
+        } else if (clientAppTO instanceof OIDCRPTO) {
+            return (T) doCreate((OIDCRPTO) clientAppTO);
+        } else {
+            throw new IllegalArgumentException("Unsupported client app: " + clientAppTO.getClass().getName());
+        }
+    }
+
+    @Override
+    public <T extends ClientApp> void update(final T clientApp, final ClientAppTO clientAppTO) {
+        if (clientAppTO instanceof SAML2SPTO) {
+            doUpdate((SAML2SP) clientApp, (SAML2SPTO) clientAppTO);
+        } else if (clientAppTO instanceof OIDCRPTO) {
+            doUpdate((OIDCRP) clientApp, (OIDCRPTO) clientAppTO);
+        } else {
+            throw new IllegalArgumentException("Unsupported client app: " + clientAppTO.getClass().getName());
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends ClientAppTO> T getClientAppTO(final ClientApp clientApp) {
+        if (clientApp instanceof SAML2SP) {
+            return (T) getClientAppTO((SAML2SP) clientApp);
+        } else if (clientApp instanceof OIDCRP) {
+            return (T) getClientAppTO((OIDCRP) clientApp);
+        } else {
+            throw new IllegalArgumentException("Unsupported client app: " + clientApp.getClass().getName());
+        }
+    }
+
+    private SAML2SP doCreate(final SAML2SPTO clientAppTO) {
+        SAML2SP saml2sp = entityFactory.newEntity(SAML2SP.class);
+        update(saml2sp, clientAppTO);
+        return saml2sp;
+    }
+
+    private void doUpdate(final SAML2SP clientApp, final SAML2SPTO clientAppTO) {
+        clientApp.setDescription(clientAppTO.getDescription());
+        clientApp.setName(clientAppTO.getName());
+        clientApp.setEntityId(clientAppTO.getEntityId());
+        clientApp.setMetadataLocation(clientAppTO.getMetadataLocation());
+        clientApp.setMetadataSignatureLocation(clientAppTO.getMetadataLocation());
+        clientApp.setSignAssertions(clientAppTO.isSignAssertions());
+        clientApp.setSignResponses(clientAppTO.isSignResponses());
+        clientApp.setEncryptionOptional(clientAppTO.isEncryptionOptional());
+        clientApp.setEncryptAssertions(clientAppTO.isEncryptAssertions());
+        clientApp.setRequiredAuthenticationContextClass(clientAppTO.getRequiredAuthenticationContextClass());
+        clientApp.setRequiredNameIdFormat(clientAppTO.getRequiredNameIdFormat());
+        clientApp.setSkewAllowance(clientAppTO.getSkewAllowance());
+        clientApp.setNameIdQualifier(clientAppTO.getNameIdQualifier());
+        clientApp.setAssertionAudiences(clientAppTO.getAssertionAudiences());
+        clientApp.setServiceProviderNameIdQualifier(clientAppTO.getServiceProviderNameIdQualifier());
+
+        if (clientAppTO.getAuthPolicy() == null) {
+            clientApp.setAuthPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAuthPolicy());
+            if (policy instanceof AuthPolicy) {
+                clientApp.setAuthPolicy((AuthPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AuthPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAccessPolicy() == null) {
+            clientApp.setAccessPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAccessPolicy());
+            if (policy instanceof AccessPolicy) {
+                clientApp.setAccessPolicy((AccessPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AccessPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAttrReleasePolicy() == null) {
+            clientApp.setAttrReleasePolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAttrReleasePolicy());
+            if (policy instanceof AttrReleasePolicy) {
+                clientApp.setAttrReleasePolicy((AttrReleasePolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AttrReleasePolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+    }
+
+    private SAML2SPTO getClientAppTO(final SAML2SP clientApp) {
+        SAML2SPTO clientAppTO = new SAML2SPTO();
+
+        clientAppTO.setName(clientApp.getName());
+        clientAppTO.setKey(clientApp.getKey());
+        clientAppTO.setDescription(clientApp.getDescription());
+        clientAppTO.setEntityId(clientApp.getEntityId());
+        clientAppTO.setMetadataLocation(clientApp.getMetadataLocation());
+        clientAppTO.setMetadataSignatureLocation(clientApp.getMetadataLocation());
+        clientAppTO.setSignAssertions(clientApp.isSignAssertions());
+        clientAppTO.setSignResponses(clientApp.isSignResponses());
+        clientAppTO.setEncryptionOptional(clientApp.isEncryptionOptional());
+        clientAppTO.setEncryptAssertions(clientApp.isEncryptAssertions());
+        clientAppTO.setRequiredAuthenticationContextClass(clientApp.getRequiredAuthenticationContextClass());
+        clientAppTO.setRequiredNameIdFormat(clientApp.getRequiredNameIdFormat());
+        clientAppTO.setSkewAllowance(clientApp.getSkewAllowance());
+        clientAppTO.setNameIdQualifier(clientApp.getNameIdQualifier());
+        clientAppTO.setAssertionAudiences(clientApp.getAssertionAudiences());
+        clientAppTO.setServiceProviderNameIdQualifier(clientApp.getServiceProviderNameIdQualifier());
+
+        clientAppTO.setAuthPolicy(clientApp.getAuthPolicy() == null
+                ? null : clientApp.getAuthPolicy().getKey());
+        clientAppTO.setAccessPolicy(clientApp.getAccessPolicy() == null
+                ? null : clientApp.getAccessPolicy().getKey());
+        clientAppTO.setAttrReleasePolicy(clientApp.getAttrReleasePolicy() == null
+                ? null : clientApp.getAttrReleasePolicy().getKey());
+
+        return clientAppTO;
+    }
+
+    private OIDCRP doCreate(final OIDCRPTO clientAppTO) {
+        OIDCRP oidcrp = entityFactory.newEntity(OIDCRP.class);
+        update(oidcrp, clientAppTO);
+        return oidcrp;
+    }
+
+    private void doUpdate(final OIDCRP clientApp, final OIDCRPTO clientAppTO) {
+        clientApp.setName(clientAppTO.getName());
+        clientApp.setDescription(clientAppTO.getDescription());
+        clientApp.setClientSecret(clientAppTO.getClientSecret());
+        clientApp.setClientId(clientAppTO.getClientId());
+        clientApp.setSignIdToken(clientAppTO.isSignIdToken());
+        clientApp.setJwks(clientAppTO.getJwks());
+        clientApp.setSubjectType(clientAppTO.getSubjectType());
+        clientApp.getRedirectUris().addAll(clientAppTO.getRedirectUris());
+        clientApp.getSupportedGrantTypes().addAll(clientAppTO.getSupportedGrantTypes());
+        clientApp.getSupportedResponseTypes().addAll(clientAppTO.getSupportedResponseTypes());
+
+        if (clientAppTO.getAuthPolicy() == null) {
+            clientApp.setAuthPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAuthPolicy());
+            if (policy instanceof AuthPolicy) {
+                clientApp.setAuthPolicy((AuthPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AuthPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAccessPolicy() == null) {
+            clientApp.setAccessPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAccessPolicy());
+            if (policy instanceof AccessPolicy) {
+                clientApp.setAccessPolicy((AccessPolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AccessPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAttrReleasePolicy() == null) {
+            clientApp.setAttrReleasePolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAttrReleasePolicy());
+            if (policy instanceof AttrReleasePolicy) {
+                clientApp.setAttrReleasePolicy((AttrReleasePolicy) policy);
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AttrReleasePolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+    }
+
+    private OIDCRPTO getClientAppTO(final OIDCRP clientApp) {
+        OIDCRPTO clientAppTO = new OIDCRPTO();
+
+        clientAppTO.setName(clientApp.getName());
+        clientAppTO.setKey(clientApp.getKey());
+        clientAppTO.setDescription(clientApp.getDescription());
+        clientAppTO.setClientId(clientApp.getClientId());
+        clientAppTO.setClientSecret(clientApp.getClientSecret());
+        clientAppTO.setSignIdToken(clientApp.isSignIdToken());
+        clientAppTO.setJwks(clientApp.getJwks());
+        clientAppTO.setSubjectType(clientApp.getSubjectType());
+        clientAppTO.getRedirectUris().addAll(clientApp.getRedirectUris());
+        clientAppTO.getSupportedGrantTypes().addAll(clientApp.getSupportedGrantTypes());
+        clientAppTO.getSupportedResponseTypes().addAll(clientApp.getSupportedResponseTypes());
+
+        clientAppTO.setAuthPolicy(clientApp.getAuthPolicy() == null
+                ? null : clientApp.getAuthPolicy().getKey());
+        clientAppTO.setAccessPolicy(clientApp.getAccessPolicy() == null
+                ? null : clientApp.getAccessPolicy().getKey());
+        clientAppTO.setAttrReleasePolicy(clientApp.getAttrReleasePolicy() == null
+                ? null : clientApp.getAttrReleasePolicy().getKey());
+
+        return clientAppTO;
+    }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCRelyingPartyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCRelyingPartyDataBinderImpl.java
deleted file mode 100644
index 972661a..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCRelyingPartyDataBinderImpl.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.data;
-
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
-import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
-import org.apache.syncope.core.persistence.api.dao.authentication.OIDCRelyingPartyDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.authentication.OIDCRelyingParty;
-import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.Policy;
-import org.apache.syncope.core.provisioning.api.data.OIDCRelyingPartyDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
-
-@Component
-public class OIDCRelyingPartyDataBinderImpl implements OIDCRelyingPartyDataBinder {
-
-    @Autowired
-    private OIDCRelyingPartyDAO oidcRelyingPartyDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private PolicyDAO policyDAO;
-
-    @Override
-    public OIDCRelyingParty create(final OIDCRelyingPartyTO applicationTO) {
-        return update(entityFactory.newEntity(OIDCRelyingParty.class), applicationTO);
-    }
-
-    @Override
-    public OIDCRelyingParty update(
-            final OIDCRelyingParty toBeUpdated,
-            final OIDCRelyingPartyTO applicationTO) {
-
-        OIDCRelyingParty application = oidcRelyingPartyDAO.save(toBeUpdated);
-
-        application.setName(applicationTO.getName());
-        application.setDescription(applicationTO.getDescription());
-        application.setClientSecret(applicationTO.getClientSecret());
-        application.setClientId(applicationTO.getClientId());
-        application.setSignIdToken(applicationTO.isSignIdToken());
-        application.setJwks(applicationTO.getJwks());
-        application.setSubjectType(applicationTO.getSubjectType());
-        application.getRedirectUris().addAll(applicationTO.getRedirectUris());
-        application.getSupportedGrantTypes().addAll(applicationTO.getSupportedGrantTypes());
-        application.getSupportedResponseTypes().addAll(applicationTO.getSupportedResponseTypes());
-
-        if (applicationTO.getAuthPolicy() == null) {
-            application.setAuthPolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAuthPolicy());
-            if (policy instanceof AuthPolicy) {
-                application.setAuthPolicy((AuthPolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AuthPolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-
-        if (applicationTO.getAccessPolicy() == null) {
-            application.setAccessPolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAccessPolicy());
-            if (policy instanceof AccessPolicy) {
-                application.setAccessPolicy((AccessPolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AccessPolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-
-        if (applicationTO.getAttrReleasePolicy() == null) {
-            application.setAttrReleasePolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAttrReleasePolicy());
-            if (policy instanceof AttrReleasePolicy) {
-                application.setAttrReleasePolicy((AttrReleasePolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AttrReleasePolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-
-        return application;
-    }
-
-    @Override
-    public OIDCRelyingPartyTO getClientApplicationTO(final OIDCRelyingParty rp) {
-        OIDCRelyingPartyTO applicationTO = new OIDCRelyingPartyTO();
-
-        applicationTO.setName(rp.getName());
-        applicationTO.setKey(rp.getKey());
-        applicationTO.setDescription(rp.getDescription());
-        applicationTO.setClientId(rp.getClientId());
-        applicationTO.setClientSecret(rp.getClientSecret());
-        applicationTO.setSignIdToken(rp.isSignIdToken());
-        applicationTO.setJwks(rp.getJwks());
-        applicationTO.setSubjectType(rp.getSubjectType());
-        applicationTO.getRedirectUris().addAll(rp.getRedirectUris());
-        applicationTO.getSupportedGrantTypes().addAll(rp.getSupportedGrantTypes());
-        applicationTO.getSupportedResponseTypes().addAll(rp.getSupportedResponseTypes());
-
-        applicationTO.setAuthPolicy(rp.getAuthPolicy() == null
-                ? null : rp.getAuthPolicy().getKey());
-        applicationTO.setAccessPolicy(rp.getAccessPolicy() == null
-                ? null : rp.getAccessPolicy().getKey());
-        applicationTO.setAttrReleasePolicy(rp.getAttrReleasePolicy() == null
-                ? null : rp.getAttrReleasePolicy().getKey());
-
-        return applicationTO;
-    }
-}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2ServiceProviderDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2ServiceProviderDataBinderImpl.java
deleted file mode 100644
index c46e119..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2ServiceProviderDataBinderImpl.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.data;
-
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
-import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
-import org.apache.syncope.core.persistence.api.dao.authentication.SAML2ServiceProviderDAO;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.authentication.SAML2ServiceProvider;
-import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.Policy;
-import org.apache.syncope.core.provisioning.api.data.SAML2ServiceProviderDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
-
-@Component
-public class SAML2ServiceProviderDataBinderImpl implements SAML2ServiceProviderDataBinder {
-
-    @Autowired
-    private SAML2ServiceProviderDAO saml2ServiceProviderDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private PolicyDAO policyDAO;
-
-    @Override
-    public SAML2ServiceProvider create(final SAML2ServiceProviderTO applicationTO) {
-        return update(entityFactory.newEntity(SAML2ServiceProvider.class), applicationTO);
-    }
-
-    @Override
-    public SAML2ServiceProvider update(
-            final SAML2ServiceProvider toBeUpdated,
-            final SAML2ServiceProviderTO applicationTO) {
-
-        SAML2ServiceProvider application = saml2ServiceProviderDAO.save(toBeUpdated);
-
-        application.setDescription(applicationTO.getDescription());
-        application.setName(applicationTO.getName());
-        application.setEntityId(applicationTO.getEntityId());
-        application.setMetadataLocation(applicationTO.getMetadataLocation());
-        application.setMetadataSignatureLocation(applicationTO.getMetadataLocation());
-        application.setSignAssertions(applicationTO.isSignAssertions());
-        application.setSignResponses(applicationTO.isSignResponses());
-        application.setEncryptionOptional(applicationTO.isEncryptionOptional());
-        application.setEncryptAssertions(applicationTO.isEncryptAssertions());
-        application.setRequiredAuthenticationContextClass(applicationTO.getRequiredAuthenticationContextClass());
-        application.setRequiredNameIdFormat(applicationTO.getRequiredNameIdFormat());
-        application.setSkewAllowance(applicationTO.getSkewAllowance());
-        application.setNameIdQualifier(applicationTO.getNameIdQualifier());
-        application.setAssertionAudiences(applicationTO.getAssertionAudiences());
-        application.setServiceProviderNameIdQualifier(applicationTO.getServiceProviderNameIdQualifier());
-
-        if (applicationTO.getAuthPolicy() == null) {
-            application.setAuthPolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAuthPolicy());
-            if (policy instanceof AuthPolicy) {
-                application.setAuthPolicy((AuthPolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AuthPolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-
-        if (applicationTO.getAccessPolicy() == null) {
-            application.setAccessPolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAccessPolicy());
-            if (policy instanceof AccessPolicy) {
-                application.setAccessPolicy((AccessPolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AccessPolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-
-        if (applicationTO.getAttrReleasePolicy() == null) {
-            application.setAttrReleasePolicy(null);
-        } else {
-            Policy policy = policyDAO.find(applicationTO.getAttrReleasePolicy());
-            if (policy instanceof AttrReleasePolicy) {
-                application.setAttrReleasePolicy((AttrReleasePolicy) policy);
-            } else {
-                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
-                sce.getElements().add("Expected " + AttrReleasePolicy.class.getSimpleName()
-                        + ", found " + policy.getClass().getSimpleName());
-                throw sce;
-            }
-        }
-        return application;
-    }
-
-    @Override
-    public SAML2ServiceProviderTO getClientApplicationTO(final SAML2ServiceProvider sp) {
-        SAML2ServiceProviderTO applicationTO = new SAML2ServiceProviderTO();
-
-        applicationTO.setName(sp.getName());
-        applicationTO.setKey(sp.getKey());
-        applicationTO.setDescription(sp.getDescription());
-        applicationTO.setEntityId(sp.getEntityId());
-        applicationTO.setMetadataLocation(sp.getMetadataLocation());
-        applicationTO.setMetadataSignatureLocation(sp.getMetadataLocation());
-        applicationTO.setSignAssertions(sp.isSignAssertions());
-        applicationTO.setSignResponses(sp.isSignResponses());
-        applicationTO.setEncryptionOptional(sp.isEncryptionOptional());
-        applicationTO.setEncryptAssertions(sp.isEncryptAssertions());
-        applicationTO.setRequiredAuthenticationContextClass(sp.getRequiredAuthenticationContextClass());
-        applicationTO.setRequiredNameIdFormat(sp.getRequiredNameIdFormat());
-        applicationTO.setSkewAllowance(sp.getSkewAllowance());
-        applicationTO.setNameIdQualifier(sp.getNameIdQualifier());
-        applicationTO.setAssertionAudiences(sp.getAssertionAudiences());
-        applicationTO.setServiceProviderNameIdQualifier(sp.getServiceProviderNameIdQualifier());
-
-        applicationTO.setAuthPolicy(sp.getAuthPolicy() == null
-                ? null : sp.getAuthPolicy().getKey());
-        applicationTO.setAccessPolicy(sp.getAccessPolicy() == null
-                ? null : sp.getAccessPolicy().getKey());
-        applicationTO.setAttrReleasePolicy(sp.getAttrReleasePolicy() == null
-                ? null : sp.getAttrReleasePolicy().getKey());
-
-        return applicationTO;
-    }
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index d92f0a9..bd208f7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -70,12 +70,10 @@ import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.NotificationTO;
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -564,28 +562,6 @@ public abstract class AbstractITCase {
         return (T) getObject(response.getLocation(), PolicyService.class, policy.getClass());
     }
 
-    protected OIDCRelyingPartyTO createOIDCRelyingParty(final OIDCRelyingPartyTO rpTO) {
-        Response response = clientAppService.create(rpTO);
-        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
-            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
-            if (ex != null) {
-                throw (RuntimeException) ex;
-            }
-        }
-        return getObject(response.getLocation(), ClientAppService.class, OIDCRelyingPartyTO.class);
-    }
-
-    protected SAML2ServiceProviderTO createSAML2SP(final SAML2ServiceProviderTO saml2spTO) {
-        Response response = clientAppService.create(saml2spTO);
-        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
-            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
-            if (ex != null) {
-                throw (RuntimeException) ex;
-            }
-        }
-        return getObject(response.getLocation(), ClientAppService.class, SAML2ServiceProviderTO.class);
-    }
-
     protected ResourceTO createResource(final ResourceTO resourceTO) {
         Response response = resourceService.create(resourceTO);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java
new file mode 100644
index 0000000..15db2c8
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.apache.syncope.fit.AbstractITCase.getObject;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AccessPolicyTO;
+import org.apache.syncope.common.lib.to.AuthPolicyTO;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.to.client.OIDCRPTO;
+import org.apache.syncope.common.lib.to.client.SAML2SPTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.types.OIDCSubjectType;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
+import org.apache.syncope.common.rest.api.service.ClientAppService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+
+public class ClientAppITCase extends AbstractITCase {
+
+    @Test
+    public void createSAML2SP() {
+        createClientApp(ClientAppType.SAML2SP, buildSAML2SP());
+    }
+
+    @Test
+    public void readSAML2SP() {
+        SAML2SPTO samlSpTO = buildSAML2SP();
+        samlSpTO = createClientApp(ClientAppType.SAML2SP, samlSpTO);
+
+        SAML2SPTO found = clientAppService.read(ClientAppType.SAML2SP, samlSpTO.getKey());
+        assertNotNull(found);
+        assertFalse(StringUtils.isBlank(found.getEntityId()));
+        assertFalse(StringUtils.isBlank(found.getMetadataLocation()));
+        assertTrue(found.isEncryptAssertions());
+        assertTrue(found.isEncryptionOptional());
+        assertNotNull(found.getRequiredNameIdFormat());
+        assertNotNull(found.getAccessPolicy());
+        assertNotNull(found.getAuthPolicy());
+    }
+
+    @Test
+    public void updateSAML2SP() {
+        SAML2SPTO samlSpTO = buildSAML2SP();
+        samlSpTO = createClientApp(ClientAppType.SAML2SP, samlSpTO);
+
+        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
+        accessPolicyTO.setKey("NewAccessPolicyTest_" + getUUIDString());
+        accessPolicyTO.setDescription("New Access policy");
+        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
+        assertNotNull(accessPolicyTO);
+
+        samlSpTO.setEntityId("newEntityId");
+        samlSpTO.setAccessPolicy(accessPolicyTO.getKey());
+
+        clientAppService.update(ClientAppType.SAML2SP, samlSpTO);
+        SAML2SPTO updated = clientAppService.read(ClientAppType.SAML2SP, samlSpTO.getKey());
+
+        assertNotNull(updated);
+        assertEquals("newEntityId", updated.getEntityId());
+        assertNotNull(updated.getAccessPolicy());
+    }
+
+    @Test
+    public void deleteSAML2SP() {
+        SAML2SPTO samlSpTO = buildSAML2SP();
+        samlSpTO = createClientApp(ClientAppType.SAML2SP, samlSpTO);
+
+        clientAppService.delete(ClientAppType.SAML2SP, samlSpTO.getKey());
+
+        try {
+            clientAppService.read(ClientAppType.SAML2SP, samlSpTO.getKey());
+            fail("This should not happen");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+    }
+
+    private SAML2SPTO buildSAML2SP() {
+        AuthPolicyTO authPolicyTO = new AuthPolicyTO();
+        authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
+        authPolicyTO.setDescription("Authentication Policy");
+        authPolicyTO = createPolicy(PolicyType.AUTH, authPolicyTO);
+        assertNotNull(authPolicyTO);
+
+        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
+        accessPolicyTO.setKey("AccessPolicyTest_" + getUUIDString());
+        accessPolicyTO.setDescription("Access policy");
+        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
+        assertNotNull(accessPolicyTO);
+
+        SAML2SPTO saml2spto = new SAML2SPTO();
+        saml2spto.setName("ExampleSAML2SP_" + getUUIDString());
+        saml2spto.setDescription("Example SAML 2.0 service provider");
+        saml2spto.setEntityId("SAML2SPEntityId_" + getUUIDString());
+        saml2spto.setMetadataLocation("file:./test.xml");
+        saml2spto.setRequiredNameIdFormat(SAML2SPNameId.EMAIL_ADDRESS);
+        saml2spto.setEncryptionOptional(true);
+        saml2spto.setEncryptAssertions(true);
+
+        saml2spto.setAuthPolicy(authPolicyTO.getKey());
+        saml2spto.setAccessPolicy(accessPolicyTO.getKey());
+
+        return saml2spto;
+    }
+
+    @Test
+    public void createOIDCRP() {
+        createClientApp(ClientAppType.OIDCRP, buildOIDCRP());
+    }
+
+    @Test
+    public void readOIDCRP() {
+        OIDCRPTO oidcrpTO = buildOIDCRP();
+        oidcrpTO = createClientApp(ClientAppType.OIDCRP, oidcrpTO);
+
+        OIDCRPTO found = clientAppService.read(ClientAppType.OIDCRP, oidcrpTO.getKey());
+        assertNotNull(found);
+        assertFalse(StringUtils.isBlank(found.getClientId()));
+        assertFalse(StringUtils.isBlank(found.getClientSecret()));
+        assertNotNull(found.getSubjectType());
+        assertFalse(found.getSupportedGrantTypes().isEmpty());
+        assertFalse(found.getSupportedResponseTypes().isEmpty());
+        assertNotNull(found.getAccessPolicy());
+        assertNotNull(found.getAuthPolicy());
+    }
+
+    @Test
+    public void updateOIDCRP() {
+        OIDCRPTO oidcrpTO = buildOIDCRP();
+        oidcrpTO = createClientApp(ClientAppType.OIDCRP, oidcrpTO);
+
+        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
+        accessPolicyTO.setKey("NewAccessPolicyTest_" + getUUIDString());
+        accessPolicyTO.setDescription("New Access policy");
+        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
+        assertNotNull(accessPolicyTO);
+
+        oidcrpTO.setClientId("newClientId");
+        oidcrpTO.setAccessPolicy(accessPolicyTO.getKey());
+
+        clientAppService.update(ClientAppType.OIDCRP, oidcrpTO);
+        OIDCRPTO updated = clientAppService.read(ClientAppType.OIDCRP, oidcrpTO.getKey());
+
+        assertNotNull(updated);
+        assertEquals("newClientId", updated.getClientId());
+        assertNotNull(updated.getAccessPolicy());
+    }
+
+    @Test
+    public void delete() {
+        OIDCRPTO oidcrpTO = buildOIDCRP();
+        oidcrpTO = createClientApp(ClientAppType.OIDCRP, oidcrpTO);
+
+        clientAppService.delete(ClientAppType.OIDCRP, oidcrpTO.getKey());
+
+        try {
+            clientAppService.read(ClientAppType.OIDCRP, oidcrpTO.getKey());
+            fail("This should not happen");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+    }
+
+    private OIDCRPTO buildOIDCRP() {
+        AuthPolicyTO authPolicyTO = new AuthPolicyTO();
+        authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
+        authPolicyTO.setDescription("Authentication Policy");
+        authPolicyTO = createPolicy(PolicyType.AUTH, authPolicyTO);
+        assertNotNull(authPolicyTO);
+
+        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
+        accessPolicyTO.setKey("AccessPolicyTest_" + getUUIDString());
+        accessPolicyTO.setDescription("Access policy");
+        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
+        assertNotNull(accessPolicyTO);
+
+        OIDCRPTO oidcrpTO = new OIDCRPTO();
+        oidcrpTO.setName("ExampleRP_" + getUUIDString());
+        oidcrpTO.setDescription("Example OIDC RP application");
+        oidcrpTO.setClientId("clientId_" + getUUIDString());
+        oidcrpTO.setClientSecret("secret");
+        oidcrpTO.setSubjectType(OIDCSubjectType.PUBLIC);
+        oidcrpTO.getSupportedGrantTypes().add("something");
+        oidcrpTO.getSupportedResponseTypes().add("something");
+
+        oidcrpTO.setAuthPolicy(authPolicyTO.getKey());
+        oidcrpTO.setAccessPolicy(accessPolicyTO.getKey());
+
+        return oidcrpTO;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends ClientAppTO> T createClientApp(final ClientAppType type, final T clientAppTO) {
+        Response response = clientAppService.create(type, clientAppTO);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return (T) getObject(response.getLocation(), ClientAppService.class, clientAppTO.getClass());
+    }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCRelyingPartyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCRelyingPartyITCase.java
deleted file mode 100644
index a7870af..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OIDCRelyingPartyITCase.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit.core;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.to.client.OIDCRelyingPartyTO;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.fit.AbstractITCase;
-import org.junit.jupiter.api.Test;
-import org.apache.syncope.common.lib.to.AccessPolicyTO;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.AuthPolicyTO;
-import org.apache.syncope.common.lib.types.OIDCSubjectType;
-
-public class OIDCRelyingPartyITCase extends AbstractITCase {
-
-    @Test
-    public void create() {
-        createOIDCRelyingParty(buildRelyingParty());
-    }
-
-    @Test
-    public void read() {
-        OIDCRelyingPartyTO rpTO = buildRelyingParty();
-        rpTO = createOIDCRelyingParty(rpTO);
-
-        OIDCRelyingPartyTO found = (OIDCRelyingPartyTO) clientAppService.read(rpTO.getKey());
-        assertNotNull(found);
-        assertFalse(StringUtils.isBlank(found.getClientId()));
-        assertFalse(StringUtils.isBlank(found.getClientSecret()));
-        assertNotNull(found.getSubjectType());
-        assertFalse(found.getSupportedGrantTypes().isEmpty());
-        assertFalse(found.getSupportedResponseTypes().isEmpty());
-        assertNotNull(found.getAccessPolicy());
-        assertNotNull(found.getAuthPolicy());
-    }
-
-    @Test
-    public void update() {
-        OIDCRelyingPartyTO rpTO = buildRelyingParty();
-        rpTO = createOIDCRelyingParty(rpTO);
-
-        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
-        accessPolicyTO.setKey("NewAccessPolicyTest_" + getUUIDString());
-        accessPolicyTO.setDescription("New Access policy");
-        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
-        assertNotNull(accessPolicyTO);
-
-        rpTO.setClientId("newClientId");
-        rpTO.setAccessPolicy(accessPolicyTO.getKey());
-
-        clientAppService.update(rpTO);
-        OIDCRelyingPartyTO updated = (OIDCRelyingPartyTO) clientAppService.read(rpTO.getKey());
-
-        assertNotNull(updated);
-        assertEquals("newClientId", updated.getClientId());
-        assertNotNull(updated.getAccessPolicy());
-    }
-
-    @Test
-    public void delete() {
-        OIDCRelyingPartyTO rpTO = buildRelyingParty();
-        rpTO = createOIDCRelyingParty(rpTO);
-
-        clientAppService.delete(rpTO.getKey());
-
-        try {
-            clientAppService.read(rpTO.getKey());
-            fail("This should not happen");
-        } catch (SyncopeClientException e) {
-            assertNotNull(e);
-        }
-    }
-
-    private OIDCRelyingPartyTO buildRelyingParty() {
-        AuthPolicyTO authPolicyTO = new AuthPolicyTO();
-        authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
-        authPolicyTO.setDescription("Authentication Policy");
-        authPolicyTO = createPolicy(PolicyType.AUTHENTICATION, authPolicyTO);
-        assertNotNull(authPolicyTO);
-
-        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
-        accessPolicyTO.setKey("AccessPolicyTest_" + getUUIDString());
-        accessPolicyTO.setDescription("Access policy");
-        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
-        assertNotNull(accessPolicyTO);
-
-        OIDCRelyingPartyTO rpTO = new OIDCRelyingPartyTO();
-        rpTO.setName("ExampleRP_" + getUUIDString());
-        rpTO.setDescription("Example OIDC RP application");
-        rpTO.setClientId("clientId_" + getUUIDString());
-        rpTO.setClientSecret("secret");
-        rpTO.setSubjectType(OIDCSubjectType.PUBLIC);
-        rpTO.getSupportedGrantTypes().add("something");
-        rpTO.getSupportedResponseTypes().add("something");
-
-        rpTO.setAuthPolicy(authPolicyTO.getKey());
-        rpTO.setAccessPolicy(accessPolicyTO.getKey());
-
-        return rpTO;
-    }
-
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
index 29c85a6..687b520 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
@@ -252,7 +252,7 @@ public class PolicyITCase extends AbstractITCase {
     @Test
     public void getAuthPolicy() {
         AuthPolicyTO policyTO =
-                policyService.read(PolicyType.AUTHENTICATION, "659b9906-4b6e-4bc0-aca0-6809dff346d4");
+                policyService.read(PolicyType.AUTH, "659b9906-4b6e-4bc0-aca0-6809dff346d4");
 
         assertNotNull(policyTO);
         assertTrue(policyTO.getUsedByRealms().isEmpty());
@@ -286,7 +286,7 @@ public class PolicyITCase extends AbstractITCase {
         assertNotNull(pushPolicyTO);
         assertEquals("TestPushRule", pushPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name()));
 
-        AuthPolicyTO authPolicyTO = createPolicy(PolicyType.AUTHENTICATION,
+        AuthPolicyTO authPolicyTO = createPolicy(PolicyType.AUTH,
                 buildAuthPolicyTO());
         assertNotNull(authPolicyTO);
         assertEquals("Test Authentication policy", authPolicyTO.getDescription());
@@ -330,7 +330,7 @@ public class PolicyITCase extends AbstractITCase {
     public void updateAuthPolicy() {
         AuthPolicyTO newAuthPolicyTO = buildAuthPolicyTO();
         assertNotNull(newAuthPolicyTO);
-        newAuthPolicyTO = createPolicy(PolicyType.AUTHENTICATION, newAuthPolicyTO);
+        newAuthPolicyTO = createPolicy(PolicyType.AUTH, newAuthPolicyTO);
 
         ImplementationTO authPolicyImplementationTO = implementationService.read(
                 AMImplementationType.AUTH_POLICY_CONFIGURATIONS, "MyDefaultAuthPolicyConf");
@@ -344,8 +344,8 @@ public class PolicyITCase extends AbstractITCase {
         authPolicyImplementationTO.setBody(POJOHelper.serialize(authPolicyConf));
 
         // update new authentication policy
-        policyService.update(PolicyType.AUTHENTICATION, newAuthPolicyTO);
-        newAuthPolicyTO = policyService.read(PolicyType.AUTHENTICATION, newAuthPolicyTO.getKey());
+        policyService.update(PolicyType.AUTH, newAuthPolicyTO);
+        newAuthPolicyTO = policyService.read(PolicyType.AUTH, newAuthPolicyTO.getKey());
         assertNotNull(newAuthPolicyTO);
 
         authPolicyConf = POJOHelper.deserialize(authPolicyImplementationTO.getBody(),
@@ -442,13 +442,13 @@ public class PolicyITCase extends AbstractITCase {
 
         AuthPolicyTO authPolicy = buildAuthPolicyTO();
 
-        AuthPolicyTO authPolicyTO = createPolicy(PolicyType.AUTHENTICATION, authPolicy);
+        AuthPolicyTO authPolicyTO = createPolicy(PolicyType.AUTH, authPolicy);
         assertNotNull(authPolicyTO);
 
-        policyService.delete(PolicyType.AUTHENTICATION, authPolicyTO.getKey());
+        policyService.delete(PolicyType.AUTH, authPolicyTO.getKey());
 
         try {
-            policyService.read(PolicyType.AUTHENTICATION, authPolicyTO.getKey());
+            policyService.read(PolicyType.AUTH, authPolicyTO.getKey());
             fail("This should not happen");
         } catch (SyncopeClientException e) {
             assertNotNull(e);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
index a8cf053..04efaba 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
@@ -227,7 +227,7 @@ public class RealmITCase extends AbstractITCase {
         AuthPolicyTO policy = new AuthPolicyTO();
         policy.setDescription("Test Authentication policy");
         policy.setKey(rule.getKey());
-        policy = createPolicy(PolicyType.AUTHENTICATION, policy);
+        policy = createPolicy(PolicyType.AUTH, policy);
         assertNotNull(policy);
 
         // 2. create realm with policy assigned
@@ -252,7 +252,7 @@ public class RealmITCase extends AbstractITCase {
         assertEquals(policy.getKey(), actual.getAuthPolicy());
 
         // 3. remove policy
-        policyService.delete(PolicyType.AUTHENTICATION, policy.getKey());
+        policyService.delete(PolicyType.AUTH, policy.getKey());
 
         // 4. verify
         actual = getRealm(actual.getFullPath()).get();
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ServiceProviderITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ServiceProviderITCase.java
deleted file mode 100644
index 7e43f8a..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ServiceProviderITCase.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.fit.core;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.AccessPolicyTO;
-import org.apache.syncope.common.lib.to.AuthPolicyTO;
-import org.apache.syncope.common.lib.to.client.SAML2ServiceProviderTO;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.common.lib.types.SAML2ServiceProviderNameId;
-import org.apache.syncope.fit.AbstractITCase;
-import org.junit.jupiter.api.Test;
-
-public class SAML2ServiceProviderITCase extends AbstractITCase {
-
-    @Test
-    public void create() {
-        createSAML2SP(buildSAML2SP());
-    }
-
-    @Test
-    public void read() {
-        SAML2ServiceProviderTO samlSpTO = buildSAML2SP();
-        samlSpTO = createSAML2SP(samlSpTO);
-
-        SAML2ServiceProviderTO found = (SAML2ServiceProviderTO) clientAppService.read(samlSpTO.getKey());
-        assertNotNull(found);
-        assertFalse(StringUtils.isBlank(found.getEntityId()));
-        assertFalse(StringUtils.isBlank(found.getMetadataLocation()));
-        assertTrue(found.isEncryptAssertions());
-        assertTrue(found.isEncryptionOptional());
-        assertNotNull(found.getRequiredNameIdFormat());
-        assertNotNull(found.getAccessPolicy());
-        assertNotNull(found.getAuthPolicy());
-    }
-
-    @Test
-    public void update() {
-        SAML2ServiceProviderTO samlSpTO = buildSAML2SP();
-        samlSpTO = createSAML2SP(samlSpTO);
-
-        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
-        accessPolicyTO.setKey("NewAccessPolicyTest_" + getUUIDString());
-        accessPolicyTO.setDescription("New Access policy");
-        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
-        assertNotNull(accessPolicyTO);
-
-        samlSpTO.setEntityId("newEntityId");
-        samlSpTO.setAccessPolicy(accessPolicyTO.getKey());
-
-        clientAppService.update(samlSpTO);
-        SAML2ServiceProviderTO updated = (SAML2ServiceProviderTO) clientAppService.read(samlSpTO.getKey());
-
-        assertNotNull(updated);
-        assertEquals("newEntityId", updated.getEntityId());
-        assertNotNull(updated.getAccessPolicy());
-    }
-
-    @Test
-    public void delete() {
-        SAML2ServiceProviderTO samlSpTO = buildSAML2SP();
-        samlSpTO = createSAML2SP(samlSpTO);
-
-        clientAppService.delete(samlSpTO.getKey());
-
-        try {
-            clientAppService.read(samlSpTO.getKey());
-            fail("This should not happen");
-        } catch (SyncopeClientException e) {
-            assertNotNull(e);
-        }
-    }
-
-    private SAML2ServiceProviderTO buildSAML2SP() {
-        AuthPolicyTO authPolicyTO = new AuthPolicyTO();
-        authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
-        authPolicyTO.setDescription("Authentication Policy");
-        authPolicyTO = createPolicy(PolicyType.AUTHENTICATION, authPolicyTO);
-        assertNotNull(authPolicyTO);
-
-        AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
-        accessPolicyTO.setKey("AccessPolicyTest_" + getUUIDString());
-        accessPolicyTO.setDescription("Access policy");
-        accessPolicyTO = createPolicy(PolicyType.ACCESS, accessPolicyTO);
-        assertNotNull(accessPolicyTO);
-
-        SAML2ServiceProviderTO saml2spto = new SAML2ServiceProviderTO();
-        saml2spto.setName("ExampleSAML2SP_" + getUUIDString());
-        saml2spto.setDescription("Example SAML 2.0 service provider");
-        saml2spto.setEntityId("SAML2SPEntityId_" + getUUIDString());
-        saml2spto.setMetadataLocation("file:./test.xml");
-        saml2spto.setRequiredNameIdFormat(SAML2ServiceProviderNameId.EMAIL_ADDRESS);
-        saml2spto.setEncryptionOptional(true);
-        saml2spto.setEncryptAssertions(true);
-
-        saml2spto.setAuthPolicy(authPolicyTO.getKey());
-        saml2spto.setAccessPolicy(accessPolicyTO.getKey());
-
-        return saml2spto;
-    }
-
-}
diff --git a/fit/wa-reference/src/main/resources/wa.properties b/fit/wa-reference/src/main/resources/wa.properties
index 77b3b51..add54f2 100644
--- a/fit/wa-reference/src/main/resources/wa.properties
+++ b/fit/wa-reference/src/main/resources/wa.properties
@@ -31,3 +31,11 @@ cas.authn.oidc.jwksFile=file:${conf.directory}/oidc.keystore
 
 cas.authn.samlIdp.entityId=https://syncope.apache.org/saml
 cas.authn.samlIdp.metadata.location=file:${conf.directory}
+
+# Disable access to the login endpoint
+# if no target application is specified.
+cas.sso.allow-missing-service-parameter=true
+
+# Disable the acceptable usage policy
+# by default for now.
+cas.acceptableUsagePolicy.enabled=false
diff --git a/pom.xml b/pom.xml
index 41670f2..8515449 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1979,6 +1979,12 @@ under the License.
         </exclusions>
       </dependency>
 
+      <dependency>
+        <groupId>org.bouncycastle</groupId>
+        <artifactId>bcpkix-jdk15on</artifactId>
+        <version>1.64</version>
+      </dependency>
+
       <!-- TEST -->
       <dependency>
         <groupId>org.apache.curator</groupId>
@@ -1999,11 +2005,6 @@ under the License.
         <scope>test</scope>
       </dependency>
       <dependency>
-        <groupId>org.bouncycastle</groupId>
-        <artifactId>bcpkix-jdk15on</artifactId>
-        <version>1.64</version>
-      </dependency>
-      <dependency>
         <groupId>org.eclipse.jetty</groupId>
         <artifactId>jetty-client</artifactId>
         <version>9.4.20.v20190813</version>
diff --git a/wa/bootstrap/pom.xml b/wa/bootstrap/pom.xml
index ecf7f01..fd9e147 100644
--- a/wa/bootstrap/pom.xml
+++ b/wa/bootstrap/pom.xml
@@ -43,18 +43,18 @@ under the License.
       <artifactId>syncope-client-am-lib</artifactId>
       <version>${project.version}</version>
     </dependency>
+
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
       <version>${project.version}</version>
-      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope.ext.self-keymaster</groupId>
       <artifactId>syncope-ext-self-keymaster-client</artifactId>
       <version>${project.version}</version>
-      <scope>compile</scope>
     </dependency>
+
     <dependency>
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-core-configuration-api</artifactId>
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java
index 294347e..7f56761 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *    http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -40,8 +40,12 @@ public class WARestClient {
 
     private SyncopeClient client;
 
-    public WARestClient(final ServiceOps serviceOps, final String anonymousUser,
-                        final String anonymousKey, final boolean useGZIPCompression) {
+    public WARestClient(
+            final ServiceOps serviceOps,
+            final String anonymousUser,
+            final String anonymousKey,
+            final boolean useGZIPCompression) {
+
         this.serviceOps = serviceOps;
         this.anonymousUser = anonymousUser;
         this.anonymousKey = anonymousKey;
@@ -53,9 +57,9 @@ public class WARestClient {
             if (client == null) {
                 try {
                     client = new SyncopeClientFactoryBean().
-                        setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
-                        setUseCompression(useGZIPCompression).
-                        create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey));
+                            setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
+                            setUseCompression(useGZIPCompression).
+                            create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey));
                 } catch (Exception e) {
                     LOG.error("Could not init SyncopeClient", e);
                 }
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java
index 2249220..fb7732b 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.wa.bootstrap;
 
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.keymaster.client.self.SelfKeymasterClientContext;
 import org.apache.syncope.common.keymaster.client.zookeper.ZookeeperKeymasterClientContext;
@@ -31,17 +33,14 @@ import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 import org.springframework.core.env.MapPropertySource;
 
-import java.util.HashMap;
-import java.util.Map;
-
 @Configuration(proxyBeanMethods = false)
-@ImportAutoConfiguration(classes = {ZookeeperKeymasterClientContext.class, SelfKeymasterClientContext.class})
+@ImportAutoConfiguration(classes = { ZookeeperKeymasterClientContext.class, SelfKeymasterClientContext.class })
 @PropertySource("classpath:wa.properties")
 @PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
 public class RestfulCloudConfigBootstrapConfiguration {
+
     private static final Logger LOG = LoggerFactory.getLogger(RestfulCloudConfigBootstrapConfiguration.class);
 
     @Value("${anonymousUser}")
@@ -62,16 +61,14 @@ public class RestfulCloudConfigBootstrapConfiguration {
     @Autowired
     @Bean
     public PropertySourceLocator configPropertySourceLocator(final WARestClient waRestClient) {
-        return new PropertySourceLocator() {
-            @Override
-            public org.springframework.core.env.PropertySource<?> locate(final Environment environment) {
-                try {
-                    LOG.info("Bootstrapping WA configuration");
-                    Map<String, Object> payload = new HashMap<>();
-                    return new MapPropertySource(getClass().getName(), payload);
-                } catch (Exception e) {
-                    throw new IllegalArgumentException("Unable to fetch settings", e);
-                }
+        return environment -> {
+            try {
+                LOG.info("Bootstrapping WA configuration");
+
+                Map<String, Object> payload = new HashMap<>();
+                return new MapPropertySource(getClass().getName(), payload);
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Unable to fetch settings", e);
             }
         };
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
index 82ae936..07661e0 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
@@ -18,22 +18,20 @@
  */
 package org.apache.syncope.wa.starter;
 
-import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
-import org.apereo.cas.services.ServiceRegistryListener;
-
+import java.util.Collection;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
-import org.apache.syncope.wa.starter.rest.SyncopeServiceRegistry;
 import org.apache.syncope.wa.WARestClient;
+import org.apache.syncope.wa.starter.rest.SyncopeServiceRegistry;
+import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
+import org.apereo.cas.services.ServiceRegistryListener;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
-import java.util.Collection;
-
 @Configuration
 public class SyncopeWAConfiguration {
 
@@ -47,8 +45,8 @@ public class SyncopeWAConfiguration {
     @Autowired
     @Bean
     public ServiceRegistryExecutionPlanConfigurer syncopeServiceRegistryConfigurer(final WARestClient restClient) {
-        SyncopeServiceRegistry registry = new SyncopeServiceRegistry(restClient,
-            applicationContext, serviceRegistryListeners);
+        SyncopeServiceRegistry registry =
+                new SyncopeServiceRegistry(restClient, applicationContext, serviceRegistryListeners);
         return plan -> plan.registerServiceRegistry(registry);
     }
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
index 500ab4d..64773eb 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *    http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,29 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.syncope.wa.starter.rest;
 
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.wa.WARestClient;
 import org.apereo.cas.services.AbstractServiceRegistry;
 import org.apereo.cas.services.RegisteredService;
 import org.apereo.cas.services.ServiceRegistryListener;
-
-import org.apache.syncope.wa.WARestClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ConfigurableApplicationContext;
 
-import java.util.Collection;
-import java.util.Collections;
-
 public class SyncopeServiceRegistry extends AbstractServiceRegistry {
+
     private static final Logger LOG = LoggerFactory.getLogger(SyncopeServiceRegistry.class);
 
     private final WARestClient restClient;
 
     public SyncopeServiceRegistry(final WARestClient restClient,
-                                  final ConfigurableApplicationContext applicationContext,
-                                  final Collection<ServiceRegistryListener> serviceRegistryListeners) {
+            final ConfigurableApplicationContext applicationContext,
+            final Collection<ServiceRegistryListener> serviceRegistryListeners) {
         super(applicationContext, serviceRegistryListeners);
         this.restClient = restClient;
     }
@@ -56,7 +54,7 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
     @Override
     public Collection<RegisteredService> load() {
         LOG.info("Loading application definitions");
-        return Collections.emptyList();
+        return List.of();
     }
 
     @Override
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index 43cef41..add54f2 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -39,5 +39,3 @@ cas.sso.allow-missing-service-parameter=true
 # Disable the acceptable usage policy
 # by default for now.
 cas.acceptableUsagePolicy.enabled=false
-
-