You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by sk...@apache.org on 2020/04/03 16:13:52 UTC

[syncope] branch SYNCOPE-163-1 updated: [SYNCOPE-163-1] Added AuthModule REST service

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

skylark17 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 39eb21e  [SYNCOPE-163-1] Added AuthModule REST service
39eb21e is described below

commit 39eb21e8a74641473202a6549805bf8ded1cfc8c
Author: skylark17 <sk...@apache.org>
AuthorDate: Fri Apr 3 16:19:44 2020 +0200

    [SYNCOPE-163-1] Added AuthModule REST service
---
 .../common/lib/auth/AbstractAuthModuleConf.java    |  34 +-
 .../syncope/common/lib/auth/AuthModuleConf.java    |  18 +-
 .../common/lib/auth/JDBCAuthModuleConf.java        |  99 +++++
 .../common/lib/auth/LDAPAuthModuleConf.java        |  17 +-
 .../common/lib/auth/OIDCAuthModuleConf.java        |   2 +-
 .../common/lib/auth/SAML2IdPAuthModuleConf.java    |   4 +-
 .../common/lib/auth/StaticAuthModuleConf.java      |   8 +-
 ...hModuleConf.java => SyncopeAuthModuleConf.java} |  47 ++-
 .../apache/syncope/common/lib/to/AuthModuleTO.java | 132 +++++++
 .../syncope/common/lib/to/client/ClientAppTO.java  |   3 +-
 .../syncope/common/lib/types/AMEntitlement.java    |  20 +-
 .../common/lib/types/AMImplementationType.java     |   4 -
 .../common/lib/types/AuthModuleConfPropSchema.java | 157 --------
 .../common/lib/types/AuthModuleConfProperty.java   | 100 -----
 ...lientAppService.java => AuthModuleService.java} |  68 ++--
 .../common/rest/api/service/ClientAppService.java  |   8 +-
 .../apache/syncope/core/logic/AuthModuleLogic.java | 131 +++++++
 .../init/ClassPathScanImplementationLookup.java    |  14 -
 .../rest/cxf/service/AuthModuleServiceImpl.java    |  65 ++++
 .../persistence/api/dao/auth/AuthModuleDAO.java    |   3 -
 .../persistence/api/entity/auth/AuthModule.java    |  22 +-
 .../auth/{AuthModule.java => AuthModuleItem.java}  |  13 +-
 .../src/test/resources/domains/MasterContent.xml   |  23 +-
 .../core/persistence/jpa/dao/JPAPolicyDAO.java     |  24 +-
 .../persistence/jpa/dao/auth/JPAAuthModuleDAO.java |  14 +-
 .../persistence/jpa/entity/JPAEntityFactory.java   |   6 +-
 .../persistence/jpa/entity/auth/JPAAuthModule.java |  77 ++--
 .../{JPAAuthModule.java => JPAAuthModuleItem.java} |  64 ++--
 .../jpa/entity/policy/JPAAccessPolicy.java         |   2 +-
 .../jpa/entity/policy/JPAAttrReleasePolicy.java    |   2 +-
 .../jpa/entity/policy/JPAAuthPolicy.java           |   2 +-
 .../core/persistence/jpa/inner/AuthModuleTest.java | 331 +++++++++++++----
 .../core/persistence/jpa/outer/PolicyTest.java     |   7 +-
 .../src/test/resources/domains/MasterContent.xml   |  27 +-
 .../api/data/AuthModuleDataBinder.java}            |  27 +-
 .../java/data/AuthModuleDataBinderImpl.java        |  79 ++++
 .../java/data/ConnInstanceDataBinderImpl.java      |   4 -
 .../java/data/ImplementationDataBinderImpl.java    |  14 -
 .../org/apache/syncope/fit/AbstractITCase.java     |  17 +
 .../apache/syncope/fit/core/AuthModuleITCase.java  | 406 +++++++++++++++++++++
 .../wa/starter/rest/SyncopeServiceRegistry.java    |   1 +
 41 files changed, 1446 insertions(+), 650 deletions(-)

diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractAuthModuleConf.java
index edec434..a4399d6 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractAuthModuleConf.java
@@ -18,29 +18,20 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.io.Serializable;
 import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
-import java.io.Serializable;
-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.ItemTO;
 
 @XmlType
-@XmlSeeAlso({ JaasAuthModuleConf.class, StaticAuthModuleConf.class, LDAPAuthModuleConf.class, OIDCAuthModuleConf.class,
-    GoogleMfaAuthModuleConf.class, SAML2IdPAuthModuleConf.class })
+@XmlSeeAlso({ JaasAuthModuleConf.class, StaticAuthModuleConf.class, LDAPAuthModuleConf.class,
+    OIDCAuthModuleConf.class, GoogleMfaAuthModuleConf.class, SAML2IdPAuthModuleConf.class, U2FAuthModuleConf.class,
+    JDBCAuthModuleConf.class, SyncopeAuthModuleConf.class, RadiusAuthModuleConf.class })
 public abstract class AbstractAuthModuleConf implements Serializable, AuthModuleConf {
 
     private static final long serialVersionUID = 4153200197344709778L;
 
     private String name;
 
-    private int order;
-
-    private List<ItemTO> profileItems = new ArrayList<>();
-
     public AbstractAuthModuleConf() {
         setName(getClass().getName());
     }
@@ -54,21 +45,4 @@ public abstract class AbstractAuthModuleConf implements Serializable, AuthModule
         this.name = name;
     }
 
-    @Override
-    public int getOrder() {
-        return order;
-    }
-
-    public void setOrder(final int order) {
-        this.order = order;
-    }
-
-    @XmlElementWrapper(name = "profileItems")
-    @XmlElement(name = "profileItem")
-    @JsonProperty("profileItems")
-    @Override
-    public List<ItemTO> getProfileItems() {
-        return profileItems;
-    }
-
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
index 8f8fa4f..28c1357 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
@@ -20,9 +20,9 @@ package org.apache.syncope.common.lib.auth;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import java.io.Serializable;
-import java.util.List;
-import org.apache.syncope.common.lib.to.ItemTO;
+import javax.xml.bind.annotation.XmlTransient;
 
