[SYNCOPE-1545] Fixing MFA settings for RegisteredService instances (#351)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
index 9ee91ca..28eb754 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
@@ -52,6 +52,8 @@
 
     protected final LoadableDetachableModel<List<String>> authModuleConfs;
 
+    protected Model<Class<? extends AuthModuleConf>> authModuleConfClass = Model.of();
+
     public AuthModuleWizardBuilder(final AuthModuleTO defaultItem, final PageReference pageRef) {
 
         super(defaultItem, pageRef);
@@ -80,23 +82,26 @@
 
     @Override
     protected WizardModel buildModelSteps(final AuthModuleTO modelObject, final WizardModel wizardModel) {
-        wizardModel.add(new Profile(modelObject, authModuleConfs));
+        wizardModel.add(new Profile(modelObject, authModuleConfs, authModuleConfClass));
         wizardModel.add(new Configuration(modelObject));
-
-        if (modelObject.getConf() instanceof GoogleMfaAuthModuleConf) {
-            wizardModel.add(new GoogleMfaAuthModuleConfLDAP((GoogleMfaAuthModuleConf) modelObject.getConf()));
-        }
-
+        wizardModel.add(new GoogleMfaAuthModuleConfLDAP(modelObject, authModuleConfClass));
         wizardModel.add(new Mapping(modelObject));
         return wizardModel;
     }
 
-    public static class Profile extends WizardStep {
+    protected static class Profile extends WizardStep {
 
         private static final long serialVersionUID = -3043839139187792810L;
 
-        Profile(final AuthModuleTO authModule, final LoadableDetachableModel<List<String>> authModuleConfs) {
+        Profile(
+                final AuthModuleTO authModule,
+                final LoadableDetachableModel<List<String>> authModuleConfs,
+                final Model<Class<? extends AuthModuleConf>> authModuleConfClass) {
+
             boolean isNew = authModule.getConf() == null;
+            if (!isNew) {
+                authModuleConfClass.setObject(authModule.getConf().getClass());
+            }
 
             AjaxTextFieldPanel key = new AjaxTextFieldPanel(
                     Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME,
@@ -125,11 +130,12 @@
                 @Override
                 protected void onEvent(final AjaxRequestTarget target) {
                     try {
-                        Class<? extends AuthModuleConf> authModuleConfClass =
+                        Class<? extends AuthModuleConf> clazz =
                                 (Class<? extends AuthModuleConf>) ClassUtils.resolveClassName(
                                         conf.getModelObject(), ClassUtils.getDefaultClassLoader());
 
-                        authModule.setConf(authModuleConfClass.getConstructor().newInstance());
+                        authModule.setConf(clazz.getConstructor().newInstance());
+                        authModuleConfClass.setObject(clazz);
                     } catch (Exception e) {
                         LOG.error("During deserialization", e);
                     }
@@ -148,12 +154,20 @@
         }
     }
 
-    protected static class GoogleMfaAuthModuleConfLDAP extends WizardStep {
+    protected static class GoogleMfaAuthModuleConfLDAP extends WizardStep implements WizardModel.ICondition {
 
         private static final long serialVersionUID = 5328049907748683944L;
 
-        GoogleMfaAuthModuleConfLDAP(final GoogleMfaAuthModuleConf authModuleConf) {
-            PropertyModel<GoogleMfaAuthModuleConf.LDAP> beanPanelModel = new PropertyModel<>(authModuleConf, "ldap");
+        private final Model<Class<? extends AuthModuleConf>> authModuleConfClass;
+
+        GoogleMfaAuthModuleConfLDAP(
+                final AuthModuleTO authModule,
+                final Model<Class<? extends AuthModuleConf>> authModuleConfClass) {
+
+            this.authModuleConfClass = authModuleConfClass;
+
+            PropertyModel<GoogleMfaAuthModuleConf.LDAP> beanPanelModel =
+                    new PropertyModel<>(authModule.getConf(), "ldap");
 
             AjaxCheckBoxPanel enable = new AjaxCheckBoxPanel("enable", "enableLDAP", new IModel<Boolean>() {
 
@@ -188,6 +202,11 @@
             add(new BeanPanel<>("bean", beanPanelModel).setRenderBodyOnly(true));
             setOutputMarkupId(true);
         }
+
+        @Override
+        public boolean evaluate() {
+            return GoogleMfaAuthModuleConf.class.equals(authModuleConfClass.getObject());
+        }
     }
 
     protected static final class Mapping extends WizardStep {
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
index d34ef77..09fecc6 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.java
@@ -22,7 +22,6 @@
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.PasswordTextField;
 import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
@@ -40,6 +39,7 @@
             final UserWrapper wrapper,
             final Boolean storePasswordInSyncope,
             final boolean templateMode) {
+
         this(id, wrapper, templateMode, storePasswordInSyncope, null);
     }
 
@@ -56,13 +56,10 @@
         Form<?> form = new Form<>("passwordInnerForm");
         add(form);
 
-        FieldPanel<String> confirmPasswordField = new AjaxPasswordFieldPanel(
+        AjaxPasswordFieldPanel confirmPasswordField = new AjaxPasswordFieldPanel(
                 "confirmPassword", "confirmPassword", new Model<>(), false, null);
-
-        confirmPasswordField.setMarkupId("confirmPassword");
-        confirmPasswordField.setPlaceholder("confirmPassword");
         ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(false);
-        form.add(confirmPasswordField);
+        form.add(confirmPasswordField.setPlaceholder("confirmPassword").setMarkupId("confirmPassword"));
 
         if (templateMode) {
             confirmPasswordField.setEnabled(false);
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.properties
new file mode 100644
index 0000000..67b4a84
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel.properties
@@ -0,0 +1,19 @@
+# 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.
+password=Password
+confirmPassword=Password (confirm)
+storePasswordInSyncope=Store password in Syncope
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_fr_CA.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_fr_CA.properties
new file mode 100644
index 0000000..7539a7c
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_fr_CA.properties
@@ -0,0 +1,19 @@
+# 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.
+password=Mot de passe
+confirmPassword=Mot de passe (confirmer)
+storePasswordInSyncope=Stocker le mot de passe dans Syncope
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_it.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_it.properties
new file mode 100644
index 0000000..a3f7197
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_it.properties
@@ -0,0 +1,19 @@
+# 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.
+password=Password
+confirmPassword=Password (conferma)
+storePasswordInSyncope=Salva password in Syncope
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ja.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ja.properties
new file mode 100644
index 0000000..683d6bc
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ja.properties
@@ -0,0 +1,19 @@
+# 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.
+password=\u30d1\u30b9\u30ef\u30fc\u30c9
+confirmPassword=\u30d1\u30b9\u30ef\u30fc\u30c9 (\u78ba\u8a8d)
+storePasswordInSyncope=Syncope \u306b\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u683c\u7d0d
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_pt_BR.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_pt_BR.properties
new file mode 100644
index 0000000..9139342
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_pt_BR.properties
@@ -0,0 +1,19 @@
+# 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.
+password=Senha
+confirmPassword=Senha (confirmar)
+storePasswordInSyncope=Salvar senha in Syncope
diff --git a/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ru.properties b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ru.properties
new file mode 100644
index 0000000..228f2a0
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/wizards/any/PasswordPanel_ru.properties
@@ -0,0 +1,23 @@
+# 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.
+
+# resource=\u00d0\u00a0\u00d0\u00b5\u00d1\u0081\u00d1\u0083\u00d1\u0080\u00d1\u0081
+password=\u041f\u0430\u0440\u043e\u043b\u044c
+# connObjectLink=\u00d0\u00a3\u00d1\u0087\u00d0\u00b5\u00d1\u0082\u00d0\u00bd\u00d0\u00b0\u00d1\u008f \u00d0\u00b7\u00d0\u00b0\u00d0\u00bf\u00d0\u00b8\u00d1\u0081\u00d1\u008c
+confirmPassword=\u041f\u0430\u0440\u043e\u043b\u044c (\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435)
+# status=\u00d0\u00a1\u00d1\u0082\u00d0\u00b0\u00d1\u0082\u00d1\u0083\u00d1\u0081
+storePasswordInSyncope=\u0425\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0432 Syncope
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
index 8ad5594..a6b1963 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
@@ -184,6 +184,7 @@
 
             send(DirectoryPanel.this, Broadcast.EXACT, data);
 
+            displayAttributeModal.size(Modal.Size.Default);
             modal.show(false);
         });
         displayAttributeModal.size(Modal.Size.Default);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
index 2a6dc1d..956c726 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.console.panels;
 
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -108,7 +109,7 @@
             @Override
             public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
                 target.add(displayAttributeModal.setContent(new UserDisplayAttributesModalPanel<>(
-                    displayAttributeModal, page.getPageReference(), pSchemaNames, dSchemaNames)));
+                        displayAttributeModal, page.getPageReference(), pSchemaNames, dSchemaNames)));
 
                 displayAttributeModal.header(new ResourceModel("any.attr.display"));
                 displayAttributeModal.addSubmitButton();
@@ -129,16 +130,16 @@
 
         panel.add(new ActionLink<>() {
 
-                      private static final long serialVersionUID = -7978723352517770644L;
+            private static final long serialVersionUID = -7978723352517770644L;
 
-                      @Override
-                      public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
-                          send(UserDirectoryPanel.this, Broadcast.EXACT,
-                              new AjaxWizard.EditItemActionEvent<>(
-                                  new UserWrapper(new UserRestClient().read(model.getObject().getKey())),
-                                  target));
-                      }
-                  }, ActionType.EDIT,
+            @Override
+            public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
+                send(UserDirectoryPanel.this, Broadcast.EXACT,
+                        new AjaxWizard.EditItemActionEvent<>(
+                                new UserWrapper(new UserRestClient().read(model.getObject().getKey())),
+                                target));
+            }
+        }, ActionType.EDIT,
                 String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_UPDATE)).
                 setRealms(realm, model.getObject().getDynRealms());
 
@@ -151,9 +152,9 @@
                 try {
                     model.setObject(restClient.read(model.getObject().getKey()));
                     restClient.mustChangePassword(
-                        model.getObject().getETagValue(),
-                        !model.getObject().isMustChangePassword(),
-                        model.getObject().getKey());
+                            model.getObject().getETagValue(),
+                            !model.getObject().isMustChangePassword(),
+                            model.getObject().getKey());
 
                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
                     target.add(container);
@@ -175,17 +176,18 @@
                 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
                     model.setObject(restClient.read(model.getObject().getKey()));
                     IModel<AnyWrapper<UserTO>> formModel = new CompoundPropertyModel<>(
-                        new AnyWrapper<>(model.getObject()));
+                            new AnyWrapper<>(model.getObject()));
                     displayAttributeModal.setFormModel(formModel);
 
                     target.add(displayAttributeModal.setContent(new ChangePasswordModal(
-                        displayAttributeModal,
-                        pageRef,
-                        new UserWrapper(model.getObject()))));
+                            displayAttributeModal,
+                            pageRef,
+                            new UserWrapper(model.getObject()))));
 
                     displayAttributeModal.header(new Model<>(
-                        getString("any.edit", new Model<>(new AnyWrapper<>(model.getObject())))));
+                            getString("any.edit", new Model<>(new AnyWrapper<>(model.getObject())))));
 
+                    displayAttributeModal.size(Modal.Size.Large);
                     displayAttributeModal.show(true);
                 }
             }, ActionType.PASSWORD_MANAGEMENT, IdRepoEntitlement.USER_UPDATE).
