You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2021/04/14 13:32:37 UTC

[syncope] branch master updated: [SYNCOPE-1545] WA > Profiles (#261)

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

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 6b51883  [SYNCOPE-1545] WA > Profiles (#261)
6b51883 is described below

commit 6b518832be49821fbfd9605cb981414f861c914d
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Wed Apr 14 15:31:03 2021 +0200

    [SYNCOPE-1545] WA > Profiles (#261)
---
 .../authprofiles/AuthProfileDirectoryPanel.java    | 476 +++++++++++++++++++++
 .../AuthProfileItemDirectoryPanel.java             | 220 ++++++++++
 .../authprofiles/AuthProfileWizardBuilder.java     |  72 ++++
 .../clientapps/ClientAppDirectoryPanel.java        |   2 +-
 .../clientapps/ClientAppModalPanelBuilder.java     |  13 +-
 .../console/clientapps/OIDCRPDirectoryPanel.java   |  20 +-
 .../client/console/commons/AMConstants.java        |  16 +
 .../apache/syncope/client/console/pages/WA.java    |   3 +-
 .../console/panels/AuthModuleDirectoryPanel.java   |   2 +-
 .../client/console/rest/AuthProfileRestClient.java |  41 +-
 .../console/wizards/AuthModuleWizardBuilder.java   |   2 +-
 .../wizards/mapping/AuthModuleMappingPanel.java    |   2 +
 .../AuthProfileDirectoryPanel.properties           |  46 ++
 .../AuthProfileDirectoryPanel_fr_CA.properties     |  46 ++
 .../AuthProfileDirectoryPanel_it.properties        |  46 ++
 .../AuthProfileDirectoryPanel_ja.properties        |  46 ++
 .../AuthProfileDirectoryPanel_pt_BR.properties     |  46 ++
 .../AuthProfileDirectoryPanel_ru.properties        |  47 ++
 .../AuthProfileItemDirectoryPanel.properties       |  20 +
 .../AuthProfileItemDirectoryPanel_fr_CA.properties |  20 +
 .../AuthProfileItemDirectoryPanel_it.properties    |  20 +
 .../AuthProfileItemDirectoryPanel_ja.properties    |  20 +
 .../AuthProfileItemDirectoryPanel_pt_BR.properties |  20 +
 .../AuthProfileItemDirectoryPanel_ru.properties    |  21 +
 .../AuthProfileWizardBuilder$Step.html             |  23 +
 .../console/panels/LinkedAccountModalPanel.java    |   6 +-
 .../syncope/client/console/topology/Topology.java  |  11 +-
 .../console/topology/TopologyTogglePanel.java      |  28 +-
 .../wizards/resources/ResourceProvisionPanel.java  |  22 +-
 .../markup/html/form/AbstractMultiPanel.java       |  27 +-
 .../resources/ui-commons/css/syncopeUI.scss        |   4 +-
 .../syncope/client/console/pages/Realms.java       |  27 +-
 .../client/console/panels/AbstractModalPanel.java  |   2 +-
 .../client/console/panels/AjaxDataTablePanel.java  |  23 +-
 .../syncope/client/console/panels/BeanPanel.java   |  29 +-
 .../panels/NetworkServiceDirectoryPanel.java       |  14 +-
 .../client/console/panels/UserDirectoryPanel.java  |  13 +-
 .../console/panels/search/MapOfListModel.java      |   4 +-
 .../console/panels/search/SearchClausePanel.java   |   2 +
 .../console/reports/ReportletDirectoryPanel.java   |   6 +-
 ...ertyColumn.java => BooleanConditionColumn.java} |  26 +-
 .../repeater/data/table/BooleanPropertyColumn.java |   4 +-
 .../repeater/data/table/DatePropertyColumn.java    |   4 +-
 .../markup/html/bootstrap/dialog/BaseModal.java    |   8 +-
 .../markup/html/form/ActionLinksTogglePanel.java   |  11 +-
 .../wicket/markup/html/form/MultiFieldPanel.java   |   2 +-
 .../client/console/wizards/WizardMgtPanel.java     |  18 +-
 .../syncope/common/lib/to/AuthProfileTO.java       |  43 +-
 .../syncope/common/lib/types/AMEntitlement.java    |  44 +-
 .../common/lib/wa/GoogleMfaAuthAccount.java        |  16 +-
 .../syncope/common/lib/wa/GoogleMfaAuthToken.java  |   8 +-
 .../common/lib/wa/ImpersonationAccount.java        |   6 +-
 .../apache/syncope/common/lib/wa/U2FDevice.java    |  10 +-
 .../common/lib/wa/WebAuthnDeviceCredential.java    |   8 +-
 .../rest/api/service/AuthProfileService.java       |  71 ++-
 .../syncope/core/logic/AuthProfileLogic.java       |  40 +-
 .../core/logic/wa/GoogleMfaAuthAccountLogic.java   |  28 +-
 .../core/logic/wa/GoogleMfaAuthTokenLogic.java     |  36 +-
 .../syncope/core/logic/wa/ImpersonationLogic.java  |  10 +-
 .../core/logic/wa/U2FRegistrationLogic.java        |  14 +-
 .../core/logic/wa/WebAuthnRegistrationLogic.java   |  47 +-
 .../rest/cxf/service/AuthProfileServiceImpl.java   |  26 +-
 .../rest/cxf/service/ClientAppServiceImpl.java     |   6 +-
 .../persistence/api/dao/auth/AuthProfileDAO.java   |   4 +-
 .../persistence/api/entity/auth/AuthProfile.java   |   6 +-
 .../jpa/dao/auth/JPAAuthProfileDAO.java            |  20 +-
 .../jpa/entity/auth/JPAAuthProfile.java            |  35 +-
 .../persistence/jpa/inner/AuthProfileTest.java     |  14 +-
 .../api/data/AuthProfileDataBinder.java            |   2 +
 .../java/data/AuthProfileDataBinderImpl.java       |  11 +-
 .../console/panels/BpmnProcessDirectoryPanel.java  |  17 +-
 .../panels/UserRequestFormDirectoryPanel.java      |  21 +-
 .../panels/OIDCProvidersDirectoryPanel.java        |  12 +-
 .../panels/AbstractSAMLSSOLoginFormPanel.java      |   2 +-
 .../console/panels/SAML2IdPsDirectoryPanel.java    |  27 +-
 .../org/apache/syncope/fit/AbstractITCase.java     |  28 +-
 .../apache/syncope/fit/core/VirSchemaITCase.java   |   5 +-
 .../fit/core/wa/GoogleMfaAuthTokenITCase.java      |  17 +-
 .../syncope/fit/core/wa/U2FRegistrationITCase.java |  22 +-
 .../syncope/fit/core/wa/WebAuthnAccountITCase.java |   4 +-
 .../apache/syncope/fit/sra/AbstractSRAITCase.java  |   2 +-
 pom.xml                                            |   6 +-
 src/site/xdoc/integration.xml                      |   5 +-
 src/site/xdoc/release-process.xml                  |   2 +-
 84 files changed, 1674 insertions(+), 582 deletions(-)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
new file mode 100644
index 0000000..4384413
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.java
@@ -0,0 +1,476 @@
+/*
+ * 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.client.console.authprofiles;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel.AuthProfileProvider;
+import org.apache.syncope.client.console.commons.AMConstants;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.panels.ModalDirectoryPanel;
+import org.apache.syncope.client.console.rest.AuthProfileRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanConditionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.to.AuthProfileTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
+import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
+import org.apache.syncope.common.lib.wa.ImpersonationAccount;
+import org.apache.syncope.common.lib.wa.U2FDevice;
+import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class AuthProfileDirectoryPanel
+        extends DirectoryPanel<AuthProfileTO, AuthProfileTO, AuthProfileProvider, AuthProfileRestClient> {
+
+    private static final long serialVersionUID = 1L;
+
+    private final BaseModal<AuthProfileTO> authProfileModal;
+
+    public AuthProfileDirectoryPanel(final String id, final PageReference pageRef) {
+        super(id, pageRef);
+
+        authProfileModal = new BaseModal<>(Constants.OUTER) {
+
+            private static final long serialVersionUID = 389935548143327858L;
+
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setFooterVisible(false);
+            }
+        };
+        authProfileModal.size(Modal.Size.Large);
+        authProfileModal.setWindowClosedCallback(target -> {
+            updateResultTable(target);
+            authProfileModal.show(false);
+        });
+        addOuterObject(authProfileModal);
+
+        addNewItemPanelBuilder(new CreateAuthProfileWizardBuilder(pageRef), true);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.AUTH_PROFILE_CREATE);
+
+        disableCheckBoxes();
+        initResultTable();
+    }
+
+    @Override
+    protected AuthProfileProvider dataProvider() {
+        return new AuthProfileProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return AMConstants.PREF_AUTHPROFILE_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return List.of();
+    }
+
+    @Override
+    protected List<IColumn<AuthProfileTO, String>> getColumns() {
+        List<IColumn<AuthProfileTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<>(
+                new StringResourceModel(Constants.KEY_FIELD_NAME, this), Constants.KEY_FIELD_NAME));
+
+        columns.add(new PropertyColumn<>(new ResourceModel("owner"), "owner", "owner"));
+
+        columns.add(new BooleanConditionColumn<AuthProfileTO>(new StringResourceModel("impersonationAccounts")) {
+
+            @Override
+            protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
+                return !rowModel.getObject().getImpersonationAccounts().isEmpty();
+            }
+        });
+        columns.add(new BooleanConditionColumn<AuthProfileTO>(new StringResourceModel("googleMfaAuthTokens")) {
+
+            @Override
+            protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
+                return !rowModel.getObject().getGoogleMfaAuthTokens().isEmpty();
+            }
+        });
+        columns.add(new BooleanConditionColumn<AuthProfileTO>(new StringResourceModel("googleMfaAuthAccounts")) {
+
+            @Override
+            protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
+                return !rowModel.getObject().getGoogleMfaAuthAccounts().isEmpty();
+            }
+        });
+        columns.add(new BooleanConditionColumn<AuthProfileTO>(new StringResourceModel("u2fRegisteredDevices")) {
+
+            @Override
+            protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
+                return !rowModel.getObject().getU2FRegisteredDevices().isEmpty();
+            }
+        });
+        columns.add(new BooleanConditionColumn<AuthProfileTO>(new StringResourceModel("webAuthnAccount")) {
+
+            @Override
+            protected boolean isCondition(final IModel<AuthProfileTO> rowModel) {
+                return !rowModel.getObject().getWebAuthnDeviceCredentials().isEmpty();
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<AuthProfileTO> getActions(final IModel<AuthProfileTO> model) {
+        ActionsPanel<AuthProfileTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                model.setObject(AuthProfileRestClient.read(model.getObject().getKey()));
+                target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
+                        authProfileModal,
+                        new AuthProfileItemDirectoryPanel<ImpersonationAccount>(
+                                "panel", authProfileModal, model.getObject(), pageRef) {
+
+                    @Override
+                    protected List<ImpersonationAccount> getItems() {
+                        return model.getObject().getImpersonationAccounts();
+                    }
+
+                    @Override
+                    protected ImpersonationAccount defaultItem() {
+                        return new ImpersonationAccount();
+                    }
+
+                    @Override
+                    protected String sortProperty() {
+                        return "impersonated";
+                    }
+
+                    @Override
+                    protected String paginatorRowsKey() {
+                        return AMConstants.PREF_AUTHPROFILE_IMPERSONATED_PAGINATOR_ROWS;
+                    }
+
+                    @Override
+                    protected List<IColumn<ImpersonationAccount, String>> getColumns() {
+                        List<IColumn<ImpersonationAccount, String>> columns = new ArrayList<>();
+                        columns.add(new PropertyColumn<>(new ResourceModel("impersonated"),
+                                "impersonated", "impersonated"));
+                        return columns;
+                    }
+                }, pageRef)));
+                authProfileModal.header(new Model<>(getString("impersonationAccounts", model)));
+                authProfileModal.show(true);
+            }
+        }, ActionLink.ActionType.TYPE_EXTENSIONS, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                model.setObject(AuthProfileRestClient.read(model.getObject().getKey()));
+                target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
+                        authProfileModal,
+                        new AuthProfileItemDirectoryPanel<GoogleMfaAuthToken>(
+                                "panel", authProfileModal, model.getObject(), pageRef) {
+
+                    @Override
+                    protected List<GoogleMfaAuthToken> getItems() {
+                        return model.getObject().getGoogleMfaAuthTokens();
+                    }
+
+                    @Override
+                    protected GoogleMfaAuthToken defaultItem() {
+                        return new GoogleMfaAuthToken();
+                    }
+
+                    @Override
+                    protected String sortProperty() {
+                        return "issueDate";
+                    }
+
+                    @Override
+                    protected String paginatorRowsKey() {
+                        return AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHTOKENS_PAGINATOR_ROWS;
+                    }
+
+                    @Override
+                    protected List<IColumn<GoogleMfaAuthToken, String>> getColumns() {
+                        List<IColumn<GoogleMfaAuthToken, String>> columns = new ArrayList<>();
+                        columns.add(new DatePropertyColumn<>(new ResourceModel("issueDate"), "issueDate", "issueDate"));
+                        columns.add(new PropertyColumn<>(new ResourceModel("otp"), "otp", "otp"));
+                        return columns;
+                    }
+                }, pageRef)));
+                authProfileModal.header(new Model<>(getString("googleMfaAuthTokens", model)));
+                authProfileModal.show(true);
+            }
+        }, ActionLink.ActionType.EDIT_APPROVAL, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                model.setObject(AuthProfileRestClient.read(model.getObject().getKey()));
+                target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
+                        authProfileModal,
+                        new AuthProfileItemDirectoryPanel<GoogleMfaAuthAccount>(
+                                "panel", authProfileModal, model.getObject(), pageRef) {
+
+                    @Override
+                    protected List<GoogleMfaAuthAccount> getItems() {
+                        return model.getObject().getGoogleMfaAuthAccounts();
+                    }
+
+                    @Override
+                    protected GoogleMfaAuthAccount defaultItem() {
+                        return new GoogleMfaAuthAccount();
+                    }
+
+                    @Override
+                    protected String sortProperty() {
+                        return "id";
+                    }
+
+                    @Override
+                    protected String paginatorRowsKey() {
+                        return AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHACCOUNTS_PAGINATOR_ROWS;
+                    }
+
+                    @Override
+                    protected List<IColumn<GoogleMfaAuthAccount, String>> getColumns() {
+                        List<IColumn<GoogleMfaAuthAccount, String>> columns = new ArrayList<>();
+                        columns.add(new PropertyColumn<>(new ResourceModel("id"), "id", "id"));
+                        columns.add(new DatePropertyColumn<>(
+                                new ResourceModel("registrationDate"), "registrationDate", "registrationDate"));
+                        columns.add(new PropertyColumn<>(new ResourceModel("name"), "name", "name"));
+                        return columns;
+                    }
+                }, pageRef)));
+                authProfileModal.header(new Model<>(getString("googleMfaAuthAccounts", model)));
+                authProfileModal.show(true);
+            }
+        }, ActionLink.ActionType.EXECUTE, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                model.setObject(AuthProfileRestClient.read(model.getObject().getKey()));
+                target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
+                        authProfileModal,
+                        new AuthProfileItemDirectoryPanel<U2FDevice>(
+                                "panel", authProfileModal, model.getObject(), pageRef) {
+
+                    @Override
+                    protected List<U2FDevice> getItems() {
+                        return model.getObject().getU2FRegisteredDevices();
+                    }
+
+                    @Override
+                    protected U2FDevice defaultItem() {
+                        return new U2FDevice();
+                    }
+
+                    @Override
+                    protected String sortProperty() {
+                        return "id";
+                    }
+
+                    @Override
+                    protected String paginatorRowsKey() {
+                        return AMConstants.PREF_AUTHPROFILE_U2FDEVICES_PAGINATOR_ROWS;
+                    }
+
+                    @Override
+                    protected List<IColumn<U2FDevice, String>> getColumns() {
+                        List<IColumn<U2FDevice, String>> columns = new ArrayList<>();
+                        columns.add(new PropertyColumn<>(new ResourceModel("id"), "id", "id"));
+                        columns.add(new DatePropertyColumn<>(
+                                new ResourceModel("issueDate"), "issueDate", "issueDate"));
+                        columns.add(new PropertyColumn<>(new ResourceModel("record"), "record", "record"));
+                        return columns;
+                    }
+                }, pageRef)));
+                authProfileModal.header(new Model<>(getString("u2fRegisteredDevices", model)));
+                authProfileModal.show(true);
+            }
+        }, ActionLink.ActionType.FO_EDIT, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                model.setObject(AuthProfileRestClient.read(model.getObject().getKey()));
+                target.add(authProfileModal.setContent(new ModalDirectoryPanel<>(
+                        authProfileModal,
+                        new AuthProfileItemDirectoryPanel<WebAuthnDeviceCredential>(
+                                "panel", authProfileModal, model.getObject(), pageRef) {
+
+                    @Override
+                    protected List<WebAuthnDeviceCredential> getItems() {
+                        return model.getObject().getWebAuthnDeviceCredentials();
+                    }
+
+                    @Override
+                    protected WebAuthnDeviceCredential defaultItem() {
+                        return new WebAuthnDeviceCredential();
+                    }
+
+                    @Override
+                    protected String sortProperty() {
+                        return "identifier";
+                    }
+
+                    @Override
+                    protected String paginatorRowsKey() {
+                        return AMConstants.PREF_AUTHPROFILE_WEBAUTHNDEVICECREDENTIALS_PAGINATOR_ROWS;
+                    }
+
+                    @Override
+                    protected List<IColumn<WebAuthnDeviceCredential, String>> getColumns() {
+                        List<IColumn<WebAuthnDeviceCredential, String>> columns = new ArrayList<>();
+                        columns.add(new PropertyColumn<>(new ResourceModel("identifier"), "identifier", "identifier"));
+                        columns.add(new PropertyColumn<>(new ResourceModel("json"), "json", "json"));
+                        return columns;
+                    }
+                }, pageRef)));
+                authProfileModal.header(new Model<>(getString("webAuthnDeviceCredentials", model)));
+                authProfileModal.show(true);
+            }
+        }, ActionLink.ActionType.HTML, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<AuthProfileTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final AuthProfileTO ignore) {
+                try {
+                    AuthProfileRestClient.delete(model.getObject().getKey());
+
+                    SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (Exception e) {
+                    LOG.error("While deleting {}", model.getObject().getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, AMEntitlement.AUTH_PROFILE_DELETE, true);
+
+        return panel;
+    }
+
+    protected static final class AuthProfileProvider extends DirectoryDataProvider<AuthProfileTO> {
+
+        private static final long serialVersionUID = -185944053385660794L;
+
+        private AuthProfileProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("owner", SortOrder.ASCENDING);
+        }
+
+        @Override
+        public Iterator<AuthProfileTO> iterator(final long first, final long count) {
+            int page = ((int) first / paginatorRows);
+            return AuthProfileRestClient.list((page < 0 ? 0 : page) + 1, paginatorRows).iterator();
+        }
+
+        @Override
+        public long size() {
+            return AuthProfileRestClient.count();
+        }
+
+        @Override
+        public IModel<AuthProfileTO> model(final AuthProfileTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+
+    private class CreateAuthProfileWizardBuilder extends AuthProfileWizardBuilder<AuthProfileTO> {
+
+        private static final long serialVersionUID = 1L;
+
+        private class NewAuthProfileStep extends AuthProfileWizardBuilder<AuthProfileTO>.Step {
+
+            private static final long serialVersionUID = 1L;
+
+            NewAuthProfileStep(final AuthProfileTO modelObject) {
+                super(modelObject);
+
+                AjaxTextFieldPanel owner = new AjaxTextFieldPanel(
+                        "bean", "owner", new PropertyModel<>(modelObject, "owner"));
+                owner.addRequiredLabel();
+                addOrReplace(owner);
+            }
+        }
+
+        CreateAuthProfileWizardBuilder(final PageReference pageRef) {
+            super(new AuthProfileTO(), new StepModel<>(), pageRef);
+        }
+
+        @Override
+        protected WizardModel buildModelSteps(final AuthProfileTO modelObject, final WizardModel wizardModel) {
+            wizardModel.add(new NewAuthProfileStep(modelObject));
+            return wizardModel;
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final AuthProfileTO modelObject) {
+            AuthProfileRestClient.create(modelObject);
+            return modelObject;
+        }
+    }
+}
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java
new file mode 100644
index 0000000..87d3951
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.java
@@ -0,0 +1,220 @@
+/*
+ * 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.client.console.authprofiles;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.rest.AuthProfileRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
+import org.apache.syncope.common.lib.BaseBean;
+import org.apache.syncope.common.lib.to.AuthProfileTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.PropertyAccessorFactory;
+
+public abstract class AuthProfileItemDirectoryPanel<I extends BaseBean>
+        extends DirectoryPanel<I, I, AuthProfileItemDirectoryPanel<I>.AuthProfileItemProvider, AuthProfileRestClient> {
+
+    private static final long serialVersionUID = 1L;
+
+    private final BaseModal<AuthProfileTO> authProfileModal;
+
+    private final AuthProfileTO authProfile;
+
+    public AuthProfileItemDirectoryPanel(
+            final String id,
+            final BaseModal<AuthProfileTO> authProfileModal,
+            final AuthProfileTO authProfile,
+            final PageReference pageRef) {
+
+        super(id, pageRef, false);
+
+        this.authProfileModal = authProfileModal;
+        this.authProfile = authProfile;
+
+        setOutputMarkupId(true);
+
+        enableUtilityButton();
+        setFooterVisibility(false);
+
+        addNewItemPanelBuilder(new AuthProfileItemWizardBuilder(pageRef), true);
+
+        disableCheckBoxes();
+        initResultTable();
+    }
+
+    protected abstract List<I> getItems();
+
+    protected abstract I defaultItem();
+
+    protected abstract String sortProperty();
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof ExitEvent) {
+            AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            authProfileModal.close(target);
+        } else if (event.getPayload() instanceof AjaxWizard.EditItemActionEvent) {
+            @SuppressWarnings("unchecked")
+            AjaxWizard.EditItemActionEvent<?> payload = (AjaxWizard.EditItemActionEvent<?>) event.getPayload();
+            payload.getTarget().ifPresent(actionTogglePanel::close);
+        }
+        super.onEvent(event);
+    }
+
+    @Override
+    protected AuthProfileItemProvider dataProvider() {
+        return new AuthProfileItemProvider(sortProperty(), rows);
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return List.of();
+    }
+
+    @Override
+    public ActionsPanel<I> getActions(final IModel<I> model) {
+        ActionsPanel<I> panel = super.getActions(model);
+
+        panel.add(new ActionLink<I>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final I ignore) {
+                send(AuthProfileItemDirectoryPanel.this, Broadcast.EXACT,
+                        new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
+            }
+        }, ActionLink.ActionType.EDIT, AMEntitlement.AUTH_PROFILE_UPDATE);
+
+        panel.add(new ActionLink<I>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final I ignore) {
+                try {
+                    getItems().remove(model.getObject());
+                    AuthProfileRestClient.update(authProfile);
+
+                    SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (Exception e) {
+                    LOG.error("While deleting {} from {}", model.getObject(), authProfile.getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, AMEntitlement.AUTH_PROFILE_UPDATE, true);
+
+        return panel;
+    }
+
+    protected class AuthProfileItemProvider extends DirectoryDataProvider<I> {
+
+        private static final long serialVersionUID = 4725679400450513556L;
+
+        private final SortableDataProviderComparator<I> comparator;
+
+        AuthProfileItemProvider(final String sort, final int paginatorRows) {
+            super(paginatorRows);
+
+            setSort(sort, SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<I> iterator(final long first, final long count) {
+            List<I> list = getItems();
+            list.sort(comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return getItems().size();
+        }
+
+        @Override
+        public IModel<I> model(final I object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+
+    private class AuthProfileItemWizardBuilder extends AuthProfileWizardBuilder<I> {
+
+        private static final long serialVersionUID = 1L;
+
+        AuthProfileItemWizardBuilder(final PageReference pageRef) {
+            super(defaultItem(), new StepModel<>(), pageRef);
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final I modelObject) {
+            if (modelObject instanceof GoogleMfaAuthAccount) {
+                BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(modelObject);
+                @SuppressWarnings("unchecked")
+                List<Serializable> values = (List<Serializable>) wrapper.getPropertyValue("scratchCodes");
+                if (values != null) {
+                    List<Integer> converted = values.stream().map(value -> {
+                        if (value instanceof Integer) {
+                            return Integer.class.cast(value);
+                        }
+                        if (value instanceof String) {
+                            try {
+                                return Integer.valueOf((String) value);
+                            } catch (NumberFormatException e) {
+                                LOG.error("Could not convert to Integer: {}", value, e);
+                            }
+                        }
+                        return null;
+                    }).filter(Objects::nonNull).collect(Collectors.toList());
+                    wrapper.setPropertyValue("scratchCodes", converted);
+                }
+            }
+
+            getItems().remove(model.getInitialModelObject());
+            getItems().add(modelObject);
+            AuthProfileRestClient.update(authProfile);
+
+            return modelObject;
+        }
+    }
+}
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
new file mode 100644
index 0000000..ae9bf98
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.authprofiles;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.client.console.panels.BeanPanel;
+import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.model.Model;
+
+public abstract class AuthProfileWizardBuilder<T extends BaseBean> extends BaseAjaxWizardBuilder<T> {
+
+    private static final long serialVersionUID = 1L;
+
+    protected final StepModel<T> model;
+
+    public AuthProfileWizardBuilder(final T defaultItem, final StepModel<T> model, final PageReference pageRef) {
+        super(defaultItem, pageRef);
+        this.model = model;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final T modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new Step(modelObject));
+        return wizardModel;
+    }
+
+    protected static class StepModel<T extends BaseBean> extends Model<T> {
+
+        private static final long serialVersionUID = 1L;
+
+        private T initialModelObject;
+
+        public void setInitialModelObject(final T initialModelObject) {
+            this.initialModelObject = SerializationUtils.clone(initialModelObject);
+        }
+
+        public T getInitialModelObject() {
+            return initialModelObject;
+        }
+    }
+
+    protected class Step extends WizardStep {
+
+        private static final long serialVersionUID = -785981096328637758L;
+
+        Step(final T modelObject) {
+            model.setObject(modelObject);
+            model.setInitialModelObject(modelObject);
+            add(new BeanPanel<>("bean", model).setRenderBodyOnly(true));
+        }
+    }
+}
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
index 9190785..d3742ee 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java
@@ -114,7 +114,7 @@ public abstract class ClientAppDirectoryPanel<T extends ClientAppTO>
 
     @Override
     public ActionsPanel<T> getActions(final IModel<T> model) {
-        final ActionsPanel<T> panel = super.getActions(model);
+        ActionsPanel<T> panel = super.getActions(model);
 
         panel.add(new ActionLink<T>() {
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
index f1aa387..f6f522a 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
@@ -242,17 +242,12 @@ public class ClientAppModalPanelBuilder<T extends ClientAppTO> extends AbstractM
                     entityId.addValidator(new UrlValidator());
                     fields.add(entityId.setRequired(true));
 
-                    AjaxTextFieldPanel metadataLocation = new AjaxTextFieldPanel(
-                            "field", "metadataLocation",
-                            new PropertyModel<>(clientAppTO, "metadataLocation"), false);
-                    metadataLocation.addValidator(new UrlValidator());
-                    fields.add(metadataLocation.setRequired(true));
+                    fields.add(new AjaxTextFieldPanel("field", "metadataLocation",
+                            new PropertyModel<>(clientAppTO, "metadataLocation"), false).setRequired(true));
 
-                    AjaxTextFieldPanel metadataSignatureLocation = new AjaxTextFieldPanel(
+                    fields.add(new AjaxTextFieldPanel(
                             "field", "metadataSignatureLocation",
-                            new PropertyModel<>(clientAppTO, "metadataSignatureLocation"), false);
-                    metadataSignatureLocation.addValidator(new UrlValidator());
-                    fields.add(metadataSignatureLocation);
+                            new PropertyModel<>(clientAppTO, "metadataSignatureLocation"), false));
 
                     fields.add(new AjaxCheckBoxPanel(
                             "field", "signAssertions", new PropertyModel<>(clientAppTO, "signAssertions")));
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/OIDCRPDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/OIDCRPDirectoryPanel.java
index 3169d59..09bb878 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/OIDCRPDirectoryPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/OIDCRPDirectoryPanel.java
@@ -20,18 +20,14 @@ package org.apache.syncope.client.console.clientapps;
 
 import java.util.List;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanConditionColumn;
 import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.ClientAppType;
-import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
-import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.StringResourceModel;
 
@@ -56,19 +52,11 @@ public class OIDCRPDirectoryPanel extends ClientAppDirectoryPanel<OIDCRPClientAp
         columns.add(new PropertyColumn<>(new StringResourceModel("clientId", this), "clientId", "clientId"));
         columns.add(new PropertyColumn<>(
                 new StringResourceModel("redirectUris", this), "redirectUris", "redirectUris"));
-        columns.add(new AbstractColumn<OIDCRPClientAppTO, String>(new StringResourceModel("logout")) {
+        columns.add(new BooleanConditionColumn<OIDCRPClientAppTO>(new StringResourceModel("logout")) {
 
             @Override
-            public void populateItem(
-                    final Item<ICellPopulator<OIDCRPClientAppTO>> item,
-                    final String componentId,
-                    final IModel<OIDCRPClientAppTO> rowModel) {
-
-                item.add(new Label(componentId, StringUtils.EMPTY));
-                if (StringUtils.isNotBlank(rowModel.getObject().getLogoutUri())) {
-                    item.add(new AttributeModifier("class", "fa fa-check"));
-                    item.add(new AttributeModifier("style", "display: table-cell; text-align: center;"));
-                }
+            protected boolean isCondition(final IModel<OIDCRPClientAppTO> rowModel) {
+                return StringUtils.isNotBlank(rowModel.getObject().getLogoutUri());
             }
         });
     }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
index be7301e..564227c 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/commons/AMConstants.java
@@ -34,8 +34,24 @@ public final class AMConstants {
             "accesspolicy.conf.requiredattrs.paginator.rows";
 
     public static final String PREF_SAML2_IDP_ENTITY_PAGINATOR_ROWS = "saml2idpentity.properties.paginator.rows";
+
     public static final String PREF_SAML2_SP_ENTITY_PAGINATOR_ROWS = "saml2spentity.properties.paginator.rows";
 
+    public static final String PREF_AUTHPROFILE_PAGINATOR_ROWS = "authprofile.paginator.rows";
+
+    public static final String PREF_AUTHPROFILE_IMPERSONATED_PAGINATOR_ROWS = "authprofile.impersonated.paginator.rows";
+
+    public static final String PREF_AUTHPROFILE_GOOGLEMFAAUTHTOKENS_PAGINATOR_ROWS =
+            "authprofile.googlemfaauthtokens.paginator.rows";
+
+    public static final String PREF_AUTHPROFILE_GOOGLEMFAAUTHACCOUNTS_PAGINATOR_ROWS =
+            "authprofile.googlemfaauthaccounts.paginator.rows";
+
+    public static final String PREF_AUTHPROFILE_U2FDEVICES_PAGINATOR_ROWS = "authprofile.u2fdevices.paginator.rows";
+
+    public static final String PREF_AUTHPROFILE_WEBAUTHNDEVICECREDENTIALS_PAGINATOR_ROWS =
+            "authprofile.webAuthnDeviceCredentials.paginator.rows";
+
     private AMConstants() {
         // private constructor for static utility class
     }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
index 4009f64..30312b4 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
@@ -32,6 +32,7 @@ import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.annotations.AMPage;
+import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel;
 import org.apache.syncope.client.console.panels.AuthModuleDirectoryPanel;
 import org.apache.syncope.client.console.clientapps.ClientApps;
 import org.apache.syncope.client.console.panels.OIDC;
@@ -197,7 +198,7 @@ public class WA extends BasePage {
 
                 @Override
                 public Panel getPanel(final String panelId) {
-                    return new AjaxTextFieldPanel(panelId, panelId, Model.of(""));
+                    return new AuthProfileDirectoryPanel(panelId, getPageReference());
                 }
             });
         }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
index 0749077..5cb21fe 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
@@ -88,7 +88,7 @@ public class AuthModuleDirectoryPanel
 
     @Override
     protected List<IColumn<AuthModuleTO, String>> getColumns() {
-        final List<IColumn<AuthModuleTO, String>> columns = new ArrayList<>();
+        List<IColumn<AuthModuleTO, String>> columns = new ArrayList<>();
         columns.add(new PropertyColumn<>(
                 new StringResourceModel(Constants.KEY_FIELD_NAME, this),
                 Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME));
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
similarity index 51%
copy from core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
copy to client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
index 58e7a63..d526783 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AuthProfileRestClient.java
@@ -16,44 +16,37 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.rest.cxf.service;
+package org.apache.syncope.client.console.rest;
 
+import java.util.List;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
 import org.apache.syncope.common.rest.api.service.AuthProfileService;
-import org.apache.syncope.core.logic.AuthProfileLogic;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
 
-import java.util.List;
+public class AuthProfileRestClient extends BaseRestClient {
 
-@Service
-public class AuthProfileServiceImpl extends AbstractServiceImpl implements AuthProfileService {
+    private static final long serialVersionUID = -7379778542101161274L;
 
-    @Autowired
-    private AuthProfileLogic logic;
+    public static int count() {
+        return getService(AuthProfileService.class).list(1, 1).getTotalCount();
+    }
 
-    @Override
-    public void delete(final String key) {
-        logic.delete(key);
+    public static List<AuthProfileTO> list(final int page, final int size) {
+        return getService(AuthProfileService.class).list(page, size).getResult();
     }
 
-    @Override
-    public void deleteByOwner(final String owner) {
-        logic.deleteByOwner(owner);
+    public static AuthProfileTO read(final String key) {
+        return getService(AuthProfileService.class).read(key);
     }
 
-    @Override
-    public AuthProfileTO readByOwner(final String owner) {
-        return logic.readByOwner(owner);
+    public static void create(final AuthProfileTO authProfile) {
+        getService(AuthProfileService.class).create(authProfile);
     }
 
-    @Override
-    public AuthProfileTO read(final String key) {
-        return logic.read(key);
+    public static void update(final AuthProfileTO authProfile) {
+        getService(AuthProfileService.class).update(authProfile);
     }
 
-    @Override
-    public List<AuthProfileTO> list() {
-        return logic.list();
+    public static void delete(final String key) {
+        getService(AuthProfileService.class).delete(key);
     }
 }
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 d8d44a9..8aeb245 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
@@ -130,7 +130,7 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
         }
     }
 
-    public static class Configuration extends WizardStep {
+    private static class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
index 0e1af8c..58c63a5 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
@@ -28,6 +28,8 @@ import org.apache.wicket.model.util.ListModel;
 
 public class AuthModuleMappingPanel extends AbstractMappingPanel {
 
+    private static final long serialVersionUID = 1L;
+
     public AuthModuleMappingPanel(final String id, final AuthModuleTO authModule) {
         super(id,
                 null,
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.properties
new file mode 100644
index 0000000..e095d91
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel.properties
@@ -0,0 +1,46 @@
+# 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.
+any.edit=Edit Parameter ${key}
+any.new=New Auth Proile
+any.edit=Edit Auth Profile
+owner=Owner
+impersonationAccounts=Impersonations
+googleMfaAuthTokens=Google MFA Tokens
+googleMfaAuthAccounts=Google MFA Accounts
+u2fRegisteredDevices=U2F Devices
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonations
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Issue Date
+edit_approval.class=fas fa-passport
+edit_approval.title=google mfa tokens
+id=Id
+registrationDate=Registration Date
+execute.class=fas fa-user-circle
+execute.title=google mfa accounts
+secretKey=Secret Key
+validationCode=Validation Code
+scratchCodes=Scratch Codes
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=u2f Devices
+identifier=Identifier
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=WebAuthn Device Credentials
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_fr_CA.properties
new file mode 100644
index 0000000..8a42e53
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_fr_CA.properties
@@ -0,0 +1,46 @@
+# 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.
+any.edit=Modifier param\u00e8tre ${key}
+any.new=New Auth Proile
+any.edit=Edit Auth Profile
+owner=Owner
+impersonationAccounts=Impersonations
+googleMfaAuthTokens=Google MFA Tokens
+googleMfaAuthAccounts=Google MFA Accounts
+u2fRegisteredDevices=U2F Devices
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonations
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Issue Date
+edit_approval.class=fas fa-passport
+edit_approval.title=token google mfa
+id=Id
+registrationDate=Registration Date
+execute.class=fas fa-user-circle
+execute.title=google mfa accounts
+secretKey=Secret Key
+validationCode=Validation Code
+scratchCodes=Scratch Codes
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=u2f Devices
+identifier=Identifier
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=WebAuthn Device Credentials
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_it.properties
new file mode 100644
index 0000000..0fd02d5
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_it.properties
@@ -0,0 +1,46 @@
+# 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.
+any.edit=Modifica parametro ${key}
+any.new=Nuovo Profilo Autenticazione
+any.edit=Modifica Profilo Autenticazione
+owner=Proprietario
+impersonationAccounts=Impersonificazioni
+googleMfaAuthTokens=Token Google MFA
+googleMfaAuthAccounts=Account Google MFA
+u2fRegisteredDevices=Dispositivi U2F
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonificazioni
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Emissione
+edit_approval.class=fas fa-passport
+edit_approval.title=token google mfa
+id=Id
+registrationDate=Data Registrazione
+execute.class=fas fa-user-circle
+execute.title=account google mfa
+secretKey=Chiave Segreta
+validationCode=Codice Validazione
+scratchCodes=Codici Scratch
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=dispositivi u2f
+identifier=Identificativo
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=Dispositivi Credenziali WebAuthn
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ja.properties
new file mode 100644
index 0000000..d2fc569
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ja.properties
@@ -0,0 +1,46 @@
+# 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.
+any.edit=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc ${key} \u3092\u7de8\u96c6
+any.new=New Auth Proile
+any.edit=Edit Auth Profile
+owner=Owner
+impersonationAccounts=Impersonations
+googleMfaAuthTokens=Google MFA Tokens
+googleMfaAuthAccounts=Google MFA Accounts
+u2fRegisteredDevices=U2F Devices
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonations
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Issue Date
+edit_approval.class=fas fa-passport
+edit_approval.title=token google mfa
+id=Id
+registrationDate=Registration Date
+execute.class=fas fa-user-circle
+execute.title=google mfa accounts
+secretKey=Secret Key
+validationCode=Validation Code
+scratchCodes=Scratch Codes
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=u2f Devices
+identifier=Identifier
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=WebAuthn Device Credentials
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..8d919ed
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_pt_BR.properties
@@ -0,0 +1,46 @@
+# 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.
+any.edit=Editar par\u00e2metro
+any.new=New Auth Proile
+any.edit=Edit Auth Profile
+owner=Owner
+impersonationAccounts=Impersonations
+googleMfaAuthTokens=Google MFA Tokens
+googleMfaAuthAccounts=Google MFA Accounts
+u2fRegisteredDevices=U2F Devices
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonations
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Issue Date
+edit_approval.class=fas fa-passport
+edit_approval.title=token google mfa
+id=Id
+registrationDate=Registration Date
+execute.class=fas fa-user-circle
+execute.title=google mfa accounts
+secretKey=Secret Key
+validationCode=Validation Code
+scratchCodes=Scratch Codes
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=u2f Devices
+identifier=Identifier
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=WebAuthn Device Credentials
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ru.properties
new file mode 100644
index 0000000..b638d45
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileDirectoryPanel_ru.properties
@@ -0,0 +1,47 @@
+# 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.
+#
+any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ${key}
+any.new=New Auth Proile
+any.edit=Edit Auth Profile
+owner=Owner
+impersonationAccounts=Impersonations
+googleMfaAuthTokens=Google MFA Tokens
+googleMfaAuthAccounts=Google MFA Accounts
+u2fRegisteredDevices=U2F Devices
+webAuthnAccount=WebAuthn
+type_extensions.title=impersonations
+type_extensions.class=fas fa-user-friends
+otp=One Time Password
+issueDate=Issue Date
+edit_approval.class=fas fa-passport
+edit_approval.title=token google mfa
+id=Id
+registrationDate=Registration Date
+execute.class=fas fa-user-circle
+execute.title=google mfa accounts
+secretKey=Secret Key
+validationCode=Validation Code
+scratchCodes=Scratch Codes
+record=Record
+fo_edit.class=fab fa-usb
+fo_edit.title=u2f Devices
+identifier=Identifier
+json=JSON
+html.class=fas fa-at
+html.title=webauthn
+webAuthnDeviceCredentials=WebAuthn Device Credentials
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.properties
new file mode 100644
index 0000000..0136508
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+any.edit=Edit Parameter ${key}
+any.new=New Impersonation
+any.edit=Edit Impersonation
+impersonated=Impersonated
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_fr_CA.properties
new file mode 100644
index 0000000..f100e8e
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_fr_CA.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+any.edit=Modifier param\u00e8tre ${key}
+any.new=New Impersonation
+any.edit=Edit Impersonation
+impersonated=Impersonated
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_it.properties
new file mode 100644
index 0000000..8bd6cc7
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_it.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+any.edit=Modifica parametro ${key}
+any.new=Nuova Impersonificazione
+any.edit=Modifica Impersonificazione
+impersonated=Impersonato
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ja.properties
new file mode 100644
index 0000000..0a3db3f
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ja.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+any.edit=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc ${key} \u3092\u7de8\u96c6
+any.new=New Impersonation
+any.edit=Edit Impersonation
+impersonated=Impersonated
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..e4ba52c
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_pt_BR.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   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.
+any.edit=Editar par\u00e2metro
+any.new=New Impersonation
+any.edit=Edit Impersonation
+impersonated=Impersonated
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ru.properties
new file mode 100644
index 0000000..c44ebf9
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileItemDirectoryPanel_ru.properties
@@ -0,0 +1,21 @@
+# 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.
+#
+any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ${key}
+any.new=New Impersonation
+any.edit=Edit Impersonation
+impersonated=Impersonated
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder$Step.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder$Step.html
new file mode 100644
index 0000000..7772c93
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder$Step.html
@@ -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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="bean"/>
+  </wicket:panel>
+</html>
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
index 3e1b62b..c184cff 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
@@ -83,8 +83,6 @@ public class LinkedAccountModalPanel extends Panel implements ModalPanel {
 
     private UserRestClient userRestClient = new UserRestClient();
 
-    private AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient();
-
     private final List<LinkedAccountTO> linkedAccountTOs;
 
     @SuppressWarnings("unchecked")
@@ -93,7 +91,7 @@ public class LinkedAccountModalPanel extends Panel implements ModalPanel {
             final PageReference pageRef,
             final boolean recounciliationOnly) {
 
-        super(BaseModal.getContentId(), model);
+        super(BaseModal.CONTENT_ID, model);
 
         final MultilevelPanel mlp = new MultilevelPanel("mlpContainer");
         mlp.setOutputMarkupId(true);
@@ -104,7 +102,7 @@ public class LinkedAccountModalPanel extends Panel implements ModalPanel {
         add(actionTogglePanel);
 
         AnyLayout anyLayout = AnyLayoutUtils.fetch(
-                anyTypeRestClient.listAnyTypes().stream().map(EntityTO::getKey).collect(Collectors.toList()));
+                AnyTypeRestClient.listAnyTypes().stream().map(EntityTO::getKey).collect(Collectors.toList()));
         LinkedAccountFormLayoutInfo linkedAccountFormLayoutInfo =
                 anyLayout.getUser() instanceof IdMUserFormLayoutInfo
                 ? IdMUserFormLayoutInfo.class.cast(anyLayout.getUser()).getLinkedAccountFormLayoutInfo()
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
index ca67ed0..a368a95 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java
@@ -46,7 +46,6 @@ import org.apache.syncope.client.console.rest.ConnectorRestClient;
 import org.apache.syncope.client.console.rest.ResourceRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.WebMarkupContainerNoVeil;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
@@ -151,15 +150,7 @@ public class Topology extends BasePage {
     public Topology() {
         modal = new BaseModal<>("resource-modal");
         body.add(modal.size(Modal.Size.Large));
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                modal.show(false);
-            }
-        });
+        modal.setWindowClosedCallback(target -> modal.show(false));
 
         TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior();
         body.add(websocket);
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index 9e7c0a3..fec019e 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -27,7 +27,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.audit.AuditHistoryModal;
 import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.ConnObjects;
 import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
 import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
@@ -40,12 +39,12 @@ import org.apache.syncope.client.console.tasks.PushTasks;
 import org.apache.syncope.client.console.tasks.SchedTasks;
 import org.apache.syncope.client.console.tasks.PullTasks;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.IndicatingOnConfirmAjaxLink;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.console.wizards.resources.AbstractResourceWizardBuilder;
 import org.apache.syncope.client.console.wizards.resources.ResourceProvision;
 import org.apache.syncope.client.console.wizards.resources.ResourceProvisionPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.ItemTO;
@@ -127,15 +126,9 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
     public void toggleWithContent(final AjaxRequestTarget target, final TopologyNode node) {
         setHeader(target, node.getDisplayName());
 
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                modal.show(false);
-                send(pageRef.getPage(), Broadcast.DEPTH, new UpdateEvent(node.getKey(), target));
-            }
+        modal.setWindowClosedCallback(t -> {
+            modal.show(false);
+            send(pageRef.getPage(), Broadcast.DEPTH, new UpdateEvent(node.getKey(), t));
         });
 
         switch (node.getKind()) {
@@ -194,7 +187,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                     LOG.error("While reloading all connectors", e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
         };
         fragment.add(reload);
@@ -273,7 +266,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                     LOG.error("While deleting resource {}", node.getKey(), e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
         };
         MetaDataRoleAuthorizationStrategy.authorize(delete, RENDER, IdMEntitlement.CONNECTOR_DELETE);
@@ -369,7 +362,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                             LOG.error("While restoring connector {}", node.getKey(), e);
                             SyncopeConsoleSession.get().onException(e);
                         }
-                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
                     }
                 }));
 
@@ -409,7 +402,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                     LOG.error("While deleting resource {}", node.getKey(), e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
         };
         MetaDataRoleAuthorizationStrategy.authorize(delete, RENDER, IdMEntitlement.RESOURCE_DELETE);
@@ -442,7 +435,6 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             public String getAjaxIndicatorMarkupId() {
                 return Constants.VEIL_INDICATOR_MARKUP_ID;
             }
-
         };
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, IdMEntitlement.RESOURCE_READ);
         fragment.add(edit);
@@ -615,7 +607,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                             LOG.error("While restoring resource {}", node.getKey(), e);
                             SyncopeConsoleSession.get().onException(e);
                         }
-                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
                     }
                 }));
 
@@ -677,7 +669,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                     LOG.error("While cloning resource {}", node.getKey(), e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
 
         };
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceProvisionPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceProvisionPanel.java
index 3952bec..34305fc 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceProvisionPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceProvisionPanel.java
@@ -26,7 +26,6 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.AbstractModalPanel;
 import org.apache.syncope.client.console.panels.ListViewPanel;
 import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
@@ -37,6 +36,7 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.ItemTO;
@@ -84,7 +84,7 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
 
         wizard = new ProvisionWizardBuilder(resourceTO, adminRealm, pageRef);
 
-        final ListViewPanel.Builder<ResourceProvision> builder = new ListViewPanel.Builder<ResourceProvision>(
+        ListViewPanel.Builder<ResourceProvision> builder = new ListViewPanel.Builder<ResourceProvision>(
                 ResourceProvision.class, pageRef) {
 
             private static final long serialVersionUID = 4907732721283972943L;
@@ -94,12 +94,12 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
                     final ResourceProvision item, final List<ResourceProvision> list) {
 
                 return Optional.ofNullable(item)
-                    .map(resourceProvision -> list.stream()
+                        .map(resourceProvision -> list.stream()
                         .filter(in -> ((resourceProvision.getKey() == null && in.getKey() == null)
-                    || (in.getKey() != null && in.getKey().equals(resourceProvision.getKey())))
-                    && ((resourceProvision.getAnyType() == null && in.getAnyType() == null)
-                    || (in.getAnyType() != null && in.getAnyType().equals(resourceProvision.getAnyType())))).
-                    findAny().orElse(null)).orElse(null);
+                        || (in.getKey() != null && in.getKey().equals(resourceProvision.getKey())))
+                        && ((resourceProvision.getAnyType() == null && in.getAnyType() == null)
+                        || (in.getAnyType() != null && in.getAnyType().equals(resourceProvision.getAnyType())))).
+                        findAny().orElse(null)).orElse(null);
             }
 
             @Override
@@ -158,7 +158,7 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
                 } catch (SyncopeClientException e) {
                     LOG.error("While contacting resource", e);
                     SyncopeConsoleSession.get().onException(e);
-                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                    ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
                 }
             }
         }, ActionLink.ActionType.MAPPING, IdMEntitlement.RESOURCE_READ).
@@ -176,7 +176,7 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
                                     resourceTO.getKey(), provision.getAnyType(), e);
                             SyncopeConsoleSession.get().onException(e);
                         }
-                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
                     }
                 }, ActionLink.ActionType.SET_LATEST_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
                 addAction(new ActionLink<ResourceProvision>() {
@@ -193,7 +193,7 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
                                     resourceTO.getKey(), provision.getAnyType(), e);
                             SyncopeConsoleSession.get().onException(e);
                         }
-                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
                     }
                 }, ActionLink.ActionType.REMOVE_SYNC_TOKEN, IdMEntitlement.RESOURCE_UPDATE).
                 addAction(new ActionLink<ResourceProvision>() {
@@ -291,7 +291,7 @@ public class ResourceProvisionPanel extends AbstractModalPanel<Serializable> {
             LOG.error("While creating or updating {}", resourceTO, e);
             SyncopeConsoleSession.get().onException(e);
         }
-        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+        ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
     }
 
     private void sortProvisions() {
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
index 3a86f00..5e45a56 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
@@ -32,6 +32,7 @@ import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.ResourceModel;
+import org.springframework.util.CollectionUtils;
 
 public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<INNER>> {
 
@@ -64,8 +65,8 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
 
         view = new InnerView("view", name, model);
 
-        final List<INNER> obj = model.getObject();
-        if (obj == null || obj.isEmpty()) {
+        List<INNER> obj = model.getObject();
+        if (CollectionUtils.isEmpty(obj)) {
             form.addOrReplace(getNoDataFragment(model, name));
         } else {
             form.addOrReplace(getDataFragment());
@@ -79,20 +80,20 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
     }
 
     private Fragment getNoDataFragment(final IModel<List<INNER>> model, final String label) {
-        final Fragment fragment = new Fragment("content", "noDataFragment", AbstractMultiPanel.this);
+        Fragment fragment = new Fragment("content", "noDataFragment", AbstractMultiPanel.this);
         fragment.add(new Label("field-label", new ResourceModel(label, label)));
         fragment.add(getPlusFragment(model));
         return fragment;
     }
 
     private Fragment getDataFragment() {
-        final Fragment contentFragment = new Fragment("content", "dataFragment", AbstractMultiPanel.this);
+        Fragment contentFragment = new Fragment("content", "dataFragment", AbstractMultiPanel.this);
         contentFragment.add(view.setOutputMarkupId(true));
         return contentFragment;
     }
 
     private Fragment getPlusFragment(final IModel<List<INNER>> model) {
-        final IndicatorAjaxSubmitLink plus = new IndicatorAjaxSubmitLink("add") {
+        IndicatorAjaxSubmitLink plus = new IndicatorAjaxSubmitLink("add") {
 
             private static final long serialVersionUID = -7978723352517770644L;
 
@@ -117,7 +118,7 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
 
         };
 
-        final Fragment fragment = new Fragment("panelPlus", "fragmentPlus", AbstractMultiPanel.this);
+        Fragment fragment = new Fragment("panelPlus", "fragmentPlus", AbstractMultiPanel.this);
         fragment.addOrReplace(plus);
         fragment.setRenderBodyOnly(true);
 
@@ -154,12 +155,11 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
 
         @Override
         protected void populateItem(final ListItem<INNER> item) {
-
-            final Panel panel = getItemPanel(item);
+            Panel panel = getItemPanel(item);
 
             item.add(panel.setRenderBodyOnly(true));
 
-            final IndicatorAjaxSubmitLink minus = new IndicatorAjaxSubmitLink("drop") {
+            IndicatorAjaxSubmitLink minus = new IndicatorAjaxSubmitLink("drop") {
 
                 private static final long serialVersionUID = -7978723352517770644L;
 
@@ -184,12 +184,9 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
 
             item.add(minus);
 
-            final Fragment fragment;
-            if (item.getIndex() == model.getObject().size() - 1) {
-                fragment = getPlusFragment(model);
-            } else {
-                fragment = new Fragment("panelPlus", "emptyFragment", AbstractMultiPanel.this);
-            }
+            Fragment fragment = item.getIndex() == model.getObject().size() - 1
+                    ? getPlusFragment(model)
+                    : new Fragment("panelPlus", "emptyFragment", AbstractMultiPanel.this);
 
             item.add(fragment.setRenderBodyOnly(true));
         }
diff --git a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
index 4ef7ebe..3aa1d53 100644
--- a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
+++ b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
@@ -1039,7 +1039,7 @@ fieldset.input-group {
 
 .modal-content div.toggle-menu {
   position: fixed;
-  right: 20%;
+  right: 200px;
 }
 
 .modal-body .box-body div.background-footer {
@@ -1243,4 +1243,4 @@ pre {
   border: none;
   float: right;
   margin-right:12px;
-}
\ No newline at end of file
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
index 7860a69..84a2910 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
@@ -30,7 +30,6 @@ import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.Bas
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.console.wizards.any.ResultPage;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
@@ -110,27 +109,15 @@ public class Realms extends BasePage {
         templateModal.size(Modal.Size.Large);
         content.add(templateModal);
 
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                target.add(realmChoicePanel.reloadRealmTree(target));
-                target.add(content);
-                modal.show(false);
-            }
+        modal.setWindowClosedCallback(target -> {
+            target.add(realmChoicePanel.reloadRealmTree(target));
+            target.add(content);
+            modal.show(false);
         });
 
-        templateModal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                target.add(content);
-                templateModal.show(false);
-            }
+        templateModal.setWindowClosedCallback(target -> {
+            target.add(content);
+            templateModal.show(false);
         });
 
         updateRealmContent(realmChoicePanel.getCurrentRealm(), parameters.get("selectedIndex").toInt(0));
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AbstractModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AbstractModalPanel.java
index 2b1ee65..7e04028 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AbstractModalPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AbstractModalPanel.java
@@ -47,7 +47,7 @@ public class AbstractModalPanel<T extends Serializable> extends Panel
     protected final PageReference pageRef;
 
     public AbstractModalPanel(final BaseModal<T> modal, final PageReference pageRef) {
-        this(BaseModal.getContentId(), modal, pageRef);
+        this(BaseModal.CONTENT_ID, modal, pageRef);
     }
 
     public AbstractModalPanel(final String id, final BaseModal<T> modal, final PageReference pageRef) {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
index ea20d49..55184d8 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
@@ -35,7 +35,6 @@ import org.apache.syncope.client.ui.commons.rest.RestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CheckGroupColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.AjaxFallbackDataTable;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.wicket.AttributeModifier;
@@ -176,22 +175,16 @@ public final class AjaxDataTablePanel<T extends Serializable, S> extends DataTab
         batchModal.size(Modal.Size.Default);
         add(batchModal);
 
-        batchModal.setWindowClosedCallback(new WindowClosedCallback() {
+        batchModal.setWindowClosedCallback(target -> {
+            batchModal.show(false);
 
-            private static final long serialVersionUID = 8804221891699487149L;
+            EventDataWrapper data = new EventDataWrapper();
+            data.setTarget(target);
+            data.setRows(builder.rowsPerPage);
 
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                batchModal.show(false);
-
-                EventDataWrapper data = new EventDataWrapper();
-                data.setTarget(target);
-                data.setRows(builder.rowsPerPage);
-
-                send(builder.pageRef.getPage(), Broadcast.BREADTH, data);
-                Optional.ofNullable((BasePage) findPage()).
-                        ifPresent(page -> page.getNotificationPanel().refresh(target));
-            }
+            send(builder.pageRef.getPage(), Broadcast.BREADTH, data);
+            Optional.ofNullable((BasePage) findPage()).
+                    ifPresent(page -> page.getNotificationPanel().refresh(target));
         });
 
         Fragment fragment = new Fragment("tablePanel", "batchAvailable", this);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
index eef709e..508f965 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
@@ -26,7 +26,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
@@ -87,6 +86,7 @@ public class BeanPanel<T extends Serializable> extends Panel {
             final IModel<T> bean,
             final Map<String, Pair<AbstractFiqlSearchConditionBuilder<?, ?, ?>, List<SearchClause>>> sCondWrapper,
             final String... excluded) {
+
         super(id, bean);
         setOutputMarkupId(true);
 
@@ -118,48 +118,44 @@ public class BeanPanel<T extends Serializable> extends Panel {
 
             private static final long serialVersionUID = 9101744072914090143L;
 
-            @SuppressWarnings({"unchecked", "rawtypes"})
+            @SuppressWarnings({ "unchecked", "rawtypes" })
             @Override
             protected void populateItem(final ListItem<String> item) {
-                final String fieldName = item.getModelObject();
+                String fieldName = item.getModelObject();
 
                 item.add(new Label("fieldName", new ResourceModel(fieldName, fieldName)));
 
                 Field field = ReflectionUtils.findField(bean.getObject().getClass(), fieldName);
-
                 if (field == null) {
                     return;
                 }
 
-                final SearchCondition scondAnnot = field.getAnnotation(SearchCondition.class);
-                final Schema schemaAnnot = field.getAnnotation(Schema.class);
+                SearchCondition scondAnnot = field.getAnnotation(SearchCondition.class);
+                Schema schemaAnnot = field.getAnnotation(Schema.class);
 
                 BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean.getObject());
 
                 Panel panel;
 
                 if (scondAnnot != null) {
-                    final String fiql = (String) wrapper.getPropertyValue(fieldName);
+                    String fiql = (String) wrapper.getPropertyValue(fieldName);
 
-                    final List<SearchClause> clauses;
-                    if (StringUtils.isEmpty(fiql)) {
-                        clauses = new ArrayList<>();
-                    } else {
-                        clauses = SearchUtils.getSearchClauses(fiql);
-                    }
+                    List<SearchClause> clauses = SearchUtils.getSearchClauses(fiql);
 
-                    final AbstractFiqlSearchConditionBuilder<?, ?, ?> builder;
+                    AbstractFiqlSearchConditionBuilder<?, ?, ?> builder;
                     switch (scondAnnot.type()) {
                         case "USER":
                             panel = new UserSearchPanel.Builder(
                                     new ListModel<>(clauses)).required(false).build("value");
                             builder = SyncopeClient.getUserSearchConditionBuilder();
                             break;
+
                         case "GROUP":
                             panel = new GroupSearchPanel.Builder(
                                     new ListModel<>(clauses)).required(false).build("value");
                             builder = SyncopeClient.getGroupSearchConditionBuilder();
                             break;
+
                         default:
                             panel = new AnyObjectSearchPanel.Builder(
                                     scondAnnot.type(),
@@ -216,7 +212,7 @@ public class BeanPanel<T extends Serializable> extends Panel {
                                 new PropertyModel<>(bean.getObject(), fieldName)).build(
                                 "value",
                                 fieldName,
-                                buildSinglePanel(bean.getObject(), field.getType(), fieldName, "panel")).hideLabel();
+                                buildSinglePanel(bean.getObject(), listItemType, fieldName, "panel")).hideLabel();
                     }
                 } else if (Map.class.equals(field.getType())) {
                     panel = new AjaxGridFieldPanel(
@@ -227,11 +223,10 @@ public class BeanPanel<T extends Serializable> extends Panel {
 
                 item.add(panel.setRenderBodyOnly(true));
             }
-
         }.setReuseItems(true).setOutputMarkupId(true));
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     private static FieldPanel buildSinglePanel(
             final Serializable bean, final Class<?> type, final String fieldName, final String id) {
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
index b242a51..86a1414 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/NetworkServiceDirectoryPanel.java
@@ -27,13 +27,11 @@ import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
 import org.apache.syncope.client.console.commons.IdRepoConstants;
 import org.apache.syncope.client.console.panels.NetworkServiceDirectoryPanel.NetworkServiceProvider;
 import org.apache.syncope.client.console.rest.SyncopeRestClient;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.wicket.PageReference;
-import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
@@ -62,15 +60,9 @@ public class NetworkServiceDirectoryPanel extends DirectoryPanel<
 
         modal.size(Modal.Size.Large);
         modal.addSubmitButton();
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                updateResultTable(target);
-                modal.show(false);
-            }
+        modal.setWindowClosedCallback(target -> {
+            updateResultTable(target);
+            modal.show(false);
         });
         setFooterVisibility(true);
 
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 63a35ee..fc5110a 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
@@ -33,7 +33,6 @@ import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.client.console.status.ChangePasswordModal;
 import org.apache.syncope.client.console.tasks.AnyPropagationTasks;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
@@ -71,15 +70,9 @@ public class UserDirectoryPanel extends AnyDirectoryPanel<UserTO, UserRestClient
     protected UserDirectoryPanel(final String id, final Builder builder, final boolean wizardInModal) {
         super(id, builder, wizardInModal);
 
-        altDefaultModal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                updateResultTable(target);
-                modal.show(false);
-            }
+        altDefaultModal.setWindowClosedCallback(target -> {
+            updateResultTable(target);
+            modal.show(false);
         });
     }
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/MapOfListModel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/MapOfListModel.java
index 372a494..ea22708 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/MapOfListModel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/MapOfListModel.java
@@ -46,9 +46,9 @@ public class MapOfListModel<T> extends PropertyModel<List<T>> {
             throw new IllegalArgumentException("Property expressions cannot start with a '.' character");
         }
 
-        final Map<String, List<T>> map = (Map<String, List<T>>) PropertyResolver.getValue(expression, target);
+        Map<String, List<T>> map = (Map<String, List<T>>) PropertyResolver.getValue(expression, target);
 
-        final List<T> res;
+        List<T> res;
         if (map.containsKey(key)) {
             res = map.get(key);
         } else {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
index abc716a..a965f2a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
@@ -906,6 +906,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                 ((AjaxTextFieldPanel) value).setChoices(Arrays.asList("true", "false"));
 
                 break;
+
             case Date:
                 SimpleDateFormat df = DATE_FORMAT.get();
                 df.applyPattern(SyncopeConstants.DEFAULT_DATE_PATTERN);
@@ -972,6 +973,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                     });
                 }
                 break;
+
             case Long:
                 value = new AjaxSpinnerFieldPanel.Builder<Long>().enableOnChange().build(
                         "value",
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
index 7d854f0..1b7f008 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
@@ -30,7 +30,6 @@ import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
 import org.apache.syncope.client.console.commons.IdRepoConstants;
 import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
-import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.panels.DirectoryPanel;
 import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.ReportRestClient;
@@ -38,6 +37,7 @@ import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.Bas
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.ui.commons.panels.ModalPanel;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
@@ -161,7 +161,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
                     LOG.error("While deleting {}", reportlet.getName(), e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
         }, ActionLink.ActionType.DELETE, IdRepoEntitlement.REPORT_UPDATE);
 
@@ -261,7 +261,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
     public void onEvent(final IEvent<?> event) {
         super.onEvent(event);
         if (event.getPayload() instanceof ExitEvent) {
-            final AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
             baseModal.show(false);
             baseModal.close(target);
         }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanConditionColumn.java
similarity index 68%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java
copy to client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanConditionColumn.java
index 23bd500..bb79625 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanConditionColumn.java
@@ -21,35 +21,25 @@ package org.apache.syncope.client.console.wicket.extensions.markup.html.repeater
 import org.apache.commons.lang3.StringUtils;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
-import org.springframework.beans.BeanWrapper;
-import org.springframework.beans.BeanWrapperImpl;
 
-/**
- * Format column's value as boolean.
- *
- * @param <T> The Model object type
- */
-public class BooleanPropertyColumn<T> extends PropertyColumn<T, String> {
-
-    private static final long serialVersionUID = 3527840552172947705L;
+public abstract class BooleanConditionColumn<T> extends AbstractColumn<T, String> {
 
-    public BooleanPropertyColumn(final IModel<String> displayModel, final String sortProperty,
-            final String propertyExpression) {
+    private static final long serialVersionUID = 1L;
 
-        super(displayModel, sortProperty, propertyExpression);
+    public BooleanConditionColumn(final IModel<String> displayModel) {
+        super(displayModel);
     }
 
+    protected abstract boolean isCondition(IModel<T> rowModel);
+
     @Override
     public void populateItem(final Item<ICellPopulator<T>> item, final String componentId, final IModel<T> rowModel) {
-        BeanWrapper bwi = new BeanWrapperImpl(rowModel.getObject());
-        Object obj = bwi.getPropertyValue(getPropertyExpression());
-
         item.add(new Label(componentId, StringUtils.EMPTY));
-        if (obj != null && Boolean.valueOf(obj.toString())) {
+        if (isCondition(rowModel)) {
             item.add(new AttributeModifier("class", "fa fa-check"));
             item.add(new AttributeModifier("style", "display: table-cell; text-align: center;"));
         }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java
index 23bd500..da6d44a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/BooleanPropertyColumn.java
@@ -37,7 +37,9 @@ public class BooleanPropertyColumn<T> extends PropertyColumn<T, String> {
 
     private static final long serialVersionUID = 3527840552172947705L;
 
-    public BooleanPropertyColumn(final IModel<String> displayModel, final String sortProperty,
+    public BooleanPropertyColumn(
+            final IModel<String> displayModel,
+            final String sortProperty,
             final String propertyExpression) {
 
         super(displayModel, sortProperty, propertyExpression);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/DatePropertyColumn.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/DatePropertyColumn.java
index fec996b..8d93623 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/DatePropertyColumn.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/extensions/markup/html/repeater/data/table/DatePropertyColumn.java
@@ -33,7 +33,9 @@ public class DatePropertyColumn<T> extends PropertyColumn<T, String> {
 
     private static final long serialVersionUID = 3527840552172947705L;
 
-    public DatePropertyColumn(final IModel<String> displayModel, final String sortProperty,
+    public DatePropertyColumn(
+            final IModel<String> displayModel,
+            final String sortProperty,
             final String propertyExpression) {
 
         super(displayModel, sortProperty, propertyExpression);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
index f58bbb1..c6bcf9e 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
@@ -168,9 +168,9 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
     }
 
     private BaseModal<T> setInternalContent(final Panel component) {
-        if (!component.getId().equals(getContentId())) {
+        if (!component.getId().equals(CONTENT_ID)) {
             throw new WicketRuntimeException("Modal content id is wrong. "
-                    + "Component ID: " + component.getId() + "; content ID: " + getContentId());
+                    + "Component ID: " + component.getId() + "; content ID: " + CONTENT_ID);
         }
 
         content.replaceWith(component);
@@ -179,10 +179,6 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
         return this;
     }
 
-    public static String getContentId() {
-        return CONTENT_ID;
-    }
-
     public BaseModal<T> setWindowClosedCallback(final WindowClosedCallback callback) {
         windowClosedCallback = callback;
         return this;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
index 9497b6c..75df2b6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
@@ -47,7 +47,6 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.syncope.client.console.panels.ToggleableTarget;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
 import org.apache.syncope.common.lib.to.NamedEntityTO;
 
@@ -130,15 +129,7 @@ public class ActionLinksTogglePanel<T extends Serializable> extends TogglePanel<
 
         updateHeader(target, modelObject);
 
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                modal.show(false);
-            }
-        });
+        modal.setWindowClosedCallback(t -> modal.show(false));
 
         Fragment frag = new Fragment("actions", "actionsFragment", this);
         frag.setOutputMarkupId(true);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
index 525fcb9..73cce5b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
@@ -86,7 +86,7 @@ public abstract class MultiFieldPanel<E extends Serializable> extends AbstractMu
 
                 @Override
                 protected FieldPanel<? extends Serializable> getItemPanel(final ListItem<E> item) {
-                    final FieldPanel<? extends Serializable> fieldPanel = panelTemplate.clone();
+                    FieldPanel<? extends Serializable> fieldPanel = panelTemplate.clone();
                     fieldPanel.setIndex(item.getIndex());
                     fieldPanel.setNewModel(item);
                     fieldPanel.settingsDependingComponents();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
index 7c0ca80..d888ab8 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -27,7 +27,6 @@ import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wizards.any.ResultPage;
 import org.apache.wicket.Component;
@@ -47,6 +46,7 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.StringResourceModel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.panels.NotificationPanel;
 import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
 import org.apache.syncope.client.ui.commons.wizards.AbstractWizardMgtPanel;
@@ -170,15 +170,15 @@ public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWiz
     @SuppressWarnings("unchecked")
     public void onEvent(final IEvent<?> event) {
         if (event.getPayload() instanceof ExitEvent) {
-            final AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
+            AjaxRequestTarget target = ExitEvent.class.cast(event.getPayload()).getTarget();
             // default behaviour: change it catching the event if needed
             modal.close(target);
         } else if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
-            final AjaxWizard.NewItemEvent<T> newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
-            final Optional<AjaxRequestTarget> target = newItemEvent.getTarget();
-            final T item = newItemEvent.getItem();
+            AjaxWizard.NewItemEvent<T> newItemEvent = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
+            Optional<AjaxRequestTarget> target = newItemEvent.getTarget();
+            T item = newItemEvent.getItem();
 
-            final boolean modalPanelAvailable = newItemEvent.getModalPanel() != null || newItemPanelBuilder != null;
+            boolean modalPanelAvailable = newItemEvent.getModalPanel() != null || newItemPanelBuilder != null;
 
             if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent && modalPanelAvailable) {
                 WizardModalPanel<?> modalPanel;
@@ -198,7 +198,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWiz
                 }
 
                 if (wizardInModal) {
-                    final IModel<T> model = new CompoundPropertyModel<>(item);
+                    IModel<T> model = new CompoundPropertyModel<>(item);
                     modal.setFormModel(model);
 
                     target.ifPresent(t -> t.add(modal.setContent(modalPanel)));
@@ -209,7 +209,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWiz
                             new Model<>(modalPanel.getItem())));
                     modal.show(true);
                 } else {
-                    final Fragment fragment = new Fragment("content", "wizard", WizardMgtPanel.this);
+                    Fragment fragment = new Fragment("content", "wizard", WizardMgtPanel.this);
 
                     fragment.add(new Label("title", newItemEvent.getResourceModel() == null
                             ? Model.of(StringUtils.EMPTY) : newItemEvent.getResourceModel()));
@@ -234,7 +234,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends AbstractWiz
             } else if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
                 SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
                 if (target.isPresent()) {
-                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target.get());
+                    ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target.get());
                 }
 
                 if (wizardInModal && showResultPage) {
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthProfileTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthProfileTO.java
index 3d81d94..422a8c7 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthProfileTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthProfileTO.java
@@ -28,8 +28,9 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
+import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
-import org.apache.syncope.common.lib.wa.WebAuthnAccount;
+import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 
 public class AuthProfileTO implements EntityTO {
 
@@ -94,8 +95,18 @@ public class AuthProfileTO implements EntityTO {
             return this;
         }
 
-        public AuthProfileTO.Builder webAuthnAccount(final WebAuthnAccount webAuthnAccount) {
-            instance.setWebAuthnAccount(webAuthnAccount);
+        public AuthProfileTO.Builder credential(final WebAuthnDeviceCredential credential) {
+            instance.getWebAuthnDeviceCredentials().add(credential);
+            return this;
+        }
+
+        public AuthProfileTO.Builder credentials(final WebAuthnDeviceCredential... credentials) {
+            instance.getWebAuthnDeviceCredentials().addAll(List.of(credentials));
+            return this;
+        }
+
+        public AuthProfileTO.Builder credentials(final Collection<WebAuthnDeviceCredential> credentials) {
+            instance.getWebAuthnDeviceCredentials().addAll(credentials);
             return this;
         }
 
@@ -108,13 +119,15 @@ public class AuthProfileTO implements EntityTO {
 
     private String owner;
 
+    private final List<ImpersonationAccount> impersonationAccounts = new ArrayList<>();
+
     private final List<GoogleMfaAuthToken> googleMfaAuthTokens = new ArrayList<>();
 
     private final List<GoogleMfaAuthAccount> googleMfaAuthAccounts = new ArrayList<>();
 
     private final List<U2FDevice> u2fRegisteredDevices = new ArrayList<>();
 
-    private WebAuthnAccount webAuthnAccount;
+    private final List<WebAuthnDeviceCredential> webAuthnDeviceCredentials = new ArrayList<>();
 
     @Override
     public String getKey() {
@@ -135,6 +148,12 @@ public class AuthProfileTO implements EntityTO {
         this.owner = owner;
     }
 
+    @JacksonXmlElementWrapper(localName = "impersonationAccounts")
+    @JacksonXmlProperty(localName = "impersonationAccount")
+    public List<ImpersonationAccount> getImpersonationAccounts() {
+        return impersonationAccounts;
+    }
+
     @JacksonXmlElementWrapper(localName = "googleMfaAuthTokens")
     @JacksonXmlProperty(localName = "googleMfaAuthToken")
     public List<GoogleMfaAuthToken> getGoogleMfaAuthTokens() {
@@ -153,12 +172,10 @@ public class AuthProfileTO implements EntityTO {
         return u2fRegisteredDevices;
     }
 
-    public WebAuthnAccount getWebAuthnAccount() {
-        return webAuthnAccount;
-    }
-
-    public void setWebAuthnAccount(final WebAuthnAccount webAuthnAccount) {
-        this.webAuthnAccount = webAuthnAccount;
+    @JacksonXmlElementWrapper(localName = "credentials")
+    @JacksonXmlProperty(localName = "credential")
+    public List<WebAuthnDeviceCredential> getWebAuthnDeviceCredentials() {
+        return webAuthnDeviceCredentials;
     }
 
     @Override
@@ -166,10 +183,11 @@ public class AuthProfileTO implements EntityTO {
         return new HashCodeBuilder().
                 append(key).
                 append(owner).
+                append(impersonationAccounts).
                 append(googleMfaAuthTokens).
                 append(googleMfaAuthAccounts).
                 append(u2fRegisteredDevices).
-                append(webAuthnAccount).
+                append(webAuthnDeviceCredentials).
                 build();
     }
 
@@ -188,10 +206,11 @@ public class AuthProfileTO implements EntityTO {
         return new EqualsBuilder().
                 append(key, other.key).
                 append(owner, other.owner).
+                append(impersonationAccounts, other.impersonationAccounts).
                 append(googleMfaAuthTokens, other.googleMfaAuthTokens).
                 append(googleMfaAuthAccounts, other.googleMfaAuthAccounts).
                 append(u2fRegisteredDevices, other.u2fRegisteredDevices).
-                append(webAuthnAccount, other.webAuthnAccount).
+                append(webAuthnDeviceCredentials, other.webAuthnDeviceCredentials).
                 build();
     }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index b777034..7de374e 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -70,44 +70,22 @@ public final class AMEntitlement {
 
     public static final String SAML2_SP_ENTITY_GET = "SAML2_SP_ENTITY_GET";
 
-    public static final String GOOGLE_MFA_DELETE_TOKEN = "GOOGLE_MFA_DELETE_TOKEN";
-
-    public static final String GOOGLE_MFA_STORE_TOKEN = "GOOGLE_MFA_STORE_TOKEN";
-
-    public static final String GOOGLE_MFA_READ_TOKEN = "GOOGLE_MFA_READ_TOKEN";
+    public static final String AUTH_PROFILE_DELETE = "AUTH_PROFILE_DELETE";
 
-    public static final String GOOGLE_MFA_LIST_TOKENS = "GOOGLE_MFA_LIST_TOKENS";
+    public static final String AUTH_PROFILE_CREATE = "AUTH_PROFILE_CREATE";
 
-    public static final String AUTH_PROFILE_DELETE = "AUTH_PROFILE_DELETE";
+    public static final String AUTH_PROFILE_UPDATE = "AUTH_PROFILE_UPDATE";
 
     public static final String AUTH_PROFILE_READ = "AUTH_PROFILE_READ";
 
     public static final String AUTH_PROFILE_LIST = "AUTH_PROFILE_LIST";
 
-    public static final String GOOGLE_MFA_DELETE_ACCOUNT = "GOOGLE_MFA_DELETE_ACCOUNT";
-
-    public static final String GOOGLE_MFA_CREATE_ACCOUNT = "GOOGLE_MFA_CREATE_ACCOUNT";
-
-    public static final String GOOGLE_MFA_UPDATE_ACCOUNT = "GOOGLE_MFA_UPDATE_ACCOUNT";
-
-    public static final String GOOGLE_MFA_READ_ACCOUNT = "GOOGLE_MFA_READ_ACCOUNT";
-
-    public static final String GOOGLE_MFA_LIST_ACCOUNTS = "GOOGLE_MFA_LIST_ACCOUNTS";
-
     public static final String OIDC_JWKS_GENERATE = "OIDC_JWKS_GENERATE";
 
     public static final String OIDC_JWKS_READ = "OIDC_JWKS_READ";
 
     public static final String OIDC_JWKS_DELETE = "OIDC_JWKS_DELETE";
 
-    public static final String U2F_DELETE_DEVICE = "U2F_DELETE_DEVICE";
-
-    public static final String U2F_CREATE_DEVICE = "U2F_CREATE_DEVICE";
-
-    public static final String U2F_READ_DEVICE = "U2F_READ_DEVICE";
-
-    public static final String U2F_SEARCH_DEVICES = "U2F_SEARCH_DEVICES";
-
     public static final String WA_CONFIG_LIST = "WA_CONFIG_LIST";
 
     public static final String WA_CONFIG_SET = "WA_CONFIG_SET";
@@ -118,22 +96,6 @@ public final class AMEntitlement {
 
     public static final String WA_CONFIG_PUSH = "WA_CONFIG_PUSH";
 
-    public static final String WEBAUTHN_DELETE_DEVICE = "WEBAUTHN_DELETE_DEVICE";
-
-    public static final String WEBAUTHN_READ_DEVICE = "WEBAUTHN_READ_DEVICE";
-
-    public static final String WEBAUTHN_UPDATE_DEVICE = "WEBAUTHN_UPDATE_DEVICE";
-
-    public static final String WEBAUTHN_CREATE_DEVICE = "WEBAUTHN_CREATE_DEVICE";
-
-    public static final String WEBAUTHN_LIST_DEVICE = "WEBAUTHN_LIST_DEVICE";
-
-    public static final String IMPERSONATION_CREATE_ACCOUNT = "IMPERSONATION_CREATE_ACCOUNT";
-
-    public static final String IMPERSONATION_DELETE_ACCOUNT = "IMPERSONATION_DELETE_ACCOUNT";
-
-    public static final String IMPERSONATION_READ_ACCOUNT = "IMPERSONATION_READ_ACCOUNT";
-
     private static final Set<String> VALUES;
 
     static {
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthAccount.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthAccount.java
index da78867..8a185bc 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthAccount.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/GoogleMfaAuthAccount.java
@@ -135,7 +135,6 @@ public class GoogleMfaAuthAccount implements BaseBean {
     @Override
     public int hashCode() {
         return new HashCodeBuilder()
-                .appendSuper(super.hashCode())
                 .append(secretKey)
                 .append(name)
                 .append(id)
@@ -156,15 +155,14 @@ public class GoogleMfaAuthAccount implements BaseBean {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        GoogleMfaAuthAccount rhs = (GoogleMfaAuthAccount) obj;
+        GoogleMfaAuthAccount other = (GoogleMfaAuthAccount) obj;
         return new EqualsBuilder()
-                .appendSuper(super.equals(obj))
-                .append(this.secretKey, rhs.secretKey)
-                .append(this.name, rhs.name)
-                .append(this.id, rhs.id)
-                .append(this.scratchCodes, rhs.scratchCodes)
-                .append(this.registrationDate, rhs.registrationDate)
-                .append(this.validationCode, rhs.validationCode)
+                .append(this.secretKey, other.secretKey)
+                .append(this.name, other.name)
+                .append(this.id, other.id)
+                .append(this.scratchCodes, other.scratchCodes)
+                .append(this.registrationDate, other.registrationDate)
+                .append(this.validationCode, other.validationCode)
                 .isEquals();
     }
 
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 a1ce05a..a19d6ec 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
@@ -73,7 +73,6 @@ public class GoogleMfaAuthToken implements BaseBean {
     @Override
     public int hashCode() {
         return new HashCodeBuilder()
-                .appendSuper(super.hashCode())
                 .append(otp)
                 .append(issueDate)
                 .toHashCode();
@@ -90,11 +89,10 @@ public class GoogleMfaAuthToken implements BaseBean {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        GoogleMfaAuthToken rhs = (GoogleMfaAuthToken) obj;
+        GoogleMfaAuthToken other = (GoogleMfaAuthToken) obj;
         return new EqualsBuilder()
-                .appendSuper(super.equals(obj))
-                .append(this.otp, rhs.otp)
-                .append(this.issueDate, rhs.issueDate)
+                .append(this.otp, other.otp)
+                .append(this.issueDate, other.issueDate)
                 .isEquals();
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/ImpersonationAccount.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/ImpersonationAccount.java
index 6d80d3c..6936b63 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/ImpersonationAccount.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/ImpersonationAccount.java
@@ -69,16 +69,16 @@ public class ImpersonationAccount implements BaseBean {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        ImpersonationAccount rhs = (ImpersonationAccount) obj;
+        ImpersonationAccount other = (ImpersonationAccount) obj;
         return new EqualsBuilder()
-                .append(this.impersonated, rhs.impersonated)
+                .append(this.impersonated, other.impersonated)
                 .isEquals();
     }
 
     @Override
     public String toString() {
         return new ToStringBuilder(this)
-                .append("key", impersonated)
+                .append("impersonated", impersonated)
                 .toString();
     }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/U2FDevice.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/U2FDevice.java
index a0c465b..106b9b6 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/U2FDevice.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/U2FDevice.java
@@ -88,7 +88,6 @@ public class U2FDevice implements BaseBean {
     @Override
     public int hashCode() {
         return new HashCodeBuilder()
-                .appendSuper(super.hashCode())
                 .append(record)
                 .append(id)
                 .append(issueDate)
@@ -106,12 +105,11 @@ public class U2FDevice implements BaseBean {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        U2FDevice rhs = (U2FDevice) obj;
+        U2FDevice other = (U2FDevice) obj;
         return new EqualsBuilder()
-                .appendSuper(super.equals(obj))
-                .append(this.record, rhs.record)
-                .append(this.id, rhs.id)
-                .append(this.issueDate, rhs.issueDate)
+                .append(this.record, other.record)
+                .append(this.id, other.id)
+                .append(this.issueDate, other.issueDate)
                 .isEquals();
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WebAuthnDeviceCredential.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WebAuthnDeviceCredential.java
index 14fdb78..40db6cf 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WebAuthnDeviceCredential.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WebAuthnDeviceCredential.java
@@ -69,7 +69,6 @@ public class WebAuthnDeviceCredential implements BaseBean {
     @Override
     public int hashCode() {
         return new HashCodeBuilder()
-                .appendSuper(super.hashCode())
                 .append(json)
                 .append(identifier)
                 .toHashCode();
@@ -86,11 +85,10 @@ public class WebAuthnDeviceCredential implements BaseBean {
         if (obj.getClass() != getClass()) {
             return false;
         }
-        WebAuthnDeviceCredential rhs = (WebAuthnDeviceCredential) obj;
+        WebAuthnDeviceCredential other = (WebAuthnDeviceCredential) obj;
         return new EqualsBuilder()
-                .appendSuper(super.equals(obj))
-                .append(this.json, rhs.json)
-                .append(this.identifier, rhs.identifier)
+                .append(this.json, other.json)
+                .append(this.identifier, other.identifier)
                 .isEquals();
     }
 
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
index 4f6a89d..ad75ce0 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuthProfileService.java
@@ -18,6 +18,13 @@
  */
 package org.apache.syncope.common.rest.api.service;
 
+import static org.apache.syncope.common.rest.api.service.JAXRSService.PARAM_PAGE;
+import static org.apache.syncope.common.rest.api.service.JAXRSService.PARAM_SIZE;
+
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -26,11 +33,18 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-import java.util.List;
 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 javax.validation.constraints.Min;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
@@ -43,31 +57,70 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 @Path("authProfiles")
 public interface AuthProfileService extends JAXRSService {
 
+    /**
+     * Returns the paginated list of existing auth profiles.
+     *
+     * @param page search page
+     * @param size search page size
+     * @return the paginated list of existing auth profiles
+     */
     @GET
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    List<AuthProfileTO> list();
+    PagedResult<AuthProfileTO> list(
+            @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue("1") int page,
+            @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue("25") int size);
 
+    /**
+     * Returns the auth profile matching the provided if key, if found.
+     *
+     * @param key auth profile key
+     * @return auth profile matching the provided if key, if found
+     */
     @GET
     @Path("{key}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     AuthProfileTO read(@NotNull @PathParam("key") String key);
 
-    @GET
-    @Path("owners/{owner}")
+    /**
+     * Create a new auth profile.
+     *
+     * @param authProfileTO auth profile to create
+     * @return Response object featuring Location header of created client app
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "201",
+                    description = "AuthProfile successfully created", headers = {
+                @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+                        @Schema(type = "string"),
+                        description = "UUID generated for the entity created"),
+                @Header(name = HttpHeaders.LOCATION, schema =
+                        @Schema(type = "string"),
+                        description = "URL of the entity created") }))
+    @POST
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    AuthProfileTO readByOwner(@NotNull @PathParam("owner") String owner);
+    Response create(@NotNull AuthProfileTO authProfileTO);
 
-    @DELETE
+    /**
+     * Updates the auth profile matching the provided if key, if found.
+     *
+     * @param authProfileTO auth profile
+     */
+    @PUT
     @Path("{key}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void delete(@NotNull @PathParam("key") String key);
+    void update(@NotNull AuthProfileTO authProfileTO);
 
+    /**
+     * Deletes the auth profile matching the provided if key, if found.
+     *
+     * @param key auth profile key
+     */
     @DELETE
-    @Path("owners/{owner}")
+    @Path("{key}")
     @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    void deleteByOwner(@NotNull @PathParam("owner") String owner);
+    void delete(@NotNull @PathParam("key") String key);
 }
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
index d311a48..160a508 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
@@ -19,7 +19,9 @@
 package org.apache.syncope.core.logic;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -36,35 +38,37 @@ public class AuthProfileLogic extends AbstractAuthProfileLogic {
         authProfileDAO.delete(key);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_DELETE + "') ")
-    public void deleteByOwner(final String owner) {
-        authProfileDAO.findByOwner(owner).ifPresent(authProfileDAO::delete);
-    }
-
     @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_READ + "') ")
     @Transactional(readOnly = true)
-    public AuthProfileTO readByOwner(final String owner) {
-        return authProfileDAO.findByOwner(owner).
+    public AuthProfileTO read(final String key) {
+        return Optional.ofNullable(authProfileDAO.find(key)).
                 map(binder::getAuthProfileTO).
-                orElseThrow(() -> new NotFoundException(owner + " not found"));
+                orElseThrow(() -> new NotFoundException(key + " not found"));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_READ + "') ")
-    @Transactional(readOnly = true)
-    public AuthProfileTO read(final String key) {
-        AuthProfile authProfile = authProfileDAO.find(key);
-        if (authProfile == null) {
-            throw new NotFoundException(key + " not found");
-        }
-        return binder.getAuthProfileTO(authProfile);
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_CREATE + "') ")
+    public AuthProfileTO create(final AuthProfileTO authProfileTO) {
+        return binder.getAuthProfileTO(authProfileDAO.save(binder.create(authProfileTO)));
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_UPDATE + "') ")
+    public void update(final AuthProfileTO authProfileTO) {
+        AuthProfile authProfile = Optional.ofNullable(authProfileDAO.find(authProfileTO.getKey())).
+                orElseThrow(() -> new NotFoundException(authProfileTO.getKey() + " not found"));
+        binder.update(authProfile, authProfileTO);
+        authProfileDAO.save(authProfile);
     }
 
     @PreAuthorize("hasRole('" + AMEntitlement.AUTH_PROFILE_LIST + "')")
     @Transactional(readOnly = true)
-    public List<AuthProfileTO> list() {
-        return authProfileDAO.findAll().
+    public Pair<Integer, List<AuthProfileTO>> list(final int page, final int size) {
+        int count = authProfileDAO.count();
+
+        List<AuthProfileTO> result = authProfileDAO.findAll(page, size).
                 stream().
                 map(binder::getAuthProfileTO).
                 collect(Collectors.toList());
+
+        return Pair.of(count, result);
     }
 }
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 a2c3540..6630b0d 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
@@ -21,7 +21,6 @@ package org.apache.syncope.core.logic.wa;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
@@ -39,11 +38,10 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
     @Autowired
     private EntityFactory entityFactory;
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_LIST_ACCOUNTS + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public List<GoogleMfaAuthAccount> list() {
-        return authProfileDAO.findAll().
+        return authProfileDAO.findAll(-1, -1).
                 stream().
                 map(AuthProfile::getGoogleMfaAuthAccounts).
                 filter(Objects::nonNull).
@@ -51,8 +49,7 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
                 collect(Collectors.toList());
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void deleteFor(final String owner) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> {
             profile.setGoogleMfaAuthAccounts(List.of());
@@ -60,17 +57,15 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void deleteAll() {
-        authProfileDAO.findAll().forEach(profile -> {
+        authProfileDAO.findAll(-1, -1).forEach(profile -> {
             profile.setGoogleMfaAuthAccounts(List.of());
             authProfileDAO.save(profile);
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_CREATE_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void create(final String owner, final GoogleMfaAuthAccount account) {
         AuthProfile profile = authProfileDAO.findByOwner(owner).orElseGet(() -> {
             AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
@@ -84,8 +79,7 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
         authProfileDAO.save(profile);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_UPDATE_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void update(final String owner, final GoogleMfaAuthAccount account) {
         AuthProfile authProfile = authProfileDAO.findByOwner(owner).
                 orElseThrow(() -> new NotFoundException("Could not find account for Owner " + owner));
@@ -97,8 +91,7 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
         }
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_READ_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public List<GoogleMfaAuthAccount> read(final String owner) {
         return authProfileDAO.findByOwner(owner).
                 stream().
@@ -109,11 +102,10 @@ public class GoogleMfaAuthAccountLogic extends AbstractAuthProfileLogic {
                 orElseThrow(() -> new NotFoundException("Could not find account for Owner " + owner));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_READ_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public GoogleMfaAuthAccount read(final long id) {
-        return authProfileDAO.findAll().
+        return authProfileDAO.findAll(-1, -1).
                 stream().
                 map(AuthProfile::getGoogleMfaAuthAccounts).
                 filter(Objects::nonNull).
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 2c0cf78..b5d445b 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
@@ -22,7 +22,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
@@ -40,22 +39,19 @@ public class GoogleMfaAuthTokenLogic extends AbstractAuthProfileLogic {
     @Autowired
     private EntityFactory entityFactory;
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final Date expirationDate) {
-        authProfileDAO.findAll().forEach(profile -> removeTokenAndSave(
+        authProfileDAO.findAll(-1, -1).forEach(profile -> removeTokenAndSave(
                 profile, token -> token.getIssueDate().compareTo(expirationDate) >= 0));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner, final int otp) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> removeTokenAndSave(
                 profile, token -> token.getOtp() == otp));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> {
             profile.setGoogleMfaAuthTokens(List.of());
@@ -63,24 +59,21 @@ public class GoogleMfaAuthTokenLogic extends AbstractAuthProfileLogic {
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final int otp) {
-        authProfileDAO.findAll().forEach(profile -> removeTokenAndSave(
+        authProfileDAO.findAll(-1, -1).forEach(profile -> removeTokenAndSave(
                 profile, token -> token.getOtp() == otp));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_DELETE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void deleteAll() {
-        authProfileDAO.findAll().forEach(profile -> {
+        authProfileDAO.findAll(-1, -1).forEach(profile -> {
             profile.setGoogleMfaAuthTokens(List.of());
             authProfileDAO.save(profile);
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_STORE_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void store(final String owner, final GoogleMfaAuthToken token) {
         AuthProfile profile = authProfileDAO.findByOwner(owner).orElseGet(() -> {
             AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
@@ -94,8 +87,7 @@ public class GoogleMfaAuthTokenLogic extends AbstractAuthProfileLogic {
         authProfileDAO.save(profile);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_READ_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public GoogleMfaAuthToken read(final String owner, final int otp) {
         return authProfileDAO.findByOwner(owner).
@@ -107,18 +99,16 @@ public class GoogleMfaAuthTokenLogic extends AbstractAuthProfileLogic {
                 orElseThrow(() -> new NotFoundException("Could not find token for Owner " + owner + " and otp " + otp));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_LIST_TOKENS + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public List<GoogleMfaAuthToken> list() {
-        return authProfileDAO.findAll().stream().
+        return authProfileDAO.findAll(-1, -1).stream().
                 map(AuthProfile::getGoogleMfaAuthTokens).
                 flatMap(List::stream).
                 collect(Collectors.toList());
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.GOOGLE_MFA_READ_TOKEN + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public List<GoogleMfaAuthToken> read(final String owner) {
         return authProfileDAO.findByOwner(owner).
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
index 6fd2d02..f1fd730 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.logic.wa;
 
 import java.util.ArrayList;
 import java.util.List;
-import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
@@ -37,15 +36,13 @@ public class ImpersonationLogic extends AbstractAuthProfileLogic {
     @Autowired
     private EntityFactory entityFactory;
 
-    @PreAuthorize("hasRole('" + AMEntitlement.IMPERSONATION_READ_ACCOUNT + "')"
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public List<ImpersonationAccount> read(final String owner) {
         return authProfileDAO.findByOwner(owner).map(AuthProfile::getImpersonationAccounts).orElse(List.of());
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.IMPERSONATION_CREATE_ACCOUNT + "')"
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void create(final String owner, final ImpersonationAccount account) {
         AuthProfile profile = authProfileDAO.findByOwner(owner).orElseGet(() -> {
             AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
@@ -64,8 +61,7 @@ public class ImpersonationLogic extends AbstractAuthProfileLogic {
         authProfileDAO.save(profile);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.IMPERSONATION_DELETE_ACCOUNT + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner, final String impersonated) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> {
             List<ImpersonationAccount> accounts = profile.getImpersonationAccounts();
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
index a2e6318..e850e74 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
@@ -26,7 +26,6 @@ import java.util.stream.Collectors;
 import org.apache.commons.lang3.builder.CompareToBuilder;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.U2FDevice;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
@@ -43,8 +42,7 @@ public class U2FRegistrationLogic extends AbstractAuthProfileLogic {
     @Autowired
     private EntityFactory entityFactory;
 
-    @PreAuthorize("hasRole('" + AMEntitlement.U2F_CREATE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void create(final String owner, final U2FDevice device) {
         AuthProfile profile = authProfileDAO.findByOwner(owner).orElseGet(() -> {
             AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
@@ -58,10 +56,9 @@ public class U2FRegistrationLogic extends AbstractAuthProfileLogic {
         authProfileDAO.save(profile);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.U2F_DELETE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final Long id, final Date expirationDate) {
-        List<AuthProfile> profiles = authProfileDAO.findAll();
+        List<AuthProfile> profiles = authProfileDAO.findAll(-1, -1);
         profiles.forEach(profile -> {
             List<U2FDevice> devices = profile.getU2FRegisteredDevices();
             if (devices != null) {
@@ -78,8 +75,7 @@ public class U2FRegistrationLogic extends AbstractAuthProfileLogic {
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.U2F_SEARCH_DEVICES + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public Pair<Integer, List<U2FDevice>> search(
             final Integer page,
             final Integer itemsPerPage, final Long id,
@@ -113,7 +109,7 @@ public class U2FRegistrationLogic extends AbstractAuthProfileLogic {
                 filter(Objects::nonNull).
                 collect(Collectors.toList());
 
-        List<U2FDevice> devices = authProfileDAO.findAll().
+        List<U2FDevice> devices = authProfileDAO.findAll(-1, -1).
                 stream().
                 map(AuthProfile::getU2FRegisteredDevices).
                 filter(Objects::nonNull).
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
index 5e6469f..27b6aa0 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.logic.wa;
 
 import java.util.List;
 import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.WebAuthnAccount;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
@@ -39,68 +38,60 @@ public class WebAuthnRegistrationLogic extends AbstractAuthProfileLogic {
     @Autowired
     private EntityFactory entityFactory;
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_LIST_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public List<WebAuthnAccount> list() {
-        return authProfileDAO.findAll().stream().
-                map(AuthProfile::getWebAuthnAccount).
+        return authProfileDAO.findAll(-1, -1).stream().
+                map(profile -> new WebAuthnAccount.Builder().
+                credentials(profile.getWebAuthnDeviceCredentials()).build()).
                 collect(Collectors.toList());
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_READ_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
     public WebAuthnAccount read(final String owner) {
-        return authProfileDAO.findByOwner(owner).
-                stream().
-                map(AuthProfile::getWebAuthnAccount).
+        return authProfileDAO.findByOwner(owner).stream().
                 findFirst().
+                map(profile -> new WebAuthnAccount.Builder().
+                credentials(profile.getWebAuthnDeviceCredentials()).build()).
                 orElseThrow(() -> new NotFoundException("Could not find account for Owner " + owner));
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_DELETE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> {
-            profile.setWebAuthnAccount(null);
+            profile.setWebAuthnDeviceCredentials(List.of());
             authProfileDAO.save(profile);
         });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_DELETE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void delete(final String owner, final String credentialId) {
-        authProfileDAO.findByOwner(owner).
-                stream().
-                findFirst().
+        authProfileDAO.findByOwner(owner).stream().findFirst().
                 ifPresent(profile -> {
-                    WebAuthnAccount webAuthnAccount = profile.getWebAuthnAccount();
-                    List<WebAuthnDeviceCredential> accounts = webAuthnAccount.getCredentials();
-                    if (accounts.removeIf(acct -> acct.getIdentifier().equals(credentialId))) {
-                        profile.setWebAuthnAccount(webAuthnAccount);
+                    List<WebAuthnDeviceCredential> credentials = profile.getWebAuthnDeviceCredentials();
+                    if (credentials.removeIf(acct -> acct.getIdentifier().equals(credentialId))) {
+                        profile.setWebAuthnDeviceCredentials(credentials);
                         authProfileDAO.save(profile);
                     }
                 });
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_CREATE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void create(final String owner, final WebAuthnAccount account) {
         AuthProfile profile = authProfileDAO.findByOwner(owner).orElseGet(() -> {
             AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
             authProfile.setOwner(owner);
             return authProfile;
         });
-        profile.setWebAuthnAccount(account);
+        profile.setWebAuthnDeviceCredentials(account.getCredentials());
         authProfileDAO.save(profile);
     }
 
-    @PreAuthorize("hasRole('" + AMEntitlement.WEBAUTHN_UPDATE_DEVICE + "') "
-            + "or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public void update(final String owner, final WebAuthnAccount account) {
         authProfileDAO.findByOwner(owner).ifPresent(profile -> {
-            profile.setWebAuthnAccount(account);
+            profile.setWebAuthnDeviceCredentials(account.getCredentials());
             authProfileDAO.save(profile);
         });
     }
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
index 58e7a63..3ee15a9 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.rest.cxf.service;
 
+import java.net.URI;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
 import org.apache.syncope.common.rest.api.service.AuthProfileService;
 import org.apache.syncope.core.logic.AuthProfileLogic;
@@ -25,6 +26,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.rest.api.RESTHeaders;
 
 @Service
 public class AuthProfileServiceImpl extends AbstractServiceImpl implements AuthProfileService {
@@ -38,22 +43,27 @@ public class AuthProfileServiceImpl extends AbstractServiceImpl implements AuthP
     }
 
     @Override
-    public void deleteByOwner(final String owner) {
-        logic.deleteByOwner(owner);
+    public AuthProfileTO read(final String key) {
+        return logic.read(key);
     }
 
     @Override
-    public AuthProfileTO readByOwner(final String owner) {
-        return logic.readByOwner(owner);
+    public Response create(final AuthProfileTO authProfileTO) {
+        AuthProfileTO created = logic.create(authProfileTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+                build();
     }
 
     @Override
-    public AuthProfileTO read(final String key) {
-        return logic.read(key);
+    public void update(final AuthProfileTO authProfileTO) {
+        logic.update(authProfileTO);
     }
 
     @Override
-    public List<AuthProfileTO> list() {
-        return logic.list();
+    public PagedResult<AuthProfileTO> list(final int page, final int size) {
+        Pair<Integer, List<AuthProfileTO>> result = logic.list(page, size);
+        return buildPagedResult(result.getRight(), page, size, result.getLeft());
     }
 }
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
index 052ec31..5a2c462 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceImpl.java
@@ -37,10 +37,10 @@ public class ClientAppServiceImpl extends AbstractServiceImpl implements ClientA
 
     @Override
     public Response create(final ClientAppType type, final ClientAppTO clientAppTO) {
-        ClientAppTO appTO = logic.create(type, clientAppTO);
-        URI location = uriInfo.getAbsolutePathBuilder().path(appTO.getKey()).build();
+        ClientAppTO created = logic.create(type, clientAppTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
         return Response.created(location).
-                header(RESTHeaders.RESOURCE_KEY, appTO.getKey()).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
                 build();
     }
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java
index c4bdf9f..7d2fe21 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java
@@ -27,7 +27,9 @@ public interface AuthProfileDAO extends DAO<AuthProfile> {
 
     AuthProfile find(String key);
 
-    List<AuthProfile> findAll();
+    int count();
+
+    List<AuthProfile> findAll(int page, int itemsPerPage);
 
     Optional<AuthProfile> findByOwner(String owner);
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java
index 7b97e0e..51a5a61 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java
@@ -23,7 +23,7 @@ import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
-import org.apache.syncope.common.lib.wa.WebAuthnAccount;
+import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 
 public interface AuthProfile extends Entity {
@@ -44,9 +44,9 @@ public interface AuthProfile extends Entity {
 
     void setGoogleMfaAuthAccounts(List<GoogleMfaAuthAccount> accounts);
 
-    WebAuthnAccount getWebAuthnAccount();
+    List<WebAuthnDeviceCredential> getWebAuthnDeviceCredentials();
 
-    void setWebAuthnAccount(WebAuthnAccount accounts);
+    void setWebAuthnDeviceCredentials(List<WebAuthnDeviceCredential> credentials);
 
     List<ImpersonationAccount> getImpersonationAccounts();
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java
index 277ece0..1d576ed 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.dao.auth;
 
 import java.util.List;
 import java.util.Optional;
+import javax.persistence.Query;
 import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
@@ -36,10 +37,25 @@ public class JPAAuthProfileDAO extends AbstractDAO<AuthProfile> implements AuthP
     }
 
     @Override
-    public List<AuthProfile> findAll() {
+    public int count() {
+        Query query = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAAuthProfile.class.getSimpleName() + " e");
+        return ((Number) query.getSingleResult()).intValue();
+    }
+
+    @Override
+    public List<AuthProfile> findAll(final int page, final int itemsPerPage) {
         TypedQuery<AuthProfile> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAAuthProfile.class.getSimpleName() + " e ",
+                "SELECT e FROM " + JPAAuthProfile.class.getSimpleName() + " e ORDER BY e.owner ASC",
                 AuthProfile.class);
+
+        // page starts from 1, while setFirtResult() starts from 0
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+
+        if (itemsPerPage >= 0) {
+            query.setMaxResults(itemsPerPage);
+        }
+
         return query.getResultList();
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java
index 2ff0c58..2ea123e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java
@@ -30,7 +30,7 @@ import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
-import org.apache.syncope.common.lib.wa.WebAuthnAccount;
+import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
@@ -44,23 +44,23 @@ public class JPAAuthProfile extends AbstractGeneratedKeyEntity implements AuthPr
 
     private static final long serialVersionUID = 57352617217394093L;
 
-    @Lob
-    private String u2fRegisteredDevices;
+    @Column(nullable = false)
+    private String owner;
 
     @Lob
-    private String googleMfaAuthAccounts;
+    private String impersonatedAccounts;
 
     @Lob
-    private String impersonatedAccounts;
+    private String googleMfaAuthAccounts;
 
     @Lob
     private String googleMfaAuthTokens;
 
     @Lob
-    private String webAuthnAccount;
+    private String u2fRegisteredDevices;
 
-    @Column(nullable = false)
-    private String owner;
+    @Lob
+    private String webAuthnDeviceCredentials;
 
     @Override
     public String getOwner() {
@@ -114,9 +114,9 @@ public class JPAAuthProfile extends AbstractGeneratedKeyEntity implements AuthPr
     @Override
     public List<ImpersonationAccount> getImpersonationAccounts() {
         return impersonatedAccounts == null
-            ? new ArrayList<>(0)
-            : POJOHelper.deserialize(impersonatedAccounts, new TypeReference<List<ImpersonationAccount>>() {
-        });
+                ? new ArrayList<>(0)
+                : POJOHelper.deserialize(impersonatedAccounts, new TypeReference<List<ImpersonationAccount>>() {
+                });
     }
 
     @Override
@@ -125,15 +125,16 @@ public class JPAAuthProfile extends AbstractGeneratedKeyEntity implements AuthPr
     }
 
     @Override
-    public WebAuthnAccount getWebAuthnAccount() {
-        return webAuthnAccount == null
-                ? null
-                : POJOHelper.deserialize(webAuthnAccount, new TypeReference<WebAuthnAccount>() {
+    public List<WebAuthnDeviceCredential> getWebAuthnDeviceCredentials() {
+        return webAuthnDeviceCredentials == null
+                ? new ArrayList<>(0)
+                : POJOHelper.deserialize(webAuthnDeviceCredentials,
+                        new TypeReference<List<WebAuthnDeviceCredential>>() {
                 });
     }
 
     @Override
-    public void setWebAuthnAccount(final WebAuthnAccount accounts) {
-        this.webAuthnAccount = POJOHelper.serialize(accounts);
+    public void setWebAuthnDeviceCredentials(final List<WebAuthnDeviceCredential> credentials) {
+        this.webAuthnDeviceCredentials = POJOHelper.serialize(credentials);
     }
 }
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 22e9e77..7d27b92 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
@@ -31,7 +31,6 @@ import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
-import org.apache.syncope.common.lib.wa.WebAuthnAccount;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
@@ -63,7 +62,7 @@ public class AuthProfileTest extends AbstractTest {
         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
         assertTrue(result.isPresent());
 
-        assertFalse(authProfileDAO.findAll().isEmpty());
+        assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
 
         AuthProfile authProfile = result.get();
         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
@@ -84,7 +83,7 @@ public class AuthProfileTest extends AbstractTest {
         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
         assertTrue(result.isPresent());
 
-        assertFalse(authProfileDAO.findAll().isEmpty());
+        assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
 
         AuthProfile authProfile = result.get();
         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
@@ -121,14 +120,14 @@ public class AuthProfileTest extends AbstractTest {
         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
         assertTrue(result.isPresent());
 
-        assertFalse(authProfileDAO.findAll().isEmpty());
+        assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
 
         AuthProfile authProfile = result.get();
         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
         assertTrue(result.isPresent());
 
         authProfile.setOwner("SyncopeCreate-NewU2F");
-        authProfile.setWebAuthnAccount(null);
+        authProfile.setWebAuthnDeviceCredentials(List.of());
         authProfileDAO.save(authProfile);
 
         assertFalse(authProfileDAO.findByOwner(id).isPresent());
@@ -143,7 +142,7 @@ public class AuthProfileTest extends AbstractTest {
         Optional<AuthProfile> result = authProfileDAO.findByOwner(id);
         assertTrue(result.isPresent());
 
-        assertFalse(authProfileDAO.findAll().isEmpty());
+        assertFalse(authProfileDAO.findAll(-1, -1).isEmpty());
 
         AuthProfile authProfile = result.get();
         result = Optional.ofNullable(authProfileDAO.find(authProfile.getKey()));
@@ -206,8 +205,7 @@ public class AuthProfileTest extends AbstractTest {
 
         AuthProfile profile = entityFactory.newEntity(AuthProfile.class);
         profile.setOwner(owner);
-        WebAuthnAccount account = new WebAuthnAccount.Builder().credentials(credentials).build();
-        profile.setWebAuthnAccount(account);
+        profile.setWebAuthnDeviceCredentials(credentials);
         return authProfileDAO.save(profile);
     }
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
index e102405..de3ed26 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
@@ -26,4 +26,6 @@ public interface AuthProfileDataBinder {
     AuthProfileTO getAuthProfileTO(AuthProfile authProfile);
 
     AuthProfile create(AuthProfileTO authProfileTO);
+
+    AuthProfile update(AuthProfile authProfile, AuthProfileTO authProfileTO);
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
index e73ea1f..dcf73ba 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
@@ -36,10 +36,11 @@ public class AuthProfileDataBinderImpl implements AuthProfileDataBinder {
         AuthProfileTO authProfileTO = new AuthProfileTO();
         authProfileTO.setKey(authProfile.getKey());
         authProfileTO.setOwner(authProfile.getOwner());
+        authProfileTO.getImpersonationAccounts().addAll(authProfile.getImpersonationAccounts());
         authProfileTO.getGoogleMfaAuthTokens().addAll(authProfile.getGoogleMfaAuthTokens());
         authProfileTO.getGoogleMfaAuthAccounts().addAll(authProfile.getGoogleMfaAuthAccounts());
         authProfileTO.getU2FRegisteredDevices().addAll(authProfile.getU2FRegisteredDevices());
-        authProfileTO.setWebAuthnAccount(authProfile.getWebAuthnAccount());
+        authProfileTO.getWebAuthnDeviceCredentials().addAll(authProfile.getWebAuthnDeviceCredentials());
         return authProfileTO;
     }
 
@@ -47,10 +48,16 @@ public class AuthProfileDataBinderImpl implements AuthProfileDataBinder {
     public AuthProfile create(final AuthProfileTO authProfileTO) {
         AuthProfile authProfile = entityFactory.newEntity(AuthProfile.class);
         authProfile.setOwner(authProfileTO.getOwner());
+        return update(authProfile, authProfileTO);
+    }
+
+    @Override
+    public AuthProfile update(final AuthProfile authProfile, final AuthProfileTO authProfileTO) {
+        authProfile.setImpersonationAccounts(authProfileTO.getImpersonationAccounts());
         authProfile.setGoogleMfaAuthTokens(authProfileTO.getGoogleMfaAuthTokens());
         authProfile.setGoogleMfaAuthAccounts(authProfileTO.getGoogleMfaAuthAccounts());
         authProfile.setU2FRegisteredDevices(authProfileTO.getU2FRegisteredDevices());
-        authProfile.setWebAuthnAccount(authProfileTO.getWebAuthnAccount());
+        authProfile.setWebAuthnDeviceCredentials(authProfileTO.getWebAuthnDeviceCredentials());
         return authProfile;
     }
 }
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
index 0357f13..79639cb 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/BpmnProcessDirectoryPanel.java
@@ -40,7 +40,6 @@ import org.apache.syncope.client.console.pages.ModelerPopupPage;
 import org.apache.syncope.client.console.panels.BpmnProcessDirectoryPanel.BpmProcessDataProvider;
 import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ImageModalPanel;
@@ -88,9 +87,7 @@ public class BpmnProcessDirectoryPanel extends DirectoryPanel<
             private static final long serialVersionUID = 1633859795677053912L;
 
             @Override
-            protected WizardModel buildModelSteps(
-                    final BpmnProcess modelObject, final WizardModel wizardModel) {
-
+            protected WizardModel buildModelSteps(final BpmnProcess modelObject, final WizardModel wizardModel) {
                 return wizardModel;
             }
 
@@ -137,15 +134,9 @@ public class BpmnProcessDirectoryPanel extends DirectoryPanel<
         utility.size(Modal.Size.Large);
         AjaxSubmitLink xmlEditorSubmit = utility.addSubmitButton();
         MetaDataRoleAuthorizationStrategy.authorize(xmlEditorSubmit, RENDER, FlowableEntitlement.BPMN_PROCESS_SET);
-        utility.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                utility.show(false);
-                utility.close(target);
-            }
+        utility.setWindowClosedCallback(target -> {
+            utility.show(false);
+            utility.close(target);
         });
         initResultTable();
     }
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
index f7ec011..8078a9d 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
@@ -36,7 +36,6 @@ import org.apache.syncope.client.console.panels.UserRequestsPanel.UserRequestSea
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
@@ -90,21 +89,15 @@ public class UserRequestFormDirectoryPanel
 
         addOuterObject(manageFormModal);
 
-        manageFormModal.setWindowClosedCallback(new WindowClosedCallback() {
+        manageFormModal.setWindowClosedCallback(target -> {
+            updateResultTable(target);
 
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                updateResultTable(target);
-
-                Serializable widget = SyncopeConsoleSession.get().getAttribute(UserRequestFormsWidget.class.getName());
-                if (widget instanceof UserRequestFormsWidget) {
-                    ((UserRequestFormsWidget) widget).refreshLatestAlerts(target);
-                }
-
-                manageFormModal.show(false);
+            Serializable widget = SyncopeConsoleSession.get().getAttribute(UserRequestFormsWidget.class.getName());
+            if (widget instanceof UserRequestFormsWidget) {
+                ((UserRequestFormsWidget) widget).refreshLatestAlerts(target);
             }
+
+            manageFormModal.show(false);
         });
 
         restClient = new UserRequestRestClient();
diff --git a/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java b/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
index 06a6dde..f65bb0e 100644
--- a/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
+++ b/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/OIDCProvidersDirectoryPanel.java
@@ -36,7 +36,6 @@ import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
@@ -102,20 +101,11 @@ public class OIDCProvidersDirectoryPanel extends DirectoryPanel<
                 setFooterVisible(false);
             }
         };
-        templateModal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                templateModal.show(false);
-            }
-        });
+        templateModal.setWindowClosedCallback(target -> templateModal.show(false));
         templateModal.size(Modal.Size.Large);
         addOuterObject(templateModal);
 
         initResultTable();
-
     }
 
     @Override
diff --git a/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/AbstractSAMLSSOLoginFormPanel.java b/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/AbstractSAMLSSOLoginFormPanel.java
index 01866e1..7ca6cd4 100644
--- a/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/AbstractSAMLSSOLoginFormPanel.java
+++ b/ext/saml2sp4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/AbstractSAMLSSOLoginFormPanel.java
@@ -49,7 +49,7 @@ public abstract class AbstractSAMLSSOLoginFormPanel extends BaseSSOLoginFormPane
 
         List<SAML2SP4UIIdPTO> available = session.getAnonymousService(SAML2SP4UIIdPService.class).list();
 
-        final Model<SAML2SP4UIIdPTO> model = new Model<>();
+        Model<SAML2SP4UIIdPTO> model = new Model<>();
         AjaxDropDownChoicePanel<SAML2SP4UIIdPTO> idps =
                 new AjaxDropDownChoicePanel<>("idps", "SAML 2.0", model, false);
         idps.setChoices(available);
diff --git a/ext/saml2sp4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java b/ext/saml2sp4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
index bc98630..2cb375b 100644
--- a/ext/saml2sp4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
+++ b/ext/saml2sp4ui/client-console/src/main/java/org/apache/syncope/client/console/panels/SAML2IdPsDirectoryPanel.java
@@ -39,7 +39,6 @@ import org.apache.syncope.client.console.rest.SAML2IdPsRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal.WindowClosedCallback;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
@@ -54,6 +53,7 @@ import org.apache.syncope.common.lib.to.SAML2SP4UIIdPTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.SAML2SP4UIEntitlement;
+import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -62,7 +62,6 @@ import org.apache.wicket.event.IEvent;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
-import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
@@ -95,15 +94,9 @@ public class SAML2IdPsDirectoryPanel extends DirectoryPanel<
 
         modal.addSubmitButton();
         modal.size(Modal.Size.Large);
-        modal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                updateResultTable(target);
-                modal.show(false);
-            }
+        modal.setWindowClosedCallback(target -> {
+            updateResultTable(target);
+            modal.show(false);
         });
 
         addOuterObject(metadataModal);
@@ -120,15 +113,7 @@ public class SAML2IdPsDirectoryPanel extends DirectoryPanel<
                 setFooterVisible(false);
             }
         };
-        templateModal.setWindowClosedCallback(new WindowClosedCallback() {
-
-            private static final long serialVersionUID = 8804221891699487139L;
-
-            @Override
-            public void onClose(final AjaxRequestTarget target) {
-                templateModal.show(false);
-            }
-        });
+        templateModal.setWindowClosedCallback(target -> templateModal.show(false));
         templateModal.size(Modal.Size.Large);
         addOuterObject(templateModal);
 
@@ -145,7 +130,7 @@ public class SAML2IdPsDirectoryPanel extends DirectoryPanel<
                 importMetadata.toggle(target, true);
             }
         };
-        ((WebMarkupContainer) get("container:content")).addOrReplace(importMetadataLink);
+        ((MarkupContainer) get("container:content")).addOrReplace(importMetadataLink);
     }
 
     @Override
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 90d9ad4..da72e84 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -50,6 +50,7 @@ import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
@@ -249,6 +250,8 @@ public abstract class AbstractITCase {
 
     protected static SyncopeClient adminClient;
 
+    protected static SyncopeClient anonymusClient;
+
     protected static SyncopeService syncopeService;
 
     protected static ApplicationService applicationService;
@@ -325,10 +328,6 @@ public abstract class AbstractITCase {
 
     protected static ClientAppService clientAppService;
 
-    protected static GoogleMfaAuthTokenService googleMfaAuthTokenService;
-
-    protected static GoogleMfaAuthAccountService googleMfaAuthAccountService;
-
     protected static AuthProfileService authProfileService;
 
     protected static SAML2SPEntityService saml2SPEntityService;
@@ -337,10 +336,14 @@ public abstract class AbstractITCase {
 
     protected static OIDCJWKSService oidcJWKSService;
 
-    protected static U2FRegistrationService u2FRegistrationService;
-
     protected static WAConfigService waConfigService;
 
+    protected static GoogleMfaAuthTokenService googleMfaAuthTokenService;
+
+    protected static GoogleMfaAuthAccountService googleMfaAuthAccountService;
+
+    protected static U2FRegistrationService u2fRegistrationService;
+
     protected static WebAuthnRegistrationService webAuthnRegistrationService;
 
     protected static ImpersonationService impersonationService;
@@ -418,14 +421,17 @@ public abstract class AbstractITCase {
         authModuleService = adminClient.getService(AuthModuleService.class);
         saml2SPEntityService = adminClient.getService(SAML2SPEntityService.class);
         saml2IdPEntityService = adminClient.getService(SAML2IdPEntityService.class);
-        googleMfaAuthTokenService = adminClient.getService(GoogleMfaAuthTokenService.class);
-        googleMfaAuthAccountService = adminClient.getService(GoogleMfaAuthAccountService.class);
         authProfileService = adminClient.getService(AuthProfileService.class);
         oidcJWKSService = adminClient.getService(OIDCJWKSService.class);
-        u2FRegistrationService = adminClient.getService(U2FRegistrationService.class);
         waConfigService = adminClient.getService(WAConfigService.class);
-        webAuthnRegistrationService = adminClient.getService(WebAuthnRegistrationService.class);
-        impersonationService = adminClient.getService(ImpersonationService.class);
+
+        anonymusClient = clientFactory.create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+
+        googleMfaAuthTokenService = anonymusClient.getService(GoogleMfaAuthTokenService.class);
+        googleMfaAuthAccountService = anonymusClient.getService(GoogleMfaAuthAccountService.class);
+        u2fRegistrationService = anonymusClient.getService(U2FRegistrationService.class);
+        webAuthnRegistrationService = anonymusClient.getService(WebAuthnRegistrationService.class);
+        impersonationService = anonymusClient.getService(ImpersonationService.class);
     }
 
     @Autowired
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
index 88554fe..0270dc8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirSchemaITCase.java
@@ -28,7 +28,6 @@ import java.security.AccessControlException;
 import java.util.List;
 import java.util.Locale;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.VirSchemaTO;
@@ -108,9 +107,7 @@ public class VirSchemaITCase extends AbstractITCase {
             assertNotNull(e);
         }
 
-        SchemaService anonymous = clientFactory.create(
-                new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY)).
-                getService(SchemaService.class);
+        SchemaService anonymous = anonymusClient.getService(SchemaService.class);
         assertFalse(anonymous.search(new SchemaQuery.Builder().type(SchemaType.VIRTUAL).build()).isEmpty());
     }
 
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 7dcca15..8e20909 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
@@ -20,7 +20,6 @@ package org.apache.syncope.fit.core.wa;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -29,10 +28,10 @@ import java.security.SecureRandom;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.Date;
-import java.util.List;
 import java.util.UUID;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.fit.AbstractITCase;
 import org.junit.jupiter.api.BeforeEach;
@@ -72,13 +71,13 @@ public class GoogleMfaAuthTokenITCase extends AbstractITCase {
         String owner = UUID.randomUUID().toString();
         GoogleMfaAuthToken token = createGoogleMfaAuthToken();
         googleMfaAuthTokenService.store(owner, token);
-        List<AuthProfileTO> results = authProfileService.list();
-        assertFalse(results.isEmpty());
-        AuthProfileTO profileTO = results.get(0);
-        assertNotNull(authProfileService.read(profileTO.getKey()));
-        assertNotNull(authProfileService.readByOwner(profileTO.getOwner()));
-        authProfileService.deleteByOwner(owner);
-        assertThrows(SyncopeClientException.class, () -> authProfileService.readByOwner(owner));
+        PagedResult<AuthProfileTO> results = authProfileService.list(1, 100);
+        assertFalse(results.getResult().isEmpty());
+        AuthProfileTO profileTO = results.getResult().stream().
+                filter(p -> owner.equals(p.getOwner())).findFirst().get();
+        assertEquals(profileTO, authProfileService.read(profileTO.getKey()));
+        authProfileService.delete(profileTO.getKey());
+        assertThrows(SyncopeClientException.class, () -> authProfileService.read(profileTO.getKey()));
     }
 
     @Test
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/U2FRegistrationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/U2FRegistrationITCase.java
index 99b132f..5612216 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/U2FRegistrationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/U2FRegistrationITCase.java
@@ -49,12 +49,12 @@ public class U2FRegistrationITCase extends AbstractITCase {
 
     @BeforeEach
     public void setup() {
-        u2FRegistrationService.delete(new U2FDeviceQuery.Builder().build());
+        u2fRegistrationService.delete(new U2FDeviceQuery.Builder().build());
     }
 
     @Test
     public void create() {
-        assertDoesNotThrow(() -> u2FRegistrationService.create(
+        assertDoesNotThrow(() -> u2fRegistrationService.create(
                 UUID.randomUUID().toString(), createDeviceRegistration()));
     }
 
@@ -62,17 +62,17 @@ public class U2FRegistrationITCase extends AbstractITCase {
     public void count() {
         String owner = UUID.randomUUID().toString();
         U2FDevice device = createDeviceRegistration();
-        u2FRegistrationService.create(owner, device);
+        u2fRegistrationService.create(owner, device);
 
-        List<U2FDevice> devices = u2FRegistrationService.search(
+        List<U2FDevice> devices = u2fRegistrationService.search(
                 new U2FDeviceQuery.Builder().owner(owner).expirationDate(
                         Date.from(LocalDate.now().minusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant())).
                         build()).getResult();
         assertEquals(1, devices.size());
 
-        u2FRegistrationService.delete(new U2FDeviceQuery.Builder().id(device.getId()).build());
+        u2fRegistrationService.delete(new U2FDeviceQuery.Builder().id(device.getId()).build());
 
-        devices = u2FRegistrationService.search(new U2FDeviceQuery.Builder().build()).getResult();
+        devices = u2fRegistrationService.search(new U2FDeviceQuery.Builder().build()).getResult();
         assertTrue(devices.isEmpty());
     }
 
@@ -80,18 +80,18 @@ public class U2FRegistrationITCase extends AbstractITCase {
     public void delete() {
         U2FDevice device = createDeviceRegistration();
         String owner = UUID.randomUUID().toString();
-        u2FRegistrationService.create(owner, device);
+        u2fRegistrationService.create(owner, device);
 
-        u2FRegistrationService.delete(new U2FDeviceQuery.Builder().owner(owner).build());
-        assertTrue(u2FRegistrationService.search(
+        u2fRegistrationService.delete(new U2FDeviceQuery.Builder().owner(owner).build());
+        assertTrue(u2fRegistrationService.search(
                 new U2FDeviceQuery.Builder().owner(owner).build()).getResult().isEmpty());
 
         Date date = Date.from(LocalDate.now().plusDays(1)
                 .atStartOfDay(ZoneId.systemDefault()).toInstant());
 
-        u2FRegistrationService.delete(new U2FDeviceQuery.Builder().expirationDate(date).build());
+        u2fRegistrationService.delete(new U2FDeviceQuery.Builder().expirationDate(date).build());
 
-        assertTrue(u2FRegistrationService.search(
+        assertTrue(u2fRegistrationService.search(
                 new U2FDeviceQuery.Builder().expirationDate(date).build()).getResult().isEmpty());
     }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WebAuthnAccountITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WebAuthnAccountITCase.java
index a9a8c3d..3026b21 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WebAuthnAccountITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WebAuthnAccountITCase.java
@@ -21,11 +21,9 @@ package org.apache.syncope.fit.core.wa;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.UUID;
-import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.wa.WebAuthnAccount;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
@@ -68,7 +66,7 @@ public class WebAuthnAccountITCase extends AbstractITCase {
         WebAuthnAccount acct = createWebAuthnRegisteredAccount();
         webAuthnRegistrationService.create(owner, acct);
         webAuthnRegistrationService.delete(owner);
-        assertThrows(SyncopeClientException.class, () -> webAuthnRegistrationService.read(owner));
+        assertTrue(webAuthnRegistrationService.read(owner).getCredentials().isEmpty());
     }
 
     @Test
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
index 7e3062b..22aad9a 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
@@ -178,7 +178,7 @@ public abstract class AbstractSRAITCase extends AbstractITCase {
 
         SRA = processBuilder.start();
 
-        await().atMost(30, TimeUnit.SECONDS).pollInterval(3, TimeUnit.SECONDS).until(() -> {
+        await().atMost(120, TimeUnit.SECONDS).pollInterval(3, TimeUnit.SECONDS).until(() -> {
             boolean connected = false;
             try (Socket socket = new Socket()) {
                 socket.connect(new InetSocketAddress("0.0.0.0", PORT));
diff --git a/pom.xml b/pom.xml
index 25fc121..48309f0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,7 @@ under the License.
 
   <ciManagement>
     <system>Jenkins</system>
-    <url>https://builds.apache.org/view/S-Z/view/Syncope/</url>
+    <url>https://ci-builds.apache.org./job/Syncope/</url>
   </ciManagement>
 
   <distributionManagement>
@@ -411,7 +411,7 @@ under the License.
     <jackson.version>2.12.3</jackson.version>
 
     <spring.version>5.3.5</spring.version>
-    <spring-security.version>5.4.5</spring-security.version>
+    <spring-security.version>5.4.6</spring-security.version>
     <spring-boot.version>2.4.4</spring-boot.version>
     <spring-cloud-gateway.version>3.0.2</spring-cloud-gateway.version>
 
@@ -439,7 +439,7 @@ under the License.
     <apachedirapi.version>2.0.0</apachedirapi.version>
 
     <log4j.version>2.14.1</log4j.version>
-    <disruptor.version>3.4.2</disruptor.version>
+    <disruptor.version>3.4.3</disruptor.version>
     
     <commons-jexl.version>3.1</commons-jexl.version>
     <commons-lang.version>3.12.0</commons-lang.version>
diff --git a/src/site/xdoc/integration.xml b/src/site/xdoc/integration.xml
index f434f77..d86a62b 100644
--- a/src/site/xdoc/integration.xml
+++ b/src/site/xdoc/integration.xml
@@ -28,7 +28,7 @@ under the License.
   <body>    
     <section name="Jenkins">
       <p>
-        <source><a href="https://builds.apache.org/view/S-Z/view/Syncope/">https://builds.apache.org/view/S-Z/view/Syncope/</a></source>
+        <source><a href="https://ci-builds.apache.org./job/Syncope/">https://ci-builds.apache.org./job/Syncope/</a></source>
       </p>
     </section>
 
@@ -58,9 +58,6 @@ under the License.
       <p>
         <source><a href="https://ci.apache.org/builders/syncope-2_1_X-docs">https://ci.apache.org/builders/syncope-2_1_X-docs</a></source>
       </p>
-      <p>
-        <source><a href="https://ci.apache.org/builders/syncope-2_0_X-docs">https://ci.apache.org/builders/syncope-2_0_X-docs</a></source>
-      </p>
     </section>
   </body>
 </document>
diff --git a/src/site/xdoc/release-process.xml b/src/site/xdoc/release-process.xml
index 6d6e59f..f13a710 100644
--- a/src/site/xdoc/release-process.xml
+++ b/src/site/xdoc/release-process.xml
@@ -586,7 +586,7 @@ svn commit -m "Promoting the staging site"]]></source>
           </li>
           <li>
             Deploy the updated Docker images to <a href="https://hub.docker.com/">DockerHub</a> by adjusting the GIT tag
-            name then running the <a href="https://builds.apache.org/job/Syncope-Release-Docker">dedicated Jenkins job</a>.
+            name then running the <a href="https://ci-builds.apache.org./job/Syncope/job/Syncope-Release-Docker/">dedicated Jenkins job</a>.
           </li>
         </ol>
       </subsection>