+@XmlTransient
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface AuthModuleConf extends Serializable {
 
@@ -32,18 +32,4 @@ public interface AuthModuleConf extends Serializable {
      * @return name of this authentication module instance
      */
     String getName();
-
-    /**
-     * Execution order of this authentication module in the policy chain.
-     *
-     * @return numeric order
-     */
-    int getOrder();
-
-    /**
-     * Specify the mapping items for the attributes fetched from the source.
-     *
-     * @return list of mapping items
-     */
-    List<ItemTO> getProfileItems();
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java
new file mode 100644
index 0000000..0141078
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java
@@ -0,0 +1,99 @@
+/*
+ * 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.common.lib.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import javax.xml.bind.annotation.XmlType;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "jdbcAuthModuleConf")
+@XmlType
+public class JDBCAuthModuleConf extends AbstractAuthModuleConf {
+
+    private static final long serialVersionUID = 8383233437907219385L;
+
+    /**
+     * SQL query to execute. Example: {@code SELECT * FROM table WHERE name=?}.
+     */
+    private String sql;
+
+    /**
+     * Password field/column name to retrieve.
+     */
+    private String fieldPassword;
+
+    /**
+     * Boolean field that should indicate whether the account is expired.
+     */
+    private String fieldExpired;
+
+    /**
+     * Boolean field that should indicate whether the account is disabled.
+     */
+    private String fieldDisabled;
+
+    /**
+     * List of column names to fetch as user attributes.
+     */
+    private final List<String> principalAttributeList = new ArrayList<>();
+
+    public String getSql() {
+        return sql;
+    }
+
+    public void setSql(final String sql) {
+        this.sql = sql;
+    }
+
+    public String getFieldPassword() {
+        return fieldPassword;
+    }
+
+    public void setFieldPassword(final String fieldPassword) {
+        this.fieldPassword = fieldPassword;
+    }
+
+    public String getFieldExpired() {
+        return fieldExpired;
+    }
+
+    public void setFieldExpired(final String fieldExpired) {
+        this.fieldExpired = fieldExpired;
+    }
+
+    public String getFieldDisabled() {
+        return fieldDisabled;
+    }
+
+    public void setFieldDisabled(final String fieldDisabled) {
+        this.fieldDisabled = fieldDisabled;
+    }
+
+    @XmlElementWrapper(name = "principalAttributeList")
+    @XmlElement(name = "principalAttributeList")
+    @JsonProperty("principalAttributeList")
+    public List<String> getPrincipalAttributeList() {
+        return principalAttributeList;
+    }
+
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
index b344c6e..afd7f90 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
@@ -75,14 +75,6 @@ public class LDAPAuthModuleConf extends AbstractAuthModuleConf {
         this.searchFilter = searchFilter;
     }
 
-    public String getBaseDn() {
-        return baseDn;
-    }
-
-    public void setBaseDn(final String baseDn) {
-        this.baseDn = baseDn;
-    }
-
     public String getUserIdAttribute() {
         return userIdAttribute;
     }
@@ -122,4 +114,13 @@ public class LDAPAuthModuleConf extends AbstractAuthModuleConf {
     public void setBindCredential(final String bindCredential) {
         this.bindCredential = bindCredential;
     }
+
+    public String getBaseDn() {
+        return baseDn;
+    }
+
+    public void setBaseDn(final String baseDn) {
+        this.baseDn = baseDn;
+    }
+
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
index c56e160..d59b34d 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
@@ -19,12 +19,12 @@
 package org.apache.syncope.common.lib.auth;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 import java.util.HashMap;
 import java.util.Map;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
index e3169fd..03b6aed 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
@@ -19,12 +19,12 @@
 package org.apache.syncope.common.lib.auth;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
 import java.util.ArrayList;
 import java.util.List;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
 
 @XmlRootElement(name = "saml2IdPAuthModuleConf")
 @XmlType
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
index 4d2112f..60d2751 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
@@ -19,12 +19,12 @@
 package org.apache.syncope.common.lib.auth;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 import java.util.HashMap;
 import java.util.Map;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
 
@@ -37,12 +37,6 @@ public class StaticAuthModuleConf extends AbstractAuthModuleConf {
     @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
     private final Map<String, String> users = new HashMap<>();
 
-    public StaticAuthModuleConf(final Map<String, String> users) {
-        setName(getClass().getSimpleName());
-        getUsers().clear();
-        getUsers().putAll(users);
-    }
-
     @XmlElementWrapper(name = "users")
     @XmlElement(name = "user")
     @JsonProperty("users")
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
similarity index 53%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
index 8f8fa4f..2ac2192 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
@@ -18,32 +18,39 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import java.io.Serializable;
-import java.util.List;
-import org.apache.syncope.common.lib.to.ItemTO;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
 
-@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
-public interface AuthModuleConf extends Serializable {
+@XmlRootElement(name = "syncopeAuthModuleConf")
+@XmlType
+public class SyncopeAuthModuleConf extends AbstractAuthModuleConf {
 
-    /**
-     * Given name of related authentication module instance.
-     *
-     * @return name of this authentication module instance
-     */
-    String getName();
+    private static final long serialVersionUID = -3334329948161152222L;
 
     /**
-     * Execution order of this authentication module in the policy chain.
-     *
-     * @return numeric order
+     * Syncope domain used for authentication, etc.
      */
-    int getOrder();
+    private String domain = "Master";
 
     /**
-     * Specify the mapping items for the attributes fetched from the source.
-     *
-     * @return list of mapping items
+     * Syncope instance URL primary used for REST.
      */
-    List<ItemTO> getProfileItems();
+    private String url;
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(final String domain) {
+        this.domain = domain;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(final String url) {
+        this.url = url;
+    }
+
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
new file mode 100644
index 0000000..492227f
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
@@ -0,0 +1,132 @@
+/*
+ * 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.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.auth.AuthModuleConf;
+
+@XmlRootElement(name = "authModule")
+@XmlType
+public class AuthModuleTO extends BaseBean implements EntityTO {
+
+    private static final long serialVersionUID = -7490425997956703057L;
+
+    private String key;
+
+    private String name;
+
+    private String description;
+
+    private final List<ItemTO> profileItems = new ArrayList<>();
+
+    private AuthModuleConf conf;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+
+    public AuthModuleConf getConf() {
+        return conf;
+    }
+
+    public void setConf(final AuthModuleConf conf) {
+        this.conf = conf;
+    }
+
+    @XmlElementWrapper(name = "profileItems")
+    @XmlElement(name = "profileItem")
+    @JsonProperty("profileItems")
+    public List<ItemTO> getProfileItems() {
+        return profileItems;
+    }
+
+    public boolean add(final ItemTO item) {
+        return Optional.ofNullable(item)
+                .filter(itemTO -> this.profileItems.contains(itemTO) || this.profileItems.add(itemTO)).isPresent();
+    }
+
+    public boolean remove(final ItemTO item) {
+        return this.profileItems.remove(item);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AuthModuleTO other = (AuthModuleTO) obj;
+        return new EqualsBuilder().
+                append(key, other.key).
+                append(name, other.name).
+                append(description, other.description).
+                append(profileItems, other.profileItems).
+                append(conf, other.conf).
+                build();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder().
+                append(key).
+                append(name).
+                append(description).
+                append(profileItems).
+                append(conf).
+                build();
+    }
+
+}
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 5a7b234..b1b4730 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
@@ -33,8 +33,7 @@ import javax.xml.bind.annotation.XmlType;
 @XmlType
 @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" })
+@JsonPropertyOrder(value = { "@class", "key", "name", "description", "authPolicy", "accessPolicy", "attReleasePolicy" })
 @Schema(subTypes = { OIDCRPTO.class, SAML2SPTO.class }, discriminatorProperty = "@class")
 public abstract class ClientAppTO extends BaseBean implements EntityTO {
 
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 16939e6..d20258b 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,16 +34,6 @@ public final class AMEntitlement {
 
     public static final String GATEWAY_ROUTE_PUSH = "GATEWAY_ROUTE_PUSH";
 
-    public static final String AUTHMODULE_READ = "AUTHMODULE_READ";
-
-    public static final String AUTHMODULE_LIST = "AUTHMODULE_LIST";
-
-    public static final String AUTHMODULE_CREATE = "AUTHMODULE_CREATE";
-
-    public static final String AUTHMODULE_UPDATE = "AUTHMODULE_CREATE";
-
-    public static final String AUTHMODULE_DELETE = "AUTHMODULE_DELETE";
-
     public static final String CLIENTAPP_READ = "CLIENTAPP_READ";
 
     public static final String CLIENTAPP_LIST = "CLIENTAPP_LIST";
@@ -54,6 +44,16 @@ public final class AMEntitlement {
 
     public static final String CLIENTAPP_DELETE = "CLIENTAPP_DELETE";
 
+    public static final String AUTH_MODULE_LIST = "AUTH_MODULE_LIST";
+
+    public static final String AUTH_MODULE_CREATE = "AUTH_MODULE_CREATE";
+
+    public static final String AUTH_MODULE_READ = "AUTH_MODULE_READ";
+
+    public static final String AUTH_MODULE_UPDATE = "AUTH_MODULE_UPDATE";
+
+    public static final String AUTH_MODULE_DELETE = "AUTH_MODULE_DELETE";
+
     private static final Set<String> VALUES;
 
     public static final String REGISTERED_CLIENT_APP_READ = "REGISTERED_CLIENT_APP_READ";
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMImplementationType.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMImplementationType.java
index 6c6be03..1c05727 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMImplementationType.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMImplementationType.java
@@ -23,8 +23,6 @@ import java.util.Map;
 
 public final class AMImplementationType {
 
-    public static final String AUTH_MODULE_CONFIGURATIONS = "AUTH_MODULE_CONFIGURATIONS";
-
     public static final String AUTH_POLICY_CONFIGURATIONS = "AUTH_POLICY_CONFIGURATIONS";
 
     public static final String ACCESS_POLICY_CONFIGURATIONS = "ACCESS_POLICY_CONFIGURATIONS";
@@ -36,8 +34,6 @@ public final class AMImplementationType {
     }
 
     private static final Map<String, String> VALUES = Map.ofEntries(
-            Pair.of(AUTH_MODULE_CONFIGURATIONS,
-                    "org.apache.syncope.common.lib.auth.AuthModuleConf"),
             Pair.of(AUTH_POLICY_CONFIGURATIONS,
                     "org.apache.syncope.common.lib.policy.AuthPolicyConf"),
             Pair.of(ATTR_RELEASE_POLICY_CONFIGURATIONS,
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfPropSchema.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfPropSchema.java
deleted file mode 100644
index 6ff8800..0000000
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfPropSchema.java
+++ /dev/null
@@ -1,157 +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.common.lib.types;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-
-@XmlRootElement
-@XmlType
-public class AuthModuleConfPropSchema implements Serializable, Comparable<AuthModuleConfPropSchema> {
-
-    private static final long serialVersionUID = -1615574676038299363L;
-
-    private String name;
-
-    private String displayName;
-
-    private String helpMessage;
-
-    private String type = String.class.getName();
-
-    private boolean required;
-
-    private int order;
-
-    private boolean confidential;
-
-    private final List<Object> defaultValues = new ArrayList<>();
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(final String name) {
-        this.name = name;
-    }
-
-    public boolean isRequired() {
-        return required;
-    }
-
-    public void setRequired(final boolean required) {
-        this.required = required;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(final String type) {
-        this.type = type;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public void setDisplayName(final String displayName) {
-        this.displayName = displayName;
-    }
-
-    public String getHelpMessage() {
-        return helpMessage;
-    }
-
-    public void setHelpMessage(final String helpMessage) {
-        this.helpMessage = helpMessage;
-    }
-
-    public int getOrder() {
-        return order;
-    }
-
-    public void setOrder(final int order) {
-        this.order = order;
-    }
-
-    public boolean isConfidential() {
-        return confidential;
-    }
-
-    public void setConfidential(final boolean confidential) {
-        this.confidential = confidential;
-    }
-
-    @XmlElementWrapper(name = "defaultValues")
-    @XmlElement(name = "defaultValue")
-    @JsonProperty("defaultValues")
-    public List<Object> getDefaultValues() {
-        return defaultValues;
-    }
-
-    @Override
-    public int compareTo(final AuthModuleConfPropSchema other) {
-        return this.getOrder() > other.getOrder()
-                ? 1
-                : this.getOrder() < other.getOrder()
-                ? -1
-                : 0;
-    }
-
-    @Override
-    public int hashCode() {
-        return new HashCodeBuilder().
-                append(name).
-                append(type).
-                append(required).
-                append(order).
-                append(confidential).
-                build();
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final AuthModuleConfPropSchema other = (AuthModuleConfPropSchema) obj;
-        return new EqualsBuilder().
-                append(name, other.name).
-                append(type, other.type).
-                append(required, other.required).
-                append(order, other.order).
-                append(confidential, other.confidential).
-                build();
-    }
-}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfProperty.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfProperty.java
deleted file mode 100644
index d3a125f..0000000
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleConfProperty.java
+++ /dev/null
@@ -1,100 +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.common.lib.types;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-
-@XmlRootElement
-@XmlType
-public class AuthModuleConfProperty implements Serializable, Comparable<AuthModuleConfProperty> {
-
-    private static final long serialVersionUID = 7291446596155549328L;
-
-    private AuthModuleConfPropSchema schema;
-
-    private final List<Object> values = new ArrayList<>();
-
-    private boolean overridable;
-
-    public AuthModuleConfPropSchema getSchema() {
-        return schema;
-    }
-
-    public void setSchema(final AuthModuleConfPropSchema schema) {
-        this.schema = schema;
-    }
-
-    @XmlElementWrapper(name = "values")
-    @XmlElement(name = "value")
-    @JsonProperty("values")
-    public List<Object> getValues() {
-        return values;
-    }
-
-    public boolean isOverridable() {
-        return overridable;
-    }
-
-    public void setOverridable(final boolean overridable) {
-        this.overridable = overridable;
-    }
-
-    @Override
-    public int compareTo(final AuthModuleConfProperty connConfProperty) {
-        return ObjectUtils.compare(this.getSchema(), connConfProperty.getSchema());
-    }
-
-    @Override
-    public int hashCode() {
-        return new HashCodeBuilder().
-                append(schema).
-                append(values).
-                append(overridable).
-                build();
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final AuthModuleConfProperty other = (AuthModuleConfProperty) obj;
-        return new EqualsBuilder().
-                append(schema, other.schema).
-                append(values, other.values).
-                append(overridable, other.overridable).
-                build();
-    }
-}
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/AuthModuleService.java
similarity index 65%
copy from common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ClientAppService.java
copy to common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthModuleService.java
index 002c5b5..83e6015 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/AuthModuleService.java
@@ -40,57 +40,49 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.to.client.ClientAppTO;
-import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
- * REST operations for applications.
+ * REST operations for authentication modules.
  */
-@Tag(name = "ClientApps")
+@Tag(name = "AuthModules")
 @SecurityRequirements({
     @SecurityRequirement(name = "BasicAuthentication"),
     @SecurityRequirement(name = "Bearer") })
-@Path("clientApps")
-public interface ClientAppService extends JAXRSService {
+@Path("authModules")
+public interface AuthModuleService extends JAXRSService {
 
     /**
-     * Returns the client app matching the given key.
+     * Returns the authentication module matching the given key.
      *
-     * @param type client app type
-     * @param key key of requested client app
-     * @param <T> response type (extending ClientAppTO)
-     * @return client app with matching id
+     * @param key key of requested authentication module
+     * @return authentication module with matching id
      */
     @GET
-    @Path("{type}/{key}")
+    @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    <T extends ClientAppTO> T read(
-            @NotNull @PathParam("type") ClientAppType type,
+    AuthModuleTO read(
             @NotNull @PathParam("key") String key);
 
     /**
-     * Returns a list of policies of the matching type.
+     * Returns a list of authentication modules of the matching type.
      *
-     * @param type Type selector for requested policies
-     * @param <T> response type (extending ClientAppTO)
-     * @return list of policies with matching type
+     * @return list of authentication modules with matching type
      */
     @GET
-    @Path("{type}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    <T extends ClientAppTO> List<T> list(@NotNull @PathParam("type") ClientAppType type);
+    List<AuthModuleTO> list();
 
     /**
-     * Create a new client app.
+     * Create a new authentication module.
      *
-     * @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
+     * @param authModuleTO AuthModule to be created (needs to match type)
+     * @return Response object featuring Location header of created authentication module
      */
     @ApiResponses(
             @ApiResponse(responseCode = "201",
-                    description = "ClientApp successfully created", headers = {
+                    description = "AuthModule successfully created", headers = {
                 @Header(name = RESTHeaders.RESOURCE_KEY, schema =
                         @Schema(type = "string"),
                         description = "UUID generated for the entity created"),
@@ -98,37 +90,37 @@ public interface ClientAppService extends JAXRSService {
                         @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 @PathParam("type") ClientAppType type, @NotNull ClientAppTO clientAppTO);
+    Response create(
+            @NotNull AuthModuleTO authModuleTO);
 
     /**
-     * Updates client app matching the given key.
+     * Updates authentication module matching the given key.
      *
-     * @param type client app type
-     * @param clientAppTO ClientApp to replace existing client app
+     * @param authModuleTO AuthModule to replace existing authentication module
      */
-    @Parameter(name = "key", description = "ClientApp's key", in = ParameterIn.PATH, schema =
+    @Parameter(name = "key", description = "AuthModule's key", in = ParameterIn.PATH, schema =
             @Schema(type = "string"))
     @ApiResponses(
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
-    @Path("{type}/{key}")
+    @Path("{key}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void update(@NotNull @PathParam("type") ClientAppType type, @NotNull ClientAppTO clientAppTO);
+    void update(
+            @NotNull AuthModuleTO authModuleTO);
 
     /**
-     * Delete client app matching the given key.
+     * Delete authentication module matching the given key.
      *
-     * @param type client app type
-     * @param key key of client app to be deleted
+     * @param key key of authentication module to be deleted
      */
     @ApiResponses(
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
-    @Path("{type}/{key}")
+    @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void delete(@NotNull @PathParam("type") ClientAppType type, @NotNull @PathParam("key") String key);
+    void delete(
+            @NotNull @PathParam("key") String key);
 }
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 002c5b5..c2e2398 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
@@ -45,7 +45,7 @@ import org.apache.syncope.common.lib.types.ClientAppType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
- * REST operations for applications.
+ * REST operations for client applications.
  */
 @Tag(name = "ClientApps")
 @SecurityRequirements({
@@ -70,11 +70,11 @@ public interface ClientAppService extends JAXRSService {
             @NotNull @PathParam("key") String key);
 
     /**
-     * Returns a list of policies of the matching type.
+     * Returns a list of client apps of the matching type.
      *
-     * @param type Type selector for requested policies
+     * @param type Type selector for requested client apps
      * @param <T> response type (extending ClientAppTO)
-     * @return list of policies with matching type
+     * @return list of client apps with matching type
      */
     @GET
     @Path("{type}")
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java
new file mode 100644
index 0000000..538dc82
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java
@@ -0,0 +1,131 @@
+/*
+ * 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 static org.apache.syncope.core.logic.AbstractLogic.LOG;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
+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 AuthModuleLogic extends AbstractTransactionalLogic<AuthModuleTO> {
+
+    @Autowired
+    private AuthModuleDataBinder binder;
+
+    @Autowired
+    private AuthModuleDAO authModuleDAO;
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_MODULE_CREATE + "')")
+    public AuthModuleTO create(final AuthModuleTO authModuleTO) {
+        return binder.getAuthModuleTO(authModuleDAO.save(binder.create(authModuleTO)));
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_MODULE_UPDATE + "')")
+    public AuthModuleTO update(final AuthModuleTO authModuleTO) {
+        AuthModule authModule = authModuleDAO.find(authModuleTO.getKey());
+        if (authModule == null) {
+            throw new NotFoundException("AuthModule " + authModuleTO.getKey() + " not found");
+        }
+
+        return binder.getAuthModuleTO(authModuleDAO.save(binder.update(authModule, authModuleTO)));
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_MODULE_LIST + "')")
+    @Transactional(readOnly = true)
+    public List<AuthModuleTO> list() {
+        return authModuleDAO.findAll().stream().
+                filter(Objects::nonNull).
+                map(authModule -> {
+                    AuthModuleTO result = null;
+                    try {
+                        result = binder.getAuthModuleTO(authModule);
+                    } catch (NotFoundException e) {
+                        LOG.error("Authentication module '{}' not found", authModule.getName());
+                    }
+
+                    return result;
+                }).collect(Collectors.toList());
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_MODULE_READ + "')")
+    @Transactional(readOnly = true)
+    public AuthModuleTO read(final String key) {
+        AuthModule authModule = authModuleDAO.find(key);
+        if (authModule == null) {
+            throw new NotFoundException("AuthModule " + key + " not found");
+        }
+
+        return binder.getAuthModuleTO(authModule);
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_MODULE_DELETE + "')")
+    public AuthModuleTO delete(final String key) {
+        AuthModule authModule = authModuleDAO.find(key);
+        if (authModule == null) {
+            throw new NotFoundException("AuthModule " + key + " not found");
+        }
+
+        AuthModuleTO deleted = binder.getAuthModuleTO(authModule);
+        authModuleDAO.delete(authModule);
+
+        return deleted;
+    }
+
+    @Override
+    protected AuthModuleTO 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 AuthModuleTO) {
+                    key = ((AuthModuleTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getAuthModuleTO(authModuleDAO.find(key));
+            } 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/init/ClassPathScanImplementationLookup.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index 07d2efd..e887aef 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -36,7 +36,6 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
 import org.springframework.core.Ordered;
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.util.ClassUtils;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
 import org.apache.syncope.common.lib.policy.AuthPolicyConf;
 
 /**
@@ -52,8 +51,6 @@ public class ClassPathScanImplementationLookup implements SyncopeCoreLoader {
 
     private Map<Class<? extends AuthPolicyConf>, Class<? extends AuthPolicyConf>> authPolicyClasses;
 
-    private Map<Class<? extends AuthModuleConf>, Class<? extends AuthModuleConf>> authModuleClasses;
-
     private Map<Class<? extends AccessPolicyConf>, Class<? extends AccessPolicyConf>> accessPolicyClasses;
 
     @Override
@@ -86,7 +83,6 @@ public class ClassPathScanImplementationLookup implements SyncopeCoreLoader {
         });
 
         authPolicyClasses = new HashMap<>();
-        authModuleClasses = new HashMap<>();
         accessPolicyClasses = new HashMap<>();
 
         scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
@@ -98,9 +94,6 @@ public class ClassPathScanImplementationLookup implements SyncopeCoreLoader {
                 if (AuthPolicyConf.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(AMImplementationType.AUTH_POLICY_CONFIGURATIONS).add(bd.getBeanClassName());
                 }
-                if (AuthModuleConf.class.isAssignableFrom(clazz) && !isAbstractClazz) {
-                    classNames.get(AMImplementationType.AUTH_MODULE_CONFIGURATIONS).add(bd.getBeanClassName());
-                }
                 if (AccessPolicyConf.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(AMImplementationType.ACCESS_POLICY_CONFIGURATIONS).add(bd.getBeanClassName());
                 }
@@ -113,7 +106,6 @@ public class ClassPathScanImplementationLookup implements SyncopeCoreLoader {
         LOG.debug("Implementation classes found: {}", classNames);
 
         authPolicyClasses = Collections.unmodifiableMap(authPolicyClasses);
-        authModuleClasses = Collections.unmodifiableMap(authModuleClasses);
         accessPolicyClasses = Collections.unmodifiableMap(accessPolicyClasses);
     }
 
@@ -127,12 +119,6 @@ public class ClassPathScanImplementationLookup implements SyncopeCoreLoader {
         return authPolicyClasses.get(authPolicyConfClass);
     }
 
-    public Class<? extends AuthModuleConf> getAuthModuleConfClass(
-            final Class<? extends AuthModuleConf> authModuleConfClass) {
-
-        return authModuleClasses.get(authModuleConfClass);
-    }
-
     public Class<? extends AccessPolicyConf> getAccessPolicyConfClass(
             final Class<? extends AccessPolicyConf> accessPolicyConfClass) {
 
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceImpl.java
new file mode 100644
index 0000000..68bdd90
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceImpl.java
@@ -0,0 +1,65 @@
+/*
+ * 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.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.AuthModuleService;
+import org.apache.syncope.core.logic.AuthModuleLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AuthModuleServiceImpl extends AbstractServiceImpl implements AuthModuleService {
+
+    @Autowired
+    private AuthModuleLogic logic;
+
+    @Override
+    public Response create(final AuthModuleTO authModuleTO) {
+        AuthModuleTO authModule = logic.create(authModuleTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(authModule.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, authModule.getKey()).
+                build();
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+    @Override
+    public List<AuthModuleTO> list() {
+        return logic.list();
+    }
+
+    @Override
+    public AuthModuleTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public void update(final AuthModuleTO authModuleTO) {
+        logic.update(authModuleTO);
+    }
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
index fcd1f90..fc5e18c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.persistence.api.dao.auth;
 
 import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
 import java.util.List;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
 
@@ -35,6 +34,4 @@ public interface AuthModuleDAO extends DAO<AuthModule> {
 
     void delete(AuthModule authModule);
 
-    List<AuthModule> findByConfiguration(Implementation configuration);
-
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
index 68c5669..5d2393e 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
@@ -18,9 +18,10 @@
  */
 package org.apache.syncope.core.persistence.api.entity.auth;
 
-import org.apache.syncope.core.persistence.api.entity.Entity;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
 import java.util.List;
+import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
 
 public interface AuthModule extends Entity {
 
@@ -28,7 +29,20 @@ public interface AuthModule extends Entity {
 
     void setName(String name);
 
-    List<? extends Implementation> getConfigurations();
+    String getDescription();
+
+    void setDescription(String description);
+
+    /**
+     * Specify the mapping items for the attributes fetched from the source.
+     *
+     * @return list of mapping items
+     */
+    List<? extends Item> getProfileItems();
+
+    boolean add(Item profileItem);
+
+    AuthModuleConf getConf();
 
-    boolean add(Implementation configuration);
+    void setConf(AuthModuleConf description);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
similarity index 70%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
index 68c5669..9f249e4 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
@@ -18,17 +18,8 @@
  */
 package org.apache.syncope.core.persistence.api.entity.auth;
 
-import org.apache.syncope.core.persistence.api.entity.Entity;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
 
-public interface AuthModule extends Entity {
+public interface AuthModuleItem extends Item {
 
-    String getName();
-
-    void setName(String name);
-
-    List<? extends Implementation> getConfigurations();
-
-    boolean add(Implementation configuration);
 }
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index be9293a..8e82492 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -49,7 +49,6 @@ under the License.
                   body='{"@class":"org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf","authModules":["LdapAuthenticationTest"]}'/>
   <AuthPolicy id="659b9906-4b6e-4bc0-aca0-6809dff346d4" name="MyDefaultAuthPolicyConf" description="an authentication policy"/>
   <AuthPolicy id="b912a0d4-a890-416f-9ab8-84ab077eb028" name="DefaultAuthPolicy" description="Default authentication policy"/>
-  <AuthModule id="be456831-593d-4003-b273-4c3fb61700df" name="DefaultAuthModule"/>
 
   <!-- access policies -->
   <Implementation id="MyDefaultAccessPolicyConf" type="ACCESS_POLICY_CONFIGURATIONS" engine="JAVA"
@@ -65,6 +64,28 @@ under the License.
                   body='{"@class":"org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf","name":"DenyAttrReleasePolicyConf"}'/>
   <AttrReleasePolicy id="219935c7-deb3-40b3-8a9a-683037e523a2" name="DenyAttrReleasePolicy" description="deny attribute release policy policy"/>
   
+  <!-- Authentication modules -->
+  <AuthModule id="be456831-593d-4003-b273-4c3fb61700df" name="DefaultLDAPAuthModule" 
+              description="LDAP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","name":"MyLDAPAuthModuleConf","userIdAttribute":"uid","bindCredential":"Password","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"dc=example,dc=org","subtreeSearch":true}'/>
+  <AuthModule id="4c3ed7e8-7008-11ea-bc55-0242ac130003" name="DefaultJDBCAuthModule"
+              description="JDBC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","name":"MyJDBCAuthModuleConf", "sql":"SELECT * FROM table WHERE name=?"}'/>
+  <AuthModule id="4c3ed4e6-7008-11ea-bc55-0242ac130003" name="DefaultGoogleMfaAuthModule"
+              description="Google Mfa auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","name":"MyGoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest"}'/>
+  <AuthModule id="4c3ed8f6-7008-11ea-bc55-0242ac130003" name="DefaultOIDCAuthModule"
+              description="OIDC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"www.testurl.com"}'/>
+  <AuthModule id="4c3ed9d2-7008-11ea-bc55-0242ac130003" name="DefaultSAML2IdPAuthModule"
+              description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "providerName":"testProviderName","serviceProviderMetadataPath":"file:/etc/metadata"}'/>
+  <AuthModule id="4c3edbbc-7008-11ea-bc55-0242ac130003" name="DefaultJaasAuthModule"
+              description="Jaas auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","name":"MyJaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value"}'/>
+  <AuthModule id="4c3edc98-7008-11ea-bc55-0242ac130003" name="DefaultStaticAuthModule"
+              description="Static auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.StaticAuthModuleConf","name":"MyStaticAuthModuleConf","users":{"user1": "testUserPassword123"}}'/>
+  <AuthModule id="4c3edd60-7008-11ea-bc55-0242ac130003" name="DefaultSyncopeAuthModule"
+              description="Syncope auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf","name":"MySyncopeAuthModuleConf","domain":"Master","url":"http://mydomain.com/syncope/rest"}'/>
+  <AuthModule id="07c528f3-63b4-4dc1-a4da-87f35b8bdec8" name="DefaultRadiusAuthModule"
+              description="Radius auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.RadiusAuthModuleConf","name":"MyRadiusAuthModuleConf","protocol":"MSCHAPv2","inetAddress":"1.2.3.4", "sharedSecret":"thesecret"}'/>
+  <AuthModule id="f6e1288d-50d9-45fe-82ee-597c42242205" name="DefaultU2FAuthModule"
+              description="U2F auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","name":"MyU2FAuthModuleConf","expireDevices":40}'/>
+
   <RelationshipType id="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType id="neighborhood" description="Models the act that an object is near another"/>
   
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 e305918..abd4dd3 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
@@ -121,29 +121,27 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
     }
 
     @Override
-    public List<AuthPolicy> findByAuthPolicy(final Implementation policy) {
+    public List<AuthPolicy> findByAuthPolicy(final Implementation conf) {
         TypedQuery<AuthPolicy> query = entityManager().createQuery("SELECT e FROM " + JPAAuthPolicy.class.
-                getSimpleName() + " e "
-                + "WHERE :authPolicy MEMBER OF e.rules", AuthPolicy.class);
-        query.setParameter("authPolicy", policy);
+                getSimpleName() + " e WHERE :authPolicy MEMBER OF e.configurations", AuthPolicy.class);
+        query.setParameter("authPolicy", conf);
         return query.getResultList();
     }
 
     @Override
-    public List<AccessPolicy> findByAccessPolicy(final Implementation policy) {
+    public List<AccessPolicy> findByAccessPolicy(final Implementation conf) {
         TypedQuery<AccessPolicy> query = entityManager().createQuery("SELECT e FROM " + JPAAuthPolicy.class.
-                getSimpleName() + " e "
-                + "WHERE :accessPolicy MEMBER OF e.rules", AccessPolicy.class);
-        query.setParameter("accessPolicy", policy);
+                getSimpleName() + " e WHERE :accessPolicy MEMBER OF e.configurations", AccessPolicy.class);
+        query.setParameter("accessPolicy", conf);
         return query.getResultList();
     }
 
     @Override
-    public List<AttrReleasePolicy> findByAttrReleasePolicy(final Implementation policy) {
-        TypedQuery<AttrReleasePolicy> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAAttrReleasePolicy.class.getSimpleName() + " e "
-                + "WHERE :attrReleasePolicy MEMBER OF e.rules", AttrReleasePolicy.class);
-        query.setParameter("attrReleasePolicy", policy);
+    public List<AttrReleasePolicy> findByAttrReleasePolicy(final Implementation conf) {
+        TypedQuery<AttrReleasePolicy> query = entityManager().createQuery("SELECT e FROM "
+                + JPAAttrReleasePolicy.class.getSimpleName() + " e WHERE :attrReleasePolicy MEMBER OF e.configurations",
+                AttrReleasePolicy.class);
+        query.setParameter("attrReleasePolicy", conf);
         return query.getResultList();
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java
index bd21ef5..dd765ad 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java
@@ -18,15 +18,14 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao.auth;
 
-import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModule;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 import javax.persistence.TypedQuery;
 import java.util.List;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
 import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModule;
 
 @Repository
 public class JPAAuthModuleDAO extends AbstractDAO<AuthModule> implements AuthModuleDAO {
@@ -47,15 +46,6 @@ public class JPAAuthModuleDAO extends AbstractDAO<AuthModule> implements AuthMod
 
     @Transactional(readOnly = true)
     @Override
-    public List<AuthModule> findByConfiguration(final Implementation configuration) {
-        TypedQuery<AuthModule> query = entityManager().createQuery("SELECT e FROM " + JPAAuthModule.class.
-                getSimpleName() + " e "
-                + "WHERE :configuration MEMBER OF e.configurations", AuthModule.class);
-        query.setParameter("configuration", configuration);
-        return query.getResultList();
-    }
-
-    @Override
     public AuthModule save(final AuthModule authModule) {
         return entityManager().merge(authModule);
     }
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 e625cad..a034449 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
@@ -57,6 +57,7 @@ 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.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
 import org.apache.syncope.core.persistence.api.entity.auth.OIDCRP;
 import org.apache.syncope.core.persistence.api.entity.auth.SAML2SP;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
@@ -108,7 +109,6 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUni
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrValue;
 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.auth.JPAAuthModule;
 import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCRP;
 import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SP;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttr;
@@ -153,6 +153,8 @@ 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.jpa.entity.auth.JPAAuthModule;
+import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModuleItem;
 
 public class JPAEntityFactory implements EntityFactory {
 
@@ -307,6 +309,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAGatewayRoute();
         } else if (reference.equals(AuthModule.class)) {
             result = (E) new JPAAuthModule();
+        } else if (reference.equals(AuthModuleItem.class)) {
+            result = (E) new JPAAuthModuleItem();
         } else if (reference.equals(AuthPolicy.class)) {
             result = (E) new JPAAuthPolicy();
         } else if (reference.equals(AccessPolicy.class)) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
index 2a91d09..e2b6586 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
@@ -18,22 +18,21 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.auth;
 
-import org.apache.syncope.common.lib.types.AMImplementationType;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
-import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
-
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
+import javax.persistence.Lob;
+import javax.persistence.OneToMany;
 import javax.persistence.Table;
-
-import java.util.ArrayList;
-import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.auth.AuthModuleConf;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPAAuthModule.TABLE)
@@ -41,18 +40,29 @@ public class JPAAuthModule extends AbstractGeneratedKeyEntity implements AuthMod
 
     public static final String TABLE = "AuthModule";
 
-    private static final long serialVersionUID = 7422422526695279794L;
+    private static final long serialVersionUID = 5681033638234853077L;
 
     @Column(unique = true, nullable = false)
     private String name;
 
-    @ManyToMany(fetch = FetchType.EAGER)
-    @JoinTable(name = TABLE + "Conf",
-            joinColumns =
-            @JoinColumn(name = "authentication_module_id"),
-            inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
-    private List<JPAImplementation> configurations = new ArrayList<>();
+    @Column(nullable = false)
+    private String description;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "mapping")
+    private final List<JPAAuthModuleItem> profileItems = new ArrayList<>();
+
+    @Lob
+    private String jsonConf;
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public void setDescription(final String description) {
+        this.description = description;
+    }
 
     @Override
     public String getName() {
@@ -65,15 +75,30 @@ public class JPAAuthModule extends AbstractGeneratedKeyEntity implements AuthMod
     }
 
     @Override
-    public List<? extends Implementation> getConfigurations() {
-        return configurations;
+    public List<? extends Item> getProfileItems() {
+        return profileItems;
+    }
+
+    @Override
+    public boolean add(final Item profileItem) {
+        checkType(profileItem, JPAAuthModuleItem.class);
+        return profileItems.contains((JPAAuthModuleItem) profileItem)
+                || profileItems.add((JPAAuthModuleItem) profileItem);
+    }
+
+    @Override
+    public AuthModuleConf getConf() {
+        AuthModuleConf conf = null;
+        if (!StringUtils.isBlank(jsonConf)) {
+            conf = POJOHelper.deserialize(jsonConf, AuthModuleConf.class);
+        }
+
+        return conf;
     }
 
     @Override
-    public boolean add(final Implementation configuration) {
-        checkType(configuration, JPAImplementation.class);
-        checkImplementationType(configuration, AMImplementationType.AUTH_MODULE_CONFIGURATIONS);
-        return configurations.contains((JPAImplementation) configuration)
-                || configurations.add((JPAImplementation) configuration);
+    public void setConf(final AuthModuleConf conf) {
+        jsonConf = POJOHelper.serialize(conf);
     }
+
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
similarity index 52%
copy from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
copy to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
index 2a91d09..bc92d49 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
@@ -18,62 +18,52 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity.auth;
 
-import org.apache.syncope.common.lib.types.AMImplementationType;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
-import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
-
-import javax.persistence.Column;
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.Cacheable;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.Table;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import javax.persistence.UniqueConstraint;
+import org.apache.syncope.common.lib.types.IdMImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
+import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractItem;
 
 @Entity
-@Table(name = JPAAuthModule.TABLE)
-public class JPAAuthModule extends AbstractGeneratedKeyEntity implements AuthModule {
+@Table(name = JPAAuthModuleItem.TABLE)
+@Cacheable
+public class JPAAuthModuleItem extends AbstractItem implements AuthModuleItem {
 
-    public static final String TABLE = "AuthModule";
+    public static final String TABLE = "AuthModuleItem";
 
-    private static final long serialVersionUID = 7422422526695279794L;
-
-    @Column(unique = true, nullable = false)
-    private String name;
+    private static final long serialVersionUID = 3165440920144995781L;
 
     @ManyToMany(fetch = FetchType.EAGER)
-    @JoinTable(name = TABLE + "Conf",
+    @JoinTable(name = TABLE + "Transformer",
             joinColumns =
-            @JoinColumn(name = "authentication_module_id"),
+            @JoinColumn(name = "item_id"),
             inverseJoinColumns =
-            @JoinColumn(name = "implementation_id"))
-    private List<JPAImplementation> configurations = new ArrayList<>();
-
-    @Override
-    public String getName() {
-        return name;
-    }
+            @JoinColumn(name = "implementation_id"),
+            uniqueConstraints =
+            @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
+    private final List<JPAImplementation> transformers = new ArrayList<>();
 
     @Override
-    public void setName(final String name) {
-        this.name = name;
+    public boolean add(final Implementation transformer) {
+        checkType(transformer, JPAImplementation.class);
+        checkImplementationType(transformer, IdMImplementationType.ITEM_TRANSFORMER);
+        return transformers.contains((JPAImplementation) transformer)
+                || this.transformers.add((JPAImplementation) transformer);
     }
 
     @Override
-    public List<? extends Implementation> getConfigurations() {
-        return configurations;
+    public List<? extends Implementation> getTransformers() {
+        return transformers;
     }
 
-    @Override
-    public boolean add(final Implementation configuration) {
-        checkType(configuration, JPAImplementation.class);
-        checkImplementationType(configuration, AMImplementationType.AUTH_MODULE_CONFIGURATIONS);
-        return configurations.contains((JPAImplementation) configuration)
-                || configurations.add((JPAImplementation) configuration);
-    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java
index 97add50..621bc1a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccessPolicy.java
@@ -54,7 +54,7 @@ public class JPAAccessPolicy extends AbstractPolicy implements AccessPolicy {
             @JoinColumn(name = "implementation_id"),
             uniqueConstraints =
             @UniqueConstraint(columnNames = { "access_policy_id", "implementation_id" }))
-    private List<JPAImplementation> configurations = new ArrayList<>();
+    private final List<JPAImplementation> configurations = new ArrayList<>();
 
     @Override
     public String getName() {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java
index 412b968..92a125a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAttrReleasePolicy.java
@@ -54,7 +54,7 @@ public class JPAAttrReleasePolicy extends AbstractPolicy implements AttrReleaseP
             @JoinColumn(name = "implementation_id"),
             uniqueConstraints =
             @UniqueConstraint(columnNames = { "attr_release_policy_id", "implementation_id" }))
-    private List<JPAImplementation> configurations = new ArrayList<>();
+    private final List<JPAImplementation> configurations = new ArrayList<>();
 
     @Override
     public String getName() {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java
index 553c40c..cdf3f97 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAuthPolicy.java
@@ -54,7 +54,7 @@ public class JPAAuthPolicy extends AbstractPolicy implements AuthPolicy {
             @JoinColumn(name = "implementation_id"),
             uniqueConstraints =
             @UniqueConstraint(columnNames = { "authentication_policy_id", "implementation_id" }))
-    private List<JPAImplementation> configurations = new ArrayList<>();
+    private final List<JPAImplementation> configurations = new ArrayList<>();
 
     @Override
     public String getName() {
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 780a9b0..0b198fa 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
@@ -19,30 +19,32 @@
 package org.apache.syncope.core.persistence.jpa.inner;
 
 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.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
 import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
+import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf;
 import org.apache.syncope.common.lib.auth.JaasAuthModuleConf;
 import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf;
 import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf;
+import org.apache.syncope.common.lib.auth.RadiusAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf;
 import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
-import org.apache.syncope.common.lib.to.ItemTO;
-import org.apache.syncope.common.lib.types.AMImplementationType;
-import org.apache.syncope.common.lib.types.ImplementationEngine;
-import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
+import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
 import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
 
 @Transactional("Master")
 public class AuthModuleTest extends AbstractTest {
@@ -50,37 +52,146 @@ public class AuthModuleTest extends AbstractTest {
     @Autowired
     private AuthModuleDAO authModuleDAO;
 
-    @Autowired
-    private ImplementationDAO implementationDAO;
-
     @Test
     public void find() {
-        AuthModule module = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
-        assertNotNull(module);
+        AuthModule authModule = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3ed8f6-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3edbbc-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3ed7e8-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3ed4e6-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3edc98-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3ed9d2-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("4c3edd60-7008-11ea-bc55-0242ac130003");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("07c528f3-63b4-4dc1-a4da-87f35b8bdec8");
+        assertNotNull(authModule);
+
+        authModule = authModuleDAO.find("f6e1288d-50d9-45fe-82ee-597c42242205");
+        assertNotNull(authModule);
 
-        module = authModuleDAO.find(UUID.randomUUID().toString());
-        assertNull(module);
+        authModule = authModuleDAO.find(UUID.randomUUID().toString());
+        assertNull(authModule);
     }
 
     @Test
     public void findAll() {
         List<AuthModule> modules = authModuleDAO.findAll();
         assertNotNull(modules);
-        assertEquals(1, modules.size());
+        assertFalse(modules.isEmpty());
+        assertTrue(modules.size() >= 10);
     }
 
     @Test
-    public void saveWithPredefinedModule() {
-        StaticAuthModuleConf conf = new StaticAuthModuleConf(Map.of("user", UUID.randomUUID().toString()));
-
-        Implementation config = getImplementation(conf);
+    public void findByAuthModuleImpl() {
+        AuthModule ldapAuthModule = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
+        assertNotNull(ldapAuthModule);
+        AuthModule jdbcAuthModule = authModuleDAO.find("4c3ed7e8-7008-11ea-bc55-0242ac130003");
+        assertNotNull(jdbcAuthModule);
+        AuthModule googleMfaAuthModule = authModuleDAO.find("4c3ed4e6-7008-11ea-bc55-0242ac130003");
+        assertNotNull(googleMfaAuthModule);
+        AuthModule oidcAuthModule = authModuleDAO.find("4c3ed8f6-7008-11ea-bc55-0242ac130003");
+        assertNotNull(oidcAuthModule);
+        AuthModule saml2IdPAuthModule = authModuleDAO.find("4c3ed9d2-7008-11ea-bc55-0242ac130003");
+        assertNotNull(saml2IdPAuthModule);
+        AuthModule jaasAuthModule = authModuleDAO.find("4c3edbbc-7008-11ea-bc55-0242ac130003");
+        assertNotNull(jaasAuthModule);
+        AuthModule staticAuthModule = authModuleDAO.find("4c3edc98-7008-11ea-bc55-0242ac130003");
+        assertNotNull(staticAuthModule);
+        AuthModule syncopeAuthModule = authModuleDAO.find("4c3edd60-7008-11ea-bc55-0242ac130003");
+        assertNotNull(syncopeAuthModule);
+        AuthModule radiusAuthModule = authModuleDAO.find("07c528f3-63b4-4dc1-a4da-87f35b8bdec8");
+        assertNotNull(radiusAuthModule);
+        AuthModule u2fAuthModule = authModuleDAO.find("f6e1288d-50d9-45fe-82ee-597c42242205");
+        assertNotNull(u2fAuthModule);
+
+        assertTrue(isSpecificConf(ldapAuthModule.getConf(), LDAPAuthModuleConf.class));
+        assertFalse(isSpecificConf(ldapAuthModule.getConf(), JDBCAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(jdbcAuthModule.getConf(), JDBCAuthModuleConf.class));
+        assertFalse(isSpecificConf(jdbcAuthModule.getConf(), GoogleMfaAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(googleMfaAuthModule.getConf(), GoogleMfaAuthModuleConf.class));
+        assertFalse(isSpecificConf(googleMfaAuthModule.getConf(), OIDCAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(oidcAuthModule.getConf(), OIDCAuthModuleConf.class));
+        assertFalse(isSpecificConf(oidcAuthModule.getConf(), SAML2IdPAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(saml2IdPAuthModule.getConf(), SAML2IdPAuthModuleConf.class));
+        assertFalse(isSpecificConf(saml2IdPAuthModule.getConf(), JaasAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(jaasAuthModule.getConf(), JaasAuthModuleConf.class));
+        assertFalse(isSpecificConf(jaasAuthModule.getConf(), StaticAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(staticAuthModule.getConf(), StaticAuthModuleConf.class));
+        assertFalse(isSpecificConf(staticAuthModule.getConf(), SyncopeAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(syncopeAuthModule.getConf(), SyncopeAuthModuleConf.class));
+        assertFalse(isSpecificConf(syncopeAuthModule.getConf(), RadiusAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(radiusAuthModule.getConf(), RadiusAuthModuleConf.class));
+        assertFalse(isSpecificConf(radiusAuthModule.getConf(), U2FAuthModuleConf.class));
+
+        assertTrue(isSpecificConf(u2fAuthModule.getConf(), U2FAuthModuleConf.class));
+        assertFalse(isSpecificConf(u2fAuthModule.getConf(), LDAPAuthModuleConf.class));
+    }
 
-        config = implementationDAO.save(config);
+    @Test
+    public void findByType() {
+        List<AuthModule> authModules = authModuleDAO.findAll();
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), LDAPAuthModuleConf.class)
+                && authModule.getName().equals("DefaultLDAPAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), JDBCAuthModuleConf.class)
+                && authModule.getName().equals("DefaultJDBCAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), GoogleMfaAuthModuleConf.class)
+                && authModule.getName().equals("DefaultGoogleMfaAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), OIDCAuthModuleConf.class)
+                && authModule.getName().equals("DefaultOIDCAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), SAML2IdPAuthModuleConf.class)
+                && authModule.getName().equals("DefaultSAML2IdPAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), JaasAuthModuleConf.class)
+                && authModule.getName().equals("DefaultJaasAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), StaticAuthModuleConf.class)
+                && authModule.getName().equals("DefaultStaticAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), SyncopeAuthModuleConf.class)
+                && authModule.getName().equals("DefaultSyncopeAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), RadiusAuthModuleConf.class)
+                && authModule.getName().equals("DefaultRadiusAuthModule")));
+        assertTrue(authModules.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), U2FAuthModuleConf.class)
+                && authModule.getName().equals("DefaultU2FAuthModule")));
+    }
 
-        assertNotNull(config);
-        assertNotNull(config.getKey());
+    @Test
+    public void saveWithStaticModule() {
+        StaticAuthModuleConf conf = new StaticAuthModuleConf();
+        conf.getUsers().put("user1", UUID.randomUUID().toString());
+        conf.getUsers().put("user2", "user2Password123");
 
-        saveAuthModule(config, "AuthModulePredefinedTest");
+        saveAuthModule("StaticAuthModuleTest", conf);
     }
 
     @Test
@@ -91,14 +202,8 @@ public class AuthModuleTest extends AbstractTest {
         conf.setLoginConfigType("JavaLoginConfig");
         conf.setRealm("SYNCOPE");
         conf.setLoginConfigurationFile("/opt/jaas/login.conf");
-        Implementation config = getImplementation(conf);
 
-        config = implementationDAO.save(config);
-
-        assertNotNull(config);
-        assertNotNull(config.getKey());
-
-        saveAuthModule(config, "AuthModuleJaasTest");
+        saveAuthModule("JaasAuthModuleTest", conf);
     }
 
     @Test
@@ -109,23 +214,9 @@ public class AuthModuleTest extends AbstractTest {
         conf.setSubtreeSearch(true);
         conf.setLdapUrl("ldap://localhost:1389");
         conf.setUserIdAttribute("uid");
-        conf.setBaseDn("cn=Directory Manager,dc=example,dc=org");
         conf.setBindCredential("Password");
-        ItemTO keyMapping = new ItemTO();
-        keyMapping.setIntAttrName("uid");
-        keyMapping.setExtAttrName("username");
-        ItemTO fullnameMapping = new ItemTO();
-        fullnameMapping.setIntAttrName("cn");
-        fullnameMapping.setExtAttrName("fullname");
-        conf.getProfileItems().addAll(List.of(fullnameMapping, keyMapping));
-        Implementation config = getImplementation(conf);
 
-        config = implementationDAO.save(config);
-
-        assertNotNull(config);
-        assertNotNull(config.getKey());
-
-        saveAuthModule(config, "AuthModuleLdapTest");
+        saveAuthModule("LDAPAuthModuleTest", conf);
     }
 
     @Test
@@ -136,14 +227,8 @@ public class AuthModuleTest extends AbstractTest {
         conf.setLabel("Syncope");
         conf.setTimeStepSize(30);
         conf.setWindowSize(3);
-        Implementation config = getImplementation(conf);
-
-        config = implementationDAO.save(config);
 
-        assertNotNull(config);
-        assertNotNull(config.getKey());
-
-        saveAuthModule(config, "AuthModuleGoogleTest");
+        saveAuthModule("GoogleMfaAuthModuleTest", conf);
     }
 
     @Test
@@ -154,49 +239,135 @@ public class AuthModuleTest extends AbstractTest {
         conf.setUserIdAttribute("username");
         conf.setResponseType("code");
         conf.setScope("openid email profile");
-        ItemTO keyMapping = new ItemTO();
-        keyMapping.setIntAttrName("uid");
-        keyMapping.setExtAttrName("username");
-        conf.getProfileItems().add(keyMapping);
-        Implementation config = getImplementation(conf);
 
-        config = implementationDAO.save(config);
+        saveAuthModule("OIDCAuthModuleTest", conf);
+    }
 
-        assertNotNull(config);
-        assertNotNull(config.getKey());
+    @Test
+    public void saveWithJDBCModule() {
+        JDBCAuthModuleConf conf = new JDBCAuthModuleConf();
+        conf.setSql("SELECT * FROM table WHERE name=?");
+        conf.setFieldPassword("password");
+        conf.getPrincipalAttributeList().addAll(List.of("sn", "cn:commonName", "givenName"));
 
-        saveAuthModule(config, "AuthModuleOIDCTest");
+        saveAuthModule("JDBCAuthModuleTest", conf);
     }
 
-    private void saveAuthModule(final Implementation config, final String name) {
-        AuthModule module = entityFactory.newEntity(AuthModule.class);
-        module.setName(name);
-        module.add(config);
-        authModuleDAO.save(module);
+    @Test
+    public void saveWithSyncopeModule() {
+        SyncopeAuthModuleConf conf = new SyncopeAuthModuleConf();
+        conf.setDomain("Master");
+        conf.setUrl("http://mydomain.com/syncope/rest");
+
+        saveAuthModule("SyncopeAuthModuleTest", conf);
+    }
 
+    @Test
+    public void saveWithSAML2IdPModule() {
+        SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf();
+        conf.setServiceProviderEntityId("testEntityId");
+        conf.setProviderName("testProviderName");
+        conf.setServiceProviderMetadataPath("file:/etc/metadata");
+
+        saveAuthModule("SAML2IdPAuthModuleTest", conf);
+    }
+
+    @Test
+    public void saveWithRadiusModule() {
+        RadiusAuthModuleConf conf = new RadiusAuthModuleConf();
+        conf.setProtocol("MSCHAPv2");
+        conf.setInetAddress("1.2.3.4");
+        conf.setSharedSecret("xyz");
+        conf.setSocketTimeout(40);
+
+        saveAuthModule("RadiusAuthModuleTest", conf);
+    }
+
+    @Test
+    public void saveWithU2FModule() {
+        U2FAuthModuleConf conf = new U2FAuthModuleConf();
+        conf.setExpireDevices(50);
+
+        saveAuthModule("U2FAuthModuleTest", conf);
+    }
+
+    @Test
+    public void updateWithLDAPModule() {
+        AuthModule module = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
         assertNotNull(module);
-        assertNotNull(module.getKey());
+        AuthModuleConf conf = module.getConf();
+        LDAPAuthModuleConf.class.cast(conf).setBaseDn("dc=example2,dc=org");
+        LDAPAuthModuleConf.class.cast(conf).setSearchFilter("cn={user2}");
+        module.setConf(conf);
 
-        assertNotNull(authModuleDAO.find(module.getKey()));
+        module = authModuleDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        AuthModule found = authModuleDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals("dc=example2,dc=org", LDAPAuthModuleConf.class.cast(found.getConf()).getBaseDn());
+        assertEquals("cn={user2}", LDAPAuthModuleConf.class.cast(found.getConf()).getSearchFilter());
     }
 
-    private Implementation getImplementation(final AuthModuleConf conf) {
-        Implementation config = entityFactory.newEntity(Implementation.class);
-        config.setKey(UUID.randomUUID().toString());
-        config.setEngine(ImplementationEngine.JAVA);
-        config.setType(AMImplementationType.AUTH_MODULE_CONFIGURATIONS);
-        config.setBody(POJOHelper.serialize(conf));
-        return config;
+    @Test
+    public void updateWithJDBCModule() {
+        AuthModule module = authModuleDAO.find("4c3ed7e8-7008-11ea-bc55-0242ac130003");
+        assertNotNull(module);
+        AuthModuleConf conf = module.getConf();
+        JDBCAuthModuleConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?");
+        module.setConf(conf);
+
+        module = authModuleDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        AuthModule found = authModuleDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals("SELECT * FROM otherTable WHERE name=?",
+                JDBCAuthModuleConf.class.cast(found.getConf()).getSql());
     }
 
     @Test
     public void delete() {
-        AuthModule authModule = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
-        assertNotNull(authModule);
+        testDelete("be456831-593d-4003-b273-4c3fb61700df");
+        testDelete("4c3ed8f6-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3edbbc-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3ed7e8-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3ed4e6-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3edc98-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3ed9d2-7008-11ea-bc55-0242ac130003");
+        testDelete("4c3edd60-7008-11ea-bc55-0242ac130003");
+        testDelete("07c528f3-63b4-4dc1-a4da-87f35b8bdec8");
+        testDelete("f6e1288d-50d9-45fe-82ee-597c42242205");
+    }
 
-        authModuleDAO.delete("be456831-593d-4003-b273-4c3fb61700df");
+    private void saveAuthModule(final String name, final AuthModuleConf conf) {
+        AuthModule module = entityFactory.newEntity(AuthModule.class);
+        module.setName(name);
+        module.setDescription("An authentication module");
+        module.setConf(conf);
+        AuthModuleItem keyMapping = entityFactory.newEntity(AuthModuleItem.class);
+        keyMapping.setIntAttrName("uid");
+        keyMapping.setExtAttrName("username");
+        AuthModuleItem fullnameMapping = entityFactory.newEntity(AuthModuleItem.class);
+        fullnameMapping.setIntAttrName("cn");
+        fullnameMapping.setExtAttrName("fullname");
+        module.add(keyMapping);
+        module.add(fullnameMapping);
+        authModuleDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        assertNotNull(authModuleDAO.find(module.getKey()));
+    }
 
-        authModule = authModuleDAO.find("be456831-593d-4003-b273-4c3fb61700df");
+    private void testDelete(final String key) {
+        AuthModule authModule = authModuleDAO.find(key);
+        assertNotNull(authModule);
+        authModuleDAO.delete(key);
+        authModule = authModuleDAO.find(key);
         assertNull(authModule);
     }
+
+    private boolean isSpecificConf(final AuthModuleConf conf, final Class<? extends AuthModuleConf> clazz) {
+        return ClassUtils.isAssignable(clazz, conf.getClass());
+    }
 }
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 75a5f3b..19acd47 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
@@ -28,19 +28,16 @@ import org.apache.syncope.core.persistence.jpa.inner.AbstractClientAppTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
-
 import javax.persistence.PersistenceException;
-
 import java.util.UUID;
+import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.OIDCRP;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 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.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRP;
-
 @Transactional("Master")
 public class PolicyTest extends AbstractClientAppTest {
 
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 29fe303..b484956 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -44,14 +44,13 @@ under the License.
                   body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWit [...]
   <PasswordPolicyRule policy_id="55e5de0b-c79c-4e66-adda-251b6fb8579a" implementation_id="DefaultPasswordRuleConf3"/>
   
-  <!-- authentication policies -->
+  <!-- Authentication policies -->
   <Implementation id="MyDefaultAuthPolicyConf" type="AUTH_POLICY_CONFIGURATIONS" engine="JAVA"
                   body='{"@class":"org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf","authModules":["LdapAuthenticationTest"]}'/>
   <AuthPolicy id="659b9906-4b6e-4bc0-aca0-6809dff346d4" name="MyDefaultAuthPolicyConf" description="an authentication policy"/>
   <AuthPolicy id="b912a0d4-a890-416f-9ab8-84ab077eb028" name="DefaultAuthPolicy" description="Default authentication policy"/>
-  <AuthModule id="be456831-593d-4003-b273-4c3fb61700df" name="DefaultAuthModule"/>
 
-  <!-- access policies -->
+  <!-- Access policies -->
   <Implementation id="MyDefaultAccessPolicyConf" type="ACCESS_POLICY_CONFIGURATIONS" engine="JAVA"
                   body='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf","name":"MyDefaultAccessPolicyConf","enabled":true,"ssoEnabled":true}'/>
   <AccessPolicy id="419935c7-deb3-40b3-8a9a-683037e523a2" name="MyDefaultAccessPolicyConf" description="an access policy"/>
@@ -64,6 +63,28 @@ under the License.
   <Implementation id="DenyAttrReleasePolicyConf" type="ATTR_RELEASE_CONFIGURATIONS" engine="JAVA"
                   body='{"@class":"org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf","name":"DenyAttrReleasePolicyConf"}'/>
   <AttrReleasePolicy id="219935c7-deb3-40b3-8a9a-683037e523a2" name="DenyAttrReleasePolicy" description="deny attribute release policy policy"/>
+  
+  <!-- Authentication modules -->
+  <AuthModule id="be456831-593d-4003-b273-4c3fb61700df" name="DefaultLDAPAuthModule" 
+              description="LDAP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","name":"MyLDAPAuthModuleConf","userIdAttribute":"uid","bindCredential":"Password","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"dc=example,dc=org","subtreeSearch":true}'/>
+  <AuthModule id="4c3ed7e8-7008-11ea-bc55-0242ac130003" name="DefaultJDBCAuthModule"
+              description="JDBC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","name":"MyJDBCAuthModuleConf", "sql":"SELECT * FROM table WHERE name=?"}'/>
+  <AuthModule id="4c3ed4e6-7008-11ea-bc55-0242ac130003" name="DefaultGoogleMfaAuthModule"
+              description="Google Mfa auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","name":"MyGoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest"}'/>
+  <AuthModule id="4c3ed8f6-7008-11ea-bc55-0242ac130003" name="DefaultOIDCAuthModule"
+              description="OIDC auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","name":"MyOIDCAuthModuleConf", "discoveryUri":"www.testurl.com"}'/>
+  <AuthModule id="4c3ed9d2-7008-11ea-bc55-0242ac130003" name="DefaultSAML2IdPAuthModule"
+              description="SAML2 IdP auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","name":"MySAML2IdPAuthModuleConf", "providerName":"testProviderName","serviceProviderMetadataPath":"file:/etc/metadata"}'/>
+  <AuthModule id="4c3edbbc-7008-11ea-bc55-0242ac130003" name="DefaultJaasAuthModule"
+              description="Jaas auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","name":"MyJaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value"}'/>
+  <AuthModule id="4c3edc98-7008-11ea-bc55-0242ac130003" name="DefaultStaticAuthModule"
+              description="Static auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.StaticAuthModuleConf","name":"MyStaticAuthModuleConf","users":{"user1": "testUserPassword123"}}'/>
+  <AuthModule id="4c3edd60-7008-11ea-bc55-0242ac130003" name="DefaultSyncopeAuthModule"
+              description="Syncope auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf","name":"MySyncopeAuthModuleConf","domain":"Master","url":"http://mydomain.com/syncope/rest"}'/>
+  <AuthModule id="07c528f3-63b4-4dc1-a4da-87f35b8bdec8" name="DefaultRadiusAuthModule"
+              description="Radius auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.RadiusAuthModuleConf","name":"MyRadiusAuthModuleConf","protocol":"MSCHAPv2","inetAddress":"1.2.3.4", "sharedSecret":"thesecret"}'/>
+  <AuthModule id="f6e1288d-50d9-45fe-82ee-597c42242205" name="DefaultU2FAuthModule"
+              description="U2F auth module" jsonConf='{"@class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","name":"MyU2FAuthModuleConf","expireDevices":40}'/>
 
   <RelationshipType id="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType id="neighborhood" description="Models the act that an object is near another"/>
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractClientAppLogic.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
similarity index 53%
rename from core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractClientAppLogic.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
index b73f319..1eab9f4 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractClientAppLogic.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
@@ -16,30 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.logic;
+package org.apache.syncope.core.provisioning.api.data;
 
-import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
 
-import java.lang.reflect.Method;
-import java.util.List;
+public interface AuthModuleDataBinder {
 
-public abstract class AbstractClientAppLogic<T extends ClientAppTO>
-        extends AbstractTransactionalLogic<ClientAppTO> {
+    AuthModule create(AuthModuleTO authModuleTO);
 
-    @Override
-    protected ClientAppTO resolveReference(final Method method, final Object... args)
-            throws UnresolvedReferenceException {
-        throw new UnresolvedReferenceException();
-    }
+    AuthModule update(AuthModule authModule, AuthModuleTO authModuleTO);
 
-    public abstract T delete(String key);
-
-    public abstract List<T> list();
-
-    public abstract T read(String key);
-
-    public abstract T create(T applicationTO);
-
-    public abstract T update(T applicationTO);
+    AuthModuleTO getAuthModuleTO(AuthModule authModule);
 
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
new file mode 100644
index 0000000..0b0e38b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.persistence.api.entity.EntityFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
+
+@Component
+public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    private AuthModule getAuthModule(final AuthModule authModule, final AuthModuleTO authModuleTO) {
+        AuthModule result = authModule;
+
+        if (result == null) {
+            result = entityFactory.newEntity(AuthModule.class);
+        }
+
+        AuthModule authenticationModule = AuthModule.class.cast(result);
+        AuthModuleTO authenticationModuleTO = AuthModuleTO.class.cast(authModuleTO);
+
+        authenticationModule.setName(authenticationModuleTO.getName());
+        authenticationModule.setConf(authenticationModuleTO.getConf());
+        authenticationModule.setDescription(authenticationModuleTO.getDescription());
+        // remove all profile items not contained in the TO
+        authenticationModule.getProfileItems().
+                removeIf(item -> !authenticationModuleTO.getProfileItems().stream().
+                anyMatch(otherItem -> item.getKey().equals(otherItem.getKey())));
+
+        return result;
+    }
+
+    @Override
+    public AuthModule create(final AuthModuleTO authModuleTO) {
+        return getAuthModule(null, authModuleTO);
+    }
+
+    @Override
+    public AuthModule update(final AuthModule authModule, final AuthModuleTO authModuleTO) {
+        return getAuthModule(authModule, authModuleTO);
+    }
+
+    @Override
+    public AuthModuleTO getAuthModuleTO(final AuthModule authModule) {
+        AuthModuleTO authModuleTO = new AuthModuleTO();
+
+        authModuleTO.setName(authModule.getName());
+        authModuleTO.setKey(authModule.getKey());
+        authModuleTO.setDescription(authModule.getDescription());
+        authModuleTO.setConf(authModule.getConf());
+        authModuleTO.getProfileItems().forEach(item -> {
+            authModuleTO.add(item);
+        });
+
+        return authModuleTO;
+    }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
index 812b1bf..9502a5d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java
@@ -24,7 +24,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.ConnPoolConfTO;
@@ -60,9 +59,6 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder {
     private RealmDAO realmDAO;
 
     @Autowired
-    private ConfParamOps confParamOps;
-
-    @Autowired
     private EntityFactory entityFactory;
 
     @Override
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
index dca7d28..9df9f8c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
@@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.policy.AccessPolicyConf;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
 import org.apache.syncope.common.lib.policy.RuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.to.ImplementationTO;
@@ -58,7 +57,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.apache.syncope.common.lib.policy.AuthPolicyConf;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 
 @Component
@@ -162,10 +160,6 @@ public class ImplementationDataBinderImpl implements ImplementationDataBinder {
                     base = AccessPolicy.class;
                     break;
 
-                case AMImplementationType.AUTH_MODULE_CONFIGURATIONS:
-                    base = AuthModule.class;
-                    break;
-
                 case AMImplementationType.AUTH_POLICY_CONFIGURATIONS:
                     base = AuthPolicy.class;
                     break;
@@ -198,14 +192,6 @@ public class ImplementationDataBinderImpl implements ImplementationDataBinder {
                         throw sce;
                     }
                     break;
-                case AMImplementationType.AUTH_MODULE_CONFIGURATIONS:
-                    AuthModuleConf authModuleConf =
-                            POJOHelper.deserialize(implementation.getBody(), AuthModuleConf.class);
-                    if (authModuleConf == null) {
-                        sce.getElements().add("Could not deserialize as AuthModule");
-                        throw sce;
-                    }
-                    break;
                 case AMImplementationType.AUTH_POLICY_CONFIGURATIONS:
                     AuthPolicyConf authPolicyConf =
                             POJOHelper.deserialize(implementation.getBody(), AuthPolicyConf.class);
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 bd208f7..3525861 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
@@ -74,6 +74,7 @@ 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.AuthModuleTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -86,6 +87,7 @@ import org.apache.syncope.common.rest.api.service.AnyObjectService;
 import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
 import org.apache.syncope.common.rest.api.service.AnyTypeService;
 import org.apache.syncope.common.rest.api.service.ApplicationService;
+import org.apache.syncope.common.rest.api.service.AuthModuleService;
 import org.apache.syncope.common.rest.api.service.CamelRouteService;
 import org.apache.syncope.common.rest.api.service.ClientAppService;
 import org.apache.syncope.common.rest.api.service.ConnectorService;
@@ -268,6 +270,8 @@ public abstract class AbstractITCase {
 
     protected static PolicyService policyService;
 
+    protected static AuthModuleService authModuleService;
+
     protected static SecurityQuestionService securityQuestionService;
 
     protected static ImplementationService implementationService;
@@ -360,6 +364,7 @@ public abstract class AbstractITCase {
         oidcProviderService = adminClient.getService(OIDCProviderService.class);
         scimConfService = adminClient.getService(SCIMConfService.class);
         clientAppService = adminClient.getService(ClientAppService.class);
+        authModuleService = adminClient.getService(AuthModuleService.class);
     }
 
     @Autowired
@@ -562,6 +567,18 @@ public abstract class AbstractITCase {
         return (T) getObject(response.getLocation(), PolicyService.class, policy.getClass());
     }
 
+    @SuppressWarnings("unchecked")
+    protected AuthModuleTO createAuthModule(final AuthModuleTO authModule) {
+        Response response = authModuleService.create(authModule);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = clientFactory.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return getObject(response.getLocation(), AuthModuleService.class, authModule.getClass());
+    }
+
     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/AuthModuleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
new file mode 100644
index 0000000..f88c03a
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
@@ -0,0 +1,406 @@
+/*
+ * 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.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
+import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf;
+import org.apache.syncope.common.lib.auth.JaasAuthModuleConf;
+import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf;
+import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf;
+import org.apache.syncope.common.lib.auth.RadiusAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf;
+import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
+import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+
+public class AuthModuleITCase extends AbstractITCase {
+
+    private enum AuthModuleSupportedType {
+        GOOGLE_MFA,
+        SAML2_IDP,
+        STATIC,
+        SYNCOPE,
+        LDAP,
+        JAAS,
+        JDBC,
+        U2F,
+        RADIUS,
+        OIDC;
+
+    };
+
+    private static AuthModuleTO buildAuthModuleTO(final AuthModuleSupportedType type) {
+        AuthModuleTO authModuleTO = new AuthModuleTO();
+        authModuleTO.setName("Test" + type + "AuthenticationModule" + getUUIDString());
+        authModuleTO.setDescription("A test " + type + " Authentication Module");
+        AuthModuleConf conf;
+
+        switch (type) {
+            case LDAP:
+                conf = new LDAPAuthModuleConf();
+                LDAPAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                LDAPAuthModuleConf.class.cast(conf).setBaseDn("dc=example,dc=org");
+                LDAPAuthModuleConf.class.cast(conf).setSearchFilter("cn={user}");
+                LDAPAuthModuleConf.class.cast(conf).setSubtreeSearch(true);
+                LDAPAuthModuleConf.class.cast(conf).setLdapUrl("ldap://localhost:1389");
+                LDAPAuthModuleConf.class.cast(conf).setUserIdAttribute("uid");
+                LDAPAuthModuleConf.class.cast(conf).setBaseDn("cn=Directory Manager,dc=example,dc=org");
+                LDAPAuthModuleConf.class.cast(conf).setBindCredential("Password");
+                break;
+
+            case GOOGLE_MFA:
+                conf = new GoogleMfaAuthModuleConf();
+                GoogleMfaAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                GoogleMfaAuthModuleConf.class.cast(conf).setCodeDigits(6);
+                GoogleMfaAuthModuleConf.class.cast(conf).setIssuer("SyncopeTest");
+                GoogleMfaAuthModuleConf.class.cast(conf).setLabel("Syncope");
+                GoogleMfaAuthModuleConf.class.cast(conf).setTimeStepSize(30);
+                GoogleMfaAuthModuleConf.class.cast(conf).setWindowSize(3);
+                break;
+
+            case JAAS:
+                conf = new JaasAuthModuleConf();
+                JaasAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                JaasAuthModuleConf.class.cast(conf).setKerberosKdcSystemProperty("sample-value");
+                JaasAuthModuleConf.class.cast(conf).setKerberosRealmSystemProperty("sample-value");
+                JaasAuthModuleConf.class.cast(conf).setLoginConfigType("JavaLoginConfig");
+                JaasAuthModuleConf.class.cast(conf).setRealm("SYNCOPE");
+                JaasAuthModuleConf.class.cast(conf).setLoginConfigurationFile("/opt/jaas/login.conf");
+                break;
+
+            case JDBC:
+                conf = new JDBCAuthModuleConf();
+                JDBCAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                JDBCAuthModuleConf.class.cast(conf).setSql("SELECT * FROM table WHERE name=?");
+                JDBCAuthModuleConf.class.cast(conf).setFieldPassword("password");
+                JDBCAuthModuleConf.class.cast(conf).getPrincipalAttributeList().addAll(
+                        List.of("sn", "cn:commonName", "givenName"));
+                break;
+
+            case OIDC:
+                conf = new OIDCAuthModuleConf();
+                OIDCAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                OIDCAuthModuleConf.class.cast(conf).setId("OIDCTestId");
+                OIDCAuthModuleConf.class.cast(conf).setDiscoveryUri("www.testurl.com");
+                OIDCAuthModuleConf.class.cast(conf).setUserIdAttribute("username");
+                OIDCAuthModuleConf.class.cast(conf).setResponseType("code");
+                OIDCAuthModuleConf.class.cast(conf).setScope("openid email profile");
+                break;
+
+            case SAML2_IDP:
+                conf = new SAML2IdPAuthModuleConf();
+                SAML2IdPAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderEntityId("testEntityId");
+                SAML2IdPAuthModuleConf.class.cast(conf).setProviderName("testProviderName");
+                SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderMetadataPath(
+                        "file:/etc/metadata");
+                break;
+
+            case SYNCOPE:
+                conf = new SyncopeAuthModuleConf();
+                SyncopeAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                SyncopeAuthModuleConf.class.cast(conf).setDomain("Master");
+                SyncopeAuthModuleConf.class.cast(conf).setUrl("http://mydomain.com/syncope/rest");
+                break;
+
+            case U2F:
+                conf = new U2FAuthModuleConf();
+                U2FAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                U2FAuthModuleConf.class.cast(conf).setExpireDevices(50);
+                break;
+
+            case RADIUS:
+                conf = new RadiusAuthModuleConf();
+                RadiusAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                RadiusAuthModuleConf.class.cast(conf).setProtocol("MSCHAPv2");
+                RadiusAuthModuleConf.class.cast(conf).setInetAddress("1.2.3.4");
+                RadiusAuthModuleConf.class.cast(conf).setSharedSecret("xyz");
+                RadiusAuthModuleConf.class.cast(conf).setSocketTimeout(40);
+                break;
+
+            case STATIC:
+            default:
+                conf = new StaticAuthModuleConf();
+                StaticAuthModuleConf.class.cast(conf).setName("TestConf" + getUUIDString());
+                StaticAuthModuleConf.class.cast(conf).getUsers().put("user1", UUID.randomUUID().toString());
+                StaticAuthModuleConf.class.cast(conf).getUsers().put("user2", "user2Password123");
+                break;
+        }
+        authModuleTO.setConf(conf);
+
+        return authModuleTO;
+    }
+
+    @Test
+    public void findAll() {
+        List<AuthModuleTO> authModuleTOs = authModuleService.list();
+        assertNotNull(authModuleTOs);
+        assertFalse(authModuleTOs.isEmpty());
+        assertTrue(authModuleTOs.size() >= 10);
+    }
+
+    @Test
+    public void listByType() {
+        List<AuthModuleTO> authModuleTOs = authModuleService.list();
+        assertNotNull(authModuleTOs);
+        assertFalse(authModuleTOs.isEmpty());
+
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), LDAPAuthModuleConf.class)
+                && authModule.getName().equals("DefaultLDAPAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), JDBCAuthModuleConf.class)
+                && authModule.getName().equals("DefaultJDBCAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), GoogleMfaAuthModuleConf.class)
+                && authModule.getName().equals("DefaultGoogleMfaAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), OIDCAuthModuleConf.class)
+                && authModule.getName().equals("DefaultOIDCAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), SAML2IdPAuthModuleConf.class)
+                && authModule.getName().equals("DefaultSAML2IdPAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), JaasAuthModuleConf.class)
+                && authModule.getName().equals("DefaultJaasAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), StaticAuthModuleConf.class)
+                && authModule.getName().equals("DefaultStaticAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), SyncopeAuthModuleConf.class)
+                && authModule.getName().equals("DefaultSyncopeAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), U2FAuthModuleConf.class)
+                && authModule.getName().equals("DefaultU2FAuthModule")));
+        assertTrue(authModuleTOs.stream().anyMatch(
+                authModule -> isSpecificConf(authModule.getConf(), RadiusAuthModuleConf.class)
+                && authModule.getName().equals("DefaultRadiusAuthModule")));
+    }
+
+    @Test
+    public void getLDAPAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("be456831-593d-4003-b273-4c3fb61700df");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), LDAPAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), JDBCAuthModuleConf.class));
+    }
+
+    @Test
+    public void getJDBCAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3ed7e8-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), JDBCAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), GoogleMfaAuthModuleConf.class));
+    }
+
+    @Test
+    public void getGoogleMfaAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3ed4e6-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), GoogleMfaAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), OIDCAuthModuleConf.class));
+    }
+
+    @Test
+    public void getOIDCAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3ed8f6-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), OIDCAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), SAML2IdPAuthModuleConf.class));
+    }
+
+    @Test
+    public void getSAML2IdPAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3ed9d2-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), SAML2IdPAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), JaasAuthModuleConf.class));
+    }
+
+    @Test
+    public void getJaasAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3edbbc-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), JaasAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), StaticAuthModuleConf.class));
+    }
+
+    @Test
+    public void getStaticAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3edc98-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), StaticAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), SyncopeAuthModuleConf.class));
+    }
+
+    @Test
+    public void getSyncopeAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("4c3edd60-7008-11ea-bc55-0242ac130003");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), SyncopeAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), RadiusAuthModuleConf.class));
+    }
+
+    @Test
+    public void getRadiusAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("07c528f3-63b4-4dc1-a4da-87f35b8bdec8");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), RadiusAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), U2FAuthModuleConf.class));
+    }
+
+    @Test
+    public void getU2FAuthModule() {
+        AuthModuleTO authModuleTO = authModuleService.read("f6e1288d-50d9-45fe-82ee-597c42242205");
+
+        assertNotNull(authModuleTO);
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getName()));
+        assertTrue(StringUtils.isNotBlank(authModuleTO.getDescription()));
+        assertTrue(isSpecificConf(authModuleTO.getConf(), U2FAuthModuleConf.class));
+        assertFalse(isSpecificConf(authModuleTO.getConf(), LDAPAuthModuleConf.class));
+    }
+
+    @Test
+    public void create() throws IOException {
+        EnumSet.allOf(AuthModuleSupportedType.class).forEach(type -> testCreate(type));
+    }
+
+    @Test
+    public void updateGoogleMfaAuthModule() {
+        AuthModuleTO googleMfaAuthModuleTO = authModuleService.read("4c3ed4e6-7008-11ea-bc55-0242ac130003");
+        assertNotNull(googleMfaAuthModuleTO);
+
+        final String authModuleName = "TestGoogleMfaAuthModule" + getUUIDString();
+        AuthModuleTO newGoogleMfaAuthModuleTO = buildAuthModuleTO(AuthModuleSupportedType.GOOGLE_MFA);
+        newGoogleMfaAuthModuleTO = createAuthModule(newGoogleMfaAuthModuleTO);
+        assertNotNull(newGoogleMfaAuthModuleTO);
+
+        AuthModuleConf conf = googleMfaAuthModuleTO.getConf();
+        assertNotNull(conf);
+        GoogleMfaAuthModuleConf.class.cast(conf).setLabel("newLabel");
+        newGoogleMfaAuthModuleTO.setConf(conf);
+
+        // update new auth module
+        authModuleService.update(newGoogleMfaAuthModuleTO);
+        newGoogleMfaAuthModuleTO = authModuleService.read(newGoogleMfaAuthModuleTO.getKey());
+        assertNotNull(newGoogleMfaAuthModuleTO);
+
+        conf = newGoogleMfaAuthModuleTO.getConf();
+        assertEquals("newLabel", GoogleMfaAuthModuleConf.class.cast(conf).getLabel());
+    }
+
+    @Test
+    public void updateLDAPAuthModule() {
+        AuthModuleTO ldapAuthModuleTO = authModuleService.read("be456831-593d-4003-b273-4c3fb61700df");
+        assertNotNull(ldapAuthModuleTO);
+
+        final String authModuleName = "TestGoogleMfaAuthModule" + getUUIDString();
+        AuthModuleTO newLdapAuthModuleTO = buildAuthModuleTO(AuthModuleSupportedType.LDAP);
+        newLdapAuthModuleTO = createAuthModule(newLdapAuthModuleTO);
+        assertNotNull(newLdapAuthModuleTO);
+
+        AuthModuleConf conf = ldapAuthModuleTO.getConf();
+        assertNotNull(conf);
+        LDAPAuthModuleConf.class.cast(conf).setSubtreeSearch(false);
+        newLdapAuthModuleTO.setConf(conf);
+
+        // update new auth module
+        authModuleService.update(newLdapAuthModuleTO);
+        newLdapAuthModuleTO = authModuleService.read(newLdapAuthModuleTO.getKey());
+        assertNotNull(newLdapAuthModuleTO);
+
+        conf = newLdapAuthModuleTO.getConf();
+        assertFalse(LDAPAuthModuleConf.class.cast(conf).isSubtreeSearch());
+    }
+
+    @Test
+    public void delete() throws IOException {
+        EnumSet.allOf(AuthModuleSupportedType.class).forEach(type -> testDelete(type));
+    }
+
+    private void testCreate(final AuthModuleSupportedType type) {
+        AuthModuleTO authModuleTO = createAuthModule(buildAuthModuleTO(type));
+        assertNotNull(authModuleTO);
+        assertTrue(authModuleTO.getName().contains(
+                "Test" + type + "AuthenticationModule"));
+        assertTrue(authModuleTO.getDescription().contains(
+                "A test " + type + " Authentication Module"));
+    }
+
+    private void testDelete(final AuthModuleSupportedType type) {
+        AuthModuleTO authModuleTO = buildAuthModuleTO(type);
+        AuthModuleTO read = createAuthModule(authModuleTO);
+        assertNotNull(read);
+        authModuleService.delete(read.getKey());
+        try {
+            authModuleService.read(read.getKey());
+            fail("This should not happen");
+        } catch (SyncopeClientException e) {
+            assertNotNull(e);
+        }
+    }
+
+    private boolean isSpecificConf(final AuthModuleConf conf, final Class<? extends AuthModuleConf> clazz) {
+        return ClassUtils.isAssignable(clazz, conf.getClass());
+    }
+}
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 2a64794..5af04e0 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
@@ -41,6 +41,7 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
     public SyncopeServiceRegistry(final WARestClient restClient,
             final ConfigurableApplicationContext applicationContext,
             final Collection<ServiceRegistryListener> serviceRegistryListeners) {
+
         super(applicationContext, serviceRegistryListeners);
         this.restClient = restClient;
         this.mapper = new RegisteredServiceMapper();