@@ -201,7 +203,7 @@
                     public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
                         try {
                             SyncopeConsoleSession.get().getAnonymousClient().getService(UserSelfService.class).
-                                requestPasswordReset(model.getObject().getUsername(), null);
+                                    requestPasswordReset(model.getObject().getUsername(), null);
 
                             SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
                             target.add(container);
@@ -230,7 +232,7 @@
                 @Override
                 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
                     target.add(utilityModal.setContent(new AnyPropagationTasks(
-                        utilityModal, AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
+                            utilityModal, AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
 
                     utilityModal.header(new StringResourceModel("any.propagation.tasks", model));
                     utilityModal.show(true);
@@ -244,7 +246,7 @@
                 @Override
                 public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
                     target.add(utilityModal.setContent(
-                        new NotificationTasks(AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
+                            new NotificationTasks(AnyTypeKind.USER, model.getObject().getKey(), pageRef)));
                     utilityModal.header(new StringResourceModel("any.notification.tasks", model));
                     utilityModal.show(true);
                     target.add(utilityModal);
@@ -255,57 +257,57 @@
         if (wizardInModal) {
             panel.add(new ActionLink<>() {
 
-                          private static final long serialVersionUID = -1978723352517770644L;
+                private static final long serialVersionUID = -1978723352517770644L;
 
-                          @Override
-                          public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
-                              model.setObject(restClient.read(model.getObject().getKey()));
-                              target.add(altDefaultModal.setContent(new AuditHistoryModal<>(
-                                  altDefaultModal,
-                                  AuditElements.EventCategoryType.LOGIC,
-                                  "UserLogic",
-                                  model.getObject(),
-                                  IdRepoEntitlement.USER_UPDATE,
-                                  pageRef) {
+                @Override
+                public void onClick(final AjaxRequestTarget target, final UserTO ignore) {
+                    model.setObject(restClient.read(model.getObject().getKey()));
+                    target.add(altDefaultModal.setContent(new AuditHistoryModal<>(
+                            altDefaultModal,
+                            AuditElements.EventCategoryType.LOGIC,
+                            "UserLogic",
+                            model.getObject(),
+                            IdRepoEntitlement.USER_UPDATE,
+                            pageRef) {
 
-                                  private static final long serialVersionUID = 959378158400669867L;
+                        private static final long serialVersionUID = 959378158400669867L;
 
-                                  @Override
-                                  protected void restore(final String json, final AjaxRequestTarget target) {
-                                      // The original audit record masks the password and the security
-                                      // answer; so we cannot use the audit record to resurrect the entry
-                                      // based on mask data.
-                                      //
-                                      // The method behavior below will reset the audit record such
-                                      // that the current security answer and the password for the object
-                                      // are always maintained, and such properties for the
-                                      // user cannot be restored using audit records.
-                                      UserTO original = model.getObject();
-                                      try {
-                                          UserTO updated = MAPPER.readValue(json, UserTO.class);
-                                          UserUR updateReq = AnyOperations.diff(updated, original, false);
-                                          updateReq.setPassword(null);
-                                          updateReq.setSecurityAnswer(null);
-                                          ProvisioningResult<UserTO> result =
-                                              restClient.update(original.getETagValue(), updateReq);
-                                          model.getObject().setLastChangeDate(result.getEntity().getLastChangeDate());
+                        @Override
+                        protected void restore(final String json, final AjaxRequestTarget target) {
+                            // The original audit record masks the password and the security
+                            // answer; so we cannot use the audit record to resurrect the entry
+                            // based on mask data.
+                            //
+                            // The method behavior below will reset the audit record such
+                            // that the current security answer and the password for the object
+                            // are always maintained, and such properties for the
+                            // user cannot be restored using audit records.
+                            UserTO original = model.getObject();
+                            try {
+                                UserTO updated = MAPPER.readValue(json, UserTO.class);
+                                UserUR updateReq = AnyOperations.diff(updated, original, false);
+                                updateReq.setPassword(null);
+                                updateReq.setSecurityAnswer(null);
+                                ProvisioningResult<UserTO> result =
+                                        restClient.update(original.getETagValue(), updateReq);
+                                model.getObject().setLastChangeDate(result.getEntity().getLastChangeDate());
 
-                                          SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
-                                          target.add(container);
-                                      } catch (Exception e) {
-                                          LOG.error("While restoring user {}", model.getObject().getKey(), e);
-                                          SyncopeConsoleSession.get().onException(e);
-                                      }
-                                      ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
-                                  }
-                              }));
+                                SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                                target.add(container);
+                            } catch (Exception e) {
+                                LOG.error("While restoring user {}", model.getObject().getKey(), e);
+                                SyncopeConsoleSession.get().onException(e);
+                            }
+                            ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        }
+                    }));
 
-                              altDefaultModal.header(new Model<>(
-                                  getString("auditHistory.title", new Model<>(new AnyWrapper<>(model.getObject())))));
+                    altDefaultModal.header(new Model<>(
+                            getString("auditHistory.title", new Model<>(new AnyWrapper<>(model.getObject())))));
 
-                              altDefaultModal.show(true);
-                          }
-                      }, ActionType.VIEW_AUDIT_HISTORY,
+                    altDefaultModal.show(true);
+                }
+            }, ActionType.VIEW_AUDIT_HISTORY,
                     String.format("%s,%s", IdRepoEntitlement.USER_READ, IdRepoEntitlement.AUDIT_LIST)).
                     setRealms(realm, model.getObject().getDynRealms());
         }
@@ -320,7 +322,7 @@
                 clone.setKey(null);
                 clone.setUsername(model.getObject().getUsername() + "_clone");
                 send(UserDirectoryPanel.this, Broadcast.EXACT,
-                    new AjaxWizard.NewItemActionEvent<>(new UserWrapper(clone), target));
+                        new AjaxWizard.NewItemActionEvent<>(new UserWrapper(clone), target));
             }
 
             @Override
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
index 0e865ec..cfa53fd 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/status/ChangePasswordModal.java
@@ -58,7 +58,7 @@
 
         this.wrapper = wrapper;
 
-        final PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false, false);
+        PasswordPanel passwordPanel = new PasswordPanel("passwordPanel", wrapper, false, false);
         passwordPanel.setOutputMarkupId(true);
         add(passwordPanel);
 
