You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by mm...@apache.org on 2021/03/15 10:51:59 UTC

[syncope] branch master updated: Upgrade to CAS 6.4 RC2 and pac4j v5 RC2 (#244)

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

mmoayyed 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 8a1ffb2  Upgrade to CAS 6.4 RC2 and pac4j v5 RC2 (#244)
8a1ffb2 is described below

commit 8a1ffb20e798ebab4db216fb4d57ef1593ada214
Author: Misagh Moayyed <mi...@gmail.com>
AuthorDate: Mon Mar 15 14:21:52 2021 +0330

    Upgrade to CAS 6.4 RC2 and pac4j v5 RC2 (#244)
    
    Co-authored-by: Francesco Chicchiriccò <il...@users.noreply.github.com>
---
 .../markup/html/bootstrap/dialog/BaseModal.java    |  1 +
 .../common/lib/auth/SAML2IdPAuthModuleConf.java    | 12 +++---
 .../syncope/common/lib/policy/AuthPolicyConf.java  |  1 +
 .../rest/api/service/SAML2IdPMetadataService.java  |  1 +
 .../rest/api/service/SAML2SPKeystoreService.java   |  1 +
 .../rest/api/service/SAML2SPMetadataService.java   |  1 +
 .../core/provisioning/api/AccountGetter.java       |  1 +
 .../core/provisioning/api/PlainAttrGetter.java     |  1 +
 .../core/provisioning/api/ProvisionSorter.java     |  1 +
 .../api/data/wa/WAClientAppDataBinder.java         |  1 +
 .../pushpull/stream/SyncopeStreamPullExecutor.java |  1 +
 .../pushpull/stream/SyncopeStreamPushExecutor.java |  1 +
 .../wa/src/main/resources/application.properties   |  7 ++++
 .../apache/syncope/core/logic/OIDCC4UILogic.java   | 17 ++++----
 .../syncope/core/logic/oidc/NoOpLogoutHandler.java |  2 +-
 .../syncope/core/logic/oidc/NoOpSessionStore.java  | 25 ++++++------
 .../syncope/core/logic/oidc/OIDC4UIContext.java    | 25 ++++++------
 .../syncope/core/logic/oidc/OIDCClientCache.java   |  8 ++--
 .../apache/syncope/core/logic/SAML2SP4UILogic.java | 22 ++++++-----
 .../core/logic/saml2/NoOpLogoutHandler.java        |  2 +-
 .../syncope/core/logic/saml2/NoOpSessionStore.java | 25 ++++++------
 .../core/logic/saml2/SAML2SP4UIContext.java        | 29 +++++++-------
 .../syncope/fit/core/AuthenticationITCase.java     |  1 -
 .../test/resources/application-saml2.properties    |  1 +
 pom.xml                                            |  4 +-
 .../org/apache/syncope/sra/SecurityConfig.java     |  2 +-
 .../sra/security/pac4j/NoOpLogoutHandler.java      |  2 +-
 .../sra/security/pac4j/NoOpSessionStore.java       | 25 ++++++------
 .../security/pac4j/ServerWebExchangeContext.java   | 13 +++----
 .../saml2/SAML2LogoutResponseWebFilter.java        |  6 ++-
 .../saml2/SAML2RequestServerLogoutHandler.java     |  4 +-
 .../SAML2WebSsoAuthenticationRequestWebFilter.java |  3 +-
 .../saml2/SAML2WebSsoAuthenticationWebFilter.java  |  7 +++-
 .../resources/debug/application-debug.properties   |  1 +
 .../bootstrap/SyncopeWAPropertySourceLocator.java  | 13 ++++---
 wa/pom.xml                                         |  2 -
 .../wa/starter/config/SyncopeWAConfiguration.java  | 45 ++++++++++++++++++++--
 .../syncope/wa/starter/mapping/AccessMapper.java   |  1 +
 .../starter/mapping/AllowedAttrReleaseMapper.java  |  2 -
 .../wa/starter/mapping/AttrReleaseMapper.java      |  1 +
 .../syncope/wa/starter/mapping/AuthMapper.java     |  1 +
 .../syncope/wa/starter/mapping/CASSPTOMapper.java  |  2 -
 .../wa/starter/mapping/ClientAppMapper.java        |  1 +
 .../wa/starter/mapping/DefaultAccessMapper.java    |  2 -
 .../wa/starter/mapping/DefaultAuthMapper.java      |  2 -
 .../syncope/wa/starter/mapping/OIDCRPTOMapper.java |  2 -
 .../starter/mapping/RegisteredServiceMapper.java   | 16 ++++++--
 .../wa/starter/mapping/SAML2SPTOMapper.java        |  2 -
 .../pac4j/saml/SyncopeWASAML2ClientCustomizer.java |  4 +-
 .../metadata/RestfulSamlIdPMetadataLocator.java    |  4 +-
 .../starter/services/SyncopeWAServiceRegistry.java |  9 ++++-
 .../src/main/resources/application.properties      |  9 ++++-
 .../apache/syncope/wa/starter/AbstractTest.java    | 23 +++++++++--
 .../wa/starter/SyncopeCoreTestingServer.java       |  2 -
 .../pac4j/saml/BaseSyncopeWASAML2ClientTest.java   |  5 ++-
 55 files changed, 247 insertions(+), 155 deletions(-)

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 4bdf2cd..f58bbb1 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
@@ -382,6 +382,7 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
      * {@link BaseModal#setWindowClosedCallback(BaseModal.WindowClosedCallback)}, no ajax
      * request will be fired.
      */
+    @FunctionalInterface
     public interface WindowClosedCallback extends IClusterable {
 
         /**
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
index 28eb879..0c4b0ed 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
@@ -67,14 +67,14 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
      * will accept assertions based on a previous authentication for one hour.
      * You can adjust this behavior by modifying this setting. The unit of time here is seconds.
      */
-    private int maximumAuthenticationLifetime = 3600;
+    private String maximumAuthenticationLifetime = "PT3600S";
 
     /**
      * Maximum skew in seconds between SP and IDP clocks.
      * This skew is added onto the {@code NotOnOrAfter} field in seconds
      * for the SAML response validation.
      */
-    private int acceptedSkew = 300;
+    private String acceptedSkew = "PT300S";
 
     /**
      * The entity id of the SP that is used in the SP metadata generation process.
@@ -224,19 +224,19 @@ public class SAML2IdPAuthModuleConf extends AbstractAuthModuleConf {
         this.identityProviderMetadataPath = identityProviderMetadataPath;
     }
 
-    public int getMaximumAuthenticationLifetime() {
+    public String getMaximumAuthenticationLifetime() {
         return maximumAuthenticationLifetime;
     }
 
-    public void setMaximumAuthenticationLifetime(final int maximumAuthenticationLifetime) {
+    public void setMaximumAuthenticationLifetime(final String maximumAuthenticationLifetime) {
         this.maximumAuthenticationLifetime = maximumAuthenticationLifetime;
     }
 
-    public int getAcceptedSkew() {
+    public String getAcceptedSkew() {
         return acceptedSkew;
     }
 
-    public void setAcceptedSkew(final int acceptedSkew) {
+    public void setAcceptedSkew(final String acceptedSkew) {
         this.acceptedSkew = acceptedSkew;
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AuthPolicyConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AuthPolicyConf.java
index f1dcb13..1966dc2 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AuthPolicyConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AuthPolicyConf.java
@@ -21,6 +21,7 @@ package org.apache.syncope.common.lib.policy;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import org.apache.syncope.common.lib.BaseBean;
 
+@FunctionalInterface
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
 public interface AuthPolicyConf extends BaseBean {
 
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
index 5fad024..f4f70e6 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2IdPMetadataService.java
@@ -38,6 +38,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 /**
  * REST operations for SAML 2.0 IdP metadata.
  */
+@FunctionalInterface
 @Tag(name = "SAML 2.0")
 @SecurityRequirements({
     @SecurityRequirement(name = "BasicAuthentication"),
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreService.java
index 47c1162..874c175 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPKeystoreService.java
@@ -38,6 +38,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 /**
  * REST operations for SAML 2.0 SP Keystore.
  */
+@FunctionalInterface
 @Tag(name = "SAML 2.0")
 @SecurityRequirements({
     @SecurityRequirement(name = "BasicAuthentication"),
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataService.java
index f90c0f4..4fba699 100644
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataService.java
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SAML2SPMetadataService.java
@@ -38,6 +38,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
 /**
  * REST operations for SAML 2.0 SP metadata.
  */
+@FunctionalInterface
 @Tag(name = "SAML 2.0")
 @SecurityRequirements({
     @SecurityRequirement(name = "BasicAuthentication"),
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
index 815b577..3210c97 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api;
 import java.util.function.Function;
 import org.apache.syncope.core.persistence.api.entity.user.Account;
 
+@FunctionalInterface
 @SuppressWarnings("squid:S1214")
 public interface AccountGetter extends Function<Account, Account> {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
index de0d306..12f74c3 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
@@ -22,6 +22,7 @@ import java.util.function.BiFunction;
 import org.apache.syncope.core.persistence.api.entity.Attributable;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 
+@FunctionalInterface
 @SuppressWarnings("squid:S1214")
 public interface PlainAttrGetter extends BiFunction<Attributable<? extends PlainAttr<?>>, String, PlainAttr<?>> {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java
index ad9d3a1..a4207c5 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api;
 import java.util.Comparator;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 
+@FunctionalInterface
 public interface ProvisionSorter extends Comparator<Provision> {
 
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
index 5cb03ad..67f9ba5 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api.data.wa;
 import org.apache.syncope.common.lib.wa.WAClientApp;
 import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
 
+@FunctionalInterface
 public interface WAClientAppDataBinder {
 
     WAClientApp getWAClientApp(ClientApp clientApp);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
index 5215e63..831dd1f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java
@@ -26,6 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.provisioning.api.Connector;
 import org.quartz.JobExecutionException;
 
+@FunctionalInterface
 public interface SyncopeStreamPullExecutor {
 
     List<ProvisioningReport> pull(
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
index c2b1e44..f8242a1 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
@@ -26,6 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.provisioning.api.Connector;
 import org.quartz.JobExecutionException;
 
+@FunctionalInterface
 public interface SyncopeStreamPushExecutor {
 
     List<ProvisioningReport> push(
diff --git a/docker/wa/src/main/resources/application.properties b/docker/wa/src/main/resources/application.properties
index a34ac87..28404d9 100644
--- a/docker/wa/src/main/resources/application.properties
+++ b/docker/wa/src/main/resources/application.properties
@@ -36,6 +36,13 @@ management.endpoint.loggers.enabled=true
 management.endpoint.refresh.enabled=true
 management.endpoint.registeredServices.enabled=true
 
+# Cache service definitions for 5 minutes
+cas.service-registry.cache.duration=PT5M
+
+# Reload services and hydrate the cache every 5 minutes
+cas.service-registry.schedule.repeat-interval=PT5M
+cas.service-registry.schedule.start-delay=PT30S
+
 ##
 # Allow configuration classes to override bean definitions from Spring Boot
 #
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogic.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogic.java
index 8cef563..5e5d25a 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogic.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogic.java
@@ -39,6 +39,7 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.logic.oidc.NoOpSessionStore;
 import org.apache.syncope.core.logic.oidc.OIDC4UIContext;
 import org.apache.syncope.core.logic.oidc.OIDCClientCache;
 import org.apache.syncope.core.logic.oidc.OIDCUserManager;
@@ -56,7 +57,6 @@ import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProviderItem;
 import org.apache.syncope.core.persistence.api.dao.OIDCC4UIProviderDAO;
 import org.pac4j.core.exception.http.WithLocationAction;
 import org.pac4j.oidc.client.OidcClient;
-import org.pac4j.oidc.config.OidcConfiguration;
 import org.pac4j.oidc.credentials.OidcCredentials;
 import org.pac4j.oidc.profile.OidcProfile;
 
@@ -84,11 +84,11 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
     @Autowired
     private OIDCUserManager userManager;
 
-    private OidcClient<OidcConfiguration> getOidcClient(final OIDCC4UIProvider op, final String callbackUrl) {
+    private OidcClient getOidcClient(final OIDCC4UIProvider op, final String callbackUrl) {
         return oidcClientClientCache.get(op.getName()).orElseGet(() -> oidcClientClientCache.add(op, callbackUrl));
     }
 
-    private OidcClient<OidcConfiguration> getOidcClient(final String opName, final String callbackUrl) {
+    private OidcClient getOidcClient(final String opName, final String callbackUrl) {
         OIDCC4UIProvider op = opDAO.findByName(opName);
         if (op == null) {
             throw new NotFoundException("OIDC Provider '" + opName + '\'');
@@ -100,11 +100,11 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
     @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     public OIDCRequest createLoginRequest(final String redirectURI, final String opName) {
         // 1. look for OidcClient
-        OidcClient<OidcConfiguration> oidcClient = getOidcClient(opName, redirectURI);
+        OidcClient oidcClient = getOidcClient(opName, redirectURI);
         oidcClient.setCallbackUrl(redirectURI);
 
         // 2. create OIDCRequest
-        WithLocationAction action = oidcClient.getRedirectionAction(new OIDC4UIContext()).
+        WithLocationAction action = oidcClient.getRedirectionAction(new OIDC4UIContext(), NoOpSessionStore.INSTANCE).
                 map(WithLocationAction.class::cast).
                 orElseThrow(() -> {
                     SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
@@ -126,7 +126,7 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
         }
 
         // 1. look for configured client
-        OidcClient<OidcConfiguration> oidcClient = getOidcClient(opName, redirectURI);
+        OidcClient oidcClient = getOidcClient(opName, redirectURI);
         oidcClient.setCallbackUrl(redirectURI);
 
         // 2. get OpenID Connect tokens
@@ -138,7 +138,7 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
 
             OIDC4UIContext ctx = new OIDC4UIContext();
 
-            oidcClient.getAuthenticator().validate(credentials, ctx);
+            oidcClient.getAuthenticator().validate(credentials, ctx, NoOpSessionStore.INSTANCE);
 
             idToken = credentials.getIdToken().getJWTClaimsSet();
             idTokenHint = credentials.getIdToken().serialize();
@@ -257,7 +257,7 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
         }
 
         // 1. look for OidcClient
-        OidcClient<OidcConfiguration> oidcClient =
+        OidcClient oidcClient =
                 getOidcClient((String) claimsSet.getClaim(JWT_CLAIM_OP_NAME), redirectURI);
         oidcClient.setCallbackUrl(redirectURI);
 
@@ -267,6 +267,7 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
 
         WithLocationAction action = oidcClient.getLogoutAction(
                 new OIDC4UIContext(),
+                NoOpSessionStore.INSTANCE,
                 profile,
                 redirectURI).
                 map(WithLocationAction.class::cast).
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpLogoutHandler.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpLogoutHandler.java
index 8dcc984..18fd27d 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpLogoutHandler.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpLogoutHandler.java
@@ -20,5 +20,5 @@ package org.apache.syncope.core.logic.oidc;
 
 import org.pac4j.core.logout.handler.LogoutHandler;
 
-public class NoOpLogoutHandler implements LogoutHandler<OIDC4UIContext> {
+public class NoOpLogoutHandler implements LogoutHandler {
 }
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpSessionStore.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpSessionStore.java
index 11cf61f..b60a9ba 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpSessionStore.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/NoOpSessionStore.java
@@ -19,9 +19,11 @@
 package org.apache.syncope.core.logic.oidc;
 
 import java.util.Optional;
+
+import org.pac4j.core.context.WebContext;
 import org.pac4j.core.context.session.SessionStore;
 
-public final class NoOpSessionStore implements SessionStore<OIDC4UIContext> {
+public final class NoOpSessionStore implements SessionStore {
 
     public static final NoOpSessionStore INSTANCE = new NoOpSessionStore();
 
@@ -30,40 +32,37 @@ public final class NoOpSessionStore implements SessionStore<OIDC4UIContext> {
     }
 
     @Override
-    public String getOrCreateSessionId(final OIDC4UIContext context) {
-        return "<NO_KEY>";
+    public Optional<String> getSessionId(final WebContext webContext, final boolean b) {
+        return Optional.empty();
     }
 
     @Override
-    public Optional<Object> get(final OIDC4UIContext context, final String key) {
+    public Optional<Object> get(final WebContext webContext, final String s) {
         return Optional.empty();
     }
 
     @Override
-    public void set(final OIDC4UIContext context, final String key, final Object value) {
-        // nothing to do
+    public void set(final WebContext webContext, final String s, final Object o) {
+
     }
 
     @Override
-    public boolean destroySession(final OIDC4UIContext context) {
+    public boolean destroySession(final WebContext webContext) {
         return true;
     }
 
     @Override
-    public Optional<?> getTrackableSession(final OIDC4UIContext context) {
+    public Optional<Object> getTrackableSession(final WebContext webContext) {
         return Optional.empty();
     }
 
     @Override
-    public Optional<SessionStore<OIDC4UIContext>> buildFromTrackableSession(
-            final OIDC4UIContext context,
-            final Object trackableSession) {
-
+    public Optional<SessionStore> buildFromTrackableSession(final WebContext webContext, final Object o) {
         return Optional.empty();
     }
 
     @Override
-    public boolean renewSession(final OIDC4UIContext context) {
+    public boolean renewSession(final WebContext webContext) {
         return false;
     }
 }
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDC4UIContext.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDC4UIContext.java
index 9eb4fb9..deaed5f 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDC4UIContext.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDC4UIContext.java
@@ -25,7 +25,6 @@ import java.util.Set;
 import javax.ws.rs.HttpMethod;
 import org.pac4j.core.context.Cookie;
 import org.pac4j.core.context.WebContext;
-import org.pac4j.core.context.session.SessionStore;
 
 public class OIDC4UIContext implements WebContext {
 
@@ -35,11 +34,6 @@ public class OIDC4UIContext implements WebContext {
     }
 
     @Override
-    public SessionStore<OIDC4UIContext> getSessionStore() {
-        return NoOpSessionStore.INSTANCE;
-    }
-
-    @Override
     public Optional<String> getRequestParameter(final String name) {
         return Optional.empty();
     }
@@ -51,7 +45,7 @@ public class OIDC4UIContext implements WebContext {
 
     @Override
     public Optional<String> getRequestAttribute(final String name) {
-        throw new UnsupportedOperationException();
+        return Optional.empty();
     }
 
     @Override
@@ -66,7 +60,7 @@ public class OIDC4UIContext implements WebContext {
 
     @Override
     public String getRemoteAddr() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
@@ -75,8 +69,13 @@ public class OIDC4UIContext implements WebContext {
     }
 
     @Override
+    public Optional<String> getResponseHeader(final String s) {
+        return Optional.empty();
+    }
+
+    @Override
     public String getServerName() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
@@ -86,17 +85,17 @@ public class OIDC4UIContext implements WebContext {
 
     @Override
     public String getScheme() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
     public boolean isSecure() {
-        throw new UnsupportedOperationException();
+        return false;
     }
 
     @Override
     public String getFullRequestURL() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
@@ -111,7 +110,7 @@ public class OIDC4UIContext implements WebContext {
 
     @Override
     public String getPath() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCClientCache.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCClientCache.java
index a56b193..063e343 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCClientCache.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCClientCache.java
@@ -50,7 +50,7 @@ public class OIDCClientCache {
 
     protected static final Logger LOG = LoggerFactory.getLogger(OIDCClientCache.class);
 
-    private final List<OidcClient<OidcConfiguration>> cache = Collections.synchronizedList(new ArrayList<>());
+    private final List<OidcClient> cache = Collections.synchronizedList(new ArrayList<>());
 
     private static OIDCProviderMetadata getDiscoveryDocument(final String issuer) {
         String discoveryDocumentURL = issuer + "/.well-known/openid-configuration";
@@ -86,11 +86,11 @@ public class OIDCClientCache {
                 Optional.ofNullable(metadata.getEndSessionEndpointURI()).map(URI::toASCIIString).orElse(null));
     }
 
-    public Optional<OidcClient<OidcConfiguration>> get(final String opName) {
+    public Optional<OidcClient> get(final String opName) {
         return cache.stream().filter(c -> opName.equals(c.getName())).findFirst();
     }
 
-    public OidcClient<OidcConfiguration> add(final OIDCC4UIProvider op, final String callbackUrl) {
+    public OidcClient add(final OIDCC4UIProvider op, final String callbackUrl) {
         OIDCProviderMetadata metadata = new OIDCProviderMetadata(
                 new Issuer(op.getIssuer()),
                 List.of(SubjectType.PUBLIC),
@@ -112,7 +112,7 @@ public class OIDCClientCache {
         config.setUseNonce(false);
         config.setLogoutHandler(new NoOpLogoutHandler());
 
-        OidcClient<OidcConfiguration> client = new OidcClient<>(config);
+        OidcClient client = new OidcClient(config);
         client.setName(op.getName());
         client.setCallbackUrlResolver(new NoParameterCallbackUrlResolver());
         client.setCallbackUrl(callbackUrl);
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
index 54d39c8..e5c3b79 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
@@ -45,6 +45,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.SAML2BindingType;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.logic.init.SAML2SP4UILoader;
+import org.apache.syncope.core.logic.saml2.NoOpSessionStore;
 import org.apache.syncope.core.logic.saml2.SAML2ClientCache;
 import org.apache.syncope.core.logic.saml2.SAML2SP4UIContext;
 import org.apache.syncope.core.logic.saml2.SAML2SP4UIUserManager;
@@ -54,6 +55,7 @@ import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
 import org.opensaml.saml.saml2.core.LogoutResponse;
 import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.StatusCode;
+import org.pac4j.core.context.session.SessionStore;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -250,12 +252,11 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
 
             if (requestedAuthnContextProvider != null) {
                 RequestedAuthnContext requestedAuthnContext = requestedAuthnContextProvider.get();
-                SAML2Configuration cfg = saml2Client.getConfiguration();
                 saml2Client.setRedirectionActionBuilder(new SAML2RedirectionActionBuilder(saml2Client) {
-
                     @Override
-                    public Optional<RedirectionAction> getRedirectionAction(final WebContext wc) {
-                        this.saml2ObjectBuilder = new SAML2AuthnRequestBuilder(cfg) {
+                    public Optional<RedirectionAction> getRedirectionAction(final WebContext wc,
+                                                                            final SessionStore sessionStore) {
+                        this.saml2ObjectBuilder = new SAML2AuthnRequestBuilder() {
 
                             @Override
                             public AuthnRequest build(final SAML2MessageContext context) {
@@ -264,7 +265,7 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
                                 return authnRequest;
                             }
                         };
-                        return super.getRedirectionAction(wc);
+                        return super.getRedirectionAction(wc, sessionStore);
                     }
                 });
             }
@@ -274,7 +275,7 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
         SAML2SP4UIContext ctx = new SAML2SP4UIContext(
                 saml2Client.getConfiguration().getAuthnRequestBindingType(),
                 null);
-        RedirectionAction action = saml2Client.getRedirectionAction(ctx).
+        RedirectionAction action = saml2Client.getRedirectionAction(ctx, NoOpSessionStore.INSTANCE).
                 orElseThrow(() -> {
                     SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
                     sce.getElements().add("No RedirectionAction generated for AuthnRequest");
@@ -301,10 +302,11 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
                     saml2Client.getConfiguration().getAuthnRequestBindingType(),
                     saml2Response);
 
-            credentials = saml2Client.getCredentialsExtractor().extract(ctx).
+            credentials = (SAML2Credentials) saml2Client.getCredentialsExtractor().
+                extract(ctx, NoOpSessionStore.INSTANCE).
                     orElseThrow(() -> new IllegalStateException("No AuthnResponse found"));
 
-            saml2Client.getAuthenticator().validate(credentials, ctx);
+            saml2Client.getAuthenticator().validate(credentials, ctx, NoOpSessionStore.INSTANCE);
         } catch (Exception e) {
             LOG.error("While validating AuthnResponse", e);
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
@@ -457,6 +459,7 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
                 saml2Client.getConfiguration().getSpLogoutRequestBindingType(), null);
         RedirectionAction action = saml2Client.getLogoutAction(
                 ctx,
+                NoOpSessionStore.INSTANCE,
                 saml2Profile, null).
                 orElseThrow(() -> {
                     SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
@@ -488,7 +491,8 @@ public class SAML2SP4UILogic extends AbstractTransactionalLogic<EntityTO> {
 
         LogoutResponse logoutResponse;
         try {
-            SAML2MessageContext saml2Ctx = saml2Client.getContextProvider().buildContext(ctx);
+            SAML2MessageContext saml2Ctx = saml2Client.getContextProvider().
+                buildContext(saml2Client, ctx, NoOpSessionStore.INSTANCE);
             saml2Client.getLogoutProfileHandler().receive(saml2Ctx);
 
             logoutResponse = (LogoutResponse) saml2Ctx.getMessageContext().getMessage();
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpLogoutHandler.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpLogoutHandler.java
index 223eaf1..301502e 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpLogoutHandler.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpLogoutHandler.java
@@ -20,5 +20,5 @@ package org.apache.syncope.core.logic.saml2;
 
 import org.pac4j.core.logout.handler.LogoutHandler;
 
-public class NoOpLogoutHandler implements LogoutHandler<SAML2SP4UIContext> {
+public class NoOpLogoutHandler implements LogoutHandler {
 }
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpSessionStore.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpSessionStore.java
index 8e21f81..c3c1bd5 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpSessionStore.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/NoOpSessionStore.java
@@ -19,9 +19,11 @@
 package org.apache.syncope.core.logic.saml2;
 
 import java.util.Optional;
+
+import org.pac4j.core.context.WebContext;
 import org.pac4j.core.context.session.SessionStore;
 
-public final class NoOpSessionStore implements SessionStore<SAML2SP4UIContext> {
+public final class NoOpSessionStore implements SessionStore {
 
     public static final NoOpSessionStore INSTANCE = new NoOpSessionStore();
 
@@ -30,40 +32,37 @@ public final class NoOpSessionStore implements SessionStore<SAML2SP4UIContext> {
     }
 
     @Override
-    public String getOrCreateSessionId(final SAML2SP4UIContext context) {
-        return "<NO_KEY>";
+    public Optional<String> getSessionId(final WebContext webContext, final boolean b) {
+        return Optional.empty();
     }
 
     @Override
-    public Optional<Object> get(final SAML2SP4UIContext context, final String key) {
+    public Optional<Object> get(final WebContext webContext, final String s) {
         return Optional.empty();
     }
 
     @Override
-    public void set(final SAML2SP4UIContext context, final String key, final Object value) {
-        // nothing to do
+    public void set(final WebContext webContext, final String s, final Object o) {
+
     }
 
     @Override
-    public boolean destroySession(final SAML2SP4UIContext context) {
+    public boolean destroySession(final WebContext webContext) {
         return true;
     }
 
     @Override
-    public Optional<?> getTrackableSession(final SAML2SP4UIContext context) {
+    public Optional<Object> getTrackableSession(final WebContext webContext) {
         return Optional.empty();
     }
 
     @Override
-    public Optional<SessionStore<SAML2SP4UIContext>> buildFromTrackableSession(
-            final SAML2SP4UIContext context,
-            final Object trackableSession) {
-
+    public Optional<SessionStore> buildFromTrackableSession(final WebContext webContext, final Object o) {
         return Optional.empty();
     }
 
     @Override
-    public boolean renewSession(final SAML2SP4UIContext context) {
+    public boolean renewSession(final WebContext webContext) {
         return false;
     }
 }
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIContext.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIContext.java
index 5c83b20..88918f4 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIContext.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIContext.java
@@ -28,7 +28,6 @@ import org.apache.syncope.common.lib.saml2.SAML2Response;
 import org.apache.syncope.common.lib.types.SAML2BindingType;
 import org.pac4j.core.context.Cookie;
 import org.pac4j.core.context.WebContext;
-import org.pac4j.core.context.session.SessionStore;
 
 public class SAML2SP4UIContext implements WebContext {
 
@@ -49,11 +48,6 @@ public class SAML2SP4UIContext implements WebContext {
     }
 
     @Override
-    public SessionStore<SAML2SP4UIContext> getSessionStore() {
-        return NoOpSessionStore.INSTANCE;
-    }
-
-    @Override
     public Optional<String> getRequestParameter(final String name) {
         switch (name) {
             case SAML2Constants.SAML_RESPONSE:
@@ -69,12 +63,12 @@ public class SAML2SP4UIContext implements WebContext {
 
     @Override
     public Map<String, String[]> getRequestParameters() {
-        throw new UnsupportedOperationException();
+        return Map.of();
     }
 
     @Override
     public Optional<String> getRequestAttribute(final String name) {
-        throw new UnsupportedOperationException();
+        return Optional.empty();
     }
 
     @Override
@@ -89,7 +83,7 @@ public class SAML2SP4UIContext implements WebContext {
 
     @Override
     public String getRemoteAddr() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
@@ -98,28 +92,33 @@ public class SAML2SP4UIContext implements WebContext {
     }
 
     @Override
+    public Optional<String> getResponseHeader(final String s) {
+        return Optional.empty();
+    }
+
+    @Override
     public String getServerName() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
     public int getServerPort() {
-        throw new UnsupportedOperationException();
+        return -1;
     }
 
     @Override
     public String getScheme() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
     public boolean isSecure() {
-        throw new UnsupportedOperationException();
+        return false;
     }
 
     @Override
     public String getFullRequestURL() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
@@ -134,7 +133,7 @@ public class SAML2SP4UIContext implements WebContext {
 
     @Override
     public String getPath() {
-        throw new UnsupportedOperationException();
+        return null;
     }
 
     @Override
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
index 0e38d87..2899bbc 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
@@ -25,7 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
-import com.fasterxml.jackson.core.type.TypeReference;
 import java.security.AccessControlException;
 import java.util.Map;
 import java.util.Set;
diff --git a/fit/wa-reference/src/test/resources/application-saml2.properties b/fit/wa-reference/src/test/resources/application-saml2.properties
index 70fd4d6..48f70ed 100644
--- a/fit/wa-reference/src/test/resources/application-saml2.properties
+++ b/fit/wa-reference/src/test/resources/application-saml2.properties
@@ -19,6 +19,7 @@ am.saml2.sp.authnrequest.binding=POST
 am.saml2.sp.logout.request.binding=POST
 am.saml2.sp.logout.response.binding=REDIRECT
 am.saml2.sp.entityId=http://localhost:8080
+am.saml2.sp.metadata=/tmp/saml2-sp-metadata.xml
 am.saml2.sp.skew=300
 am.saml2.idp=http://localhost:9080/syncope-wa/idp/metadata
 am.saml2.keystore=classpath:/saml.keystore.jks
diff --git a/pom.xml b/pom.xml
index 06bb5cb..5d6c3e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -449,9 +449,9 @@ under the License.
 
     <modernizer-maven.version>2.1.0</modernizer-maven.version>
 
-    <pac4j.version>4.3.1</pac4j.version>
+    <pac4j.version>5.0.0-RC2</pac4j.version>
 
-    <cas.version>6.4.0-RC1</cas.version>
+    <cas.version>6.4.0-RC2</cas.version>
     <cas-client.version>3.6.2</cas-client.version>
 
     <h2.version>1.4.200</h2.version>
diff --git a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
index 21645b4..8a285fb 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
@@ -261,7 +261,7 @@ public class SecurityConfig {
         cfg.setWantsAssertionsSigned(true);
         cfg.setAuthnRequestSigned(true);
         cfg.setSpLogoutRequestSigned(true);
-
+        cfg.setServiceProviderMetadataResourceFilepath(env.getProperty("am.saml2.sp.metadata"));
         cfg.setAcceptedSkew(env.getProperty("am.saml2.sp.skew", int.class));
 
         cfg.setLogoutHandler(new NoOpLogoutHandler());
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpLogoutHandler.java b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpLogoutHandler.java
index e370826..e41e730 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpLogoutHandler.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpLogoutHandler.java
@@ -20,6 +20,6 @@ package org.apache.syncope.sra.security.pac4j;
 
 import org.pac4j.core.logout.handler.LogoutHandler;
 
-public class NoOpLogoutHandler implements LogoutHandler<ServerWebExchangeContext> {
+public class NoOpLogoutHandler implements LogoutHandler {
 
 }
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpSessionStore.java b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpSessionStore.java
index e9629f4..a7d43ab 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpSessionStore.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/NoOpSessionStore.java
@@ -19,9 +19,11 @@
 package org.apache.syncope.sra.security.pac4j;
 
 import java.util.Optional;
+
+import org.pac4j.core.context.WebContext;
 import org.pac4j.core.context.session.SessionStore;
 
-public final class NoOpSessionStore implements SessionStore<ServerWebExchangeContext> {
+public final class NoOpSessionStore implements SessionStore {
 
     public static final NoOpSessionStore INSTANCE = new NoOpSessionStore();
 
@@ -30,40 +32,37 @@ public final class NoOpSessionStore implements SessionStore<ServerWebExchangeCon
     }
 
     @Override
-    public String getOrCreateSessionId(final ServerWebExchangeContext context) {
-        return "<NO_KEY>";
+    public Optional<String> getSessionId(final WebContext webContext, final boolean b) {
+        return Optional.empty();
     }
 
     @Override
-    public Optional<Object> get(final ServerWebExchangeContext context, final String key) {
+    public Optional<Object> get(final WebContext webContext, final String s) {
         return Optional.empty();
     }
 
     @Override
-    public void set(final ServerWebExchangeContext context, final String key, final Object value) {
-        // nothing to do
+    public void set(final WebContext webContext, final String s, final Object o) {
+
     }
 
     @Override
-    public boolean destroySession(final ServerWebExchangeContext context) {
+    public boolean destroySession(final WebContext webContext) {
         return true;
     }
 
     @Override
-    public Optional<?> getTrackableSession(final ServerWebExchangeContext context) {
+    public Optional<Object> getTrackableSession(final WebContext webContext) {
         return Optional.empty();
     }
 
     @Override
-    public Optional<SessionStore<ServerWebExchangeContext>> buildFromTrackableSession(
-            final ServerWebExchangeContext context,
-            final Object trackableSession) {
-
+    public Optional<SessionStore> buildFromTrackableSession(final WebContext webContext, final Object o) {
         return Optional.empty();
     }
 
     @Override
-    public boolean renewSession(final ServerWebExchangeContext context) {
+    public boolean renewSession(final WebContext webContext) {
         return false;
     }
 }
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
index a5ed233..7cce9f7 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/pac4j/ServerWebExchangeContext.java
@@ -26,7 +26,6 @@ import java.util.Optional;
 import org.apache.commons.lang3.ArrayUtils;
 import org.pac4j.core.context.Cookie;
 import org.pac4j.core.context.WebContext;
-import org.pac4j.core.context.session.SessionStore;
 import org.pac4j.core.util.CommonHelper;
 import org.springframework.http.HttpCookie;
 import org.springframework.http.HttpHeaders;
@@ -54,11 +53,6 @@ public class ServerWebExchangeContext implements WebContext {
     }
 
     @Override
-    public SessionStore<ServerWebExchangeContext> getSessionStore() {
-        return NoOpSessionStore.INSTANCE;
-    }
-
-    @Override
     public Optional<String> getRequestAttribute(final String name) {
         return Optional.ofNullable(exchange.getAttribute(name));
     }
@@ -125,7 +119,12 @@ public class ServerWebExchangeContext implements WebContext {
 
     @Override
     public void setResponseHeader(final String name, final String value) {
-        this.exchange.getResponse().getHeaders().set(name, value);
+
+    }
+
+    @Override
+    public Optional<String> getResponseHeader(final String s) {
+        return Optional.ofNullable(this.exchange.getResponse().getHeaders().getFirst(s));
     }
 
     @Override
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2LogoutResponseWebFilter.java b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2LogoutResponseWebFilter.java
index 79733ef..4f89ad9 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2LogoutResponseWebFilter.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2LogoutResponseWebFilter.java
@@ -20,6 +20,7 @@ package org.apache.syncope.sra.security.saml2;
 
 import java.util.Optional;
 import org.apache.syncope.sra.SessionConfig;
+import org.apache.syncope.sra.security.pac4j.NoOpSessionStore;
 import org.apache.syncope.sra.security.pac4j.RedirectionActionUtils;
 import org.apache.syncope.sra.security.pac4j.ServerWebExchangeContext;
 import org.pac4j.core.exception.http.OkAction;
@@ -82,7 +83,8 @@ public class SAML2LogoutResponseWebFilter implements WebFilter {
             final ServerWebExchange exchange, final WebFilterChain chain, final ServerWebExchangeContext swec) {
 
         try {
-            SAML2MessageContext ctx = saml2Client.getContextProvider().buildContext(swec);
+            SAML2MessageContext ctx = saml2Client.getContextProvider().
+                buildContext(this.saml2Client, swec, NoOpSessionStore.INSTANCE);
             saml2Client.getLogoutProfileHandler().receive(ctx);
         } catch (OkAction e) {
             LOG.debug("LogoutResponse was actually validated but no postLogoutURL was set", e);
@@ -103,7 +105,7 @@ public class SAML2LogoutResponseWebFilter implements WebFilter {
 
                     return session.invalidate().then(Mono.defer(() -> {
                         try {
-                            saml2Client.getCredentialsExtractor().extract(swec);
+                            saml2Client.getCredentialsExtractor().extract(swec, NoOpSessionStore.INSTANCE);
                         } catch (RedirectionAction action) {
                             return RedirectionActionUtils.handle(action, swec);
                         }
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2RequestServerLogoutHandler.java b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2RequestServerLogoutHandler.java
index 973bd20..ef0b032 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2RequestServerLogoutHandler.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2RequestServerLogoutHandler.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.sra.security.saml2;
 
+import org.apache.syncope.sra.security.pac4j.NoOpSessionStore;
 import org.apache.syncope.sra.security.pac4j.RedirectionActionUtils;
 import org.apache.syncope.sra.SessionConfig;
 import org.apache.syncope.sra.security.pac4j.ServerWebExchangeContext;
@@ -57,7 +58,8 @@ public class SAML2RequestServerLogoutHandler implements ServerLogoutHandler {
 
                     cacheManager.getCache(SessionConfig.DEFAULT_CACHE).evictIfPresent(session.getId());
                     return session.invalidate().then(
-                            saml2Client.getLogoutAction(swec, credentials.getUserProfile(), null).
+                            saml2Client.getLogoutAction(swec,
+                                NoOpSessionStore.INSTANCE, credentials.getUserProfile(), null).
                                     map(action -> RedirectionActionUtils.handle(action, swec)).
                                     orElseThrow(() -> new IllegalStateException("No action generated")));
                 }).onErrorResume(Mono::error);
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationRequestWebFilter.java b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationRequestWebFilter.java
index 14a5b8d..6d9e9ec 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationRequestWebFilter.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationRequestWebFilter.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.sra.security.saml2;
 
+import org.apache.syncope.sra.security.pac4j.NoOpSessionStore;
 import org.apache.syncope.sra.security.pac4j.RedirectionActionUtils;
 import org.apache.syncope.sra.security.pac4j.ServerWebExchangeContext;
 import org.pac4j.saml.client.SAML2Client;
@@ -57,7 +58,7 @@ public class SAML2WebSsoAuthenticationRequestWebFilter implements WebFilter {
 
                     ServerWebExchangeContext swec = new ServerWebExchangeContext(exchange);
 
-                    return saml2Client.getRedirectionAction(swec).
+                    return saml2Client.getRedirectionAction(swec, NoOpSessionStore.INSTANCE).
                             map(action -> RedirectionActionUtils.handle(action, swec)).
                             orElseThrow(() -> new IllegalStateException("No action generated"));
                 }).onErrorResume(Mono::error);
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationWebFilter.java b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationWebFilter.java
index 6adc933..8b8239a 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationWebFilter.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2WebSsoAuthenticationWebFilter.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.sra.security.saml2;
 
 import java.net.URI;
+
+import org.apache.syncope.sra.security.pac4j.NoOpSessionStore;
 import org.apache.syncope.sra.security.pac4j.ServerWebExchangeContext;
 import org.apache.syncope.sra.security.web.server.DoNothingIfCommittedServerRedirectStrategy;
 import org.apache.syncope.sra.session.SessionUtils;
@@ -79,10 +81,11 @@ public class SAML2WebSsoAuthenticationWebFilter extends AuthenticationWebFilter
                 flatMap(matchResult -> {
                     ServerWebExchangeContext swec = new ServerWebExchangeContext(exchange).setForm(form);
 
-                    SAML2Credentials credentials = saml2Client.getCredentialsExtractor().extract(swec).
+                    SAML2Credentials credentials = (SAML2Credentials) saml2Client.getCredentialsExtractor().
+                        extract(swec, NoOpSessionStore.INSTANCE).
                             orElseThrow(() -> new IllegalStateException("No AuthnResponse found"));
 
-                    saml2Client.getAuthenticator().validate(credentials, swec);
+                    saml2Client.getAuthenticator().validate(credentials, swec, NoOpSessionStore.INSTANCE);
 
                     return Mono.just(new SAML2AuthenticationToken(credentials));
                 }));
diff --git a/sra/src/test/resources/debug/application-debug.properties b/sra/src/test/resources/debug/application-debug.properties
index 79a2950..c6ab6c7 100644
--- a/sra/src/test/resources/debug/application-debug.properties
+++ b/sra/src/test/resources/debug/application-debug.properties
@@ -36,6 +36,7 @@ am.oidc.client.secret=oidcTestClientSecret
 #am.saml2.sp.logout.response.binding=REDIRECT
 #am.saml2.sp.entityId=http://localhost:8080
 #am.saml2.sp.skew=300
+#am.saml2.sp.metadata=/tmp/saml2-sp-metadata.xml
 #am.saml2.idp=http://localhost:9080/syncope-wa/idp/metadata
 #am.saml2.keystore=classpath:/saml.keystore.jks
 #am.saml2.keystore.type=jks
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
index 340064f..3a2b117 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
@@ -50,10 +50,10 @@ import org.apereo.cas.configuration.model.support.jdbc.JdbcAuthenticationPropert
 import org.apereo.cas.configuration.model.support.jdbc.authn.QueryJdbcAuthenticationProperties;
 import org.apereo.cas.configuration.model.support.ldap.AbstractLdapAuthenticationProperties;
 import org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.DuoSecurityMultifactorProperties;
+import org.apereo.cas.configuration.model.support.mfa.DuoSecurityMultifactorAuthenticationProperties;
 import org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.gauth.GoogleAuthenticatorMultifactorProperties;
-import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorProperties;
+import org.apereo.cas.configuration.model.support.mfa.gauth.GoogleAuthenticatorMultifactorAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorAuthenticationProperties;
 import org.apereo.cas.configuration.model.support.pac4j.Pac4jDelegatedAuthenticationProperties;
 import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties;
 import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties;
@@ -177,7 +177,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
             final String authModule,
             final DuoMfaAuthModuleConf conf) {
 
-        DuoSecurityMultifactorProperties props = new DuoSecurityMultifactorProperties();
+        DuoSecurityMultifactorAuthenticationProperties props = new DuoSecurityMultifactorAuthenticationProperties();
         props.setName(authModule);
         props.setDuoApiHost(conf.getApiHost());
         props.setDuoApplicationKey(conf.getApplicationKey());
@@ -206,7 +206,8 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
             final String authModule,
             final GoogleMfaAuthModuleConf conf) {
 
-        GoogleAuthenticatorMultifactorProperties props = new GoogleAuthenticatorMultifactorProperties();
+        GoogleAuthenticatorMultifactorAuthenticationProperties props =
+            new GoogleAuthenticatorMultifactorAuthenticationProperties();
         props.setName(authModule);
         props.setIssuer(conf.getIssuer());
         props.setCodeDigits(conf.getCodeDigits());
@@ -236,7 +237,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
             final String authModule,
             final U2FAuthModuleConf conf) {
 
-        U2FMultifactorProperties props = new U2FMultifactorProperties();
+        U2FMultifactorAuthenticationProperties props = new U2FMultifactorAuthenticationProperties();
         props.setName(authModule);
         props.setExpireDevices(conf.getExpireDevices());
         props.setExpireDevicesTimeUnit(TimeUnit.valueOf(conf.getExpireDevicesTimeUnit()));
diff --git a/wa/pom.xml b/wa/pom.xml
index 2c826ca..0dd676a 100644
--- a/wa/pom.xml
+++ b/wa/pom.xml
@@ -33,8 +33,6 @@ under the License.
   <packaging>pom</packaging>
   
   <properties>
-    <nimbus-jose-jwt.version>8.20.2</nimbus-jose-jwt.version>
-
     <rootpom.basedir>${basedir}/..</rootpom.basedir>
   </properties>
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
index b7977ec..43cf07f 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
@@ -36,13 +36,19 @@ import org.apache.syncope.wa.starter.gauth.credential.SyncopeWAGoogleMfaAuthCred
 import org.apache.syncope.wa.starter.gauth.token.SyncopeWAGoogleMfaAuthTokenRepository;
 import org.apache.syncope.wa.starter.mapping.AccessMapFor;
 import org.apache.syncope.wa.starter.mapping.AccessMapper;
+import org.apache.syncope.wa.starter.mapping.AllowedAttrReleaseMapper;
 import org.apache.syncope.wa.starter.mapping.AttrReleaseMapFor;
 import org.apache.syncope.wa.starter.mapping.AttrReleaseMapper;
 import org.apache.syncope.wa.starter.mapping.AuthMapFor;
 import org.apache.syncope.wa.starter.mapping.AuthMapper;
+import org.apache.syncope.wa.starter.mapping.CASSPTOMapper;
 import org.apache.syncope.wa.starter.mapping.ClientAppMapFor;
 import org.apache.syncope.wa.starter.mapping.ClientAppMapper;
+import org.apache.syncope.wa.starter.mapping.DefaultAccessMapper;
+import org.apache.syncope.wa.starter.mapping.DefaultAuthMapper;
+import org.apache.syncope.wa.starter.mapping.OIDCRPTOMapper;
 import org.apache.syncope.wa.starter.mapping.RegisteredServiceMapper;
+import org.apache.syncope.wa.starter.mapping.SAML2SPTOMapper;
 import org.apache.syncope.wa.starter.oidc.SyncopeWAOIDCJWKSGeneratorService;
 import org.apache.syncope.wa.starter.pac4j.saml.SyncopeWASAML2ClientCustomizer;
 import org.apache.syncope.wa.starter.saml.idp.metadata.RestfulSamlIdPMetadataGenerator;
@@ -52,7 +58,7 @@ import org.apache.syncope.wa.starter.u2f.SyncopeWAU2FDeviceRepository;
 import org.apereo.cas.adaptors.u2f.storage.U2FDeviceRepository;
 import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
 import org.apereo.cas.configuration.CasConfigurationProperties;
-import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorProperties;
+import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorAuthenticationProperties;
 import org.apereo.cas.oidc.jwks.OidcJsonWebKeystoreGeneratorService;
 import org.apereo.cas.otp.repository.credentials.OneTimeTokenCredentialRepository;
 import org.apereo.cas.otp.repository.token.OneTimeTokenRepository;
@@ -124,6 +130,36 @@ public class SyncopeWAConfiguration {
                         new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT"));
     }
 
+    @Bean
+    public AttrReleaseMapper allowedAttrReleaseMapper() {
+        return new AllowedAttrReleaseMapper();
+    }
+
+    @Bean
+    public ClientAppMapper casSPTOMapper() {
+        return new CASSPTOMapper();
+    }
+
+    @Bean
+    public AccessMapper defaultAccessMapper() {
+        return new DefaultAccessMapper();
+    }
+
+    @Bean
+    public AuthMapper defaultAuthMapper() {
+        return new DefaultAuthMapper();
+    }
+
+    @Bean
+    public ClientAppMapper oidcRPTOMapper() {
+        return new OIDCRPTOMapper();
+    }
+
+    @Bean
+    public ClientAppMapper saml2SPTOMapper() {
+        return new SAML2SPTOMapper();
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public RegisteredServiceMapper registeredServiceMapper() {
@@ -194,7 +230,8 @@ public class SyncopeWAConfiguration {
     @Autowired
     @Bean
     public SamlIdPMetadataLocator samlIdPMetadataLocator(final WARestClient restClient) {
-        return new RestfulSamlIdPMetadataLocator(CipherExecutor.noOpOfStringToString(), restClient);
+        return new RestfulSamlIdPMetadataLocator(CipherExecutor.noOpOfStringToString(),
+            Caffeine.newBuilder().build(), restClient);
     }
 
     @Autowired
@@ -218,7 +255,7 @@ public class SyncopeWAConfiguration {
 
     @Autowired
     @Bean
-    public DelegatedClientFactoryCustomizer<Client<?>> delegatedClientCustomizer(final WARestClient restClient) {
+    public DelegatedClientFactoryCustomizer<Client> delegatedClientCustomizer(final WARestClient restClient) {
         return new SyncopeWASAML2ClientCustomizer(restClient);
     }
 
@@ -258,7 +295,7 @@ public class SyncopeWAConfiguration {
     @Autowired
     @RefreshScope
     public U2FDeviceRepository u2fDeviceRepository(final WARestClient restClient) {
-        U2FMultifactorProperties u2f = casProperties.getAuthn().getMfa().getU2f();
+        U2FMultifactorAuthenticationProperties u2f = casProperties.getAuthn().getMfa().getU2f();
         LocalDate expirationDate = LocalDate.now(ZoneId.systemDefault()).
                 minus(u2f.getExpireDevices(), DateTimeUtils.toChronoUnit(u2f.getExpireDevicesTimeUnit()));
         LoadingCache<String, String> requestStorage = Caffeine.newBuilder().
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AccessMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AccessMapper.java
index d0eb5ab..4edd6bf 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AccessMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AccessMapper.java
@@ -21,6 +21,7 @@ package org.apache.syncope.wa.starter.mapping;
 import org.apache.syncope.common.lib.policy.AccessPolicyConf;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 
+@FunctionalInterface
 public interface AccessMapper {
 
     RegisteredServiceAccessStrategy build(AccessPolicyConf conf);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AllowedAttrReleaseMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AllowedAttrReleaseMapper.java
index 9cdece1..c1eb0ab 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AllowedAttrReleaseMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AllowedAttrReleaseMapper.java
@@ -26,10 +26,8 @@ import org.apereo.cas.services.RegisteredServiceConsentPolicy;
 import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
 import org.apereo.cas.services.consent.DefaultRegisteredServiceConsentPolicy;
 import org.apereo.cas.util.model.TriStateBoolean;
-import org.springframework.stereotype.Component;
 
 @AttrReleaseMapFor(attrReleasePolicyConfClass = AllowedAttrReleasePolicyConf.class)
-@Component
 public class AllowedAttrReleaseMapper implements AttrReleaseMapper {
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
index ba5e916..42233eb 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
@@ -21,6 +21,7 @@ package org.apache.syncope.wa.starter.mapping;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 
+@FunctionalInterface
 public interface AttrReleaseMapper {
 
     RegisteredServiceAttributeReleasePolicy build(AttrReleasePolicyConf conf);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
index c021b66..4997dc7 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AuthMapper.java
@@ -21,6 +21,7 @@ package org.apache.syncope.wa.starter.mapping;
 import org.apache.syncope.common.lib.policy.AuthPolicyConf;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 
+@FunctionalInterface
 public interface AuthMapper {
 
     RegisteredServiceAuthenticationPolicy build(AuthPolicyConf conf);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPTOMapper.java
index d8870cc..a7cb01e 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPTOMapper.java
@@ -25,10 +25,8 @@ import org.apereo.cas.services.RegisteredService;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
-import org.springframework.stereotype.Component;
 
 @ClientAppMapFor(clientAppClass = CASSPTO.class)
-@Component
 public class CASSPTOMapper implements ClientAppMapper {
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
index a7945ac..b0fff5e 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
@@ -24,6 +24,7 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 
+@FunctionalInterface
 public interface ClientAppMapper {
 
     RegisteredService build(
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAccessMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAccessMapper.java
index 26832dc..bdcda1f 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAccessMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAccessMapper.java
@@ -22,10 +22,8 @@ import org.apache.syncope.common.lib.policy.AccessPolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
 import org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
-import org.springframework.stereotype.Component;
 
 @AccessMapFor(accessPolicyConfClass = DefaultAccessPolicyConf.class)
-@Component
 public class DefaultAccessMapper implements AccessMapper {
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
index c79acd8..ae9fa81 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAuthMapper.java
@@ -25,10 +25,8 @@ import org.apache.syncope.common.lib.policy.DefaultAuthPolicyCriteriaConf;
 import org.apereo.cas.services.AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria;
 import org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
-import org.springframework.stereotype.Component;
 
 @AuthMapFor(authPolicyConfClass = DefaultAuthPolicyConf.class)
-@Component
 public class DefaultAuthMapper implements AuthMapper {
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPTOMapper.java
index 25aaf27..a6cc276 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPTOMapper.java
@@ -43,10 +43,8 @@ import org.apereo.cas.util.spring.ApplicationContextProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationContext;
-import org.springframework.stereotype.Component;
 
 @ClientAppMapFor(clientAppClass = OIDCRPTO.class)
-@Component
 public class OIDCRPTOMapper implements ClientAppMapper {
 
     private static final Logger LOG = LoggerFactory.getLogger(OIDCRPTOMapper.class);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
index af7c9e4..5991515 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
@@ -21,6 +21,9 @@ package org.apache.syncope.wa.starter.mapping;
 import java.util.Map;
 import java.util.Optional;
 import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apereo.cas.services.RegisteredService;
 import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
@@ -29,6 +32,8 @@ import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
 
 public class RegisteredServiceMapper {
 
+    private static final Logger LOG = LoggerFactory.getLogger(RegisteredServiceMapper.class);
+
     protected final Map<String, AuthMapper> authPolicyConfMappers;
 
     protected final Map<String, AccessMapper> accessPolicyConfMappers;
@@ -50,6 +55,13 @@ public class RegisteredServiceMapper {
     }
 
     public RegisteredService toRegisteredService(final WAClientApp clientApp) {
+        String key = clientApp.getClientAppTO().getClass().getName();
+        ClientAppMapper clientAppMapper = clientAppTOMappers.get(key);
+        if (clientAppMapper == null) {
+            LOG.warn("Unable to locate ClientAppMapper using key {}", key);
+            return null;
+        }
+        
         RegisteredServiceAuthenticationPolicy authPolicy = null;
         if (clientApp.getAuthPolicyConf() != null) {
             AuthMapper authMapper =
@@ -78,10 +90,6 @@ public class RegisteredServiceMapper {
             }
         }
 
-        ClientAppMapper clientAppMapper = clientAppTOMappers.get(clientApp.getClientAppTO().getClass().getName());
-        if (clientAppMapper == null) {
-            return null;
-        }
         return clientAppMapper.build(clientApp, authPolicy, accessStrategy, attributeReleasePolicy);
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPTOMapper.java
index f266dd2..9c1c86c 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPTOMapper.java
@@ -25,10 +25,8 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.support.saml.services.SamlRegisteredService;
-import org.springframework.stereotype.Component;
 
 @ClientAppMapFor(clientAppClass = SAML2SPTO.class)
-@Component
 public class SAML2SPTOMapper implements ClientAppMapper {
 
     @Override
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
index 704cdb1..7590845 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/SyncopeWASAML2ClientCustomizer.java
@@ -27,7 +27,7 @@ import org.pac4j.saml.config.SAML2Configuration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SyncopeWASAML2ClientCustomizer implements DelegatedClientFactoryCustomizer<Client<?>> {
+public class SyncopeWASAML2ClientCustomizer implements DelegatedClientFactoryCustomizer<Client> {
 
     private static final Logger LOG = LoggerFactory.getLogger(SyncopeWASAML2ClientCustomizer.class);
 
@@ -38,7 +38,7 @@ public class SyncopeWASAML2ClientCustomizer implements DelegatedClientFactoryCus
     }
 
     @Override
-    public void customize(final Client<?> client) {
+    public void customize(final Client client) {
         if (client instanceof SAML2Client) {
             LOG.debug("Customizing SAML2 client {}", client.getName());
             final SAML2Client saml2Client = (SAML2Client) client;
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
index bac0013..8900e2d 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/RestfulSamlIdPMetadataLocator.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.wa.starter.saml.idp.metadata;
 
+import com.github.benmanes.caffeine.cache.Cache;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.SAML2IdPMetadataTO;
@@ -40,9 +41,10 @@ public class RestfulSamlIdPMetadataLocator extends AbstractSamlIdPMetadataLocato
 
     public RestfulSamlIdPMetadataLocator(
             final CipherExecutor<String, String> metadataCipherExecutor,
+            final Cache<String, SamlIdPMetadataDocument> metadataCache,
             final WARestClient restClient) {
 
-        super(metadataCipherExecutor);
+        super(metadataCipherExecutor, metadataCache);
         this.restClient = restClient;
     }
 
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java
index b0c4902..9573d33 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/SyncopeWAServiceRegistry.java
@@ -63,7 +63,12 @@ public class SyncopeWAServiceRegistry extends AbstractServiceRegistry {
     public boolean delete(final RegisteredService registeredService) {
         throw new UnsupportedOperationException("Deleting registered services from WA is not supported");
     }
-    
+
+    @Override
+    public void deleteAll() {
+        throw new UnsupportedOperationException("Bulk deleting registered services from WA is not supported");
+    }
+
     @Override
     public Collection<RegisteredService> load() {
         SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
@@ -145,4 +150,6 @@ public class SyncopeWAServiceRegistry extends AbstractServiceRegistry {
                     getService(WAClientAppService.class).read(name, null));
         }
     }
+
+
 }
diff --git a/wa/starter/src/main/resources/application.properties b/wa/starter/src/main/resources/application.properties
index b933c78..a2d254d 100644
--- a/wa/starter/src/main/resources/application.properties
+++ b/wa/starter/src/main/resources/application.properties
@@ -40,9 +40,14 @@ management.endpoint.loggers.enabled=true
 management.endpoint.refresh.enabled=true
 management.endpoint.registeredServices.enabled=true
 
-cas.events.core.enabled=false
+# Cache service definitions for 5 minutes
+cas.service-registry.cache.duration=PT5M
+
+# Reload services and hydrate the cache every 5 minutes
+cas.service-registry.schedule.repeat-interval=PT5M
+cas.service-registry.schedule.start-delay=PT30S
 
-server.servlet.session.tracking-modes=cookie
+cas.events.core.enabled=false
 
 ##
 # Allow configuration classes to override bean definitions from Spring Boot
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
index 640fa2c..ff336c0 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
@@ -20,13 +20,19 @@ package org.apache.syncope.wa.starter;
 
 import java.util.UUID;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
 import org.springframework.test.context.ContextConfiguration;
 
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
-    "cas.authn.accept.users=mrossi::password",
-    "cas.authn.syncope.url=http://localhost:8080",
-    "cas.sso.allow-missing-service-parameter=true"
+@SpringBootTest(
+    classes = AbstractTest.SyncopeTestConfiguration.class,
+    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+    properties = {
+        "cas.authn.accept.users=mrossi::password",
+        "cas.authn.syncope.url=http://localhost:8080",
+        "cas.sso.allow-missing-service-parameter=true"
 })
 @ContextConfiguration(initializers = ZookeeperTestingServer.class)
 public abstract class AbstractTest {
@@ -37,4 +43,13 @@ public abstract class AbstractTest {
     protected static String getUUIDString() {
         return UUID.randomUUID().toString().substring(0, 8);
     }
+
+    @TestConfiguration
+    @ComponentScan("org.apache.syncope.wa.starter")
+    public static class SyncopeTestConfiguration {
+        @Bean
+        public SyncopeCoreTestingServer syncopeCoreTestingServer() {
+            return new SyncopeCoreTestingServer();
+        }
+    }
 }
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
index bc6bc06..7b0b905 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
@@ -32,7 +32,6 @@ import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.stereotype.Component;
 
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.NotFoundException;
@@ -45,7 +44,6 @@ import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
-@Component
 public class SyncopeCoreTestingServer implements ApplicationListener<ContextRefreshedEvent> {
 
     public static final List<WAClientApp> APPS = new ArrayList<>();
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
index 253b6f8..f7b26e3 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/BaseSyncopeWASAML2ClientTest.java
@@ -35,9 +35,11 @@ import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
 import org.pac4j.saml.client.SAML2Client;
 import org.pac4j.saml.config.SAML2Configuration;
 import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.FileSystemResource;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.math.BigInteger;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -83,12 +85,13 @@ public abstract class BaseSyncopeWASAML2ClientTest {
         return cert;
     }
 
-    protected static SAML2Client getSAML2Client() {
+    protected static SAML2Client getSAML2Client() throws Exception {
         SAML2Configuration saml2Configuration = new SAML2Configuration();
         saml2Configuration.setKeystorePassword("password");
         saml2Configuration.setPrivateKeyPassword("password");
         saml2Configuration.setKeystoreAlias("Syncope");
         saml2Configuration.setIdentityProviderMetadataResource(new ClassPathResource("idp-metadata.xml"));
+        saml2Configuration.setServiceProviderMetadataResource(new FileSystemResource(File.createTempFile("sp-metadata", ".xml")));
         SAML2Client client = new SAML2Client(saml2Configuration);
         client.setCallbackUrl("https://syncope.apache.org");
         return client;