@@ -70,13 +70,13 @@
 
     @Override
     public void onSubmit(final AjaxRequestTarget target) {
-        final UserTO inner = wrapper.getInnerObject();
+        UserTO inner = wrapper.getInnerObject();
 
         try {
             if (StringUtils.isBlank(inner.getPassword()) || statusModel.getObject().isEmpty()) {
                 SyncopeConsoleSession.get().error(getString(Constants.OPERATION_ERROR));
             } else {
-                final List<String> resources = new ArrayList<>();
+                List<String> resources = new ArrayList<>();
                 boolean isOnSyncope = false;
                 for (StatusBean sb : statusModel.getObject()) {
                     if (sb.getResource().equals(Constants.SYNCOPE)) {
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
index e3f3524..fc2cbec 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
@@ -143,7 +143,7 @@
 execute.alt=execute icon
 
 password_management.class=fas fa-shield-alt
-password_management.title=gestione password
+password_management.title=gestisci password
 password_management.alt=manage password icon
 
 request_password_reset.class=fas fa-user-secret
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
index 146cfcd..d85ce3a 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-public class DuoMfaAuthModuleConf implements AuthModuleConf {
+public class DuoMfaAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -2883257599439312426L;
 
@@ -30,6 +30,11 @@
 
     private String apiHost;
 
+    @Override
+    public String getFriendlyName() {
+        return "Duo Security";
+    }
+
     public String getIntegrationKey() {
         return integrationKey;
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
index 61fb552..c5c1d99 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
@@ -20,7 +20,7 @@
 
 import java.io.Serializable;
 
-public class GoogleMfaAuthModuleConf implements AuthModuleConf {
+public class GoogleMfaAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -7883257599139312426L;
 
@@ -154,6 +154,11 @@
 
     private LDAP ldap;
 
+    @Override
+    public String getFriendlyName() {
+        return "Google Authenticator";
+    }
+
     public String getIssuer() {
         return issuer;
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/MFAAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/MFAAuthModuleConf.java
new file mode 100644
index 0000000..1932063
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/MFAAuthModuleConf.java
@@ -0,0 +1,27 @@
+/*
+ * 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.JsonIgnore;
+
+public interface MFAAuthModuleConf extends AuthModuleConf {
+
+    @JsonIgnore
+    String getFriendlyName();
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
index 951b670..7ac948b 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
@@ -18,11 +18,12 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-public class SimpleMfaAuthModuleConf implements AuthModuleConf {
+public class SimpleMfaAuthModuleConf implements MFAAuthModuleConf {
+
     private static final long serialVersionUID = -7663257599139312426L;
 
     private long timeToKillInSeconds = 30L;
-    
+
     private int tokenLength = 6;
 
     private String bypassGroovyScript;
@@ -35,6 +36,11 @@
 
     private String emailText;
 
+    @Override
+    public String getFriendlyName() {
+        return "CAS Simple Multifactor Authentication";
+    }
+
     public String getEmailFrom() {
         return emailFrom;
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
index b355ac3..dc15038 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.common.lib.auth;
 
-public class U2FAuthModuleConf implements AuthModuleConf {
+public class U2FAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -1235771400318503131L;
 
@@ -30,6 +30,11 @@
 
     private String expireDevicesTimeUnit = "DAYS";
 
+    @Override
+    public String getFriendlyName() {
+        return "FIDO U2F";
+    }
+
     public long getExpireRegistrations() {
         return expireRegistrations;
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthToken.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthToken.java
index 055f41b..470103a 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthToken.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthToken.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.common.lib.wa;
 
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -32,7 +32,7 @@
 
         private final GoogleMfaAuthToken instance = new GoogleMfaAuthToken();
 
-        public GoogleMfaAuthToken.Builder issueDate(final OffsetDateTime issueDate) {
+        public GoogleMfaAuthToken.Builder issueDate(final LocalDateTime issueDate) {
             instance.setIssueDate(issueDate);
             return this;
         }
@@ -49,7 +49,7 @@
 
     private int otp;
 
-    private OffsetDateTime issueDate;
+    private LocalDateTime issueDate;
 
     public int getOtp() {
         return otp;
@@ -59,11 +59,11 @@
         this.otp = otp;
     }
 
-    public OffsetDateTime getIssueDate() {
+    public LocalDateTime getIssueDate() {
         return issueDate;
     }
 
-    public void setIssueDate(final OffsetDateTime issueDate) {
+    public void setIssueDate(final LocalDateTime issueDate) {
         this.issueDate = issueDate;
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
index f66dbca..ab30e9d 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
@@ -18,12 +18,17 @@
  */
 package org.apache.syncope.common.lib.wa;
 
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.apache.syncope.common.lib.BaseBean;
 import org.apache.syncope.common.lib.policy.AccessPolicyTO;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.ClientAppTO;
 
 public class WAClientApp implements BaseBean {
@@ -34,6 +39,8 @@
 
     private AccessPolicyTO accessPolicy;
 
+    private final List<AuthModuleTO> authModules = new ArrayList<>();
+
     private AuthPolicyTO authPolicy;
 
     private AttrReleasePolicyTO attrReleasePolicy;
@@ -56,6 +63,12 @@
         this.accessPolicy = accessPolicy;
     }
 
+    @JacksonXmlElementWrapper(localName = "authModules")
+    @JacksonXmlProperty(localName = "authModule")
+    public List<AuthModuleTO> getAuthModules() {
+        return authModules;
+    }
+
     public AuthPolicyTO getAuthPolicy() {
         return authPolicy;
     }
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/GoogleMfaAuthTokenService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/GoogleMfaAuthTokenService.java
index 40a1d9e..053f9e2 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/GoogleMfaAuthTokenService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/GoogleMfaAuthTokenService.java
@@ -21,7 +21,7 @@
 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
 import io.swagger.v3.oas.annotations.security.SecurityRequirements;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -48,7 +48,7 @@
     @Path("tokens")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void delete(@QueryParam("expirationDate") OffsetDateTime expirationDate);
+    void delete(@QueryParam("expirationDate") LocalDateTime expirationDate);
 
     @DELETE
     @Path("tokens/{owner}/{otp}")
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/DateParamConverterProvider.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/DateParamConverterProvider.java
index 0d2c943..55d966d 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/DateParamConverterProvider.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/DateParamConverterProvider.java
@@ -20,6 +20,7 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
@@ -28,7 +29,7 @@
 
 public class DateParamConverterProvider implements ParamConverterProvider {
 
-    private static class DateParamConverter implements ParamConverter<OffsetDateTime> {
+    protected static class OffsetDateTimeParamConverter implements ParamConverter<OffsetDateTime> {
 
         @Override
         public OffsetDateTime fromString(final String value) {
@@ -45,13 +46,33 @@
         }
     }
 
+    protected static class LocalDateTimeParamConverter implements ParamConverter<LocalDateTime> {
+
+        @Override
+        public LocalDateTime fromString(final String value) {
+            try {
+                return LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+            } catch (DateTimeParseException e) {
+                throw new IllegalArgumentException("Unparsable date: " + value, e);
+            }
+        }
+
+        @Override
+        public String toString(final LocalDateTime value) {
+            return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value);
+        }
+    }
+
     @Override
     @SuppressWarnings("unchecked")
     public <T> ParamConverter<T> getConverter(
             final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
 
         if (OffsetDateTime.class.equals(rawType)) {
-            return (ParamConverter<T>) new DateParamConverter();
+            return (ParamConverter<T>) new OffsetDateTimeParamConverter();
+        }
+        if (LocalDateTime.class.equals(rawType)) {
+            return (ParamConverter<T>) new LocalDateTimeParamConverter();
         }
 
         return null;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
index efcca61..cecaeaf 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
@@ -74,11 +74,12 @@
                 stream().
                 allMatch(acct -> acct.getId() == id)).
                 findFirst().
-                ifPresentOrElse(profile -> {
-                    if (profile.getGoogleMfaAuthAccounts().removeIf(acct -> acct.getId() == id)) {
-                        authProfileDAO.save(profile);
-                    }
-                },
+                ifPresentOrElse(
+                        profile -> {
+                            if (profile.getGoogleMfaAuthAccounts().removeIf(acct -> acct.getId() == id)) {
+                                authProfileDAO.save(profile);
+                            }
+                        },
                         () -> {
                             throw new NotFoundException("Could not find account for id " + id);
                         });
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
index 31a9c74..1270381 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.core.logic.wa;
 
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -46,16 +46,24 @@
         this.entityFactory = entityFactory;
     }
 
+    protected void removeTokenAndSave(final AuthProfile profile, final Predicate<GoogleMfaAuthToken> criteria) {
+        List<GoogleMfaAuthToken> tokens = profile.getGoogleMfaAuthTokens();
+        if (tokens.removeIf(criteria)) {
+            profile.setGoogleMfaAuthTokens(tokens);
+            authProfileDAO.save(profile);
+        }
+    }
+
     @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
-    public void delete(final OffsetDateTime expirationDate) {
+    public void delete(final LocalDateTime expirationDate) {
         authProfileDAO.findAll(-1, -1).forEach(profile -> removeTokenAndSave(
                 profile, token -> token.getIssueDate().compareTo(expirationDate) >= 0));
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner, final int otp) {
-        authProfileDAO.findByOwner(owner).ifPresent(profile -> removeTokenAndSave(
-                profile, token -> token.getOtp() == otp));
+        authProfileDAO.findByOwner(owner).
+                ifPresent(profile -> removeTokenAndSave(profile, token -> token.getOtp() == otp));
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
@@ -89,6 +97,7 @@
         });
 
         List<GoogleMfaAuthToken> tokens = profile.getGoogleMfaAuthTokens();
+        tokens.removeIf(t -> t.getOtp() == token.getOtp());
         tokens.add(token);
         profile.setGoogleMfaAuthTokens(tokens);
         authProfileDAO.save(profile);
@@ -103,7 +112,7 @@
                 flatMap(List::stream).
                 filter(token -> token.getOtp() == otp).
                 findFirst().
-                orElseThrow(() -> new NotFoundException("Could not find token for Owner " + owner + " and otp " + otp));
+                orElseThrow(() -> new NotFoundException("Could not find token for owner " + owner + " and otp " + otp));
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
@@ -122,12 +131,4 @@
                 map(AuthProfile::getGoogleMfaAuthTokens).
                 orElse(List.of());
     }
-
-    private void removeTokenAndSave(final AuthProfile profile, final Predicate<GoogleMfaAuthToken> criteria) {
-        List<GoogleMfaAuthToken> tokens = profile.getGoogleMfaAuthTokens();
-        if (tokens.removeIf(criteria)) {
-            profile.setGoogleMfaAuthTokens(tokens);
-            authProfileDAO.save(profile);
-        }
-    }
 }
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceImpl.java
index 6c9a337..484bb2a 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceImpl.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.core.rest.cxf.service.wa;
 
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import java.util.List;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
@@ -37,7 +37,7 @@
     }
 
     @Override
-    public void delete(final OffsetDateTime expirationDate) {
+    public void delete(final LocalDateTime expirationDate) {
         if (expirationDate == null) {
             logic.deleteAll();
         } else {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
index 204f954..1d61fde 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
@@ -391,8 +391,8 @@
 
     @ConditionalOnMissingBean
     @Bean
-    public AuthModuleDAO authModuleDAO() {
-        return new JPAAuthModuleDAO();
+    public AuthModuleDAO authModuleDAO(final PolicyDAO policyDAO) {
+        return new JPAAuthModuleDAO(policyDAO);
     }
 
     @ConditionalOnMissingBean
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 3254097..565ea5d 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
@@ -20,14 +20,23 @@
 
 import java.util.List;
 import javax.persistence.TypedQuery;
+import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
 import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModule;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPAAuthModuleDAO extends AbstractDAO<AuthModule> implements AuthModuleDAO {
 
+    protected final PolicyDAO policyDAO;
+
+    public JPAAuthModuleDAO(final PolicyDAO policyDAO) {
+        this.policyDAO = policyDAO;
+    }
+
     @Transactional(readOnly = true)
     @Override
     public AuthModule find(final String key) {
@@ -59,6 +68,16 @@
 
     @Override
     public void delete(final AuthModule authModule) {
+        policyDAO.find(AuthPolicy.class).stream().
+                filter(policy -> policy.getConf() instanceof DefaultAuthPolicyConf).
+                forEach(policy -> {
+                    DefaultAuthPolicyConf conf = (DefaultAuthPolicyConf) policy.getConf();
+                    if (conf.getAuthModules().remove(authModule.getKey())) {
+                        policy.setConf(conf);
+                        policyDAO.save(policy);
+                    }
+                });
+
         entityManager().remove(authModule);
     }
 }
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
index 8dd8319..ec39613 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
@@ -22,6 +22,7 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.util.List;
 import java.util.Optional;
@@ -184,7 +185,7 @@
     private AuthProfile createAuthProfileWithToken(final String owner, final Integer otp) {
         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
         profile.setOwner(owner);
-        GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(OffsetDateTime.now()).token(otp).build();
+        GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(otp).build();
         profile.setGoogleMfaAuthTokens(List.of(token));
         return authProfileDAO.save(profile);
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index 766ddb7..cb1f728 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -1258,8 +1258,10 @@
     public WAClientAppDataBinder waClientAppDataBinder(
             final ClientAppDataBinder clientAppDataBinder,
             final PolicyDataBinder policyDataBinder,
+            final AuthModuleDataBinder authModuleDataBinder,
             final AuthModuleDAO authModuleDAO) {
 
-        return new WAClientAppDataBinderImpl(clientAppDataBinder, policyDataBinder, authModuleDAO);
+        return new WAClientAppDataBinderImpl(
+                clientAppDataBinder, policyDataBinder, authModuleDataBinder, authModuleDAO);
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
index 1010367..2e25d26 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
@@ -24,6 +24,7 @@
 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.api.entity.auth.ClientApp;
+import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
 import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
 import org.slf4j.Logger;
@@ -38,15 +39,19 @@
 
     protected final PolicyDataBinder policyDataBinder;
 
+    protected final AuthModuleDataBinder authModuleDataBinder;
+
     protected final AuthModuleDAO authModuleDAO;
 
     public WAClientAppDataBinderImpl(
             final ClientAppDataBinder clientAppDataBinder,
             final PolicyDataBinder policyDataBinder,
+            final AuthModuleDataBinder authModuleDataBinder,
             final AuthModuleDAO authModuleDAO) {
 
         this.clientAppDataBinder = clientAppDataBinder;
         this.policyDataBinder = policyDataBinder;
+        this.authModuleDataBinder = authModuleDataBinder;
         this.authModuleDAO = authModuleDAO;
     }
 
@@ -70,6 +75,8 @@
                     if (authModule == null) {
                         LOG.warn("AuthModule " + authModule + " not found");
                     } else {
+                        waClientApp.getAuthModules().add(authModuleDataBinder.getAuthModuleTO(authModule));
+
                         authModule.getItems().
                                 forEach(item -> waClientApp.getReleaseAttrs().put(
                                 item.getIntAttrName(), item.getExtAttrName()));
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
index 87995c6..7d4776c 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/GoogleMfaAuthTokenITCase.java
@@ -25,7 +25,7 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.security.SecureRandom;
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import java.util.UUID;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
@@ -41,12 +41,12 @@
 
     private static GoogleMfaAuthToken createGoogleMfaAuthToken() {
         int token = SECURE_RANDOM.ints(100_000, 999_999).findFirst().getAsInt();
-        return new GoogleMfaAuthToken.Builder().token(token).issueDate(OffsetDateTime.now()).build();
+        return new GoogleMfaAuthToken.Builder().token(token).issueDate(LocalDateTime.now()).build();
     }
 
     @BeforeEach
     public void setup() {
-        googleMfaAuthTokenService.delete((OffsetDateTime) null);
+        googleMfaAuthTokenService.delete((LocalDateTime) null);
     }
 
     @Test
@@ -109,7 +109,7 @@
     public void deleteByDate() {
         String owner = UUID.randomUUID().toString();
         createGoogleMfaAuthToken();
-        googleMfaAuthTokenService.delete(OffsetDateTime.now().minusDays(1));
+        googleMfaAuthTokenService.delete(LocalDateTime.now().minusDays(1));
         assertTrue(googleMfaAuthTokenService.read(owner).getResult().isEmpty());
         assertEquals(0, googleMfaAuthTokenService.read(owner).getTotalCount());
     }
diff --git a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
index 1938635..f1df615 100644
--- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
@@ -47,10 +47,10 @@
 endif::[]
 interface and extending appropriately the
 ifeval::["{snapshotOrRelease}" == "release"]
-https://github.com/apache/syncope/blob/syncope-{docVersion}/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java[SyncopeWAPropertySourceLocator^]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java[WAPropertySourceLocator^]
 endif::[]
 ifeval::["{snapshotOrRelease}" == "snapshot"]
-https://github.com/apache/syncope/blob/master/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java[SyncopeWAPropertySourceLocator^]
+https://github.com/apache/syncope/blob/master/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java[WAPropertySourceLocator^]
 endif::[]
 class.
 ====
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
similarity index 94%
rename from wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
rename to wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
index 45a3b72..6b36823 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
@@ -27,7 +27,7 @@
 @Configuration(proxyBeanMethods = false)
 @PropertySource("classpath:wa.properties")
 @PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
-public class SyncopeWABootstrapConfiguration {
+public class WABootstrapConfiguration {
 
     @Configuration(proxyBeanMethods = false)
     public static class WAClientConfiguration {
@@ -50,7 +50,7 @@
     public static class PropertySourceConfiguration {
         @Bean
         public PropertySourceLocator configPropertySourceLocator(final WARestClient waRestClient) {
-            return new SyncopeWAPropertySourceLocator(waRestClient);
+            return new WAPropertySourceLocator(waRestClient);
         }
     }
 }
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
similarity index 92%
rename from wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
rename to wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
index c76e29f..bf6b799 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
@@ -72,13 +72,13 @@
 import org.springframework.core.env.PropertySource;
 
 @Order
-public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
+public class WAPropertySourceLocator implements PropertySourceLocator {
 
-    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWAPropertySourceLocator.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(WAPropertySourceLocator.class);
 
     protected final WARestClient waRestClient;
 
-    public SyncopeWAPropertySourceLocator(final WARestClient waRestClient) {
+    public WAPropertySourceLocator(final WARestClient waRestClient) {
         this.waRestClient = waRestClient;
     }
 
@@ -104,13 +104,13 @@
             final SyncopeAuthModuleConf conf,
             final String address) {
 
-        SyncopeAuthenticationProperties syncopeProps = new SyncopeAuthenticationProperties();
-        syncopeProps.setName(authModule);
-        syncopeProps.setDomain(conf.getDomain());
-        syncopeProps.setUrl(StringUtils.substringBefore(address, "/rest"));
+        SyncopeAuthenticationProperties props = new SyncopeAuthenticationProperties();
+        props.setName(authModule);
+        props.setDomain(conf.getDomain());
+        props.setUrl(StringUtils.substringBefore(address, "/rest"));
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().setSyncope(syncopeProps);
+        casProperties.getAuthn().setSyncope(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
@@ -125,15 +125,15 @@
             final String authModule,
             final StaticAuthModuleConf conf) {
 
-        AcceptAuthenticationProperties staticProps = new AcceptAuthenticationProperties();
-        staticProps.setName(authModule);
+        AcceptAuthenticationProperties props = new AcceptAuthenticationProperties();
+        props.setName(authModule);
         String users = conf.getUsers().entrySet().stream().
                 map(entry -> entry.getKey() + "::" + entry.getValue()).
                 collect(Collectors.joining(","));
-        staticProps.setUsers(users);
+        props.setUsers(users);
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().setAccept(staticProps);
+        casProperties.getAuthn().setAccept(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
@@ -148,22 +148,22 @@
             final String authModule,
             final LDAPAuthModuleConf conf) {
 
-        LdapAuthenticationProperties ldapProps = new LdapAuthenticationProperties();
-        ldapProps.setName(authModule);
-        ldapProps.setLdapUrl(conf.getLdapUrl());
-        ldapProps.setBaseDn(conf.getBaseDn());
-        ldapProps.setSearchFilter(conf.getSearchFilter());
-        ldapProps.setBindDn(conf.getBindDn());
-        ldapProps.setBindCredential(conf.getBindCredential());
+        LdapAuthenticationProperties props = new LdapAuthenticationProperties();
+        props.setName(authModule);
+        props.setLdapUrl(conf.getLdapUrl());
+        props.setBaseDn(conf.getBaseDn());
+        props.setSearchFilter(conf.getSearchFilter());
+        props.setBindDn(conf.getBindDn());
+        props.setBindCredential(conf.getBindCredential());
         if (StringUtils.isNotBlank(conf.getBindDn()) && StringUtils.isNotBlank(conf.getBindCredential())) {
-            ldapProps.setType(AbstractLdapAuthenticationProperties.AuthenticationTypes.AUTHENTICATED);
+            props.setType(AbstractLdapAuthenticationProperties.AuthenticationTypes.AUTHENTICATED);
         }
-        ldapProps.setPrincipalAttributeId(conf.getUserIdAttribute());
-        ldapProps.setSubtreeSearch(conf.isSubtreeSearch());
-        ldapProps.setPrincipalAttributeList(conf.getPrincipalAttributeList());
+        props.setPrincipalAttributeId(conf.getUserIdAttribute());
+        props.setSubtreeSearch(conf.isSubtreeSearch());
+        props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getLdap().add(ldapProps);
+        casProperties.getAuthn().getLdap().add(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(
@@ -250,13 +250,13 @@
             final String authModule,
             final GoogleMfaAuthModuleConf conf) {
 
-        GoogleAuthenticatorMultifactorProperties gauthProps = new GoogleAuthenticatorMultifactorProperties();
-        gauthProps.setName(authModule);
-        gauthProps.getCore().setIssuer(conf.getIssuer());
-        gauthProps.getCore().setCodeDigits(conf.getCodeDigits());
-        gauthProps.getCore().setLabel(conf.getLabel());
-        gauthProps.getCore().setTimeStepSize(conf.getTimeStepSize());
-        gauthProps.getCore().setWindowSize(conf.getWindowSize());
+        GoogleAuthenticatorMultifactorProperties props = new GoogleAuthenticatorMultifactorProperties();
+        props.setName(authModule);
+        props.getCore().setIssuer(conf.getIssuer());
+        props.getCore().setCodeDigits(conf.getCodeDigits());
+        props.getCore().setLabel(conf.getLabel());
+        props.getCore().setTimeStepSize(conf.getTimeStepSize());
+        props.getCore().setWindowSize(conf.getWindowSize());
 
         if (conf.getLdap() != null) {
             LdapGoogleAuthenticatorMultifactorProperties ldapProps = new LdapGoogleAuthenticatorMultifactorProperties();
@@ -266,11 +266,11 @@
             ldapProps.setBindDn(conf.getLdap().getBindDn());
             ldapProps.setSearchFilter(conf.getLdap().getSearchFilter());
             ldapProps.setLdapUrl(conf.getLdap().getUrl());
-            gauthProps.setLdap(ldapProps);
+            props.setLdap(ldapProps);
         }
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getMfa().setGauth(gauthProps);
+        casProperties.getAuthn().getMfa().setGauth(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(
diff --git a/wa/bootstrap/src/main/resources/META-INF/spring.factories b/wa/bootstrap/src/main/resources/META-INF/spring.factories
index 0729129..f170163 100644
--- a/wa/bootstrap/src/main/resources/META-INF/spring.factories
+++ b/wa/bootstrap/src/main/resources/META-INF/spring.factories
@@ -16,4 +16,4 @@
 # under the License.
 
 org.springframework.cloud.bootstrap.BootstrapConfiguration=\
- org.apache.syncope.wa.bootstrap.SyncopeWABootstrapConfiguration
+ org.apache.syncope.wa.bootstrap.WABootstrapConfiguration
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
index 7fbf212..16c1bac 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
@@ -23,7 +23,7 @@
 import java.time.ZoneId;
 import java.util.Date;
 import java.util.Map;
-import org.apache.syncope.wa.starter.config.SyncopeWARefreshContextJob;
+import org.apache.syncope.wa.starter.config.WARefreshContextJob;
 
 import org.apereo.cas.config.GoogleAuthenticatorLdapConfiguration;
 import org.apereo.cas.configuration.CasConfigurationProperties;
@@ -128,7 +128,7 @@
             Trigger trigger = TriggerBuilder.newTrigger().startAt(date).build();
             JobKey jobKey = new JobKey(getClass().getSimpleName());
 
-            JobDetail job = JobBuilder.newJob(SyncopeWARefreshContextJob.class).
+            JobDetail job = JobBuilder.newJob(WARefreshContextJob.class).
                     withIdentity(jobKey).
                     build();
             LOG.info("Scheduled job to refresh application context @ [{}]", date);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
similarity index 92%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
index 2f42a5a..c0da902 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
@@ -23,7 +23,6 @@
 import java.util.Map;
 import java.util.Set;
 import org.apereo.cas.audit.spi.AbstractAuditTrailManager;
-import com.fasterxml.jackson.databind.json.JsonMapper;
 import java.time.OffsetDateTime;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
@@ -34,13 +33,11 @@
 import org.apache.syncope.wa.bootstrap.WARestClient;
 import org.apereo.inspektr.audit.AuditActionContext;
 
-public class SyncopeWAAuditTrailManager extends AbstractAuditTrailManager {
-
-    private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
+public class WAAuditTrailManager extends AbstractAuditTrailManager {
 
     private final WARestClient waRestClient;
 
-    public SyncopeWAAuditTrailManager(final WARestClient restClient) {
+    public WAAuditTrailManager(final WARestClient restClient) {
         super(true);
         this.waRestClient = restClient;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
similarity index 88%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
index 2d8e595..ceba2db 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WAContext.java
@@ -40,10 +40,10 @@
 import org.apache.syncope.wa.bootstrap.WARestClient;
 import org.apache.syncope.wa.starter.actuate.SyncopeCoreHealthIndicator;
 import org.apache.syncope.wa.starter.actuate.SyncopeWAInfoContributor;
-import org.apache.syncope.wa.starter.audit.SyncopeWAAuditTrailManager;
-import org.apache.syncope.wa.starter.events.SyncopeWAEventRepository;
-import org.apache.syncope.wa.starter.gauth.SyncopeWAGoogleMfaAuthCredentialRepository;
-import org.apache.syncope.wa.starter.gauth.SyncopeWAGoogleMfaAuthTokenRepository;
+import org.apache.syncope.wa.starter.audit.WAAuditTrailManager;
+import org.apache.syncope.wa.starter.events.WAEventRepository;
+import org.apache.syncope.wa.starter.gauth.WAGoogleMfaAuthCredentialRepository;
+import org.apache.syncope.wa.starter.gauth.WAGoogleMfaAuthTokenRepository;
 import org.apache.syncope.wa.starter.mapping.AccessMapFor;
 import org.apache.syncope.wa.starter.mapping.AccessMapper;
 import org.apache.syncope.wa.starter.mapping.AttrReleaseMapFor;
@@ -59,16 +59,17 @@
 import org.apache.syncope.wa.starter.mapping.OIDCRPClientAppTOMapper;
 import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
 import org.apache.syncope.wa.starter.mapping.SAML2SPClientAppTOMapper;
-import org.apache.syncope.wa.starter.oidc.SyncopeWAOIDCJWKSGeneratorService;
-import org.apache.syncope.wa.starter.pac4j.saml.SyncopeWASAML2ClientCustomizer;
+import org.apache.syncope.wa.starter.oidc.WAOIDCJWKSGeneratorService;
+import org.apache.syncope.wa.starter.pac4j.saml.WASAML2ClientCustomizer;
 import org.apache.syncope.wa.starter.saml.idp.metadata.RestfulSamlIdPMetadataGenerator;
 import org.apache.syncope.wa.starter.saml.idp.metadata.RestfulSamlIdPMetadataLocator;
-import org.apache.syncope.wa.starter.services.SyncopeWAServiceRegistry;
-import org.apache.syncope.wa.starter.surrogate.SyncopeWASurrogateAuthenticationService;
-import org.apache.syncope.wa.starter.u2f.SyncopeWAU2FDeviceRepository;
-import org.apache.syncope.wa.starter.webauthn.SyncopeWAWebAuthnCredentialRepository;
+import org.apache.syncope.wa.starter.services.WAServiceRegistry;
+import org.apache.syncope.wa.starter.surrogate.WASurrogateAuthenticationService;
+import org.apache.syncope.wa.starter.u2f.WAU2FDeviceRepository;
+import org.apache.syncope.wa.starter.webauthn.WAWebAuthnCredentialRepository;
 import org.apereo.cas.adaptors.u2f.storage.U2FDeviceRepository;
 import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
+import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
 import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.configuration.model.support.mfa.gauth.LdapGoogleAuthenticatorMultifactorProperties;
@@ -101,7 +102,7 @@
 import org.springframework.context.annotation.ScopedProxyMode;
 
 @Configuration(proxyBeanMethods = false)
-public class SyncopeWAConfiguration {
+public class WAContext {
 
     private static String version(final ConfigurableApplicationContext ctx) {
         return ctx.getEnvironment().getProperty("version");
@@ -162,7 +163,10 @@
 
     @ConditionalOnMissingBean
     @Bean
-    public RegisteredServiceMapper registeredServiceMapper(final ConfigurableApplicationContext ctx) {
+    public RegisteredServiceMapper registeredServiceMapper(
+            final ConfigurableApplicationContext ctx,
+            final ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan) {
+
         Map<String, AuthMapper> authPolicyConfMappers = new HashMap<>();
         ctx.getBeansOfType(AuthMapper.class).forEach((name, bean) -> {
             AuthMapFor authMapFor = ctx.findAnnotationOnBean(name, AuthMapFor.class);
@@ -197,6 +201,8 @@
         });
 
         return new RegisteredServiceMapper(
+                ctx,
+                authenticationEventExecutionPlan,
                 authPolicyConfMappers,
                 accessPolicyConfMappers,
                 attrReleasePolicyConfMappers,
@@ -211,7 +217,7 @@
             @Qualifier("serviceRegistryListeners")
             final ObjectProvider<List<ServiceRegistryListener>> serviceRegistryListeners) {
 
-        SyncopeWAServiceRegistry registry = new SyncopeWAServiceRegistry(
+        WAServiceRegistry registry = new WAServiceRegistry(
                 restClient, registeredServiceMapper, ctx,
                 Optional.ofNullable(serviceRegistryListeners.getIfAvailable()).orElseGet(ArrayList::new));
         return plan -> plan.registerServiceRegistry(registry);
@@ -235,7 +241,7 @@
 
     @Bean
     public AuditTrailExecutionPlanConfigurer auditConfigurer(final WARestClient restClient) {
-        return plan -> plan.registerAuditTrailManager(new SyncopeWAAuditTrailManager(restClient));
+        return plan -> plan.registerAuditTrailManager(new WAAuditTrailManager(restClient));
     }
 
     @ConditionalOnMissingBean(name = "syncopeWaEventRepositoryFilter")
@@ -250,12 +256,12 @@
             @Qualifier("syncopeWAEventRepositoryFilter")
             final CasEventRepositoryFilter syncopeWAEventRepositoryFilter) {
 
-        return new SyncopeWAEventRepository(syncopeWAEventRepositoryFilter, restClient);
+        return new WAEventRepository(syncopeWAEventRepositoryFilter, restClient);
     }
 
     @Bean
     public DelegatedClientFactoryCustomizer<Client> delegatedClientCustomizer(final WARestClient restClient) {
-        return new SyncopeWASAML2ClientCustomizer(restClient);
+        return new WASAML2ClientCustomizer(restClient);
     }
 
     @Bean
@@ -263,7 +269,7 @@
             final CasConfigurationProperties casProperties,
             final WARestClient restClient) {
 
-        return new SyncopeWAGoogleMfaAuthTokenRepository(
+        return new WAGoogleMfaAuthTokenRepository(
                 restClient, casProperties.getAuthn().getMfa().getGauth().getCore().getTimeStepSize());
     }
 
@@ -292,7 +298,7 @@
             return new LdapGoogleAuthenticatorTokenCredentialRepository(
                     cipherExecutor, googleAuthenticatorInstance, connectionFactory, ldap);
         }
-        return new SyncopeWAGoogleMfaAuthCredentialRepository(restClient, googleAuthenticatorInstance);
+        return new WAGoogleMfaAuthCredentialRepository(restClient, googleAuthenticatorInstance);
     }
 
     @Bean
@@ -304,7 +310,7 @@
                 getProperty("cas.authn.oidc.jwks.size", int.class, 2048);
         JWSAlgorithm algorithm = ctx.getEnvironment().
                 getProperty("cas.authn.oidc.jwks.algorithm", JWSAlgorithm.class, JWSAlgorithm.RS256);
-        return new SyncopeWAOIDCJWKSGeneratorService(restClient, size, algorithm);
+        return new WAOIDCJWKSGeneratorService(restClient, size, algorithm);
     }
 
     @Bean
@@ -312,7 +318,7 @@
             final CasConfigurationProperties casProperties,
             final WARestClient restClient) {
 
-        return new SyncopeWAWebAuthnCredentialRepository(casProperties, restClient);
+        return new WAWebAuthnCredentialRepository(casProperties, restClient);
     }
 
     @Bean
@@ -326,12 +332,12 @@
         LoadingCache<String, String> requestStorage = Caffeine.newBuilder().
                 expireAfterWrite(u2f.getExpireRegistrations(), u2f.getExpireRegistrationsTimeUnit()).
                 build(key -> StringUtils.EMPTY);
-        return new SyncopeWAU2FDeviceRepository(casProperties, requestStorage, restClient, expirationDate);
+        return new WAU2FDeviceRepository(casProperties, requestStorage, restClient, expirationDate);
     }
 
     @Bean
     public SurrogateAuthenticationService surrogateAuthenticationService(final WARestClient restClient) {
-        return new SyncopeWASurrogateAuthenticationService(restClient);
+        return new WASurrogateAuthenticationService(restClient);
     }
 
     @ConditionalOnMissingBean
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWARefreshContextJob.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WARefreshContextJob.java
similarity index 91%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWARefreshContextJob.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WARefreshContextJob.java
index d7aee55..6acac0d 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWARefreshContextJob.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/WARefreshContextJob.java
@@ -18,24 +18,23 @@
  */
 package org.apache.syncope.wa.starter.config;
 
+import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.wa.bootstrap.WARestClient;
-import org.apache.commons.lang.StringUtils;
 import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator;
 import org.apereo.cas.support.saml.services.idp.metadata.SamlIdPMetadataDocument;
 import org.apereo.cas.util.AsciiArtUtils;
-
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cloud.context.refresh.ContextRefresher;
-import java.util.Optional;
-import org.quartz.JobExecutionException;
 
-public class SyncopeWARefreshContextJob implements Job {
+public class WARefreshContextJob implements Job {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWARefreshContextJob.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WARefreshContextJob.class);
 
     @Autowired
     private ContextRefresher contextRefresher;
@@ -43,7 +42,7 @@
     @Autowired
     private SamlIdPMetadataGenerator metadataGenerator;
 
-    public SyncopeWARefreshContextJob() {
+    public WARefreshContextJob() {
     }
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
similarity index 95%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
index 0f614aa..f72ce18 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
@@ -39,15 +39,15 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWAEventRepository extends AbstractCasEventRepository {
+public class WAEventRepository extends AbstractCasEventRepository {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAEventRepository.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WAEventRepository.class);
 
     private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
 
     private final WARestClient waRestClient;
 
-    public SyncopeWAEventRepository(
+    public WAEventRepository(
             final CasEventRepositoryFilter eventRepositoryFilter,
             final WARestClient restClient) {
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthCredentialRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
similarity index 62%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthCredentialRepository.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
index 144eb87..cb2b4fa0 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthCredentialRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
@@ -35,30 +35,30 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWAGoogleMfaAuthCredentialRepository extends BaseGoogleAuthenticatorTokenCredentialRepository {
+public class WAGoogleMfaAuthCredentialRepository extends BaseGoogleAuthenticatorTokenCredentialRepository {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAGoogleMfaAuthTokenRepository.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(WAGoogleMfaAuthTokenRepository.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
-    public SyncopeWAGoogleMfaAuthCredentialRepository(
+    public WAGoogleMfaAuthCredentialRepository(
             final WARestClient waRestClient, final IGoogleAuthenticator googleAuthenticator) {
 
         super(CipherExecutor.noOpOfStringToString(), googleAuthenticator);
         this.waRestClient = waRestClient;
     }
 
-    private static GoogleMfaAuthAccount mapGoogleMfaAuthAccount(final OneTimeTokenAccount account) {
-        return new GoogleMfaAuthAccount.Builder()
-                .registrationDate(OffsetDateTime.now())
-                .scratchCodes(account.getScratchCodes())
-                .validationCode(account.getValidationCode())
-                .secretKey(account.getSecretKey())
-                .id(account.getId())
-                .build();
+    protected GoogleMfaAuthAccount mapGoogleMfaAuthAccount(final OneTimeTokenAccount account) {
+        return new GoogleMfaAuthAccount.Builder().
+                registrationDate(OffsetDateTime.now()).
+                scratchCodes(account.getScratchCodes()).
+                validationCode(account.getValidationCode()).
+                secretKey(account.getSecretKey()).
+                id(account.getId()).
+                build();
     }
 
-    private static GoogleAuthenticatorAccount mapGoogleMfaAuthAccount(final GoogleMfaAuthAccount account) {
+    protected GoogleAuthenticatorAccount mapGoogleMfaAuthAccount(final GoogleMfaAuthAccount account) {
         return GoogleAuthenticatorAccount.builder().
                 secretKey(account.getSecretKey()).
                 validationCode(account.getValidationCode()).
@@ -68,15 +68,18 @@
                 build();
     }
 
+    protected GoogleMfaAuthAccountService service() {
+        return waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class);
+    }
+
     @Override
     public OneTimeTokenAccount get(final long id) {
         try {
-            GoogleMfaAuthAccount account =
-                    waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).read(id);
+            GoogleMfaAuthAccount account = service().read(id);
             if (account != null) {
                 return mapGoogleMfaAuthAccount(account);
             }
-        } catch (final SyncopeClientException e) {
+        } catch (SyncopeClientException e) {
             if (e.getType() == ClientExceptionType.NotFound) {
                 LOG.info("Could not locate account for id {}", id);
             } else {
@@ -89,12 +92,13 @@
     @Override
     public OneTimeTokenAccount get(final String username, final long id) {
         try {
-            waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).read(username).
+            return service().read(username).
                     getResult().stream().
                     filter(account -> account.getId() == id).
-                    map(SyncopeWAGoogleMfaAuthCredentialRepository::mapGoogleMfaAuthAccount).
-                    collect(Collectors.toList());
-        } catch (final SyncopeClientException e) {
+                    map(this::mapGoogleMfaAuthAccount).
+                    findFirst().
+                    orElse(null);
+        } catch (SyncopeClientException e) {
             if (e.getType() == ClientExceptionType.NotFound) {
                 LOG.info("Could not locate account for owner {} and id {}", username, id);
             } else {
@@ -107,11 +111,11 @@
     @Override
     public Collection<? extends OneTimeTokenAccount> get(final String username) {
         try {
-            waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).read(username).
+            return service().read(username).
                     getResult().stream().
-                    map(SyncopeWAGoogleMfaAuthCredentialRepository::mapGoogleMfaAuthAccount).
+                    map(this::mapGoogleMfaAuthAccount).
                     collect(Collectors.toList());
-        } catch (final SyncopeClientException e) {
+        } catch (SyncopeClientException e) {
             if (e.getType() == ClientExceptionType.NotFound) {
                 LOG.info("Could not locate account for owner {}", username);
             } else {
@@ -123,22 +127,22 @@
 
     @Override
     public Collection<? extends OneTimeTokenAccount> load() {
-        return waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).list().
+        return service().list().
                 getResult().stream().
-                map(SyncopeWAGoogleMfaAuthCredentialRepository::mapGoogleMfaAuthAccount).
+                map(this::mapGoogleMfaAuthAccount).
                 collect(Collectors.toList());
     }
 
     @Override
     public OneTimeTokenAccount save(final OneTimeTokenAccount tokenAccount) {
-        GoogleMfaAuthAccount account = new GoogleMfaAuthAccount.Builder()
-                .registrationDate(OffsetDateTime.now())
-                .scratchCodes(tokenAccount.getScratchCodes())
-                .validationCode(tokenAccount.getValidationCode())
-                .secretKey(tokenAccount.getSecretKey())
-                .name(tokenAccount.getName())
-                .id(tokenAccount.getId())
-                .build();
+        GoogleMfaAuthAccount account = new GoogleMfaAuthAccount.Builder().
+                registrationDate(OffsetDateTime.now()).
+                scratchCodes(tokenAccount.getScratchCodes()).
+                validationCode(tokenAccount.getValidationCode()).
+                secretKey(tokenAccount.getSecretKey()).
+                name(tokenAccount.getName()).
+                id(tokenAccount.getId()).
+                build();
         waRestClient.getSyncopeClient().
                 getService(GoogleMfaAuthAccountService.class).create(tokenAccount.getUsername(), account);
         return mapGoogleMfaAuthAccount(account);
@@ -147,34 +151,49 @@
     @Override
     public OneTimeTokenAccount update(final OneTimeTokenAccount tokenAccount) {
         GoogleMfaAuthAccount acct = mapGoogleMfaAuthAccount(tokenAccount);
-        waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).
-                update(tokenAccount.getUsername(), acct);
+        service().update(tokenAccount.getUsername(), acct);
         return tokenAccount;
     }
 
     @Override
     public void deleteAll() {
-        waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).deleteAll();
+        service().deleteAll();
     }
 
     @Override
     public void delete(final String username) {
-        waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).delete(username);
+        try {
+            service().delete(username);
+        } catch (SyncopeClientException e) {
+            if (e.getType() == ClientExceptionType.NotFound) {
+                LOG.info("Could not locate account for owner {}", username);
+            } else {
+                LOG.error(e.getMessage(), e);
+            }
+        }
     }
 
     @Override
     public void delete(final long id) {
-        waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).delete(id);
+        service().delete(id);
     }
 
     @Override
     public long count() {
-        return waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).list().getTotalCount();
+        return service().list().getTotalCount();
     }
 
     @Override
     public long count(final String username) {
-        return waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class).
-                read(username).getTotalCount();
+        try {
+            return service().read(username).getTotalCount();
+        } catch (SyncopeClientException e) {
+            if (e.getType() == ClientExceptionType.NotFound) {
+                LOG.info("Could not locate account for owner {}", username);
+            } else {
+                LOG.error(e.getMessage(), e);
+            }
+            return 0L;
+        }
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
similarity index 75%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
index 1a36073..95f887c 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
@@ -19,8 +19,6 @@
 package org.apache.syncope.wa.starter.gauth;
 
 import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.rest.api.service.wa.GoogleMfaAuthTokenService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -30,15 +28,15 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWAGoogleMfaAuthTokenRepository extends BaseOneTimeTokenRepository {
+public class WAGoogleMfaAuthTokenRepository extends BaseOneTimeTokenRepository {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAGoogleMfaAuthTokenRepository.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(WAGoogleMfaAuthTokenRepository.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
-    private final long expireTokensInSeconds;
+    protected final long expireTokensInSeconds;
 
-    public SyncopeWAGoogleMfaAuthTokenRepository(final WARestClient waRestClient, final long expireTokensInSeconds) {
+    public WAGoogleMfaAuthTokenRepository(final WARestClient waRestClient, final long expireTokensInSeconds) {
         this.waRestClient = waRestClient;
         this.expireTokensInSeconds = expireTokensInSeconds;
     }
@@ -49,15 +47,15 @@
 
     @Override
     protected void cleanInternal() {
-        service().delete(OffsetDateTime.now().minusSeconds(expireTokensInSeconds));
+        service().delete(LocalDateTime.now().minusSeconds(expireTokensInSeconds));
     }
 
     @Override
     public void store(final OneTimeToken token) {
-        GoogleMfaAuthToken tokenTO = new GoogleMfaAuthToken.Builder()
-                .token(token.getToken())
-                .issueDate(OffsetDateTime.of(token.getIssuedDateTime(), OffsetDateTime.now().getOffset()))
-                .build();
+        GoogleMfaAuthToken tokenTO = new GoogleMfaAuthToken.Builder().
+                token(token.getToken()).
+                issueDate(token.getIssuedDateTime()).
+                build();
         service().store(token.getUserId(), tokenTO);
     }
 
@@ -66,8 +64,7 @@
         try {
             GoogleMfaAuthToken tokenTO = service().read(username, otp);
             GoogleAuthenticatorToken token = new GoogleAuthenticatorToken(tokenTO.getOtp(), username);
-            LocalDateTime dateTime = tokenTO.getIssueDate().toInstant().atZone(ZoneOffset.UTC).toLocalDateTime();
-            token.setIssuedDateTime(dateTime);
+            token.setIssuedDateTime(tokenTO.getIssueDate());
             return token;
         } catch (final Exception e) {
             LOG.debug("Unable to fetch token {} for user {}", otp, username);
@@ -92,7 +89,7 @@
 
     @Override
     public void removeAll() {
-        service().delete((OffsetDateTime) null);
+        service().delete((LocalDateTime) null);
     }
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
index 4c1064a..ee62c2b 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AbstractClientAppMapper.java
@@ -27,6 +27,7 @@
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.services.RegisteredServiceProperty;
 
 abstract class AbstractClientAppMapper implements ClientAppMapper {
@@ -48,12 +49,16 @@
 
     protected void setPolicies(
             final RegexRegisteredService service,
-            final RegisteredServiceAuthenticationPolicy authenticationPolicy,
+            final RegisteredServiceAuthenticationPolicy authPolicy,
+            final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
             final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
 
-        if (authenticationPolicy != null) {
-            service.setAuthenticationPolicy(authenticationPolicy);
+        if (authPolicy != null) {
+            service.setAuthenticationPolicy(authPolicy);
+        }
+        if (mfaPolicy != null) {
+            service.setMultifactorPolicy(mfaPolicy);
         }
         if (accessStrategy != null) {
             service.setAccessStrategy(accessStrategy);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
index 25d6b89..4e262bc 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
@@ -18,11 +18,22 @@
  */
 package org.apache.syncope.wa.starter.mapping;
 
+import java.util.List;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @FunctionalInterface
 public interface AuthMapper {
 
-    RegisteredServiceAuthenticationPolicy build(AuthPolicyTO policy);
+    Pair<RegisteredServiceAuthenticationPolicy, RegisteredServiceMultifactorPolicy> build(
+            ConfigurableApplicationContext ctx,
+            ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan,
+            AuthPolicyTO policy,
+            List<AuthModuleTO> authModules);
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
index 5c7baf9..cbaa5f3 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
@@ -25,6 +25,7 @@
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 
 @ClientAppMapFor(clientAppClass = CASSPClientAppTO.class)
 public class CASSPClientAppTOMapper extends AbstractClientAppMapper {
@@ -32,7 +33,8 @@
     @Override
     public RegisteredService map(
             final WAClientApp clientApp,
-            final RegisteredServiceAuthenticationPolicy authenticationPolicy,
+            final RegisteredServiceAuthenticationPolicy authPolicy,
+            final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
             final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
 
@@ -42,7 +44,7 @@
         service.setServiceId(cas.getServiceId());
         setCommon(service, cas);
 
-        setPolicies(service, authenticationPolicy, accessStrategy, attributeReleasePolicy);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
 
         return service;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
index 4951adb..acb38a6 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
@@ -23,6 +23,7 @@
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 
 @FunctionalInterface
 public interface ClientAppMapper {
@@ -30,6 +31,7 @@
     RegisteredService map(
             WAClientApp clientApp,
             RegisteredServiceAuthenticationPolicy authPolicy,
+            RegisteredServiceMultifactorPolicy mfaPolicy,
             RegisteredServiceAccessStrategy accessStrategy,
             RegisteredServiceAttributeReleasePolicy attributeReleasePolicy);
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
index 6fe0a50..e6d78ef 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
@@ -19,22 +19,56 @@
 package org.apache.syncope.wa.starter.mapping;
 
 import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.auth.MFAAuthModuleConf;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
+import org.apereo.cas.authentication.AuthenticationHandler;
+import org.apereo.cas.authentication.MultifactorAuthenticationHandler;
+import org.apereo.cas.authentication.MultifactorAuthenticationProvider;
 import org.apereo.cas.services.AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria;
 import org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @AuthMapFor(authPolicyConfClass = DefaultAuthPolicyConf.class)
 public class DefaultAuthMapper implements AuthMapper {
 
+    protected static final Logger LOG = LoggerFactory.getLogger(DefaultAuthMapper.class);
+
     @Override
-    public RegisteredServiceAuthenticationPolicy build(final AuthPolicyTO policy) {
+    public Pair<RegisteredServiceAuthenticationPolicy, RegisteredServiceMultifactorPolicy> build(
+            final ConfigurableApplicationContext ctx,
+            final ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan,
+            final AuthPolicyTO policy,
+            final List<AuthModuleTO> authModules) {
+
         DefaultRegisteredServiceAuthenticationPolicy authPolicy = new DefaultRegisteredServiceAuthenticationPolicy();
 
+        Set<String> mfaAuthHandlers = new HashSet<>();
+
         DefaultAuthPolicyConf policyConf = (DefaultAuthPolicyConf) policy.getConf();
         if (!policyConf.getAuthModules().isEmpty()) {
-            authPolicy.setRequiredAuthenticationHandlers(new HashSet<>(policyConf.getAuthModules()));
+            mfaAuthHandlers.addAll(authenticationEventExecutionPlan.getObject().getAuthenticationHandlers().stream().
+                    filter(MultifactorAuthenticationHandler.class::isInstance).
+                    filter(mfaAuthHander -> policyConf.getAuthModules().contains(mfaAuthHander.getName())).
+                    map(AuthenticationHandler::getName).
+                    collect(Collectors.toSet()));
+
+            Set<String> authHandlers = new HashSet<>(policyConf.getAuthModules());
+            authHandlers.removeAll(mfaAuthHandlers);
+            authPolicy.setRequiredAuthenticationHandlers(authHandlers);
         }
 
         AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria criteria =
@@ -42,6 +76,27 @@
         criteria.setTryAll(policyConf.isTryAll());
         authPolicy.setCriteria(criteria);
 
-        return authPolicy;
+        DefaultRegisteredServiceMultifactorPolicy mfaPolicy = null;
+        if (!mfaAuthHandlers.isEmpty()) {
+            Set<String> fns = mfaAuthHandlers.stream().
+                    map(handler -> authModules.stream().filter(am -> handler.equals(am.getKey())).findFirst()).
+                    filter(Optional::isPresent).
+                    map(Optional::get).
+                    filter(am -> am.getConf() instanceof MFAAuthModuleConf).
+                    map(am -> ((MFAAuthModuleConf) am.getConf()).getFriendlyName()).
+                    collect(Collectors.toSet());
+
+            Set<String> mfaProviders = ctx.getBeansOfType(MultifactorAuthenticationProvider.class).values().stream().
+                    filter(map -> fns.contains(map.getFriendlyName())).
+                    map(MultifactorAuthenticationProvider::getId).
+                    collect(Collectors.toSet());
+
+            mfaPolicy = new DefaultRegisteredServiceMultifactorPolicy();
+            mfaPolicy.setBypassEnabled(false);
+            mfaPolicy.setForceExecution(true);
+            mfaPolicy.setMultifactorAuthenticationProviders(mfaProviders);
+        }
+
+        return Pair.of(authPolicy, mfaPolicy);
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
index f70789b..614df26 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
@@ -40,6 +40,7 @@
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
 import org.apereo.cas.util.spring.ApplicationContextProvider;
 import org.slf4j.Logger;
@@ -56,7 +57,8 @@
     @Override
     public RegisteredService map(
             final WAClientApp clientApp,
-            final RegisteredServiceAuthenticationPolicy authenticationPolicy,
+            final RegisteredServiceAuthenticationPolicy authPolicy,
+            final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
             final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
 
@@ -83,7 +85,7 @@
         }
         service.setLogoutUrl(rp.getLogoutUri());
 
-        setPolicies(service, authenticationPolicy, accessStrategy, attributeReleasePolicy);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
         if (attributeReleasePolicy != null) {
             ChainingAttributeReleasePolicy chain = new ChainingAttributeReleasePolicy();
             if (attributeReleasePolicy instanceof ReturnMappedAttributeReleasePolicy) {
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
index 0d80109..d316e02 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
@@ -20,19 +20,28 @@
 
 import java.util.Map;
 import java.util.Optional;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
 import org.apereo.cas.services.RegisteredService;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.context.ConfigurableApplicationContext;
 
 public class RegisteredServiceMapper {
 
     private static final Logger LOG = LoggerFactory.getLogger(RegisteredServiceMapper.class);
 
+    protected final ConfigurableApplicationContext ctx;
+
+    protected final ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan;
+
     protected final Map<String, AuthMapper> authPolicyConfMappers;
 
     protected final Map<String, AccessMapper> accessPolicyConfMappers;
@@ -42,11 +51,15 @@
     protected final Map<String, ClientAppMapper> clientAppTOMappers;
 
     public RegisteredServiceMapper(
+            final ConfigurableApplicationContext ctx,
+            final ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan,
             final Map<String, AuthMapper> authPolicyConfMappers,
             final Map<String, AccessMapper> accessPolicyConfMappers,
             final Map<String, AttrReleaseMapper> attrReleasePolicyConfMappers,
             final Map<String, ClientAppMapper> clientAppTOMappers) {
 
+        this.ctx = ctx;
+        this.authenticationEventExecutionPlan = authenticationEventExecutionPlan;
         this.authPolicyConfMappers = authPolicyConfMappers;
         this.accessPolicyConfMappers = accessPolicyConfMappers;
         this.attrReleasePolicyConfMappers = attrReleasePolicyConfMappers;
@@ -62,11 +75,20 @@
         }
 
         RegisteredServiceAuthenticationPolicy authPolicy = null;
+        RegisteredServiceMultifactorPolicy mfaPolicy = null;
         if (clientApp.getAuthPolicy() != null) {
             AuthMapper authMapper = authPolicyConfMappers.get(
                     clientApp.getAuthPolicy().getConf().getClass().getName());
-            authPolicy = Optional.ofNullable(authMapper).
-                    map(mapper -> mapper.build(clientApp.getAuthPolicy())).orElse(null);
+            Pair<RegisteredServiceAuthenticationPolicy, RegisteredServiceMultifactorPolicy> mapped =
+                    Optional.ofNullable(authMapper).map(mapper -> mapper.build(
+                    ctx, authenticationEventExecutionPlan, clientApp.getAuthPolicy(), clientApp.getAuthModules())).
+                            orElseGet(() -> Pair.of(null, null));
+            if (mapped.getLeft() != null) {
+                authPolicy = mapped.getLeft();
+            }
+            if (mapped.getRight() != null) {
+                mfaPolicy = mapped.getRight();
+            }
         }
 
         RegisteredServiceAccessStrategy accessStrategy = null;
@@ -89,6 +111,6 @@
             }
         }
 
-        return clientAppMapper.map(clientApp, authPolicy, accessStrategy, attributeReleasePolicy);
+        return clientAppMapper.map(clientApp, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
index 9e7dcec..4646771 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
@@ -26,6 +26,7 @@
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.support.saml.services.SamlRegisteredService;
 import org.apereo.cas.util.model.TriStateBoolean;
 
@@ -35,7 +36,8 @@
     @Override
     public RegisteredService map(
             final WAClientApp clientApp,
-            final RegisteredServiceAuthenticationPolicy authenticationPolicy,
+            final RegisteredServiceAuthenticationPolicy authPolicy,
+            final RegisteredServiceMultifactorPolicy mfaPolicy,
             final RegisteredServiceAccessStrategy accessStrategy,
             final RegisteredServiceAttributeReleasePolicy attributeReleasePolicy) {
 
@@ -60,7 +62,7 @@
         }
         service.setServiceProviderNameIdQualifier(sp.getServiceProviderNameIdQualifier());
 
-        setPolicies(service, authenticationPolicy, accessStrategy, attributeReleasePolicy);
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
 
         return service;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/SyncopeWAOIDCJWKSGeneratorService.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
similarity index 93%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/SyncopeWAOIDCJWKSGeneratorService.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
index 6b481e1..e82f1ac 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/SyncopeWAOIDCJWKSGeneratorService.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
@@ -18,11 +18,8 @@
  */
 package org.apache.syncope.wa.starter.oidc;
 
-import org.apereo.cas.oidc.jwks.generator.OidcJsonWebKeystoreGeneratorService;
-
 import java.nio.charset.StandardCharsets;
 import java.util.Optional;
-
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.OIDCJWKSTO;
@@ -30,6 +27,7 @@
 import org.apache.syncope.common.lib.types.JWSAlgorithm;
 import org.apache.syncope.common.rest.api.service.OIDCJWKSService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apereo.cas.oidc.jwks.generator.OidcJsonWebKeystoreGeneratorService;
 import org.jose4j.jwk.JsonWebKey;
 import org.jose4j.jwk.JsonWebKeySet;
 import org.slf4j.Logger;
@@ -37,9 +35,9 @@
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.core.io.Resource;
 
-public class SyncopeWAOIDCJWKSGeneratorService implements OidcJsonWebKeystoreGeneratorService {
+public class WAOIDCJWKSGeneratorService implements OidcJsonWebKeystoreGeneratorService {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAOIDCJWKSGeneratorService.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WAOIDCJWKSGeneratorService.class);
 
     private final WARestClient waRestClient;
 
@@ -47,7 +45,7 @@
 
     private final JWSAlgorithm algorithm;
 
-    public SyncopeWAOIDCJWKSGeneratorService(
+    public WAOIDCJWKSGeneratorService(
             final WARestClient restClient, final int size, final JWSAlgorithm algorithm) {
 
         this.waRestClient = restClient;
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizer.java
similarity index 74%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizer.java
index 0ae4c16..2456334 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizer.java
@@ -18,22 +18,21 @@
  */
 package org.apache.syncope.wa.starter.pac4j.saml;
 
-import org.apereo.cas.support.pac4j.authentication.DelegatedClientFactoryCustomizer;
-
 import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apereo.cas.support.pac4j.authentication.DelegatedClientFactoryCustomizer;
 import org.pac4j.core.client.Client;
 import org.pac4j.saml.client.SAML2Client;
 import org.pac4j.saml.config.SAML2Configuration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWASAML2ClientCustomizer implements DelegatedClientFactoryCustomizer<Client> {
+public class WASAML2ClientCustomizer implements DelegatedClientFactoryCustomizer<Client> {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientCustomizer.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WASAML2ClientCustomizer.class);
 
-    private final WARestClient restClient;
+    protected final WARestClient restClient;
 
-    public SyncopeWASAML2ClientCustomizer(final WARestClient restClient) {
+    public WASAML2ClientCustomizer(final WARestClient restClient) {
         this.restClient = restClient;
     }
 
@@ -43,8 +42,8 @@
             LOG.debug("Customizing SAML2 client {}", client.getName());
             SAML2Client saml2Client = (SAML2Client) client;
             SAML2Configuration configuration = saml2Client.getConfiguration();
-            configuration.setKeystoreGenerator(new SyncopeWASAML2ClientKeystoreGenerator(restClient, saml2Client));
-            configuration.setMetadataGenerator(new SyncopeWASAML2ClientMetadataGenerator(restClient, saml2Client));
+            configuration.setKeystoreGenerator(new WASAML2ClientKeystoreGenerator(restClient, saml2Client));
+            configuration.setMetadataGenerator(new WASAML2ClientMetadataGenerator(restClient, saml2Client));
         }
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
similarity index 92%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
index 113a826..3d07135 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGenerator.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
@@ -33,15 +33,15 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWASAML2ClientKeystoreGenerator extends BaseSAML2KeystoreGenerator {
+public class WASAML2ClientKeystoreGenerator extends BaseSAML2KeystoreGenerator {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientKeystoreGenerator.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WASAML2ClientKeystoreGenerator.class);
 
     private final WARestClient restClient;
 
     private final SAML2Client saml2Client;
 
-    SyncopeWASAML2ClientKeystoreGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
+    WASAML2ClientKeystoreGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
         super(saml2Client.getConfiguration());
         this.restClient = restClient;
         this.saml2Client = saml2Client;
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
similarity index 88%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
index 87d9f89..081c57b 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGenerator.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
@@ -31,22 +31,22 @@
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 import org.opensaml.saml.metadata.resolver.MetadataResolver;
 
-public class SyncopeWASAML2ClientMetadataGenerator extends BaseSAML2MetadataGenerator {
+public class WASAML2ClientMetadataGenerator extends BaseSAML2MetadataGenerator {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientMetadataGenerator.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WASAML2ClientMetadataGenerator.class);
 
     private final WARestClient restClient;
 
     private final SAML2Client saml2Client;
 
-    SyncopeWASAML2ClientMetadataGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
+    WASAML2ClientMetadataGenerator(final WARestClient restClient, final SAML2Client saml2Client) {
         this.restClient = restClient;
         this.saml2Client = saml2Client;
     }
 
     @Override
     protected AbstractBatchMetadataResolver createMetadataResolver(final Resource metadataResource) {
-        return new SyncopeWASAML2MetadataResolver(restClient, saml2Client);
+        return new WASAML2MetadataResolver(restClient, saml2Client);
     }
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
similarity index 88%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
index 3eb884b..42e2c60 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolver.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
@@ -28,15 +28,15 @@
 import org.slf4j.LoggerFactory;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 
-public class SyncopeWASAML2MetadataResolver extends AbstractReloadingMetadataResolver {
+public class WASAML2MetadataResolver extends AbstractReloadingMetadataResolver {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2MetadataResolver.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WASAML2MetadataResolver.class);
 
     private final WARestClient restClient;
 
     private final SAML2Client saml2Client;
 
-    SyncopeWASAML2MetadataResolver(final WARestClient restClient, final SAML2Client saml2Client) {
+    WASAML2MetadataResolver(final WARestClient restClient, final SAML2Client saml2Client) {
         this.restClient = restClient;
         this.saml2Client = saml2Client;
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
similarity index 97%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
index b62724a..523be11 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
@@ -18,13 +18,14 @@
  */
 package org.apache.syncope.wa.starter.services;
 
-import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
 import org.apereo.cas.services.AbstractServiceRegistry;
 import org.apereo.cas.services.OidcRegisteredService;
 import org.apereo.cas.services.RegisteredService;
@@ -33,17 +34,16 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ConfigurableApplicationContext;
-import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 
-public class SyncopeWAServiceRegistry extends AbstractServiceRegistry {
+public class WAServiceRegistry extends AbstractServiceRegistry {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAServiceRegistry.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WAServiceRegistry.class);
 
     private final WARestClient waRestClient;
 
     private final RegisteredServiceMapper registeredServiceMapper;
 
-    public SyncopeWAServiceRegistry(
+    public WAServiceRegistry(
             final WARestClient restClient,
             final RegisteredServiceMapper registeredServiceMapper,
             final ConfigurableApplicationContext applicationContext,
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationService.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
similarity index 90%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationService.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
index 8d42f04..b5fe2f9 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationService.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
@@ -30,13 +30,13 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWASurrogateAuthenticationService implements SurrogateAuthenticationService {
+public class WASurrogateAuthenticationService implements SurrogateAuthenticationService {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASurrogateAuthenticationService.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WASurrogateAuthenticationService.class);
 
     private final WARestClient waRestClient;
 
-    public SyncopeWASurrogateAuthenticationService(final WARestClient waRestClient) {
+    public WASurrogateAuthenticationService(final WARestClient waRestClient) {
         this.waRestClient = waRestClient;
     }
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/SyncopeWAU2FDeviceRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
similarity index 96%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/SyncopeWAU2FDeviceRepository.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
index 72a3545..a66a196 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/SyncopeWAU2FDeviceRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
@@ -38,15 +38,15 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.util.CollectionUtils;
 
-public class SyncopeWAU2FDeviceRepository extends BaseU2FDeviceRepository {
+public class WAU2FDeviceRepository extends BaseU2FDeviceRepository {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAU2FDeviceRepository.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WAU2FDeviceRepository.class);
 
     private final WARestClient waRestClient;
 
     private final OffsetDateTime expirationDate;
 
-    public SyncopeWAU2FDeviceRepository(
+    public WAU2FDeviceRepository(
             final CasConfigurationProperties casProperties,
             final LoadingCache<String, String> requestStorage,
             final WARestClient waRestClient,
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/SyncopeWAWebAuthnCredentialRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
similarity index 94%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/SyncopeWAWebAuthnCredentialRepository.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
index e5fd732..d62ffe2 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/SyncopeWAWebAuthnCredentialRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
@@ -38,13 +38,13 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWAWebAuthnCredentialRepository extends BaseWebAuthnCredentialRepository {
+public class WAWebAuthnCredentialRepository extends BaseWebAuthnCredentialRepository {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAWebAuthnCredentialRepository.class);
+    private static final Logger LOG = LoggerFactory.getLogger(WAWebAuthnCredentialRepository.class);
 
     private final WARestClient waRestClient;
 
-    public SyncopeWAWebAuthnCredentialRepository(final CasConfigurationProperties properties,
+    public WAWebAuthnCredentialRepository(final CasConfigurationProperties properties,
             final WARestClient waRestClient) {
         super(properties, CipherExecutor.noOpOfStringToString());
         this.waRestClient = waRestClient;
diff --git a/wa/starter/src/main/resources/META-INF/spring.factories b/wa/starter/src/main/resources/META-INF/spring.factories
index 0db2a5d..ad7631e 100644
--- a/wa/starter/src/main/resources/META-INF/spring.factories
+++ b/wa/starter/src/main/resources/META-INF/spring.factories
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.syncope.wa.starter.config.SyncopeWAConfiguration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.syncope.wa.starter.config.WAContext
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
index 5fd9098..355a784 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
@@ -20,7 +20,7 @@
 
 import com.fasterxml.jackson.databind.json.JsonMapper;
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
-import java.time.OffsetDateTime;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -124,7 +124,7 @@
         private final Map<String, GoogleMfaAuthToken> tokens = new HashMap<>();
 
         @Override
-        public void delete(final OffsetDateTime expirationDate) {
+        public void delete(final LocalDateTime expirationDate) {
             if (expirationDate == null) {
                 tokens.clear();
             } else {
@@ -155,9 +155,9 @@
 
         @Override
         public GoogleMfaAuthToken read(final String owner, final int otp) {
-            return tokens.entrySet().stream()
-                    .filter(to -> to.getValue().getOtp() == otp && to.getKey().equalsIgnoreCase(owner))
-                    .findFirst().get().getValue();
+            return tokens.entrySet().stream().
+                    filter(to -> to.getValue().getOtp() == otp && to.getKey().equalsIgnoreCase(owner)).
+                    findFirst().get().getValue();
         }
 
         @Override
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeWAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
similarity index 99%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeWAServiceRegistryTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
index 858d533..343224b 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeWAServiceRegistryTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
@@ -53,7 +53,7 @@
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public class SyncopeWAServiceRegistryTest extends AbstractTest {
+public class WAServiceRegistryTest extends AbstractTest {
 
     @Autowired
     private WARestClient wARestClient;
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
similarity index 92%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
index 9d2f878..a95dfb3 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
@@ -32,7 +32,7 @@
 import org.junit.jupiter.api.Test;
 import org.apache.syncope.common.rest.api.service.AuditService;
 
-public class SyncopeWAAuditTrailManagerTest extends AbstractTest {
+public class WAAuditTrailManagerTest extends AbstractTest {
 
     private static AuditService loggerService;
 
@@ -51,7 +51,7 @@
     public void saveAuditRecord() {
         AuditActionContext audit = new AuditActionContext("principal", "resourceOperatedUpon", "actionPerformed",
                 "applicationCode", new Date(), "clientIpAddress", "serverIpAddress", "userAgent");
-        SyncopeWAAuditTrailManager auditTrailManager = new SyncopeWAAuditTrailManager(getWaRestClient());
+        WAAuditTrailManager auditTrailManager = new WAAuditTrailManager(getWaRestClient());
         auditTrailManager.saveAuditRecord(audit);
         verify(loggerService).create(any(AuditEntry.class));
     }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
similarity index 92%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
index cd6ac5a..138ad5a 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
@@ -33,7 +33,7 @@
 import org.junit.jupiter.api.Test;
 import org.apache.syncope.common.rest.api.service.AuditService;
 
-public class SyncopeWAEventRepositoryTest extends AbstractTest {
+public class WAEventRepositoryTest extends AbstractTest {
 
     private static AuditService auditService;
 
@@ -51,10 +51,9 @@
     @Test
     public void saveInternal() {
         CasEvent event = new CasEvent(1L, "Auth", "principalId", "creationTime", Map.of("timestamp", "1"));
-        SyncopeWAEventRepository eventRepository = new SyncopeWAEventRepository(CasEventRepositoryFilter.noOp(),
+        WAEventRepository eventRepository = new WAEventRepository(CasEventRepositoryFilter.noOp(),
                 getWaRestClient());
         eventRepository.saveInternal(event);
         verify(auditService).create(any(AuditEntry.class));
     }
-
 }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/SyncopeWAGoogleMfaAuthTokenRepositoryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/WAGoogleMfaAuthTokenRepositoryTest.java
similarity index 95%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/SyncopeWAGoogleMfaAuthTokenRepositoryTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/WAGoogleMfaAuthTokenRepositoryTest.java
index e30c2de..835ddbf 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/SyncopeWAGoogleMfaAuthTokenRepositoryTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/gauth/token/WAGoogleMfaAuthTokenRepositoryTest.java
@@ -26,7 +26,7 @@
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public class SyncopeWAGoogleMfaAuthTokenRepositoryTest extends AbstractTest {
+public class WAGoogleMfaAuthTokenRepositoryTest extends AbstractTest {
 
     @Autowired
     private OneTimeTokenRepository tokenRepository;
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseWASAML2ClientTest.java
similarity index 91%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseWASAML2ClientTest.java
index f7b26e3..55dd525 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseWASAML2ClientTest.java
@@ -16,9 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.syncope.wa.starter.pac4j.saml;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+import java.util.Date;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1Integer;
@@ -37,20 +48,7 @@
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.FileSystemResource;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.Signature;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateFactory;
-import java.util.Base64;
-import java.util.Date;
-
-public abstract class BaseSyncopeWASAML2ClientTest {
+public abstract class BaseWASAML2ClientTest {
 
     protected static Certificate createSelfSignedCert(final KeyPair keyPair) throws Exception {
         final X500Name dn = new X500Name("cn=Unknown");
@@ -64,7 +62,8 @@
         final Date expiration = new Date(System.currentTimeMillis() + 100000);
         certGen.setEndDate(new Time(expiration));
 
-        final AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE);
+        final AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption,
+                DERNull.INSTANCE);
         certGen.setSignature(sigAlgID);
         certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
 
@@ -80,7 +79,7 @@
         v.add(new DERBitString(sig.sign()));
 
         final Certificate cert = CertificateFactory.getInstance("X.509")
-            .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
+                .generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER)));
         cert.verify(keyPair.getPublic());
         return cert;
     }
@@ -91,7 +90,8 @@
         saml2Configuration.setPrivateKeyPassword("password");
         saml2Configuration.setKeystoreAlias("Syncope");
         saml2Configuration.setIdentityProviderMetadataResource(new ClassPathResource("idp-metadata.xml"));
-        saml2Configuration.setServiceProviderMetadataResource(new FileSystemResource(File.createTempFile("sp-metadata", ".xml")));
+        saml2Configuration.setServiceProviderMetadataResource(new FileSystemResource(File.createTempFile("sp-metadata",
+                ".xml")));
         SAML2Client client = new SAML2Client(saml2Configuration);
         client.setCallbackUrl("https://syncope.apache.org");
         return client;
@@ -105,13 +105,13 @@
         keyPairGenerator.initialize(4096);
         KeyPair keyPair = keyPairGenerator.generateKeyPair();
         Certificate certificate = createSelfSignedCert(keyPair);
-        ks.setKeyEntry("Syncope", keyPair.getPrivate(), "password".toCharArray(), new Certificate[]{certificate});
+        ks.setKeyEntry("Syncope", keyPair.getPrivate(), "password".toCharArray(), new Certificate[] { certificate });
         return ks;
     }
 
     protected static String getKeystoreAsString() throws Exception {
         char[] pwdArray = "password".toCharArray();
-        try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
+        try ( ByteArrayOutputStream fos = new ByteArrayOutputStream()) {
             getKeystore().store(fos, pwdArray);
             fos.flush();
             return Base64.getEncoder().encodeToString(fos.toByteArray());
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
similarity index 90%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
index 3d88325..ade1552 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizerTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
@@ -35,7 +35,7 @@
 import org.springframework.core.io.ClassPathResource;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 
-public class SyncopeWASAML2ClientCustomizerTest extends BaseSyncopeWASAML2ClientTest {
+public class WASAML2ClientCustomizerTest extends BaseWASAML2ClientTest {
 
     @Test
     public void customize() throws Exception {
@@ -55,11 +55,11 @@
         when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(service);
         when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
 
-        SyncopeWASAML2ClientCustomizer customizer = new SyncopeWASAML2ClientCustomizer(restClient);
+        WASAML2ClientCustomizer customizer = new WASAML2ClientCustomizer(restClient);
         SAML2Client client = getSAML2Client();
         customizer.customize(client);
         client.init();
-        assertTrue(client.getConfiguration().getKeystoreGenerator() instanceof SyncopeWASAML2ClientKeystoreGenerator);
-        assertTrue(client.getConfiguration().toMetadataGenerator() instanceof SyncopeWASAML2ClientMetadataGenerator);
+        assertTrue(client.getConfiguration().getKeystoreGenerator() instanceof WASAML2ClientKeystoreGenerator);
+        assertTrue(client.getConfiguration().toMetadataGenerator() instanceof WASAML2ClientMetadataGenerator);
     }
 }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
similarity index 92%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
index 300a61e..2fe958e 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientKeystoreGeneratorTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
@@ -36,7 +36,7 @@
 import org.springframework.core.io.ClassPathResource;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 
-public class SyncopeWASAML2ClientKeystoreGeneratorTest extends BaseSyncopeWASAML2ClientTest {
+public class WASAML2ClientKeystoreGeneratorTest extends BaseWASAML2ClientTest {
 
     private static WARestClient getWaRestClient() throws Exception {
         WARestClient restClient = mock(WARestClient.class);
@@ -60,7 +60,7 @@
     @Test
     public void generate() throws Exception {
         SAML2Client client = getSAML2Client();
-        SAML2KeystoreGenerator generator = new SyncopeWASAML2ClientKeystoreGenerator(getWaRestClient(), client);
+        SAML2KeystoreGenerator generator = new WASAML2ClientKeystoreGenerator(getWaRestClient(), client);
         assertDoesNotThrow(generator::generate);
     }
 }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
similarity index 93%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
index c97626d..a7756e6 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientMetadataGeneratorTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
@@ -39,7 +39,7 @@
 import org.springframework.core.io.ClassPathResource;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 
-public class SyncopeWASAML2ClientMetadataGeneratorTest extends BaseSyncopeWASAML2ClientTest {
+public class WASAML2ClientMetadataGeneratorTest extends BaseWASAML2ClientTest {
 
     private static WARestClient getWaRestClient() throws IOException {
         WARestClient restClient = mock(WARestClient.class);
@@ -65,7 +65,7 @@
         String keystoreFile = File.createTempFile("keystore", "jks").getCanonicalPath();
         client.getConfiguration().setKeystoreResourceFilepath(keystoreFile);
 
-        SAML2MetadataGenerator generator = new SyncopeWASAML2ClientMetadataGenerator(getWaRestClient(), client);
+        SAML2MetadataGenerator generator = new WASAML2ClientMetadataGenerator(getWaRestClient(), client);
         EntityDescriptor entityDescriptor = generator.buildEntityDescriptor();
         String metadata = generator.getMetadata(entityDescriptor);
         assertNotNull(generator.storeMetadata(metadata, null, false));
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
similarity index 93%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
index 9b9abc6..7526726 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2MetadataResolverTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
@@ -36,7 +36,7 @@
 import org.springframework.core.io.ClassPathResource;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 
-public class SyncopeWASAML2MetadataResolverTest extends BaseSyncopeWASAML2ClientTest {
+public class WASAML2MetadataResolverTest extends BaseWASAML2ClientTest {
 
     @Test
     public void fetchMetadata() throws Exception {
@@ -59,7 +59,7 @@
         when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(saml2SPMetadataService);
         when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
 
-        SyncopeWASAML2MetadataResolver resolver = new SyncopeWASAML2MetadataResolver(restClient, client);
+        WASAML2MetadataResolver resolver = new WASAML2MetadataResolver(restClient, client);
         assertNotNull(resolver.fetchMetadata());
     }
 }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationServiceTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
similarity index 96%
rename from wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationServiceTest.java
rename to wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
index 4ec97c4..a9c10d2 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/SyncopeWASurrogateAuthenticationServiceTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
@@ -32,7 +32,7 @@
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
-public class SyncopeWASurrogateAuthenticationServiceTest extends AbstractTest {
+public class WASurrogateAuthenticationServiceTest extends AbstractTest {
 
     @Autowired
     private WARestClient waRestClient;