[java-plugin-shibd-oidc] 01/04: Move state handling to new State Manager mechanisms

Codeberg noreply at shibboleth.net
Wed May 6 15:47:28 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/e3bb47ca52fa6465af3361b79da7151e0740badb

commit e3bb47ca52fa6465af3361b79da7151e0740badb
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Tue May 5 13:35:42 2026 +0100

    Move state handling to new State Manager mechanisms
    
     - Move state handling to new mechanisms in the sp-server
     - Move to new State Data context
     - Fix javadoc
     - Fix checkstyle
     - Add missing package-info
---
 resources/checkstyle/checkstyle-suppressions.xml   |   9 +
 resources/checkstyle/checkstyle.xml                |   4 +-
 .../sp/oidc/BasicOIDCProtocolSupportService.java   |   4 +-
 .../logic/RequestObjectRequiredAndSupported.java   |   3 +-
 .../sp/oidc/config/logic/package-info.java         |  18 +
 .../sp/oidc/config/navigate/package-info.java      |  18 +
 .../oidc/context/AuthnRequestStateDataContext.java |  75 ---
 .../sp/oidc/context/navigate/package-info.java     |  18 +
 .../shibboleth/sp/oidc/context/package-info.java   |  18 +
 .../messaging/AbstractAgentMessageHandler.java     | 111 ----
 .../AbstractAgentRequestMessageHandler.java        | 101 ---
 .../AbstractApplicationMessageHandler.java         |  69 --
 .../oidc/messaging/context/logic/package-info.java |  18 +
 ...onStateFromCorrelationCookieLookupFunction.java |  53 --
 .../RFPFromCorrelationCookieLookupFunction.java    |  53 --
 .../navigate/RFPFromOAuthStateLookupFunction.java  |  48 --
 .../RequestCorrelationFromStateLookupFunction.java | 184 ------
 .../navigate/StateFromJSONStateLookupFunction.java |  49 --
 .../navigate/StateFromResponseLookupFunction.java  |  51 ++
 .../sp/oidc/messaging/navigate/package-info.java   |  18 +
 .../profile/AuthenticationRequestStateData.java    | 257 +++-----
 .../shibboleth/sp/oidc/profile/OIDCConstants.java  |  12 +-
 .../sp/oidc/profile/OIDCInitiatorConstants.java    |  11 +-
 .../shibboleth/sp/oidc/profile/package-info.java   |  18 +
 .../sp/consumer/oidc/code/post/post-beans.xml      |   5 -
 .../sp/consumer/oidc/code/query/query-beans.xml    |   5 -
 .../idp/flows/sp/consumer/oidc/oidc-beans.xml      | 144 +----
 .../idp/flows/sp/consumer/oidc/oidc-flow.xml       |  11 +-
 .../idp/flows/sp/initiator/oidc/oidc-beans.xml     |  79 +--
 .../idp/flows/sp/initiator/oidc/oidc-flow.xml      |   6 +-
 .../shibboleth/idp/flows/sp/oidc-common-beans.xml  |  43 +-
 .../net/shibboleth/sp/service/agent/postconfig.xml |   2 +-
 .../shibboleth/idp/module/conf/sp/oidc.properties  |   3 -
 .../flows/AbstractOIDCTokenConsumerFlowTest.java   | 533 ++++++++++++++++
 .../oidc/flows/OIDCSessionInitiatorFlowTest.java   |  46 +-
 .../sp/oidc/flows/OIDCTokenConsumerFlowTest.java   | 696 ++-------------------
 ...DCTokenConsumerFlowUsingStorageServiceTest.java | 148 +++++
 .../shibboleth/sp/oidc/flows/TestConstants.java    | 144 ++---
 ...DCEnvironmentApplicationContextInitializer.java |  14 +-
 ...ationContextInitializerWithStorageService.java} |  27 +-
 .../shibboleth/idp/module/conf/sp/sp.properties    |   2 +-
 .../impl/OIDCProviderMetadataLookupHandler.java    |   4 +-
 .../impl/DecodeStateAsJsonObjectConsumer.java      |   2 +-
 .../oidc/profile/decoding/impl/package-info.java   |  18 +
 .../oidc/profile/impl/AbstractHttpOAuthAction.java |   3 +-
 .../AbstractProviderMetadataLookupFunction.java    |   2 +-
 .../impl/AuthTimeRequestedActivationCondition.java |  47 +-
 ...thenticationRequestStateForStorageStrategy.java | 117 +---
 .../AuthenticationRequestTimeLookupFunction.java   |  37 +-
 .../impl/CreateAuthenticationStateData.java        | 121 ++++
 .../profile/impl/ExchangeCodeForAccessToken.java   |   4 +-
 .../sp/oidc/profile/impl/ExtractOIDCClaims.java    |   3 +-
 .../impl/InitializeAuthorizationRequest.java       |   2 +-
 ...izeOAuth2ClientAuthenticationMethodHandler.java |  17 +-
 .../impl/InitializeOAuth2ClientContext.java        |   4 +-
 .../profile/impl/InitializePeerEntityContext.java  | 101 +++
 .../sp/oidc/profile/impl/IssueStateCookie.java     | 206 ------
 .../profile/impl/MapStateTokenToStateValue.java    | 177 ------
 .../profile/impl/MapStateValueToStateToken.java    | 165 -----
 ...AgeFromAuthenticationRequestLookupFunction.java |  33 +-
 ...omAuthenticationRequestStateLookupStrategy.java |  15 +-
 .../PKCECodeVerifierFromStateLookupStrategy.java   |  43 +-
 .../impl/PrepareOIDCInboundMessageContext.java     |   2 +-
 .../impl/RedirectUriFromStateLookupStrategy.java   |  46 +-
 .../RequestedACRClaimsFromStateLookupStrategy.java |  35 +-
 .../RequestedACRValidationActivationCondition.java |   2 +-
 .../sp/oidc/profile/impl/ResolveStateCookie.java   | 230 -------
 ...AuthenticationRequestToPeerContextConsumer.java | 174 ------
 .../impl/SetAuthenticationStateTokenConsumer.java  |  50 --
 .../profile/impl/StandardExtractionStrategy.java   |   1 +
 .../profile/impl/StateTokenLookupStrategy.java     |  72 +++
 .../impl/ValidateUserInfoJSONObjectClaims.java     |   2 +-
 .../AuthTimeRequestedActivationConditionTest.java  |  17 +-
 ...ticationRequestStateForStorageStrategyTest.java |  95 +--
 ...uthenticationRequestTimeLookupFunctionTest.java |  16 +-
 .../sp/oidc/profile/impl/IssueStateCookieTest.java | 173 -----
 ...romAuthenticationRequestLookupFunctionTest.java |  10 +-
 ...uestedACRClaimsFromStateLookupStrategyTest.java |  14 +-
 ...uestedACRValidationActivationConditionTest.java |   4 +-
 .../oidc/profile/impl/ResolveStateCookieTest.java  | 178 ------
 ...enticationRequestToPeerContextConsumerTest.java | 146 -----
 81 files changed, 1765 insertions(+), 3751 deletions(-)

diff --git a/resources/checkstyle/checkstyle-suppressions.xml b/resources/checkstyle/checkstyle-suppressions.xml
new file mode 100644
index 0000000..5063f85
--- /dev/null
+++ b/resources/checkstyle/checkstyle-suppressions.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE suppressions PUBLIC
+  "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
+  "https://checkstyle.org/dtds/suppressions_1_2.dtd">
+
+<suppressions>
+   
+</suppressions>
diff --git a/resources/checkstyle/checkstyle.xml b/resources/checkstyle/checkstyle.xml
index e3f1203..ac8b0f3 100644
--- a/resources/checkstyle/checkstyle.xml
+++ b/resources/checkstyle/checkstyle.xml
@@ -108,7 +108,7 @@
     <property name="max" value="1000"/>
   </module>
   <module name="Header">
-    <property name="header" value="/*\n * Licensed to the University Corporation for Advanced Internet Development,\n * Inc. (UCAID) under one or more contributor license agreements.  See the\n * NOTICE file distributed with this work for additional information regarding\n * copyright ownership. The UCAID licenses this file to You under the Apache\n * License, Version 2.0 (the "License"); you may not use this file except in\n * compliance with the License.  You may obtain a cop [...]
+    <property name="header" value="/*\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, eit [...]
     <property name="fileExtensions" value="java"/>
   </module>
   <module name="JavadocPackage"/>
@@ -116,7 +116,7 @@
     <property name="max" value="120"/>
   </module>
   <module name="SuppressionFilter">
-    <property name="file" value="resources/checkstyle-suppressions.xml"/>
+    <property name="file" value="resources/checkstyle/checkstyle-suppressions.xml"/>
     <property name="optional" value="false"/>
   </module>
 </module>
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/BasicOIDCProtocolSupportService.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/BasicOIDCProtocolSupportService.java
index c3cf21e..3c48e02 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/BasicOIDCProtocolSupportService.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/BasicOIDCProtocolSupportService.java
@@ -31,7 +31,7 @@ public class BasicOIDCProtocolSupportService extends BasicProtocolSupportService
     /** Metadata source. */
     @NonnullAfterInit private ReloadableService<ProviderMetadataResolver> metadataResolver;
 
-    /** [{@inheritDoc} */
+    /** {@inheritDoc}. */
     @NonnullAfterInit
     public ReloadableService<ProviderMetadataResolver> getMetadataResolver() {
         return metadataResolver;
@@ -49,7 +49,7 @@ public class BasicOIDCProtocolSupportService extends BasicProtocolSupportService
         metadataResolver = Constraint.isNotNull(service, "ProviderMetadataResolver service cannot be null");
     }
 
-    /** {@inheritDoc} */
+    /** {@inheritDoc}. */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
 
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/RequestObjectRequiredAndSupported.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/RequestObjectRequiredAndSupported.java
index 1e4bec2..68d3532 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/RequestObjectRequiredAndSupported.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/RequestObjectRequiredAndSupported.java
@@ -71,7 +71,8 @@ public class RequestObjectRequiredAndSupported extends AbstractRelyingPartyPredi
         
         boolean requestObjectRequestedFromConfig = false;        
         final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
-        if (rpc != null && rpc.getProfileConfig() instanceof final OIDCAuthenticationRelyingPartyProfileConfiguration rpConfig) {
+        if (rpc != null && rpc.getProfileConfig() 
+                instanceof final OIDCAuthenticationRelyingPartyProfileConfiguration rpConfig) {
             requestObjectRequestedFromConfig = rpConfig.isUseRequestObject(input);
         }
         
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/package-info.java
new file mode 100644
index 0000000..c5f560e
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/logic/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Predicates for RP-specific messaging configuration.
+ */
+package net.shibboleth.sp.oidc.config.logic;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/navigate/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/navigate/package-info.java
new file mode 100644
index 0000000..bd6680b
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/config/navigate/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Functions to navigate into profile configuration information.
+ */
+package net.shibboleth.sp.oidc.config.navigate;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/AuthnRequestStateDataContext.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/AuthnRequestStateDataContext.java
deleted file mode 100644
index 99fd52b..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/AuthnRequestStateDataContext.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.context;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.BaseContext;
-
-import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
-
-/**
- * A context to hold the token used to recover state information about the original authentication request, alongside
- * any recovered information. 
- * 
- * <p>On initiation requests, it is likely only the token will be populated. In consumer flows, it is likely both the
- * token and the recovered state is populated.</p>
- */
-public class AuthnRequestStateDataContext extends BaseContext {
-    
-    /** The token used as a key to the stored authentication request state.*/
-    @Nullable private String token;
-    
-    /** The authentication state data. */
-    @Nullable private AuthenticationRequestStateData authnState;
-    
-    /**
-     * Set the token used as a key to the stored authentication request state.
-     * 
-     * @param tokenIn the token to set.
-     */
-    @Nonnull public AuthnRequestStateDataContext setToken(@Nullable final String tokenIn) {
-        token = tokenIn;
-        return this;
-    }
-    /**
-     * Return the token used as a key to the stored authentication request state.
-     * 
-     * @return the token.
-     */
-    @Nullable public String getToken() {
-        return token;
-    }
-    
-    /**
-     * Sets the authentication state recovered from the authentication request.
-     * 
-     * @param state The authnState to set
-     */
-    @Nonnull public AuthnRequestStateDataContext setAuthnState(@Nullable final AuthenticationRequestStateData state) {      
-        authnState = state;
-        return this;
-    }
-    
-    /**
-     * Get the authentication state recovered from the authentication request.
-     * 
-     * @return the authentication state
-     */
-    @Nullable public AuthenticationRequestStateData getAuthnState() {
-        return authnState;
-    }
-
-}
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/navigate/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/navigate/package-info.java
new file mode 100644
index 0000000..518fced
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/navigate/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Functions that navigate messaging contexts.
+ */
+package net.shibboleth.sp.oidc.context.navigate;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/package-info.java
new file mode 100644
index 0000000..8bb753a
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/context/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Context classes for RP-specific information needed by profile actions.
+ */
+package net.shibboleth.sp.oidc.context;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentMessageHandler.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentMessageHandler.java
deleted file mode 100644
index deac1fe..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentMessageHandler.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.messaging.handler.MessageHandlerException;
-
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.Agent;
-import net.shibboleth.sp.context.AgentRequestContext;
-import net.shibboleth.sp.ddf.DDF;
-import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
-
-/**
- * TODO javadoc and should we use as a message handler
- */
-public abstract class AbstractAgentMessageHandler extends AbstractAgentRequestMessageHandler {
-    
-    /** Cached agent from context. */
-    @NonnullBeforeExec private Agent agent;
-
-    /** Whether to create the output objects into which the message will be encoded. */
-    private boolean createOutputObjects;
-    
-    /**
-     * Gets whether to create an output {@link DDF} and {@link RemotedHttpServletResponse}.
-     *
-     * @return whether to create output objects
-     */
-    public boolean isCreateOutputObjects() {
-        return createOutputObjects;
-    }
-    
-    /**
-     * Sets whether to create an output {@link DDF} and {@link RemotedHttpServletResponse}.
-     * 
-     * <p>Defaults to false.</p>
-     * 
-     * @param flag flag to set
-     */
-    public void setCreateOutputObjects(final boolean flag) {
-        checkSetterPreconditions();
-        
-        createOutputObjects = flag;
-    }
-    
-    /** {@inheritDoc}  */
-    @Override
-    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
-        
-        if (!super.doPreInvoke(messageContext)) {
-            return false;
-        }
-        agent = ensureAgentRequestContext().getAgent();
-        if (agent == null) {
-            throw new MessageHandlerException("No Agent found in context");
-        }
-        
-        return true;
-    }
-    
-    /**
-     * Gets the {@link Agent} for this request.
-     * 
-     * @return the agent, or null
-     */
-    @Nullable public Agent getAgent() {
-        return agent;
-    }
-    
-    /**
-     * Gets the {@link Agent} for this request.
-     * 
-     * @return the agent
-     */
-    @Nonnull public Agent ensureAgent() {
-        return Constraint.isNotNull(agent, "Agent was null");
-    }
-    
-    /**
-     * If {@link #isCreateOutputObjects()} is true, then this ensures an output
-     * {@link DDF} is in place and if creating one, adds the structure and installs
-     * the wrapper for a {@link RemotedHttpServletResponse}.
-     */
-    protected void ensureOutputObjects() {
-        final AgentRequestContext agentRequestContext = ensureAgentRequestContext();
-        if (isCreateOutputObjects() && agentRequestContext.getOutput() == null) {
-            final DDF output = new DDF(null);
-            agentRequestContext.setOutput(output);
-            agentRequestContext.setRemotedHttpServletResponse(new RemotedHttpServletResponse(
-                    output.structure().addmember(RemotedHttpServletResponse.STRUCTURE_NAME)));
-        }
-    }
-
-}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentRequestMessageHandler.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentRequestMessageHandler.java
deleted file mode 100644
index 243ff3d..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractAgentRequestMessageHandler.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.messaging.context.navigate.ParentContextLookup;
-import org.opensaml.messaging.handler.AbstractMessageHandler;
-import org.opensaml.messaging.handler.MessageHandlerException;
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.context.AgentRequestContext;
-
-/**
- * TODO javadoc and should we use as a message handler
- */
-public abstract class AbstractAgentRequestMessageHandler extends AbstractMessageHandler {
-    
-    /** Lookup strategy for {@link AgentRequestContext}. */
-    @Nonnull private Function<MessageContext,AgentRequestContext> agentRequestContextLookupStrategy;
-
-    /** Cached context to populate. */
-    @NonnullBeforeExec private AgentRequestContext agentRequestContext;
-    
-    /**
-     * Gets the cached request context located by the lookup strategy.
-     * 
-     * @return cached request context or null
-     */
-    @Nullable public AgentRequestContext getAgentRequestContext() {
-        return agentRequestContext;
-    }
-
-    /**
-     * Gets the cached request context located by the lookup strategy.
-     * 
-     * @return cached request context
-     */
-    @Nonnull public AgentRequestContext ensureAgentRequestContext() {
-        return Constraint.isNotNull(agentRequestContext, "AgentRequestContext was null");
-    }
-
-    /** Constructor. */
-    protected AbstractAgentRequestMessageHandler() {
-        // By default msgCtx (up)-> ProfileRequestContext (down)-> AgentRequestContext 
-        agentRequestContextLookupStrategy = new ChildContextLookup<>(AgentRequestContext.class)
-                .compose(new ParentContextLookup<>(ProfileRequestContext.class));
-        
-    }
-    
-    /**
-     * Sets the lookup strategy for the {@link AgentRequestContext}.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setAgentRequestContextLookupStrategy(
-            @Nonnull final Function<MessageContext,AgentRequestContext> strategy) {
-        checkSetterPreconditions();
-        
-        agentRequestContextLookupStrategy = Constraint.isNotNull(strategy,
-                "AgentRequestContext lookup strategy cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
-        
-        if (!super.doPreInvoke(messageContext)) {
-            return false;
-        }
-        
-        agentRequestContext = agentRequestContextLookupStrategy.apply(messageContext);
-        if (agentRequestContext == null) {
-            throw new MessageHandlerException("No AgentRequestContext found");
-        }
-        
-        return true;
-    }
-
-
-
-}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractApplicationMessageHandler.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractApplicationMessageHandler.java
deleted file mode 100644
index f380043..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/AbstractApplicationMessageHandler.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.messaging.handler.MessageHandlerException;
-
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.Application;
-
-/**
- * TODO javadoc and should we use as a message handler
- */
-public abstract class AbstractApplicationMessageHandler extends AbstractAgentMessageHandler {
-    
-    /** Cached Application from context. */
-    @NonnullBeforeExec private Application application;
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
-        
-        if (!super.doPreInvoke(messageContext)) {
-            return false;
-        }
-        
-        application = ensureAgentRequestContext().getApplication();
-        if (application == null) {
-            throw new MessageHandlerException("No Application found in context");
-        }
-        
-        return true;
-    }
-    
-    /**
-     * Gets the {@link Application} for this request.
-     * 
-     * @return the application, or null
-     */
-    @Nullable public Application getApplication() {
-        return application;
-    }
-    
-    /**
-     * Gets the {@link Application} for this request.
-     * 
-     * @return the application
-     */
-    @Nonnull public Application ensureApplication() {
-        return Constraint.isNotNull(application, "Application was null");
-    }
-
-}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/context/logic/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/context/logic/package-info.java
new file mode 100644
index 0000000..1611dca
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/context/logic/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Predicates for RP-specific messaging configuration.
+ */
+package net.shibboleth.sp.oidc.messaging.context.logic;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/AuthenticationStateFromCorrelationCookieLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/AuthenticationStateFromCorrelationCookieLookupFunction.java
deleted file mode 100644
index 115d708..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/AuthenticationStateFromCorrelationCookieLookupFunction.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
-
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import net.minidev.json.JSONObject;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.oidc.context.CorrelationCookieStateContext;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
-
-/**
- * A function that extracts the authentication state token from the correlation cookie, if present, and returns it.
- */
-public class AuthenticationStateFromCorrelationCookieLookupFunction 
-    implements Function<CorrelationCookieStateContext,String> {
-
-    /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final CorrelationCookieStateContext stateContext) {
-        if (stateContext == null) {
-            return null;
-        }
-        final JSONObject correlationCookie = stateContext.getCorrelationCookieValue();
-        if (correlationCookie == null) {
-            return null;
-        }
-        final String authnStateToken = correlationCookie.getAsString(OIDCConstants.AUTHN_STATE_FIELD);
-        if (StringSupport.trimOrNull(authnStateToken) != null) {
-            return authnStateToken;
-        }
-        return null;
-        
-    }
-    
-    
-
-}
-
-
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromCorrelationCookieLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromCorrelationCookieLookupFunction.java
deleted file mode 100644
index 6dde325..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromCorrelationCookieLookupFunction.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
-
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import net.minidev.json.JSONObject;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.oidc.context.CorrelationCookieStateContext;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
-
-/**
- * A function that extracts the Request Forgery Protection nonce from the correlation cookie, if present, and 
- * returns it.
- */
-public class RFPFromCorrelationCookieLookupFunction implements Function<CorrelationCookieStateContext,String> {
-
-    /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final CorrelationCookieStateContext stateContext) {
-        if (stateContext == null) {
-            return null;
-        }
-        final JSONObject correlationCookie = stateContext.getCorrelationCookieValue();
-        if (correlationCookie == null) {
-            return null;
-        }
-        final String rfpToken = correlationCookie.getAsString(OIDCConstants.RFP_FIELD);
-        if (StringSupport.trimOrNull(rfpToken) != null) {
-            return rfpToken;
-        }
-        return null;
-        
-    }
-    
-    
-
-}
-
-
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromOAuthStateLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromOAuthStateLookupFunction.java
deleted file mode 100644
index 2f22c2a..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RFPFromOAuthStateLookupFunction.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
-
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import net.minidev.json.JSONObject;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.oidc.context.OAuthStateContext;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
-
-/**
- * A lookup function that extracts the request forgery protection value from a Base64URL encoded JSON object.
- */
-public class RFPFromOAuthStateLookupFunction implements Function<OAuthStateContext,String> {
-
-    /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final OAuthStateContext stateContext) {
-        if (stateContext == null) {
-            return null;
-        }
-        final JSONObject stateJson = stateContext.getStateJson();
-        if (stateJson == null) {
-            return null;
-        }
-        final String nonceFromState = stateJson.getAsString(OIDCConstants.RFP_FIELD);
-        if (StringSupport.trimOrNull(nonceFromState) != null) {
-            return nonceFromState;
-        }
-        return null;
-        
-    }
-
-}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RequestCorrelationFromStateLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RequestCorrelationFromStateLookupFunction.java
deleted file mode 100644
index 8187070..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/RequestCorrelationFromStateLookupFunction.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
-
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.nimbusds.jose.util.StandardCharset;
-
-import net.minidev.json.JSONObject;
-import net.minidev.json.JSONValue;
-import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.codec.Base64Support;
-import net.shibboleth.shared.codec.EncodingException;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.PredicateSupport;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.shared.security.DataSealerException;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
-
-/**
- *  A {@link Function} that creates a request correlation cookie value with the token/key of the stored authentication 
- *  request state, and the request forgery protection (RFP) value that was stored in the OAuth state parameter. 
- *  The resulting cookie value is Base64URL encoded JSON string (noting the sealer will already base64 encode the
- *  sealed String). 
- */
-public class RequestCorrelationFromStateLookupFunction extends AbstractIdentifiableInitializableComponent 
-        implements Function<ProfileRequestContext,String> {
-    
-    /** Class logger. */
-    @Nonnull
-    private final Logger log = LoggerFactory.getLogger(RequestCorrelationFromStateLookupFunction.class);
-    
-    /** A strategy to locate the {@link AuthnRequestStateDataContext}.*/
-    @Nonnull 
-    private final Function<ProfileRequestContext, AuthnRequestStateDataContext> authnRequestDataStateLookupStrategy;
-    
-    /** Strategy to locate the authentication request. */
-    @Nonnull 
-    private final Function<ProfileRequestContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
-    
-    /** Optional data sealer to use. */
-    @Nullable private DataSealer dataSealer;
-    
-    /** 
-     * If the dataSealer is provided should it be used to seal the authentication request state? Defaults to true, 
-     * that is, if the dataSealer is provided, always seal state.
-     */
-    @Nonnull private Predicate<ProfileRequestContext> sealState;
-    
-    
-    /**
-     * 
-     * Constructor.
-     *
-     * @param dataLookuptrategy a strategy to locate the {@link AuthnRequestStateDataContext}
-     * @param authnRequestStrategy a strategy to locate the {@link OIDCAuthenticationRequest}
-     */
-    public RequestCorrelationFromStateLookupFunction(@ParameterName(name="authenticationRequestDataStateLookupStrategy")
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> dataLookuptrategy,
-                @ParameterName(name="authenticationRequestLookupStrategy")
-                    @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationRequest> authnRequestStrategy) {
-        
-        authnRequestDataStateLookupStrategy = 
-                Constraint.isNotNull(dataLookuptrategy, "AuthnRequestStateDataContext lookup strategy cannot be null");
-        authenticationRequestLookupStrategy = 
-                Constraint.isNotNull(authnRequestStrategy, "OIDCAuthenticationRequest lookup strategy cannot be null");
-        sealState = PredicateSupport.alwaysTrue();
-    }
-    
-    
-    /**
-     * Sets {@link DataSealer} to use.
-     * 
-     * @param sealer data sealer
-     */
-    public void setDataSealer(@Nullable final DataSealer sealer) {
-        checkSetterPreconditions();
-        
-        dataSealer = sealer;
-    }
-    
-    /**
-     * Set the predicate to determine whether to seal the state.
-     * 
-     * @param predicate the seal state predicate to set.
-     */
-    public void setSealStatePredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
-        checkSetterPreconditions();
-        
-        sealState = Constraint.isNotNull(predicate, "Seal state predicate can not be null");
-    }
-    
-    /**
-     * Set the flag to determine whether to seal the state.
-     * 
-     * @param flag the flag to set.
-     */
-    public void setSealState(final boolean flag) {
-        checkSetterPreconditions();
-        
-        sealState = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
-    }
-
-    /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final ProfileRequestContext prc) {
-        final AuthnRequestStateDataContext stateContext = authnRequestDataStateLookupStrategy.apply(prc);
-        
-        if (stateContext == null || stateContext.getToken() == null) {
-            log.error("No authentication request state found, can not create correlation cookie");
-            return null;
-        }
-        
-        final OIDCAuthenticationRequest authnRequest = authenticationRequestLookupStrategy.apply(prc);
-        
-        String csrfNonce = null;
-        if (authnRequest != null) {
-            final JSONObject stateJson = authnRequest.getStateJson();
-            if (stateJson != null) {
-                final Object nonceFromOAuthStateAsObject = stateJson.get(OIDCConstants.RFP_FIELD);
-                if (nonceFromOAuthStateAsObject instanceof final String nonce) {
-                    csrfNonce = nonce;
-                }
-            }
-        }
-        if (csrfNonce == null) {
-            log.error("Unable to generate request correlation cookie, no request forgery protection value found");
-            return null;
-        }
-        
-        final JSONObject stateObject = new JSONObject();
-        stateObject.appendField(OIDCConstants.AUTHN_STATE_FIELD, stateContext.getToken());
-        stateObject.appendField(OIDCConstants.RFP_FIELD, csrfNonce);
-        final String stateJsonString = stateObject.toJSONString(JSONValue.COMPRESSION);
-        if (stateJsonString == null || stateJsonString.isEmpty()) {
-            return null;
-        }
-        try {
-            final DataSealer localDataSealer = dataSealer;
-            if (localDataSealer != null && sealState.test(prc)) {
-                log.debug("{}: Request correlation cookie is sealed", getId());
-                final String sealed = localDataSealer.wrap(stateJsonString);
-                final byte[] sealedAsBytes = sealed.getBytes(StandardCharset.UTF_8);
-                assert sealedAsBytes != null;
-                return Base64Support.encodeURLSafe(sealedAsBytes); 
-            } else {
-                log.warn("{}: Request correlation cookie was NOT sealed, either DataSealer is not configured or "
-                        + "sealing predicate returned false. Sealing should be enabled in production", getId());
-                final byte[] stateJsonAsBytes = stateJsonString.getBytes(StandardCharset.UTF_8);
-                assert stateJsonAsBytes != null;
-                return Base64Support.encodeURLSafe(stateJsonAsBytes);
-            }
-            
-        } catch (final EncodingException | DataSealerException e) {
-            log.error("Unable to generate request correlation cookie", e);
-            return null;
-        }
-
-    }
-
-}
-
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromJSONStateLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromJSONStateLookupFunction.java
deleted file mode 100644
index b79528e..0000000
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromJSONStateLookupFunction.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
-
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import net.minidev.json.JSONObject;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.oidc.context.OAuthStateContext;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
-
-/**
- * Lookup strategy that extracts the 'state' parameter from the OAuth 2.0 state JSON Object in the state context. This
- * state is used to carry a reference to the resource URL. 
- */
-public class StateFromJSONStateLookupFunction implements Function<OAuthStateContext,String> {
-
-    /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final OAuthStateContext stateContext) {
-        if (stateContext == null) {
-            return null;
-        }
-        final JSONObject stateJson = stateContext.getStateJson();
-        if (stateJson == null) {
-            return null;
-        }
-        final String stateFromState = stateJson.getAsString(OIDCConstants.STATE_FIELD);
-        if (StringSupport.trimOrNull(stateFromState) != null) {
-            return stateFromState;
-        }
-        return null;
-        
-    }
-
-}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromResponseLookupFunction.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromResponseLookupFunction.java
new file mode 100644
index 0000000..7c3e1da
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/StateFromResponseLookupFunction.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.messaging.navigate;
+
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
+
+/**
+ * Lookup strategy that extracts the OAuthn 2.0 authentication response 'state' parameter. 
+ */
+public class StateFromResponseLookupFunction implements Function<ProfileRequestContext,String> {
+
+    /** {@inheritDoc} */
+    @Nullable public String apply(@Nullable final ProfileRequestContext prc) {
+        if (prc == null) {
+            return null;
+        }
+        final MessageContext inbound = prc.getInboundMessageContext();
+        if (inbound == null) {
+            return null;
+        }
+        if (inbound.getMessage() instanceof final AuthenticationResponse response) {
+            final State responseState = response.getState();
+            if (responseState != null) {
+                return responseState.getValue();
+            }
+        }
+        return null;
+        
+    }
+
+}
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/package-info.java
new file mode 100644
index 0000000..042a52e
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/messaging/navigate/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Functions to navigate into profile messages.
+ */
+package net.shibboleth.sp.oidc.messaging.navigate;
\ No newline at end of file
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
index 5e11959..ff0c047 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/AuthenticationRequestStateData.java
@@ -15,10 +15,7 @@
 package net.shibboleth.sp.oidc.profile;
 
 
-import java.net.URI;
 import java.time.Duration;
-import java.time.Instant;
-import java.util.List;
 import java.util.Objects;
 
 import javax.annotation.Nonnull;
@@ -27,34 +24,28 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
 import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
+import com.nimbusds.openid.connect.sdk.Prompt;
 import com.nimbusds.openid.connect.sdk.claims.ACR;
 import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest.Entry;
 
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.sp.state.StateData;
 
 /**
- * A DTO class that carries authentication request information that needs to be recovered to validate the authentication
- * response. This class is designed for JSON serialization and deserialization for storage e.g. in a cookie.
+ * A DTO class that carries OIDC specific authentication request information that needs to be recovered to validate the
+ * authentication response. This class is designed for JSON serialization and deserialization for storage.
  */
 @JsonInclude(JsonInclude.Include.NON_NULL)
 @NotThreadSafe
-public class AuthenticationRequestStateData {
+public class AuthenticationRequestStateData extends StateData {
     
-    /** The identifier of the client that is making the authentication request. */
-    @Nullable private String clientId;
-
     /** The id_token nonce value. */
     @Nullable private String nonce;
 
-    /** The expected issuer of the authentication response. */
-    @Nullable private String authenticatingAuthority;
-
     /** The optional PKCE code verifier. */
     @Nullable private String pkceCodeVerifier;
     
@@ -66,47 +57,6 @@ public class AuthenticationRequestStateData {
      * or the claims parameter requested auth_time. 
      */
     private boolean authTimeRequired;
-    
-    /** The time at which the RP made the authentication request to the OP.*/
-    @Nullable private Instant authnRequestTime;
-    
-    /** List of authentication context class references requested in the authentication request.*/
-    @Nonnull @Unmodifiable @NotLive private List<String> acrs;
-    
-    /**
-     * The redirect_uri value of the authentication request, included here to be
-     * able to include it in any further Token endpoint requests.
-     */
-    @Nullable private URI redirectUri;
-    
-    /** Constructor.*/
-    public AuthenticationRequestStateData() {
-        acrs = CollectionSupport.emptyList();
-    }
-    
-    /**
-     * Set the identifier of the client that is making the authentication request. Can be used to ensure the audience
-     * of the response matches the client that made the request.
-     * 
-     * @param id The client ID to set.
-     * 
-     * @return the updated object
-     */
-    @Nonnull public AuthenticationRequestStateData setClientId(@Nullable final String id) {
-        clientId = id;
-        return this;
-    }
-    
-    /**
-     * Get the identifier of the client that is making the authentication request. Can be used to ensure the audience
-     * of the response matches the client that made the request.
-     * 
-     * @return the client ID.
-     */
-    @JsonProperty("client_id")
-    @Nullable public String getClientId() {
-        return clientId;
-    }
 
     /**
      * Get the nonce expected in the id_token response.
@@ -130,27 +80,6 @@ public class AuthenticationRequestStateData {
         return this;
     }
 
-    /**
-     * Get the expected issuer of the authentication response.
-     * 
-     * @return the authentication authority
-     */
-    @JsonProperty("authority")
-    @Nullable public String getAuthenticationAuthority() {
-        return authenticatingAuthority;
-    }
-
-    /**
-     * Set the expected issuer of the authentication response.
-     * 
-     * @param authority the authentication authority to set
-     * 
-     * @return the updated object
-     */
-    @Nonnull public AuthenticationRequestStateData setAuthenticationAuthority(@Nullable final String authority) {
-        authenticatingAuthority = authority;
-        return this;
-    }
 
     /**
      * Get the PKCE code verifier used to generate the code challenge in the authentication request.
@@ -210,8 +139,8 @@ public class AuthenticationRequestStateData {
     }
     
     /**
-     * Is auth_time required inside the id_token? either because the max_age parameter was used, or the claims parameter 
-     * requested it.
+     * Is auth_time required inside the id_token? either because the max_age parameter was used, or the claims
+     * parameter requested it.
      * 
      * @return true if auth_time has been requested and is required inside the id_token, false otherwise.
      */
@@ -219,74 +148,7 @@ public class AuthenticationRequestStateData {
     public boolean isAuthTimeRequired() {
         return authTimeRequired;
     }
-    
-    /**
-     * Set the time at which this RP sent this authentication request to the OP.
-     * 
-     * @param time the time the request was made
-     * 
-     * @return the updated object
-     */
-    @Nonnull public AuthenticationRequestStateData setAuthnRequestTime(@Nullable final Instant time) {
-        authnRequestTime = time;
-        return this;
-    }
-    
-    /**
-     * Get the time at which this RP sent this authentication request to the OP.
-     * 
-     * @return the time the request was made
-     */
-    @JsonProperty("authn_request_time")
-    @Nullable public Instant getAuthnRequestTime() {
-        return authnRequestTime;
-    }
-    
-    /**
-     * Set the acrs requested in the authentication request.
-     * 
-     * @param acrsIn The acrs to set.
-     * 
-     * @return the updated object
-     */
-    public AuthenticationRequestStateData setAcrs(@Nullable final List<String> acrsIn) {
-        if (acrsIn != null) {
-            acrs = CollectionSupport.copyToList(acrsIn);
-        }
-        return this;
-    }
-    /**
-     * Get the acrs requested in the authentication request.
-     * 
-     * @return the acrs.
-     */
-    @JsonProperty("requested_acrs")
-    @Nonnull @Unmodifiable @NotLive public List<String> getAcrs() {
-        return CollectionSupport.copyToList(acrs);
-    }
-    
-    /**
-     * Get the redirect_uri value of the authentication request.
-     * 
-     * @return Returns the redirectUri.
-     */
-    @JsonProperty("redirect_uri")
-    @Nullable public URI getRedirectUri() {
-        return redirectUri;
-    }
-    
-    /**
-     * Set the redirect_uri value of the authentication request, included here to be able to include it in any further 
-     * Token endpoint requests.
-     * 
-     * @param uri The redirectUri to set.
-     * 
-     * @return the updated object
-     */
-    public AuthenticationRequestStateData setRedirectUri(@Nullable final URI uri) {
-        redirectUri = uri;
-        return this;
-    }
+   
     
     /**
      * Create an instance of this class from the given authentication request and authentication authority.
@@ -296,24 +158,39 @@ public class AuthenticationRequestStateData {
      * 
      * @return a populated instance of this class
      */
+    @SuppressWarnings("null")
     public static AuthenticationRequestStateData from(
             @Nonnull final OIDCAuthenticationRequest request, @Nonnull final String authenticationAuthority) {
         
-        @SuppressWarnings("null")
-        final AuthenticationRequestStateData state = new AuthenticationRequestStateData()
+        // Specify OIDC specific information first
+        final AuthenticationRequestStateData state = new AuthenticationRequestStateData()            
                 .setNonce(request.getNonce() != null ? request.getNonce().getValue() : null)
-                .setAuthenticationAuthority(authenticationAuthority)
                 .setPkceCodeVerifier(request.getCodeVerifier())
-                .setMaxAge(request.getMaxAge())
-                .setAuthnRequestTime(request.getAuthnRequestTime())
-                .setClientId(request.getClientID().getValue())
-                .setAcrs(request.getAcrs().stream().filter(Objects::nonNull).map(ACR::getValue).toList())
-                .setRedirectUri(request.getRedirectURI());
+                .setMaxAge(request.getMaxAge());
+        // Now set the generic state information
+        state.setAcrs(request.getAcrs().stream().filter(Objects::nonNull).map(ACR::getValue).toList())
+                .setResponseLocation(request.getRedirectURI() != null ? request.getRedirectURI().toString() : null)
+                .setAuthenticationAuthority(authenticationAuthority)
+                .setIssuer(request.getClientID().getValue())
+                .setRequestTime(request.getAuthnRequestTime());
 
-        state.setAuthTimeRequired(isAuthTimeRequired(request));
+        state.setAuthTimeRequired(isAuthTimeRequired(request));        
+        state.setPassive(determineIsPassive(request));
+        
         return state;
     }
     
+    /**
+     * Determine if the authentication request is passive by checking if the prompt parameter contains "none".
+     * 
+     * @param request the authentication request
+     * @return true if the prompt parameter contains "none", false otherwise.
+     */
+    static boolean determineIsPassive(@Nonnull final OIDCAuthenticationRequest request) {
+        final Prompt localPrompt = request.getPrompt();
+        return localPrompt != null && localPrompt.contains(Prompt.Type.NONE);
+    }
+    
     /**
      * Determine if auth_time is required inside the id_token either because the max_age parameter was used, or the 
      * claims parameter requested it as an ESSENTIAL claim.
@@ -347,34 +224,56 @@ public class AuthenticationRequestStateData {
     @Override
     public String toString() {
 
-        final String nonceSuffix = mask(nonce);        
-        String pkceCodeVerifierSuffix = mask(pkceCodeVerifier);
+        final String nonceSuffix = mask(getNonce());        
+        String pkceCodeVerifierSuffix = mask(getPkceCodeVerifier());
         pkceCodeVerifierSuffix = pkceCodeVerifierSuffix == null ? "" : pkceCodeVerifierSuffix;
+        
+        return MoreObjects.toStringHelper(this)
+                .add("clientAddress", getClientAddress())
+                .add("issuer", getIssuer())
+                .add("authenticatingAuthority", getAuthenticationAuthority())
+                .add("requestTime", getRequestTime())
+                .add("acrs", getAcrs())
+                .add("reponseLocation", getResponseLocation())
+                .add("resource", getResource())
+                .add("nonce", nonceSuffix)
+                .add("pkceCodeVerifier", pkceCodeVerifierSuffix)
+                .add("maxAge", getMaxAge())
+                .add("authTimeRequired", isAuthTimeRequired())
+                .toString();
+    }
 
-        return "AuthenticationRequestStateData [nonce="+nonceSuffix+", authenticatingAuthority=" + authenticatingAuthority
-                + ", pkceCodeVerifier="+pkceCodeVerifierSuffix+", maxAge=" + maxAge + ", authTimeRequired="
-                + authTimeRequired + ", authnRequestTime=" + authnRequestTime + ", acrs=" + acrs + ", "
-                        + "redirect_uri="+redirectUri+"]";
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                super.hashCode(),
+                authTimeRequired,
+                maxAge,
+                nonce,
+                pkceCodeVerifier
+                );
     }
-    
-    /**
-     * A method to mask all but the last 2 characters of a string value for logging purposes.
-     * 
-     * @param value the string value to mask
-     * @return the masked string
-     */
-    private static String mask(final String value) {
-        if (value == null) {
-            return null;
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
         }
-        if (value.length() <= 4) {
-            return "****";
+        if (!super.equals(obj)) {
+            return false;
         }
-        return "****" + value.substring(value.length() - 2);
-    }
-
-
-
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AuthenticationRequestStateData other = (AuthenticationRequestStateData) obj;
+        return authTimeRequired == other.authTimeRequired 
+                && Objects.equals(maxAge, other.maxAge)
+                && Objects.equals(nonce, other.nonce) 
+                && Objects.equals(pkceCodeVerifier, other.pkceCodeVerifier);
+    }    
+        
     
 }
 
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCConstants.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCConstants.java
index 20dc9c9..e4783d4 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCConstants.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCConstants.java
@@ -22,12 +22,7 @@ import net.shibboleth.shared.annotation.constraint.NotEmpty;
  * OIDC related constants.
  */
 public final class OIDCConstants {
-    
-    /** Private constructor. */
-    private OIDCConstants() {
 
-    }    
-    
     /** 
      * Name of the state field to add to the state JSON object. The state field represents the current 'state'
      * value of the DDF.
@@ -38,6 +33,11 @@ public final class OIDCConstants {
     @Nonnull @NotEmpty public static final String AUTHN_STATE_FIELD = "authnState";
     
     /** Name of the Request Forgery Protection (nonce) field.*/
-    @Nonnull @NotEmpty public static final String RFP_FIELD = "rfp";
+    @Nonnull @NotEmpty public static final String RFP_FIELD = "rfp";    
+    
+    /** Private constructor. */
+    private OIDCConstants() {
+
+    }   
 
 }
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCInitiatorConstants.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCInitiatorConstants.java
index 167f8cc..2a607d6 100644
--- a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCInitiatorConstants.java
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/OIDCInitiatorConstants.java
@@ -40,20 +40,21 @@ public final class OIDCInitiatorConstants {
     /** IsPassive input parameter. */
     @Nonnull @NotEmpty public static final String IS_PASSIVE = "isPassive";
     
-    /** Prompt parameter */
+    /** Prompt parameter. */
     @Nonnull @NotEmpty public static final String PROMPT = "prompt";
     
-    /** Scope parameter */
+    /** Scope parameter. */
     @Nonnull @NotEmpty public static final String SCOPE = "scope";
     
-    /** Resource parameter */
+    /** Resource parameter. */
     @Nonnull @NotEmpty public static final String RESOURCE = "resource";
     
-    /** ui_locales parameter */
+    /** ui_locales parameter. */
     @Nonnull @NotEmpty public static final String UI_LOCALES = "ui_locales";
 
     /** authnContextClassRef input parameter. */
-    @Nonnull @NotEmpty public static final String AUTHN_CONTEXT_CLASS_REF = AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME;
+    @Nonnull @NotEmpty public static final String AUTHN_CONTEXT_CLASS_REF = 
+            AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME;
 
     /** Private constructor. */
     private OIDCInitiatorConstants() {
diff --git a/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/package-info.java b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/package-info.java
new file mode 100644
index 0000000..f05bf07
--- /dev/null
+++ b/sp-oidc-api/src/main/java/net/shibboleth/sp/oidc/profile/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * API classes for SP profile actions.
+ */
+package net.shibboleth.sp.oidc.profile;
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/post/post-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/post/post-beans.xml
index 3f5e9b7..6f9c87d 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/post/post-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/post/post-beans.xml
@@ -14,11 +14,6 @@
                 p:checkDuringInit="false"
                 p:postDecodeStrategyFailureIsError="true"  
                 p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="postDecodeStrategy">
-                    <bean class="net.shibboleth.sp.oidc.profile.decoding.impl.DecodeStateAsJsonObjectConsumer"
-                        p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-                        p:unsealStatePredicate="#{getObject('%{sp.oidc.sealOAuthStatePredicate:}') ?: ((%{sp.oidc.sealOAuthState:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"/>
-                </property>
             </bean>
         </constructor-arg>
     </bean>
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/query/query-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/query/query-beans.xml
index 3983c6d..d513905 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/query/query-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/code/query/query-beans.xml
@@ -14,11 +14,6 @@
                 p:checkDuringInit="false"
                 p:postDecodeStrategyFailureIsError="true"                
                 p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="postDecodeStrategy">
-                    <bean class="net.shibboleth.sp.oidc.profile.decoding.impl.DecodeStateAsJsonObjectConsumer"
-                        p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-                        p:unsealStatePredicate="#{getObject('%{sp.oidc.sealOAuthStatePredicate:}') ?: ((%{sp.oidc.sealOAuthState:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"/>
-                </property>
             </bean>
         </constructor-arg>
     </bean>
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
index df8e5f9..bf089e1 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-beans.xml
@@ -13,119 +13,40 @@
 
     
     <import resource="classpath:/META-INF/net/shibboleth/idp/flows/sp/oidc-common-beans.xml" />
-
-
-    <!--      
-        Map the state reference from the OAuth state parameter to the stored off resource URL. 
-    -->
-    <bean id="MapStateTokenToResource"
-        class="net.shibboleth.sp.profile.impl.MapStateTokenToResource" scope="prototype"
-        p:stateTokenLookupStrategy-ref="StateFromStateLookup" 
-        p:createOutputObjects="true"/>
-        
-    <!-- 
-        Retrieve the correlation cookie from the state token (reference) in the OAuth state parameter. The cookie is used 
-        to protect against request forgery, relate response to request, and contains the authn state token used to recover 
-        authentication state.
-        
-        Store the cookie value into the context for later inspection.
-     -->
-    <bean id="ProcessCorrelationCookie" class="net.shibboleth.sp.oidc.profile.impl.ResolveStateCookie" scope="prototype"
-        p:cookieManager-ref="shibboleth.sp.RemotedCookieManager"
-        p:cookiePrefix="%{sp.correlation.cookiePrefix:__Host-shibsp_req_}"
-        p:createOutputObjects="true"
-        p:errorFatal="true"
-        p:stateTokenLookupStrategy-ref="StateFromStateLookup">
-        <property name="cookieValueConsumerStrategy">
-            <bean class="net.shibboleth.sp.oidc.profile.impl.SetCorrelationCookieValueToContextConsumer"
-                p:correlationCookieStateContextLookupStrategy-ref="shibboleth.ChildLookupOrCreate.CorrelationCookieStateContextFromInboundOAuthStateContext"
-                p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-                p:unsealStatePredicate="#{getObject('%{sp.oidc.sealCorrelationCookiePredicate:}') ?: ((%{sp.oidc.sealCorrelationCookie:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"/>
-        </property>
-    </bean>
     
-    <!-- 
-        Retrieve the state token from the the OAuth 2.0 state parameter which is used to map to the correlation cookie 
-        and the resource URL. The state parameter value will be decoded into the OAuth state context by this point.
-    -->
-    <bean id="StateFromStateLookup" parent="shibboleth.Functions.Compose">
-        <constructor-arg name="g">
-            <bean parent="shibboleth.Functions.Compose">
-                <constructor-arg name="g">
-                    <bean class="net.shibboleth.sp.oidc.messaging.navigate.StateFromJSONStateLookupFunction"/>
-                </constructor-arg>
-                <constructor-arg name="f">     
-                    <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
-                        c:type="#{ T(net.shibboleth.sp.oidc.context.OAuthStateContext) }" />
-                </constructor-arg>
-            </bean>
-        </constructor-arg>
-        <constructor-arg name="f" ref="shibboleth.MessageContextLookup.Inbound"/>
-    </bean>
     
-    <!--  
-        Retrieve the stored authentication state/value from the storage manager using the authn state token in the correlation cookie     
+    <!--
+         
+         Uses the OAuth 2.0 state parameter to reference a browser cookie (by name) that
+         stores the authentication state, or a pointer to that state within the storage
+         manager.
+    
+         The presence of a cookie whose name matches the OAuth 2.0 state value enables
+         state verification and provides protection against cross-site request forgery (CSRF).
+         The state value must exactly match the name of the corresponding cookie.
+    
+         The cookie name (and therefore the state value) is generated using a
+         cryptographically secure, high-entropy identifier that is infeasible for an attacker
+         to guess. Binding the cookie to the browser session and validating it against the
+         state value returned in the authorization response mitigates CSRF attacks.
+         
     -->
-    <bean id="RecoverAuthenticationState" class="net.shibboleth.sp.oidc.profile.impl.MapStateTokenToStateValue" scope="prototype"
+    <bean id="RecoverStateData"
+        class="net.shibboleth.sp.profile.impl.RecoverStateData" scope="prototype"
+        p:stateTokenLookupStrategy-ref="OAuthStateLookup"
         p:errorFatal="true"
-        p:stateTokenLookupStrategy-ref="AuthenticationStateFromCorrelationCookieLookup">
-        <property name="stateValueConsumer">
-            <bean class="net.shibboleth.sp.oidc.profile.impl.SetAuthenticationRequestToPeerContextConsumer"
-                p:objectMapper-ref="shibboleth.JSONObjectMapper"
-                p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-                p:unsealStatePredicate="#{getObject('%{sp.oidc.sealAuthenticationStatePredicate:}') ?: ((%{sp.oidc.sealAuthenticationState:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"
-                />
-        </property>
-    </bean>
+        p:stateDataContextCreationStrategy-ref="shibboleth.ChildLookupOrCreate.StateDataContextFromInbound"
+        p:stateDataClass="net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData"
+        p:createOutputObjects="true"/>
     
-    <bean id="AuthenticationStateFromCorrelationCookieLookup" parent="shibboleth.Functions.Compose">
-        <constructor-arg name="g">
-            <bean parent="shibboleth.Functions.Compose">
-                <constructor-arg name="g">
-                    <bean class="net.shibboleth.sp.oidc.messaging.navigate.AuthenticationStateFromCorrelationCookieLookupFunction"/>
-                </constructor-arg>
-                <constructor-arg name="f" ref="shibboleth.ChildLookup.CorrelationCookieStateContextFromOAuthStateContext"/>     
-            </bean>
-        </constructor-arg>
-        <constructor-arg name="f" ref="shibboleth.MessageContextLookup.Inbound"/>
-    </bean>
+    <bean id="OAuthStateLookup" class="net.shibboleth.sp.oidc.messaging.navigate.StateFromResponseLookupFunction"/>
+
 
     <bean id="ValidateAuthenticationResponseResult" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.ValidateAuthenticationResponseResult" />
-        
-    <bean id="ValidateResponseStateMatchesRequest" scope="prototype"
-        class="net.shibboleth.sp.oidc.profile.impl.ValidateResponseState"
-        p:rfpTokenFromOAuthStateLookupStrategy-ref="RFPFromOAuthStateLookup" 
-        p:rfpFromCookieLookupStrategy-ref="RFPFromCorrelationCookieLookup"/>
-        
-   <!-- Get the Request Forgery Protection value from the correlation cookie  -->
-   <bean id="RFPFromCorrelationCookieLookup" parent="shibboleth.Functions.Compose">
-        <constructor-arg name="g">
-            <bean parent="shibboleth.Functions.Compose">
-                <constructor-arg name="g">
-                    <bean class="net.shibboleth.sp.oidc.messaging.navigate.RFPFromCorrelationCookieLookupFunction"/>
-                </constructor-arg>
-                <constructor-arg name="f" ref="shibboleth.ChildLookup.CorrelationCookieStateContextFromOAuthStateContext"/>     
-            </bean>
-        </constructor-arg>
-        <constructor-arg name="f" ref="shibboleth.MessageContextLookup.Inbound"/>
-    </bean>
-        
-    <!-- Get the Request Forgery Protection value from the OAuth 2.0 state parameter -->
-    <bean id="RFPFromOAuthStateLookup" parent="shibboleth.Functions.Compose">
-        <constructor-arg name="g">
-            <bean parent="shibboleth.Functions.Compose">
-                <constructor-arg name="g">
-                    <bean class="net.shibboleth.sp.oidc.messaging.navigate.RFPFromOAuthStateLookupFunction"/>
-                </constructor-arg>
-                <constructor-arg name="f">     
-                    <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
-                        c:type="#{ T(net.shibboleth.sp.oidc.context.OAuthStateContext) }" />
-                </constructor-arg>
-            </bean>
-        </constructor-arg>
-        <constructor-arg name="f" ref="shibboleth.MessageContextLookup.Inbound"/>
-    </bean>
+    
+    <bean id="InitializePeerEntityContext" class="net.shibboleth.sp.oidc.profile.impl.InitializePeerEntityContext"
+     p:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/>
         
      <bean id="InitializeRelyingPartyContextFromOIDCPeer"
         class="net.shibboleth.sp.oidc.profile.impl.InitializeRelyingPartyContextFromOIDCPeer" scope="prototype" />
@@ -204,11 +125,11 @@
         p:oAuth2ClientAuthenticationContextLookupStrategy-ref="shibboleth.ChildLookupOrCreate.OAuth2ClientAuthenticationContextFromInboundMessageContext">
         <property name="redirectUriLookupStrategy">
              <bean class="net.shibboleth.sp.oidc.profile.impl.RedirectUriFromStateLookupStrategy" scope="prototype" 
-             c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/>   
+             c:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/>   
         </property>
         <property name="pkceCodeVerifierLookupStrategy">
              <bean class="net.shibboleth.sp.oidc.profile.impl.PKCECodeVerifierFromStateLookupStrategy" scope="prototype" 
-             c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/>   
+             c:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/>   
         </property>  
     </bean>
     
@@ -428,11 +349,11 @@
    <bean id="AuthenticationRequestTimeLookupFunction" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.AuthenticationRequestTimeLookupFunction"
         p:clockSkew="%{sp.oidc.idtoken.jwt.verifier.authnRequestClockSkew:PT0S}"
-        p:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/> 
+        p:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/> 
 
    <bean id="DefaultAuthTimeActivationCondition" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.AuthTimeRequestedActivationCondition"
-        c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/>
+        c:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/>
 
  
     <bean id="ACRClaimValidator" class="net.shibboleth.oidc.security.jwt.claims.impl.ACRClaimsValidator" scope="prototype"
@@ -443,7 +364,7 @@
     
     <bean id="DefaultRequestedACRClaimLookupStrategy" scope="prototype"
                 class="net.shibboleth.sp.oidc.profile.impl.RequestedACRClaimsFromStateLookupStrategy"
-                c:authenticationRequestLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound"/> 
+                c:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromInbound"/> 
     
     <bean id="DefaultRequestedACRValidationActivationCondition" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.RequestedACRValidationActivationCondition"
@@ -451,8 +372,7 @@
     
     <!-- End ID Token Claims Validation -->
     
-    <bean id="CheckUserInfoRequiredCondition" class=" net.shibboleth.oidc.profile.config.logic.UserInfoLookupPredicate" />
-    
+    <bean id="CheckUserInfoRequiredCondition" class=" net.shibboleth.oidc.profile.config.logic.UserInfoLookupPredicate" />    
     
     <bean id="UserInfoEndpointLookup" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.UserInfoEndpointLookup"
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-flow.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-flow.xml
index f8955b3..99f0586 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-flow.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/oidc/oidc-flow.xml
@@ -5,13 +5,11 @@
 
     <action-state id="DoProfileWork">
 <!--        <evaluate expression="PopulateMetricContext" />-->
-       <evaluate expression="DecodeMessage" />       
-       <evaluate expression="MapStateTokenToResource" />
-       <evaluate expression="ProcessCorrelationCookie"/>       
-       <evaluate expression="RecoverAuthenticationState" />
-       <evaluate expression="ValidateResponseStateMatchesRequest" />
-       <evaluate expression="ValidateAuthenticationResponseResult" />  <!-- Needed at the end, so state is unset even if error from OP -->
+       <evaluate expression="DecodeMessage" />    
+       <evaluate expression="RecoverStateData" /> <!-- also performs state correlation checking for CSRF protection -->             
+       <evaluate expression="ValidateAuthenticationResponseResult" />
        
+       <evaluate expression="InitializePeerEntityContext"/>
        <evaluate expression="ProviderMetadataLookup" />        
        <evaluate expression="InitializeRelyingPartyContextFromOIDCPeer" />
        <evaluate expression="SelectRelyingPartyConfiguration" />
@@ -90,6 +88,7 @@
         <transition on="InvalidAcessToken" to="MessageProcessingError" />
         <transition on="InvalidIdToken" to="MessageProcessingError" />
         <transition on="InvalidUserInfoClaims" to="MessageProcessingError" />
+        <transition on="InputOutputError" to="MessageProcessingError" />
     </global-transitions>
     
     
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
index 58d70d2..e05866e 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-beans.xml
@@ -15,7 +15,6 @@
         static-field="net.shibboleth.oidc.profile.config.OIDCSSOProfileConfiguration.PROFILE_ID" />
     <!-- end -->
 
-
     <bean id="ValidateSessionInitiatorRequest"
         class="net.shibboleth.sp.profile.impl.ValidateSessionInitiatorRequest" 
         scope="prototype" 
@@ -263,30 +262,33 @@
         class="net.shibboleth.oidc.profile.config.logic.SignRequestObjectPredicate"
         p:relyingPartyContextLookupStrategy-ref="shibboleth.ChildLookup.RelyingParty" />
 
-   <!-- 
-        Store off required authentication state via the storage token manager. The token returned from the storage manager
-        is referenced in the correlation cookie for later recovery.
-   -->
-    <bean id="StoreAuthenticationState"
-        class="net.shibboleth.sp.oidc.profile.impl.MapStateValueToStateToken" scope="prototype"
-        p:createOutputObjects="true" 
-        p:errorFatal="%{sp.stateToken.errorsFatal:true}"
-        p:stateValueLookupStrategy-ref="AuthenticationRequestStateForStorageStrategy"
-        p:stateTokenConsumer-ref="AuthenticationStateTokenConsumer" />
-
-    <!-- A consumer that stores off the authentication state token (key) -->
-    <bean id="AuthenticationStateTokenConsumer"
-        class="net.shibboleth.sp.oidc.profile.impl.SetAuthenticationStateTokenConsumer" />
-
-    <!-- A strategy for encoding and sealing any state from the authentication request needed for validating the response -->
-    <bean id="AuthenticationRequestStateForStorageStrategy"
+    <!-- 
+        Create the Authentication State Data from the authentication request that is preserved for later recovery
+        in the consumer flow.
+    -->
+    <bean id="CreateAuthenticationStateData" class="net.shibboleth.sp.oidc.profile.impl.CreateAuthenticationStateData"
+        scope="prototype"
+        p:stateDataContextCreationStrategy-ref="shibboleth.ChildLookupOrCreate.StateDataContextFromOutbound"
+        p:stateDataLookupStrategy-ref="AuthenticationRequestStateForStorageStrategy"/>
+
+    <!-- 
+        A strategy for creating Authentication Stata Data from the authentication request. Preserved and recovered
+        between request and response, needed for validating the response. 
+    -->
+    <bean id="AuthenticationRequestStateForStorageStrategy" scope="prototype"
         class="net.shibboleth.sp.oidc.profile.impl.AuthenticationRequestStateForStorageStrategy"
         p:authenticationAuthorityLookupStrategy-ref="shibboleth.RelyingPartyIdLookup.Simple"
-        p:authenticationRequestLookupStrategy-ref="shibboleth.AuthenticationRequestLookup.FromOutbound"
-        p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-        p:sealStatePredicate="#{getObject('%{sp.oidc.sealAuthenticationStatePredicate:}') ?: ((%{sp.oidc.sealAuthenticationState:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"
-        p:objectMapper="#{getObject('%{sp.oidc.jsonObjectMapper:}') ?: getObject('shibboleth.JSONObjectMapper')}" />
-
+        p:authenticationRequestLookupStrategy-ref="shibboleth.AuthenticationRequestLookup.FromOutbound" /> 
+    
+    <!-- 
+        Store off the authentication state data in the state data context so it can be recovered later when processing 
+        the response. By default, failure to store off the state will be fatal.
+     -->
+    <bean id="PreserveState"
+        class="net.shibboleth.sp.profile.PreserveStateDataAction" scope="prototype"
+        p:createOutputObjects="true"
+        p:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromOutbound"
+        p:errorFatal="%{sp.stateToken.errorsFatal:true}" />
 
     <bean id="HandleOutboundMessage" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
         scope="prototype" c:messageHandler-ref="PreEncodeMessageHandler" c:executionDirection="OUTBOUND">
@@ -299,12 +301,12 @@
         scope="prototype">
         <property name="handlers">
             <list>
+                <!-- Pull out the state here, rather than during processToken of the PreserveState step -->
                 <bean id="AddState" class="net.shibboleth.oidc.profile.messaging.handler.impl.AddStateHandler"
                     scope="prototype">
                     <property name="parameterValueLookupStrategy">
-                        <bean class="net.shibboleth.sp.oidc.profile.impl.StateLookupStrategy" scope="prototype"
-                            p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-                            p:sealStatePredicate="#{getObject('%{sp.oidc.sealOAuthStatePredicate:}') ?: ((%{sp.oidc.sealOAuthState:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"/>
+                        <bean class="net.shibboleth.sp.oidc.profile.impl.StateTokenLookupStrategy" scope="prototype"
+                        c:stateDataContextLookupStrategy-ref="shibboleth.ChildLookup.StateDataContextFromOutbound"/>
                     </property>
                 </bean>
 
@@ -339,30 +341,7 @@
             </list>
         </property>
     </bean>
-
-    <!-- 
-        Issue the correlation cookie that relates request to response and stores of parameters needed to recover
-        authentication state when processing the response.
-    -->
-    <bean id="IssueRequestCorrelationCookie" class="net.shibboleth.sp.oidc.profile.impl.IssueStateCookie"
-        scope="prototype" 
-        p:cookieManager-ref="shibboleth.sp.RemotedCookieManager"
-        p:cookiePrefix="#{'%{sp.correlation.cookiePrefix:__Host-shibsp_req_}'.trim()}" 
-        p:createOutputObjects="true"
-        p:errorFatal="%{sp.stateToken.errorsFatal:true}" 
-        p:stateValueLookupStrategy-ref="RequestCorrelationCookieFromStateStrategy"/>
-    
-    <!-- 
-        Create a request correlation cookie. The cookie contains an authentication token that maps back to stored 
-        authentication state alongside a request forgery protection nonce used to prevent CSRF attacks.
-    -->
-    <bean id="RequestCorrelationCookieFromStateStrategy" 
-        class="net.shibboleth.sp.oidc.messaging.navigate.RequestCorrelationFromStateLookupFunction"
-        c:authenticationRequestDataStateLookupStrategy-ref="shibboleth.ChildLookup.AuthenticationRequestStateDataFromOutbound"
-        c:authenticationRequestLookupStrategy-ref="shibboleth.AuthenticationRequestLookup.FromOutbound"
-        p:dataSealer-ref="#{'%{sp.dataSealer:shibboleth.DataSealer}'.trim()}"
-        p:sealStatePredicate="#{getObject('%{sp.oidc.sealCorrelationCookiePredicate:}') ?: ((%{sp.oidc.sealCorrelationCookie:true}) ? getObject('shibboleth.Conditions.TRUE') : getObject('shibboleth.Conditions.FALSE'))}"/>
-        
+ 
     <bean id="EncodeMessage" class="net.shibboleth.sp.profile.impl.EncodeMessage" scope="prototype"
         p:createOutputObjects="true" p:messageEncoderFactory-ref="messageEncoderFactory" />
 
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-flow.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-flow.xml
index aeee613..f4f38de 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-flow.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/oidc/oidc-flow.xml
@@ -47,9 +47,9 @@
     
     <action-state id="BuildOutboundMessage">
         <!-- Must map authentication state to a state token first, so we can add the token to the state in HandleOutboundMessage -->
-        <evaluate expression="StoreAuthenticationState"/> 
-        <evaluate expression="HandleOutboundMessage" />        
-        <evaluate expression="IssueRequestCorrelationCookie" />
+        <evaluate expression="CreateAuthenticationStateData"/> 
+        <evaluate expression="PreserveState"/>
+        <evaluate expression="HandleOutboundMessage" />
         <evaluate expression="EncodeMessage" />
         <evaluate expression="'proceed'" />
          
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/oidc-common-beans.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/oidc-common-beans.xml
index f78af5c..cf87cb0 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/oidc-common-beans.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/oidc-common-beans.xml
@@ -73,11 +73,16 @@
     class="net.shibboleth.profile.context.navigate.SpringExpressionContextLookupFunction"
         c:_0="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }" 
         c:outputType="#{T(com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata)}"
-        c:expression="#input.getProviderInformation()" /> 
-        
-    <bean id="shibboleth.ChildLookup.AuthnRequestStateDataContext"
+        c:expression="#input.getProviderInformation()" />
+           
+   <bean id="shibboleth.ChildLookup.StateDataContext"
+        class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+        c:type="#{ T(net.shibboleth.sp.context.StateDataContext) }" />
+   
+   <bean id="shibboleth.ChildLookupOrCreate.StateDataContext"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
-        c:type="#{ T(net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext) }" />
+        c:type="#{ T(net.shibboleth.sp.context.StateDataContext) }"
+        c:createContext="true" />
         
    <bean id="shibboleth.ChildLookup.OAuth2ClientContext"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
@@ -218,26 +223,46 @@
         </constructor-arg>
     </bean>
     
-    <bean id="shibboleth.ChildLookup.AuthenticationRequestStateDataFromInbound" 
+    <bean id="shibboleth.ChildLookup.StateDataContextFromOutbound" 
             parent="shibboleth.Functions.Compose">
         <constructor-arg name="g">
-            <ref bean="shibboleth.ChildLookup.AuthnRequestStateDataContext" />
+            <ref bean="shibboleth.ChildLookup.StateDataContext" />
         </constructor-arg>
         <constructor-arg name="f">
-            <ref bean="shibboleth.MessageContextLookup.Inbound" />
+            <ref bean="shibboleth.MessageContextLookup.Outbound" />
         </constructor-arg>
     </bean>
     
-    <bean id="shibboleth.ChildLookup.AuthenticationRequestStateDataFromOutbound" 
+    <bean id="shibboleth.ChildLookupOrCreate.StateDataContextFromOutbound" 
             parent="shibboleth.Functions.Compose">
         <constructor-arg name="g">
-            <ref bean="shibboleth.ChildLookup.AuthnRequestStateDataContext" />
+            <ref bean="shibboleth.ChildLookupOrCreate.StateDataContext" />
         </constructor-arg>
         <constructor-arg name="f">
             <ref bean="shibboleth.MessageContextLookup.Outbound" />
         </constructor-arg>
     </bean>
     
+    <bean id="shibboleth.ChildLookup.StateDataContextFromInbound" 
+            parent="shibboleth.Functions.Compose">
+        <constructor-arg name="g">
+            <ref bean="shibboleth.ChildLookup.StateDataContext" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <ref bean="shibboleth.MessageContextLookup.Inbound" />
+        </constructor-arg>
+    </bean>
+    
+    <bean id="shibboleth.ChildLookupOrCreate.StateDataContextFromInbound" 
+            parent="shibboleth.Functions.Compose">
+        <constructor-arg name="g">
+            <ref bean="shibboleth.ChildLookupOrCreate.StateDataContext" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <ref bean="shibboleth.MessageContextLookup.Inbound" />
+        </constructor-arg>
+    </bean>
+    
     <!-- Common Actions -->
     
     <bean id="ProviderMetadataLookup" parent="WebFlowInboundMessageHandlerAdaptor" scope="prototype">
diff --git a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
index 3b41faf..fba5ada 100644
--- a/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
+++ b/sp-oidc-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
@@ -101,7 +101,7 @@
         p:userInfoHttpRequestMethod="%{sp.oidc.userinfo.httpRequestMethod:GET}"
         p:scopes="%{sp.oidc.scopes:#{null}}"
         p:responseModes="%{sp.oauth2.responseModes:}"
-        p:shouldValidateAcrValue="%{sp.oidc.idToken.validateAcrValue:true}">
+        p:validateAcrValue="%{sp.oidc.idToken.validateAcrValue:true}">
     </bean>
     
      <util:constant id="OIDC.SSO.FEATURE_ESSENTIAL_ACR_REQUEST"
diff --git a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
index 4e8c81e..5439d38 100644
--- a/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
+++ b/sp-oidc-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/oidc.properties
@@ -11,13 +11,10 @@
 # The OP's keyset document fetch interval i.e. how long should keys from the OP be cached.
 #sp.oidc.keyfetch.interval = PT30M
 
-
 # IMPORTANT: The below settings are for intermediate cases which should be regarded experimental in the alpha version.
 
-
 # Global Request And Response Settings
 
-
 # Comma seperated list of additional scopes e.g. PROFILE or EMAIL. The openid scope is added by default.
 #sp.oidc.scopes = 
 # The HTTP method use to send the authorization request
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java
new file mode 100644
index 0000000..d7900e2
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/AbstractOIDCTokenConsumerFlowTest.java
@@ -0,0 +1,533 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.flows;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.mockito.Mockito;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.oauth2.sdk.AuthorizationCode;
+import com.nimbusds.oauth2.sdk.AuthorizationResponse;
+import com.nimbusds.oauth2.sdk.ErrorObject;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.TokenErrorResponse;
+import com.nimbusds.oauth2.sdk.TokenResponse;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.oauth2.sdk.token.AccessToken;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
+import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
+import com.nimbusds.openid.connect.sdk.claims.UserInfo;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
+
+import net.minidev.json.JSONObject;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.net.URISupport;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.flows.AbstractSPFlowTest;
+import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
+import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
+import net.shibboleth.sp.oidc.testing.TestHelper;
+import net.shibboleth.sp.profile.ConsumerConstants;
+
+/**
+ * An abstract test class for OIDC token consumer flow tests. 
+ * Provides helper methods to construct OIDC responses and mock endpoints.
+ */
+public abstract class AbstractOIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
+    
+    /** Dummy signing key of the dummy OP we are testing against. */
+    @Autowired @Qualifier("dummy.op.signing.Credential") protected JWKCredential opSigningCredential;
+    
+    /** Dummy encryption key of the RP/SP. */
+    @Autowired @Qualifier("dummy.sp.encryption.Credential") protected JWKCredential rpEncryptionCredential;
+    
+    /** The mocked HttpClient to use when responding to Token and UserInfo requests.*/
+    private HttpClient httpClient;
+    
+    /** The OP metadata to use.*/
+    private OIDCProviderMetadata metadata;
+
+    /**
+     * Constructor.
+     *
+     * @param id the flow id
+     */
+    protected AbstractOIDCTokenConsumerFlowTest(@Nonnull final String id) {
+        super(id);
+
+    }
+    
+    /** 
+     * Pre-test work.
+     *  
+     * @throws Exception on error
+     */
+    @BeforeMethod
+    public void beforeMethod() throws Exception{
+        setDefaultAuth();
+        final ApplicationContext applicationContext2 = applicationContext;
+        // Check the mock HTTP client has been defined
+        if (applicationContext2 != null) {
+            httpClient = applicationContext2.getBean("Mock.HttpClient", HttpClient.class);
+        } else {
+            fail("Mocked Http Client could not be found");
+        }
+        if (httpClient == null) {
+            fail("Mocked Http Client could not be found");
+        }
+        // Add a default metadata response
+        final var metadataFromFile = new ClassPathResource("metadata/openid-configuration.json");
+        final String json = new String(metadataFromFile.getInputStream().readAllBytes(), StandardCharsets.UTF_8);        
+        metadata = OIDCProviderMetadata.parse(JSONObjectUtils.parse(json));
+        
+    }
+    
+    /**
+     * Construct a successful OIDC token response with signed id_token.
+     * 
+     * @param expiry expiry time
+     * @param issuedAt issue time
+     * @param nonce the nonce
+     * @return the tokens
+     * @throws ParseException 
+     * @throws JOSEException 
+     */
+    protected OIDCTokenResponse constructSuccessfulTokenResponseSigned(
+            @Nonnull final Instant expiry, @Nonnull final Instant issuedAt, @Nullable final String nonce, 
+            @Nullable final Map<String, Object> additionalClaims) 
+                    throws JOSEException, ParseException {
+
+         final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
+                .subject("jdoe")
+                .issuer(TestConstants.ISSUER)
+                .audience(TestConstants.CLIENT_ID)
+                .expirationTime(Date.from(expiry))
+                    .issueTime(Date.from(issuedAt));
+        
+         if (nonce != null) {
+             claimsSetBuilder.claim("nonce", nonce);
+         }
+         if (additionalClaims != null) {
+             additionalClaims.entrySet()
+                 .stream()
+                 .forEach(entry -> claimsSetBuilder.claim(entry.getKey(), entry.getValue()));
+         }
+         
+         final JWTClaimsSet claimsSet = claimsSetBuilder.build();
+
+         
+         final JWT signedIdToken = 
+                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
+         
+         final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
+         final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
+         return new OIDCTokenResponse((new OIDCTokens(signedIdToken, accessToken, refreshToken)));
+    }
+    
+    /**
+     * Construct an error OIDC token response.
+     * 
+     * @param code the error code
+     * @param description the error description
+     * @param httpCode the http code to return
+     */
+    protected TokenErrorResponse constructErrorTokenResponseSigned(
+            final String code, final String description, final int httpCode) 
+                    throws JOSEException, ParseException {
+        
+        final ErrorObject error = new ErrorObject(code, description, httpCode);
+        final TokenErrorResponse errorResponse = new TokenErrorResponse(error);
+
+        return errorResponse;
+    }
+
+    /**
+     * Construct a successful OIDC token response with a signed and encrypted id_token.
+     * 
+     * @param expiry expiry time
+     * @param issuedAt issue time
+     * @param nonce the nonce
+     * @return the tokens
+     * @throws ParseException 
+     * @throws JOSEException 
+     */
+    protected OIDCTokenResponse constructSuccessfulTokenResponseSignedAndEncrypted(
+            @Nonnull final Instant expiry, @Nonnull final Instant issuedAt, @Nullable final String nonce,
+            @Nullable final Map<String, Object> additionalClaims) 
+                    throws JOSEException, ParseException {
+
+         final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
+                 .subject("jdoe")
+                 .issuer(TestConstants.ISSUER)
+                 .audience(TestConstants.CLIENT_ID)
+                 .expirationTime(Date.from(expiry))
+                     .issueTime(Date.from(issuedAt));
+         
+         if (nonce != null) {
+             claimsSetBuilder.claim("nonce", nonce);
+         }
+         if (additionalClaims != null) {
+             additionalClaims.entrySet()
+                 .stream()
+                 .forEach(entry -> claimsSetBuilder.claim(entry.getKey(), entry.getValue()));
+         }
+         final JWTClaimsSet claimsSet = claimsSetBuilder.build();
+         
+         final JWT encryptedIdToken = 
+                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.RSA_OAEP_256, 
+                         EncryptionMethod.A128CBC_HS256, opSigningCredential, rpEncryptionCredential);
+         
+         final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
+         final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
+         return new OIDCTokenResponse((new OIDCTokens(encryptedIdToken, accessToken, refreshToken)));
+    }
+    
+    /**
+     * Construct a successful OIDC user info response in JSON format. application/json.
+     * 
+     * @return the user info response
+     */
+    protected UserInfoSuccessResponse constructJSONUserInfoResponse() {
+        final JSONObject json = new JSONObject();
+        json.appendField("sub", "jdoe");
+        json.appendField("name", "John Doe");
+        json.appendField("nickname" , "John");
+        return  new UserInfoSuccessResponse(new UserInfo(json));
+    }
+    
+    /**
+     * Construct a successful OIDC user info response in JWT format, signed.
+     * 
+     * @return the user info response
+     * @throws ParseException 
+     * @throws JOSEException 
+     */
+    protected UserInfoSuccessResponse constructJWTUserInfoResponseSigned() throws JOSEException, ParseException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+                .subject("jdoe")
+                .issuer(TestConstants.ISSUER)
+                .audience(TestConstants.CLIENT_ID)
+                .claim("name", "John Doe")
+                .claim("nickname","John").build();
+       
+        final JWT signedUserInfoToken = 
+                TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
+        return  new UserInfoSuccessResponse(signedUserInfoToken);
+    }
+    
+    /**
+     * Mock OIDC Token, UserInfo, and Keys endpoints. Returning the supplied responses. 
+     * Noting these mocks may not exercise the response decoders, as the responses are returned directly.
+     * 
+     * @param tokenResponse the token response
+     * @param userInfoResponse the user info response
+     * 
+     * @throws IOException on error.
+     */
+    protected void mockOIDCEndpoints(final TokenResponse tokenResponse, 
+            final UserInfoSuccessResponse userInfoResponse) throws IOException {
+        
+        Mockito.when(httpClient.execute(
+                Mockito.argThat(req -> req != null && req.getRequestUri().toString()
+                .contains(".well-known/openid-configuration")),
+                Mockito.any(HttpContext.class),
+                Mockito.any(HttpClientResponseHandler.class)))
+            .thenReturn(metadata);
+        
+        Mockito.when(httpClient.execute(
+                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/token")),
+                Mockito.any(HttpContext.class),
+                Mockito.any(HttpClientResponseHandler.class)))
+            .thenReturn(tokenResponse);
+
+        Mockito.when(httpClient.execute(
+                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/userinfo")),
+                Mockito.any(HttpContext.class),
+                Mockito.any(HttpClientResponseHandler.class)))
+            .thenReturn(userInfoResponse);
+        
+        // Fetch from the keys endpoint mocking the call used by the RemoteJwkSetCache     
+        final String jwk = TestHelper.createJWKJSONStringFrom(opSigningCredential);
+        assertNotNull(jwk);
+        final ClassicHttpResponse classicHttpResponse = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(classicHttpResponse.getCode()).thenReturn(200);
+        Mockito.when(classicHttpResponse.getEntity())
+               .thenReturn(new StringEntity("{\"keys\":["+jwk+"]}", ContentType.APPLICATION_JSON));
+
+        Mockito.when(httpClient.executeOpen(
+                Mockito.nullable(HttpHost.class),
+                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/keys")),
+                Mockito.any(HttpContext.class)))
+            .thenReturn(classicHttpResponse);
+    }
+    
+    /**
+     * Check that the output message contains an instruction to unset cookies using Max-Age=0.
+     * 
+     * @param output the DDF output
+     */
+    protected void validateCookiesAreUnset(@Nullable final DDF output) {
+        if (output == null) {
+            fail("DDF output can not be null");
+        }
+        // Check cookies are unset
+        final DDF headers = output.getmember("http.headers");
+        // Check that any Set-Cookie is an unset, we do not set new cookies in the consumer flow
+        if (headers.islist()) {
+            headers.forEach(header -> {
+                if (header.isstring() && "Set-Cookie".equals(header.name())){
+                    Assert.assertTrue(header.string().contains("Max-Age=0"),"Cookies must be unset");
+                }
+            });
+        }
+    }
+    
+    /**
+     * Decode an encoded response and run assertion checks against it.
+     * 
+     * @param result
+     *            flow execution result
+     * @param claimNames
+     *            set of claims to check for
+     * @param resource
+     *            resource URL used in final redirect
+     * 
+     * @return the output object
+     */
+    @Nonnull protected DDF validateOutputMessage(@Nonnull final FlowExecutionResult result,
+            @Nullable final Set<String> claimNames, @Nullable final String resource) {
+        
+        final ProfileRequestContext prc = retrieveProfileRequestContext(result);
+        assert prc != null;
+        final AgentRequestContext arc = prc.ensureSubcontext(AgentRequestContext.class);
+        final DDF output = arc.getOutput();
+        
+        assert output != null;
+        Assert.assertTrue(output.isstruct());
+        final DDF http = output.getmember(RemotedHttpServletRequest.STRUCTURE_NAME);
+        Assert.assertTrue(http.isstruct());
+        
+        final byte[] redirect = http.getmember(RemotedHttpServletResponse.REDIRECT).unsafe_string();
+        Assert.assertEquals(resource != null ? resource.getBytes(StandardCharsets.UTF_8) : null,  redirect);
+        
+        final Set<String> mutableIds = new HashSet<>(claimNames);
+        for (final DDF attr : output.getmember(ConsumerConstants.SESSION_ATTRIBUTES).asList()) {
+            Assert.assertTrue(mutableIds.contains(attr.name()));
+            mutableIds.remove(attr.name());
+            if ("mail".equals(attr.name())) {
+                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
+                        CollectionSupport.singletonList("jdoe at example.org"));
+            } else if ("displayName".equals(attr.name())) {
+                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
+                        CollectionSupport.singletonList("John Doe"));
+            } else if ("eduPersonScopedAffiliation".equals(attr.name())) {
+                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("value").string();}).toList(),
+                        CollectionSupport.listOf("member"));
+                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("scope").string();}).toList(),
+                        CollectionSupport.listOf("example.org"));
+            }
+        }
+        Assert.assertTrue(mutableIds.isEmpty());
+        
+        validateCookiesAreUnset(output);
+        //TODO ADD BACK
+        
+//        try {
+//            final DDF s = output.getmember(ConsumerConstants.SESSION_OPAQUE);
+//            assert s != null;
+//            Assert.assertTrue(s.isstruct());
+//            final DDF nameIdDdf = s.getmember("nameID");
+//            Assert.assertTrue(nameIdDdf.isstring());
+//            final String nameIdString = nameIdDdf.string();
+//            assert nameIdString != null;
+//            final byte[] opaque = Base64Support.decodeURLSafe(nameIdString);
+//            try (final ByteArrayInputStream in = new ByteArrayInputStream(opaque)) {
+//                final XMLObject obj = XMLObjectSupport.unmarshallFromInputStream(parserPool, in);
+//                if (obj instanceof final NameID nameID) {
+//                    Assert.assertEquals(nameID.getValue(), "jdoe at example.org");
+//                    Assert.assertEquals(nameID.getFormat(), NameIDType.EMAIL);
+//                    Assert.assertEquals(nameID.getSPProvidedID(), sessionIndex);
+//                } else {
+//                    Assert.fail("Session data was not a NameID");
+//                }
+//            }
+//        } catch (final DecodingException|IOException|UnmarshallingException|XMLParserException e) {
+//            Assert.fail(e.getMessage());
+//        }
+        
+        return output;
+        
+    }
+
+
+    /**
+     * Builds a dummy OIDC authorization code response.
+     * 
+     * @param redirectURI
+     *            redirect URI
+     * @param mode
+     *            response mode
+     * 
+     * @return authentication success response for the token consumer flow
+     */
+    protected AuthenticationSuccessResponse buildOIDCAuthorizationCodeResponse(
+            @Nonnull final String redirect, @Nonnull final ResponseMode mode, final String stateString) {
+
+        final AuthorizationCode code = new AuthorizationCode("dummy-auth-code");
+        final State state = new State(stateString);
+        URI redirectURI = null;
+        try {
+            redirectURI = new URI(redirect);
+        } catch (final URISyntaxException e) {
+            fail("Bad redirect URI", e);
+        }
+
+        // Build a dummy success response (authorization code)
+        return new AuthenticationSuccessResponse(
+            redirectURI,
+            code, 
+            null, 
+            null,            
+            state, 
+            null,            
+            mode 
+        );
+    }
+    
+    /**
+     * Build a invalid_request error response, from openID connect core.
+     * 
+     * @return the error response
+     */
+    protected AuthenticationErrorResponse buildErrorResponse(final String stateString) {
+
+        final URI redirectURI = URI.create(TestConstants.RESPONSE_URL);
+        final State state = new State(stateString);
+        
+        final ErrorObject error = new ErrorObject(
+            "invalid_request",
+            "Unsupported response_type value",
+            400,
+            null);
+        
+        return new AuthenticationErrorResponse(
+            redirectURI,
+            error,
+            state,
+            ResponseMode.QUERY);
+
+    }
+    
+    
+    /**
+     * Encodes an OIDC response into the query string of a redirect URI embedded in a remoted message.
+     * 
+     * @param response OIDC response to encode
+     * 
+     * @return input object suitable for token consumer flow
+     * 
+     * @throws IOException on error 
+     */
+    @Nonnull protected DDF buildRemotedQueryStringResponse(@Nonnull final AuthorizationResponse response) {                
+
+        final DDF obj = new DDF(null).structure();
+        obj.addmember(ConsumerConstants.BASE_URL).unsafe_string(TestConstants.RESOURCE_URL.getBytes(StandardCharsets.UTF_8));
+        final DDF http = obj.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();
+        
+        http.addmember(RemotedHttpServletRequest.METHOD).string("GET");
+        http.addmember(RemotedHttpServletRequest.REMOTE_ADDR).string("192.168.1.1");
+        http.addmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string(TestConstants.RESPONSE_URL.getBytes(StandardCharsets.UTF_8));
+        final URI responseURI = response.toURI();
+        http.addmember(RemotedHttpServletRequest.QUERY_STRING).string(Constraint
+                .isNotNull(responseURI.getQuery(), "Query string is null"));
+        return obj;
+    }
+    
+    /**
+     * Encodes an OIDC response into a form_post embedded in a remoted message.
+     * 
+     * @param response OIDC response to encode
+     * 
+     * @return input object suitable for token consumer flow
+     * 
+     * @throws IOException on error 
+     */
+    @Nonnull protected DDF buildRemotedFormPostResponse(@Nonnull final AuthorizationResponse response) {                
+
+        final DDF obj = new DDF(null).structure();
+        obj.addmember(ConsumerConstants.BASE_URL).unsafe_string(TestConstants.RESOURCE_URL.getBytes(StandardCharsets.UTF_8));
+        final DDF http = obj.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();
+        
+        http.addmember(RemotedHttpServletRequest.METHOD).string("POST");
+        http.addmember(RemotedHttpServletRequest.REMOTE_ADDR).string("192.168.1.1");
+        http.addmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string(
+                TestConstants.RESPONSE_URL.getBytes(StandardCharsets.UTF_8));
+        http.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("application/x-www-form-urlencoded");
+        if (response instanceof final AuthenticationSuccessResponse successResponse) {
+            http.addmember(RemotedHttpServletRequest.BODY).unsafe_string(Constraint.isNotNull(URISupport.buildQuery(
+                    CollectionSupport.listOf(new Pair<>("code", successResponse.getAuthorizationCode().getValue()), 
+                            new Pair<>("state", response.getState().getValue()))), "Query string is null").getBytes(StandardCharsets.UTF_8));
+        }
+        
+        return obj;
+    }
+
+}
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
index 4595c27..8cf10fd 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCSessionInitiatorFlowTest.java
@@ -34,7 +34,6 @@ import org.apache.hc.core5.http.io.HttpClientResponseHandler;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.mockito.Mockito;
 import org.opensaml.messaging.decoder.MessageDecodingException;
-import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.io.ClassPathResource;
@@ -67,7 +66,6 @@ import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
 import net.shibboleth.sp.oidc.profile.OIDCInitiatorConstants;
 import net.shibboleth.sp.profile.InitiatorConstants;
 import net.shibboleth.sp.profile.SPConstants;
-import net.shibboleth.sp.profile.impl.IssueCorrelationCookie;
 
 @ContextConfiguration(
         locations = {
@@ -162,7 +160,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();        
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL);       
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -197,26 +195,6 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         assertNotNull(request.getCodeChallengeMethod().compareTo(CodeChallengeMethod.S256));
     }
     
-    /**
-     * Basic flow test but where the resource URL is not supplied.
-     * 
-     * @throws IOException on error
-     * @throws MessageDecodingException 
-     */
-    @Test
-    public void testFailure_NoTarget() throws IOException, MessageDecodingException {
-        setDefaultAuth();
-        
-        final DDF input = new DDF(null).structure();
-        input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();        
-        input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);     
-        setApplicationRequest("test-oidc-application-with-ro", input);
-
-        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        assertFlowExecutionResult(result, FLOW_ID);
-        assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
-    }
-    
     /**
      * Basic flow test which uses the default profile configuration.
      * 
@@ -255,7 +233,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL);     
         input.addmember(SPConstants.STATE).string("state");
-        setApplicationRequest("test-oidc-application-without-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -278,7 +256,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.FORCE_AUTHN).integer(1);
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -303,7 +281,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.MAX_AGE).longinteger(60l);
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -327,7 +305,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.PROMPT).string("none");
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -354,7 +332,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         aclist.add(new DDF(null).string("loa1"));
         aclist.add(new DDF(null).string("loa2"));
         
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -423,7 +401,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         resourceList.add(new DDF(null).string("https://cal.example.com"));
         resourceList.add(new DDF(null).string("https://mail.example.com"));
         
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -449,7 +427,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.SCOPE).string("email");
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -473,7 +451,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.UI_LOCALES).string("fr-CA fr en");
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -498,7 +476,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
         input.addmember(SPConstants.TARGET).unsafe_string(RESOURCE_URL); 
         input.addmember(OIDCInitiatorConstants.DISPLAY).string("page");
-        setApplicationRequest("test-oidc-application-with-ro", input);
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
@@ -580,7 +558,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
                 if ("Set-Cookie".equals(header.name())) {
                     final String cookie = header.string();
                     assert cookie != null;
-                    if (cookie.startsWith("__Host-" + IssueCorrelationCookie.DEFAULT_COOKIE_PREFIX)) {
+                   // if (cookie.startsWith("__Host-" + IssueCorrelationCookie.DEFAULT_COOKIE_PREFIX)) {
                         //TODO our use of the correlation cookie
                         
 //                        final Boolean passive = authnRequest.isPassive();
@@ -589,7 +567,7 @@ public class OIDCSessionInitiatorFlowTest extends AbstractSPFlowTest {
 //                                "__Host-_shibsp_req_" + input.getmember(SPConstants.STATE).string() + passiveDelim + authnRequest.getID()
 //                                    + "; HttpOnly=true; Path=/; SameSite=None; Secure=true");
                         foundCorrelationCookie = true;
-                    }
+                   // }
                 }
             }
            //TODO Not Set yet:  Assert.assertTrue(foundCorrelationCookie);
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
index 479f0a2..26f52a2 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowTest.java
@@ -14,89 +14,35 @@
 
 package net.shibboleth.sp.oidc.flows;
 
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.fail;
-
 import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.text.ParseException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
-import org.apache.hc.client5.http.classic.HttpClient;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.io.HttpClientResponseHandler;
-import org.apache.hc.core5.http.io.entity.StringEntity;
-import org.apache.hc.core5.http.protocol.HttpContext;
-import org.mockito.Mockito;
 import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.io.ClassPathResource;
+import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.web.WebAppConfiguration;
 import org.springframework.webflow.executor.FlowExecutionResult;
-import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.nimbusds.jose.EncryptionMethod;
-import com.nimbusds.jose.JOSEException;
-import com.nimbusds.jose.JWEAlgorithm;
-import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jwt.JWT;
-import com.nimbusds.jwt.JWTClaimsSet;
-import com.nimbusds.oauth2.sdk.AuthorizationCode;
-import com.nimbusds.oauth2.sdk.AuthorizationResponse;
-import com.nimbusds.oauth2.sdk.ErrorObject;
 import com.nimbusds.oauth2.sdk.ResponseMode;
-import com.nimbusds.oauth2.sdk.TokenErrorResponse;
-import com.nimbusds.oauth2.sdk.TokenResponse;
-import com.nimbusds.oauth2.sdk.id.State;
-import com.nimbusds.oauth2.sdk.token.AccessToken;
-import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
-import com.nimbusds.oauth2.sdk.token.RefreshToken;
-import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
-import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
-import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
-import com.nimbusds.openid.connect.sdk.UserInfoSuccessResponse;
-import com.nimbusds.openid.connect.sdk.claims.UserInfo;
-import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
-import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
 
-import net.minidev.json.JSONObject;
 import net.shibboleth.idp.spring.IdPPropertiesApplicationContextInitializer;
 import net.shibboleth.idp.test.PreferFileSystemApplicationContextInitializer;
-import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.net.URISupport;
-import net.shibboleth.sp.context.AgentRequestContext;
 import net.shibboleth.sp.ddf.DDF;
-import net.shibboleth.sp.flows.AbstractSPFlowTest;
-import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
-import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
 import net.shibboleth.sp.oidc.testing.TestHelper;
-import net.shibboleth.sp.profile.ConsumerConstants;
 
 /**
- * Unit test for the SP session-initiator flow.
+ * Unit test for the SP token-consumer flow.
+ * 
+ * <p>Warning, each test method dirties the context, and Spring will throw away everything and re-initialise
+ * the tests. This allows for things like replay caches to work correctly, at the expense of test execution performance
+ * </p>
  */
 @ContextConfiguration(
         locations = {
@@ -110,19 +56,9 @@ import net.shibboleth.sp.profile.ConsumerConstants;
         )
 @WebAppConfiguration
 @SuppressWarnings({ "unchecked", "rawtypes", "null" })
-public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {    
-    
-    /** Dummy signing key of the dummy OP we are testing against. */
-    @Autowired @Qualifier("dummy.op.signing.Credential") protected JWKCredential opSigningCredential;
-    
-    /** Dummy encryption key of the RP/SP. */
-    @Autowired @Qualifier("dummy.sp.encryption.Credential") protected JWKCredential rpEncryptionCredential;
-    
-    /** The mocked HttpClient to use when responding to Token and UserInfo requests.*/
-    private HttpClient httpClient;
+ at DirtiesContext(classMode  = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+public class OIDCTokenConsumerFlowTest extends AbstractOIDCTokenConsumerFlowTest {    
     
-    /** The OP metadata to use.*/
-    private OIDCProviderMetadata metadata;
 
     /** Constructor. */
     public OIDCTokenConsumerFlowTest() {
@@ -134,23 +70,10 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
      *  
      * @throws Exception on error
      */
+    @Override
     @BeforeMethod
     public void beforeMethod() throws Exception{
-        setDefaultAuth();
-        final ApplicationContext applicationContext2 = applicationContext;
-        // Check the mock HTTP client has been defined
-        if (applicationContext2 != null) {
-            httpClient = applicationContext2.getBean("Mock.HttpClient", HttpClient.class);
-        } else {
-            fail("Mocked Http Client could not be found");
-        }
-        if (httpClient == null) {
-            fail("Mocked Http Client could not be found");
-        }
-        // Add a default metadata response
-        final var metadataFromFile = new ClassPathResource("metadata/openid-configuration.json");
-        final String json = new String(metadataFromFile.getInputStream().readAllBytes(), StandardCharsets.UTF_8);        
-        metadata = OIDCProviderMetadata.parse(JSONObjectUtils.parse(json));    
+        super.beforeMethod();
         
     }
     
@@ -168,18 +91,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                                TestConstants.AUTHENTICATION_RFP));        
+                        TestConstants.STATE_TOKEN);        
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -207,18 +126,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.FORM_POST, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                                TestConstants.AUTHENTICATION_RFP));        
+                        TestConstants.STATE_TOKEN);        
         final DDF input = buildRemotedFormPostResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID_POST_RESPONSE_MODE,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID_POST_RESPONSE_MODE, input);
 
@@ -245,18 +160,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                                TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID_PRIVATE_KEY_JWT,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         // Use application which requires private_key_jwt
         setApplicationRequest(TestConstants.APPLICATION_ID_PRIVATE_KEY_JWT, input);
@@ -284,18 +195,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                                TestConstants.AUTHENTICATION_RFP));   
+                        TestConstants.STATE_TOKEN);   
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN,
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null)));
+                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -322,18 +229,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));        
+                        TestConstants.STATE_TOKEN);        
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN,
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null)));
+                TestConstants.buildAuthenticationState(Duration.ofMinutes(1), true, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -358,18 +261,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -396,18 +295,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -435,18 +330,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1"))));
+                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1")), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -476,18 +367,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1"))));
+                TestConstants.buildAuthenticationState(null, false, CollectionSupport.listOf("loa1")), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -515,18 +402,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -552,18 +435,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP)); 
+                        TestConstants.STATE_TOKEN); 
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -589,18 +468,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP)); 
+                        TestConstants.STATE_TOKEN); 
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -622,18 +497,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
 
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP));         
+                        TestConstants.STATE_TOKEN);         
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
         
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
         final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
@@ -656,18 +527,14 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP)); 
+                        TestConstants.STATE_TOKEN); 
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN,
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -682,27 +549,23 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
     }
     
     /**
-     * Test failure, the RFP value in the returned state does not match that stored in the 
-     * correlation cookie.
+     * Test failure, the OAuth 2.0 state value does match to a state cookie which provides CSRF protection alongside
+     * the authentication state. 
      * 
      * @throws IOException on error
      */
     @Test
-    public void testFail_RequestForgeryProtectionValueMisMatch() throws Exception {
+    public void testFail_StateValueMisMatch() throws Exception {
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                        TestConstants.AUTHENTICATION_RFP)); 
+                        "different-state"); 
         final DDF input = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                "bad-rfp", //this is a bad rfp value in the cookie
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -712,38 +575,7 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         final DDF output = assertOutputMessageEvent(result, EventIds.MESSAGE_PROC_ERROR);
         System.out.println("test output: " + output.toString());
     }
-    
-    /**
-     * Test failure: the correlation cookie state token in the OAuth state parameter does not reference the 
-     * correlation cookie. Hence the correlation cookie can not be recovered, which is a fatal error.
-     * 
-     * @throws IOException on error
-     */
-    @Test
-    public void testFail_CorrelationCookieMismatch() throws Exception {
-        final AuthenticationSuccessResponse response = 
-                buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.AUTHENTICATION_STATE_WRONG, 
-                                TestConstants.AUTHENTICATION_RFP)); 
-        final DDF input = buildRemotedQueryStringResponse(response);
-        
-        // Add cookies
-        input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
-                TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
-                TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP, 
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
-
-        setApplicationRequest(TestConstants.APPLICATION_ID, input);
-
-        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
-        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
-        assertFlowExecutionOutcome(result.getOutcome());
-        assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
-    }
-   
+      
     
     /**
      * Test error response from the OP.
@@ -752,18 +584,13 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testFail_ErrorResponse() throws Exception {
-        final DDF input = buildRemotedQueryStringResponse(buildErrorResponse(
-                TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_RFP)));
+        final DDF input = buildRemotedQueryStringResponse(buildErrorResponse(TestConstants.STATE_TOKEN));
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -777,24 +604,23 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
     
     /**
      * Test error response from OP followed by a replayed success response. It should fail as the state has been
-     * replayed.
+     * replayed. That is, if the state originally sent that resulted in an error from the OP, where added to a 
+     * forged success response, it will not be allowed by the SP even if it matches to the one in stored state (a cookie
+     * for example).
      * 
      * @throws IOException on error
      */
-    //@Test
+    @Test
     public void testFail_ErrorResponse_FollowedByReplayedSuccess() throws Exception {
-        final DDF input = buildRemotedQueryStringResponse(buildErrorResponse(
-                TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_RFP)));
+        
+        // First create a failed response
+        final DDF input = buildRemotedQueryStringResponse(buildErrorResponse(TestConstants.STATE_TOKEN));
         
         // Add cookies
         input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));
+                TestConstants.buildAuthenticationState(null, false, null), true));
 
         setApplicationRequest(TestConstants.APPLICATION_ID, input);
 
@@ -805,23 +631,19 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
         System.out.println("DDF: "+output.toString());        
         validateCookiesAreUnset(output);
         
-        // Now construct a valid response, as the state is re-used this should fail early, and will not need the
-        // mocked endpoints etc.
+        // Now construct a valid response using the state sent in the failed response, as the state is re-used this 
+        // should fail early.
         
         final AuthenticationSuccessResponse response = 
                 buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
-                        TestConstants.buildOAuthStateString(TestConstants.STATE_TOKEN, 
-                                TestConstants.AUTHENTICATION_RFP)); 
+                        TestConstants.STATE_TOKEN); 
         final DDF inputSuccessResponse = buildRemotedQueryStringResponse(response);
         
         // Add cookies
         inputSuccessResponse.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
                 TestConstants.STATE_TOKEN, 
-                TestConstants.AUTHENTICATION_STATE_TOKEN,
                 TestConstants.APPLICATION_ID,
-                TestConstants.AUTHENTICATION_RFP,
-                TestConstants.TARGET_URL,
-                TestConstants.buildAuthenticationState(null, false, null)));       
+                TestConstants.buildAuthenticationState(null, false, null), true));       
         
         setApplicationRequest(TestConstants.APPLICATION_ID, inputSuccessResponse);
         
@@ -829,411 +651,11 @@ public class OIDCTokenConsumerFlowTest extends AbstractSPFlowTest {
                 flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
         assertFlowExecutionResult(resultReplayFail, TestConstants.FLOW_ID);
         assertFlowExecutionOutcome(resultReplayFail.getOutcome());
-        // TODO change to appropriate for replay error
-        assertOutputMessageEvent(resultReplayFail, EventIds.ACCESS_DENIED);
         System.out.println("DDF: "+output.toString());    
+        assertOutputMessageEvent(resultReplayFail, EventIds.MESSAGE_PROC_ERROR);        
         validateCookiesAreUnset(output);
         
     }
   
     
-    /**
-     * Construct a successful OIDC token response with signed id_token.
-     * 
-     * @param expiry expiry time
-     * @param issuedAt issue time
-     * @param nonce the nonce
-     * @return the tokens
-     * @throws ParseException 
-     * @throws JOSEException 
-     */
-    private OIDCTokenResponse constructSuccessfulTokenResponseSigned(
-            @Nonnull final Instant expiry, @Nonnull final Instant issuedAt, @Nullable final String nonce, 
-            @Nullable final Map<String, Object> additionalClaims) 
-                    throws JOSEException, ParseException {
-
-         final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
-                .subject("jdoe")
-                .issuer(TestConstants.ISSUER)
-                .audience(TestConstants.CLIENT_ID)
-                .expirationTime(Date.from(expiry))
-                    .issueTime(Date.from(issuedAt));
-        
-         if (nonce != null) {
-             claimsSetBuilder.claim("nonce", nonce);
-         }
-         if (additionalClaims != null) {
-             additionalClaims.entrySet()
-                 .stream()
-                 .forEach(entry -> claimsSetBuilder.claim(entry.getKey(), entry.getValue()));
-         }
-         
-         final JWTClaimsSet claimsSet = claimsSetBuilder.build();
-
-         
-         final JWT signedIdToken = 
-                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
-         
-         final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
-         final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
-         return new OIDCTokenResponse((new OIDCTokens(signedIdToken, accessToken, refreshToken)));
-    }
-    
-    /**
-     * Construct an error OIDC token response.
-     * 
-     * @param code the error code
-     * @param description the error description
-     * @param httpCode the http code to return
-     */
-    private TokenErrorResponse constructErrorTokenResponseSigned(
-            final String code, final String description, final int httpCode) 
-                    throws JOSEException, ParseException {
-        
-        final ErrorObject error = new ErrorObject(code, description, httpCode);
-        final TokenErrorResponse errorResponse = new TokenErrorResponse(error);
-
-        return errorResponse;
-    }
-
-    /**
-     * Construct a successful OIDC token response with a signed and encrypted id_token.
-     * 
-     * @param expiry expiry time
-     * @param issuedAt issue time
-     * @param nonce the nonce
-     * @return the tokens
-     * @throws ParseException 
-     * @throws JOSEException 
-     */
-    private OIDCTokenResponse constructSuccessfulTokenResponseSignedAndEncrypted(
-            @Nonnull final Instant expiry, @Nonnull final Instant issuedAt, @Nullable final String nonce,
-            @Nullable final Map<String, Object> additionalClaims) 
-                    throws JOSEException, ParseException {
-
-         final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
-                 .subject("jdoe")
-                 .issuer(TestConstants.ISSUER)
-                 .audience(TestConstants.CLIENT_ID)
-                 .expirationTime(Date.from(expiry))
-                     .issueTime(Date.from(issuedAt));
-         
-         if (nonce != null) {
-             claimsSetBuilder.claim("nonce", nonce);
-         }
-         if (additionalClaims != null) {
-             additionalClaims.entrySet()
-                 .stream()
-                 .forEach(entry -> claimsSetBuilder.claim(entry.getKey(), entry.getValue()));
-         }
-         final JWTClaimsSet claimsSet = claimsSetBuilder.build();
-         
-         final JWT encryptedIdToken = 
-                 TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, JWEAlgorithm.RSA_OAEP_256, 
-                         EncryptionMethod.A128CBC_HS256, opSigningCredential, rpEncryptionCredential);
-         
-         final AccessToken accessToken = new BearerAccessToken("fake-access-token-value", 3600, null);
-         final RefreshToken refreshToken = new RefreshToken("fake-refresh-token-value");
-         return new OIDCTokenResponse((new OIDCTokens(encryptedIdToken, accessToken, refreshToken)));
-    }
-    
-    /**
-     * Construct a successful OIDC user info response in JSON format. application/json.
-     * 
-     * @return the user info response
-     */
-    private UserInfoSuccessResponse constructJSONUserInfoResponse() {
-        final JSONObject json = new JSONObject();
-        json.appendField("sub", "jdoe");
-        json.appendField("name", "John Doe");
-        json.appendField("nickname" , "John");
-        return  new UserInfoSuccessResponse(new UserInfo(json));
-    }
-    
-    /**
-     * Construct a successful OIDC user info response in JWT format, signed.
-     * 
-     * @return the user info response
-     * @throws ParseException 
-     * @throws JOSEException 
-     */
-    private UserInfoSuccessResponse constructJWTUserInfoResponseSigned() throws JOSEException, ParseException {
-        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
-                .subject("jdoe")
-                .issuer(TestConstants.ISSUER)
-                .audience(TestConstants.CLIENT_ID)
-                .claim("name", "John Doe")
-                .claim("nickname","John").build();
-       
-        final JWT signedUserInfoToken = 
-                TestHelper.createJWT(claimsSet, JWSAlgorithm.RS256, null, null, opSigningCredential, null);
-        return  new UserInfoSuccessResponse(signedUserInfoToken);
-    }
-    
-    /**
-     * Mock OIDC Token, UserInfo, and Keys endpoints. Returning the supplied responses. 
-     * Noting these mocks may not exercise the response decoders, as the responses are returned directly.
-     * 
-     * @param tokenResponse the token response
-     * @param userInfoResponse the user info response
-     * 
-     * @throws IOException on error.
-     */
-    private void mockOIDCEndpoints(final TokenResponse tokenResponse, 
-            final UserInfoSuccessResponse userInfoResponse) throws IOException {
-        
-        Mockito.when(httpClient.execute(
-                Mockito.argThat(req -> req != null && req.getRequestUri().toString()
-                .contains(".well-known/openid-configuration")),
-                Mockito.any(HttpContext.class),
-                Mockito.any(HttpClientResponseHandler.class)))
-            .thenReturn(metadata);
-        
-        Mockito.when(httpClient.execute(
-                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/token")),
-                Mockito.any(HttpContext.class),
-                Mockito.any(HttpClientResponseHandler.class)))
-            .thenReturn(tokenResponse);
-
-        Mockito.when(httpClient.execute(
-                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/userinfo")),
-                Mockito.any(HttpContext.class),
-                Mockito.any(HttpClientResponseHandler.class)))
-            .thenReturn(userInfoResponse);
-        
-        // Fetch from the keys endpoint mocking the call used by the RemoteJwkSetCache     
-        final String jwk = TestHelper.createJWKJSONStringFrom(opSigningCredential);
-        assertNotNull(jwk);
-        final ClassicHttpResponse classicHttpResponse = Mockito.mock(ClassicHttpResponse.class);
-        Mockito.when(classicHttpResponse.getCode()).thenReturn(200);
-        Mockito.when(classicHttpResponse.getEntity())
-               .thenReturn(new StringEntity("{\"keys\":["+jwk+"]}", ContentType.APPLICATION_JSON));
-
-        Mockito.when(httpClient.executeOpen(
-                Mockito.nullable(HttpHost.class),
-                Mockito.argThat(req -> req != null && req.getRequestUri().toString().contains("/keys")),
-                Mockito.any(HttpContext.class)))
-            .thenReturn(classicHttpResponse);
-    }
-    
-    /**
-     * Check that the output message contains an instruction to unset cookies using Max-Age=0, and specifically the 
-     * correlation cookie. 
-     * 
-     * @param output the DDF output
-     */
-    private void validateCookiesAreUnset(@Nullable final DDF output) {
-        if (output == null) {
-            fail("DDF output can not be null");
-        }
-        // Check cookies are unset
-        final DDF headers = output.getmember("http.headers");
-        // Check that any Set-Cookie is an unset, we do not set new cookies in the consumer flow
-        final AtomicBoolean correlationCookieUnset = new AtomicBoolean(false);
-        if (headers.islist()) {
-            headers.forEach(header -> {
-                if (header.isstring() && "Set-Cookie".equals(header.name())){
-                    Assert.assertTrue(header.string().contains("Max-Age=0"),"Cookies must be unset");
-                }
-                // Specific check for the correlation cookie unset, which is required
-                if (header.string().contains(TestConstants.CORRELATION_COOKIE_PREFIX)) {
-                    Assert.assertTrue(header.string().contains("Max-Age=0"),"Correlation cookies must be unset");
-                    correlationCookieUnset.set(true);
-                }
-            });
-        }
-        Assert.assertTrue(correlationCookieUnset.get(),"Correlation cookies must be unset");
-    }
-    
-    /**
-     * Decode an encoded response and run assertion checks against it.
-     * 
-     * @param result
-     *            flow execution result
-     * @param claimNames
-     *            set of claims to check for
-     * @param resource
-     *            resource URL used in final redirect
-     * 
-     * @return the output object
-     */
-    @Nonnull private DDF validateOutputMessage(@Nonnull final FlowExecutionResult result,
-            @Nullable final Set<String> claimNames, @Nullable final String resource) {
-        
-        final ProfileRequestContext prc = retrieveProfileRequestContext(result);
-        assert prc != null;
-        final AgentRequestContext arc = prc.ensureSubcontext(AgentRequestContext.class);
-        final DDF output = arc.getOutput();
-        
-        assert output != null;
-        Assert.assertTrue(output.isstruct());
-        final DDF http = output.getmember(RemotedHttpServletRequest.STRUCTURE_NAME);
-        Assert.assertTrue(http.isstruct());
-        
-        final byte[] redirect = http.getmember(RemotedHttpServletResponse.REDIRECT).unsafe_string();
-        Assert.assertEquals(resource != null ? resource.getBytes(StandardCharsets.UTF_8) : null,  redirect);
-        
-        final Set<String> mutableIds = new HashSet<>(claimNames);
-        for (final DDF attr : output.getmember(ConsumerConstants.SESSION_ATTRIBUTES).asList()) {
-            Assert.assertTrue(mutableIds.contains(attr.name()));
-            mutableIds.remove(attr.name());
-            if ("mail".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
-                        CollectionSupport.singletonList("jdoe at example.org"));
-            } else if ("displayName".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(DDF::string).toList(),
-                        CollectionSupport.singletonList("John Doe"));
-            } else if ("eduPersonScopedAffiliation".equals(attr.name())) {
-                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("value").string();}).toList(),
-                        CollectionSupport.listOf("member"));
-                Assert.assertEquals(attr.asList().stream().map(ddf -> {return ddf.getmember("scope").string();}).toList(),
-                        CollectionSupport.listOf("example.org"));
-            }
-        }
-        Assert.assertTrue(mutableIds.isEmpty());
-        
-        validateCookiesAreUnset(output);
-        //TODO ADD BACK
-        
-//        try {
-//            final DDF s = output.getmember(ConsumerConstants.SESSION_OPAQUE);
-//            assert s != null;
-//            Assert.assertTrue(s.isstruct());
-//            final DDF nameIdDdf = s.getmember("nameID");
-//            Assert.assertTrue(nameIdDdf.isstring());
-//            final String nameIdString = nameIdDdf.string();
-//            assert nameIdString != null;
-//            final byte[] opaque = Base64Support.decodeURLSafe(nameIdString);
-//            try (final ByteArrayInputStream in = new ByteArrayInputStream(opaque)) {
-//                final XMLObject obj = XMLObjectSupport.unmarshallFromInputStream(parserPool, in);
-//                if (obj instanceof final NameID nameID) {
-//                    Assert.assertEquals(nameID.getValue(), "jdoe at example.org");
-//                    Assert.assertEquals(nameID.getFormat(), NameIDType.EMAIL);
-//                    Assert.assertEquals(nameID.getSPProvidedID(), sessionIndex);
-//                } else {
-//                    Assert.fail("Session data was not a NameID");
-//                }
-//            }
-//        } catch (final DecodingException|IOException|UnmarshallingException|XMLParserException e) {
-//            Assert.fail(e.getMessage());
-//        }
-        
-        return output;
-        
-    }
-
-
-    /**
-     * Builds a dummy OIDC authorization code response.
-     * 
-     * @param redirectURI
-     *            redirect URI
-     * @param mode
-     *            response mode
-     * 
-     * @return authentication success response for the token consumer flow
-     */
-    private AuthenticationSuccessResponse buildOIDCAuthorizationCodeResponse(
-            @Nonnull final String redirect, @Nonnull final ResponseMode mode, final String stateString) {
-
-        final AuthorizationCode code = new AuthorizationCode("dummy-auth-code");
-        final State state = new State(stateString);
-        URI redirectURI = null;
-        try {
-            redirectURI = new URI(redirect);
-        } catch (final URISyntaxException e) {
-            fail("Bad redirect URI", e);
-        }
-
-        // Build a dummy success response (authorization code)
-        return new AuthenticationSuccessResponse(
-            redirectURI,
-            code, 
-            null, 
-            null,            
-            state, 
-            null,            
-            mode 
-        );
-    }
-    
-    /**
-     * Build a invalid_request error response, from openID connect core.
-     * 
-     * @return the error response
-     */
-    private AuthenticationErrorResponse buildErrorResponse(final String stateString) {
-
-        final URI redirectURI = URI.create(TestConstants.RESPONSE_URL);
-        final State state = new State(stateString);
-        
-        final ErrorObject error = new ErrorObject(
-            "invalid_request",
-            "Unsupported response_type value",
-            400,
-            null);
-        
-        return new AuthenticationErrorResponse(
-            redirectURI,
-            error,
-            state,
-            ResponseMode.QUERY);
-
-    }
-    
-    
-    /**
-     * Encodes an OIDC response into the query string of a redirect URI embedded in a remoted message.
-     * 
-     * @param response OIDC response to encode
-     * 
-     * @return input object suitable for token consumer flow
-     * 
-     * @throws IOException on error 
-     */
-    @Nonnull private DDF buildRemotedQueryStringResponse(@Nonnull final AuthorizationResponse response) {                
-
-        final DDF obj = new DDF(null).structure();
-        obj.addmember(ConsumerConstants.BASE_URL).unsafe_string(TestConstants.RESOURCE_URL.getBytes(StandardCharsets.UTF_8));
-        final DDF http = obj.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();
-        
-        http.addmember(RemotedHttpServletRequest.METHOD).string("GET");
-        http.addmember(RemotedHttpServletRequest.REMOTE_ADDR).string("192.168.1.1");
-        http.addmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string(TestConstants.RESPONSE_URL.getBytes(StandardCharsets.UTF_8));
-        final URI responseURI = response.toURI();
-        http.addmember(RemotedHttpServletRequest.QUERY_STRING).string(Constraint
-                .isNotNull(responseURI.getQuery(), "Query string is null"));
-        return obj;
-    }
-    
-    /**
-     * Encodes an OIDC response into a form_post embedded in a remoted message.
-     * 
-     * @param response OIDC response to encode
-     * 
-     * @return input object suitable for token consumer flow
-     * 
-     * @throws IOException on error 
-     */
-    @Nonnull private DDF buildRemotedFormPostResponse(@Nonnull final AuthorizationResponse response) {                
-
-        final DDF obj = new DDF(null).structure();
-        obj.addmember(ConsumerConstants.BASE_URL).unsafe_string(TestConstants.RESOURCE_URL.getBytes(StandardCharsets.UTF_8));
-        final DDF http = obj.addmember(RemotedHttpServletRequest.STRUCTURE_NAME).structure();
-        
-        http.addmember(RemotedHttpServletRequest.METHOD).string("POST");
-        http.addmember(RemotedHttpServletRequest.REMOTE_ADDR).string("192.168.1.1");
-        http.addmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string(
-                TestConstants.RESPONSE_URL.getBytes(StandardCharsets.UTF_8));
-        http.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("application/x-www-form-urlencoded");
-        if (response instanceof final AuthenticationSuccessResponse successResponse) {
-            http.addmember(RemotedHttpServletRequest.BODY).unsafe_string(Constraint.isNotNull(URISupport.buildQuery(
-                    CollectionSupport.listOf(new Pair<>("code", successResponse.getAuthorizationCode().getValue()), 
-                            new Pair<>("state", response.getState().getValue()))), "Query string is null").getBytes(StandardCharsets.UTF_8));
-        }
-        
-        return obj;
-    }
-    
-    
 }
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowUsingStorageServiceTest.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowUsingStorageServiceTest.java
new file mode 100644
index 0000000..2a60bd8
--- /dev/null
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/OIDCTokenConsumerFlowUsingStorageServiceTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.flows;
+
+import static org.testng.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+
+import org.opensaml.storage.StorageService;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.webflow.executor.FlowExecutionResult;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+
+import net.shibboleth.idp.spring.IdPPropertiesApplicationContextInitializer;
+import net.shibboleth.idp.test.PreferFileSystemApplicationContextInitializer;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.impl.StorageServiceStateManager;
+
+/**
+ * Unit test for the SP token-consumer flow but using the storage service to handle state, as initialised by the
+ * context initializer.
+ * 
+ * <p>Requires interaction with the storage service to add the state data, and then the flow should be able to retrieve. 
+ * The flow is configured to use the storage service in the context initializer, and the test adds the state data to 
+ * the storage service directly in the pre-test setup method.</p>
+ * 
+ * <p>Warning, each test method dirties the context, and Spring will throw away everything and re-initialise
+ * the tests. This allows for things like replay caches to work correctly, at the expense of test execution performance
+ * </p>
+ */
+ at ContextConfiguration(
+        locations = {
+                "classpath:/net/shibboleth/sp/oidc-test-beans.xml", },
+        initializers = {
+                TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService.class,
+                PreferFileSystemApplicationContextInitializer.class,
+                IdPPropertiesApplicationContextInitializer.class
+                },
+        inheritInitializers = false
+        )
+ at WebAppConfiguration
+ at SuppressWarnings({ "unchecked", "rawtypes", "null" })
+ at DirtiesContext(classMode  = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+public class OIDCTokenConsumerFlowUsingStorageServiceTest extends AbstractOIDCTokenConsumerFlowTest {    
+
+
+    /** Constructor. */
+    public OIDCTokenConsumerFlowUsingStorageServiceTest() {
+        super(TestConstants.FLOW_ID);
+    }
+    
+    /** 
+     * Pre-test work.
+     *  
+     * @throws Exception on error
+     */
+    @Override
+    @BeforeMethod
+    public void beforeMethod() throws Exception{
+        super.beforeMethod();
+        
+        // Grab the storage service being using
+        final StorageService storageService = 
+                applicationContext.getBean("shibboleth.StorageService", StorageService.class);
+        final ObjectMapper om = applicationContext.getBean("shibboleth.JSONObjectMapper", ObjectMapper.class);
+        
+
+        assertNotNull(storageService, "StorageService bean could not be accessed");
+        assertNotNull(om, "JSONObjectMapper bean could not be accessed");
+
+        // Add an authentication record to the storage service
+        final AuthenticationRequestStateData stateData = 
+                TestConstants.buildAuthenticationRequestStateData(null, false, null);
+        
+        final String serializedState = om.writeValueAsString(stateData);
+        final String encoded = Base64Support.encode(serializedState.getBytes(StandardCharsets.UTF_8), false);
+        
+        //TODO this is brittle
+        final StringBuilder builder = new StringBuilder(StorageServiceStateManager.class.getName());
+        builder.append('!').append("testsp.example.org").append('!').append(TestConstants.APPLICATION_ID);
+        final String context = builder.toString();
+        storageService.create(context, 
+                TestConstants.STATE_COOKIE_STORAGE_KEY, encoded, Instant.now().plus(Duration.ofMinutes(30)).toEpochMilli());
+        
+    }
+    
+
+    /**
+     * Test successful flow with a signed id_token and a plain user info response.
+     * 
+     * @throws IOException on error
+     */
+    @Test
+    public void testSuccess_SignedIDToken_PlainUserInfo() throws Exception {
+        
+        mockOIDCEndpoints(constructSuccessfulTokenResponseSigned(Instant.now().plusSeconds(3600), Instant.now(), null, null), 
+                constructJSONUserInfoResponse());
+
+        final AuthenticationSuccessResponse response = 
+                buildOIDCAuthorizationCodeResponse(TestConstants.RESPONSE_URL, ResponseMode.QUERY, 
+                        TestConstants.STATE_TOKEN);        
+        final DDF input = buildRemotedQueryStringResponse(response);
+        
+        // Add cookies
+        input.addmember("http.headers.Cookie").unsafe_string(TestConstants.buildCookieHeader(
+                TestConstants.STATE_TOKEN, 
+                TestConstants.APPLICATION_ID,
+                TestConstants.STATE_COOKIE_STORAGE_KEY, false));
+        
+        setApplicationRequest(TestConstants.APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(TestConstants.FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, TestConstants.FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageSuccess(result);
+        assert output != null;
+        System.out.println("test output: " + output.toString());
+        validateOutputMessage(result, CollectionSupport.setOf("sub","mail","displayName","eduPersonScopedAffiliation"),
+                TestConstants.RESOURCE_URL);
+    }
+  
+    
+}
\ No newline at end of file
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
index 480edda..2858279 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestConstants.java
@@ -16,7 +16,6 @@ package net.shibboleth.sp.oidc.flows;
 
 import static org.testng.Assert.fail;
 
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
@@ -34,15 +33,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.json.JsonMapper;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.nimbusds.jose.util.StandardCharset;
 
-import net.minidev.json.JSONObject;
-import net.minidev.json.JSONValue;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.codec.Base64Support;
-import net.shibboleth.shared.codec.EncodingException;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
-import net.shibboleth.sp.oidc.profile.OIDCConstants;
 
 /** A class to hold constants used for tests.*/
 public final class TestConstants {
@@ -67,23 +61,14 @@ public final class TestConstants {
     /** Resource URL. */
     @Nonnull public static final String RESOURCE_URL = "https://sp.example.org/secure";
     
-    /** 
-     * The request forgery protection nonce sent in the authentication request and part of the state returned in an 
-     * authentication response. Not the nonce in an id_token.
-     */
-    @Nonnull public static final String AUTHENTICATION_RFP = "f26d922221226cd183729bf13247dbe2";
-    
     /** The nonce in the ID token.*/
     @Nonnull public static final String ID_TOKEN_NONCE = "bd1b5f211250c57e";    
+    
+    /** The key to the storage record that gets held in the request state cookie set by the Storage Service.*/
+    @Nonnull public static final String STATE_COOKIE_STORAGE_KEY = "c516767a37f59fe32e049ac1cb1a8921";
 
     /** State token string used in cookie names. */
-    public static final String STATE_TOKEN = "1761316967710_1622a5c726da8f7b36e24f19eed82aea";
-    
-    /** Authentication state token used to reference the stored authentication state. */
-    public static final String AUTHENTICATION_STATE_TOKEN = "1761316967710_authnstate";
-    
-    /** State string used in cookie names. A state which should not match to the a correlation cookie. */
-    public static final String AUTHENTICATION_STATE_WRONG = "1761316967710_wrong";
+    public static final String STATE_TOKEN = "1761316967710_1622a5c726da8f7b36e24f19eed82aea";    
 
     /** Application ID used in cookie names. */
     public static final String APPLICATION_ID = "test-oidc-application-with-ro";
@@ -97,101 +82,73 @@ public final class TestConstants {
     /** Target URL encoded in cookie. */
     public static final String TARGET_URL = "https://sp.example.org/secure";
     
-    /** The defaulted prefix of the correlation cookie.*/
-    public static final String CORRELATION_COOKIE_PREFIX = "__Host-shibsp_req_";
-    
     /** The defaulted prefix for state tokens.*/
-    public static final String STATE_TOKEN_PREFIX = "__Host-shibsp_state__";
+    public static final String STATE_TOKEN_PREFIX = "__Host-shibsp_state_";   
+    
     
     /**
      * Build cookie header bytes for the given parameters. These take the form of:
      * <pre>
-     * __Host-_shibsp_req_{stateToken}={(authnStateToken, rfp)}; (correlation cookie)
-     * __Host-shibsp_state_{appId}_{stateToken}={targetURL};  (resource URL state)
-     * __Host-shibsp_state_{appId}_{authnStateToken}={authn_request_json};   (authentication state)
+     * __Host-shibsp_state_{appId}_{stateToken}={contents};
      * </pre>
      * 
-     * @param stateToken the token value used for mapping response to the stored correlation cookie and target URL
-     * @param authnStateToken the token value, held inside the 'req' correlation cookie, of the persisted authentication state.
+     * @param stateToken the token value used for mapping the OAuth 2.0 state to the cookie
      * @param appID the application ID
-     * @param rfp the request forgery protection nonce used to protect against CSRF in the request and response OAuth 
-     *                  state parameter not to be confused with the OIDC ID token nonce. Stored inside the correlation cookie
-     * @param targetURL the target URL to redirect to after processing
-     * @param authnRequestStateJSON the authentication request state JSON for recovery of important parameters
+     * @param stateCookieContents the contents of the state cookie, either the authentication request state JSON 
+     *                          or a key to the authentication request state in the storage manager
+     * @param urlEncode TODO
      * @return the cookie header bytes
      * @throws Exception on error
      */
-    public static byte[] buildCookieHeader(final String stateToken,
-            final String authnStateToken, final String appID, final String rfp, 
-            final String targetURL, final String authnRequestStateJSON) throws Exception {
+    public static byte[] buildCookieHeader(final String stateToken, final String appID, 
+            final String stateCookieContents, final boolean urlEncode) throws Exception {
         
-
-        final String targetUrlB64 = Base64Support.encodeURLSafe(targetURL.getBytes(StandardCharsets.UTF_8));
-        final String authnJsonEnc = Base64Support.encodeURLSafe(authnRequestStateJSON.getBytes(StandardCharsets.UTF_8));
+        String contentsEncoded = null;
+        if (urlEncode) {
+            contentsEncoded = 
+                    Base64Support.encodeURLSafe(stateCookieContents.getBytes(StandardCharsets.UTF_8));
+        } else {
+            contentsEncoded = stateCookieContents;
+        }
 
         // Compose the header with semicolons and spacing as in the example
         final StringBuilder sb = new StringBuilder();
-        sb.append(CORRELATION_COOKIE_PREFIX).append(stateToken).append('=')
-            .append(buildCorrelationCookieString(authnStateToken, rfp)).append(";\n");
+       
         sb.append(STATE_TOKEN_PREFIX).append(appID).append('_').append(stateToken)
-          .append('=').append(targetUrlB64).append("; \n");
-        sb.append(STATE_TOKEN_PREFIX).append(appID).append('_').append(authnStateToken).append('=').append(authnJsonEnc).append(";\n");
+          .append('=').append(contentsEncoded).append("; \n");
+       
         System.out.println(sb.toString());
         return sb.toString().getBytes("UTF-8");
         
     }
     
     /**
-     * Build the OAuth state string for the given state and RFP. This is used for the state parameter in the 
-     * authentication request, and contains important information about the authentication request.
+     * Build the authentication request state data for the given parameters. 
      * 
-     * @param state the state used to map to the correlation cookie bound to the browser session.
-     * @param rfp the request forgery protection nonce used to protect against CSRF in the request and response OAuth 
-     *          state parameter.
-     * @return the JSON serialized and Base64 encoded state string for the given parameters
-     */
-    public static String buildOAuthStateString(final String state, final String rfp) {
-        final JSONObject stateObject = new JSONObject();
-        stateObject.appendField(OIDCConstants.STATE_FIELD, state);
-        stateObject.appendField(OIDCConstants.RFP_FIELD,rfp);
-        final String stateJsonString = stateObject.toJSONString(JSONValue.COMPRESSION);
-        try {
-            final byte[] stateJsonAsBytes = stateJsonString.getBytes(StandardCharset.UTF_8);
-            assert stateJsonAsBytes != null;
-            return Base64Support.encodeURLSafe(stateJsonAsBytes);
-        } catch (final EncodingException e) {
-            fail(e.getMessage());
-            return null;
-        }
-        
-    }
-    
-    /**
-     * Build the correlation cookie value for the given authentication state token and RFP. The authnStateToken is used
-     * to recover the authentication state stored off in the state token manager. The RFP is used to protect against 
-     * CSRF in the authentication request and response, and should be matched to that present in the OAuth state 
-     * parameter. Both are stored in the cookie value, which is JSON serialized and Base64 encoded.
-     * 
-     * @param authnStateToken the token value used for recovery of the authentication state stored off in the state 
-     *          token manager.
-     * @param rfp the request forgery protection nonce used to protect against CSRF in the request and response
+     * @param maxAge the maximum age of the authentication request, used for validation of the authentication response
+     * @param authTimeRequired a flag whether the authentication time is required, used for validation of the authentication response
+     * @param acrs the ACRs to request, used for validation of the authentication response. May be null if no ACRs are requested.
      * @return
      */
-    public static String buildCorrelationCookieString(final String authnStateToken, final String rfp) {
-        final JSONObject stateObject = new JSONObject();
-        stateObject.appendField(OIDCConstants.AUTHN_STATE_FIELD, authnStateToken);
-        stateObject.appendField(OIDCConstants.RFP_FIELD,rfp);
-        final String stateJsonString = stateObject.toJSONString(JSONValue.COMPRESSION);
-        try {
-            final byte[] stateJsonAsBytes = stateJsonString.getBytes(StandardCharset.UTF_8);
-            assert stateJsonAsBytes != null;
-            return Base64Support.encodeURLSafe(stateJsonAsBytes);
-        } catch (final EncodingException e) {
-            fail(e.getMessage());
-            return null;
+    public static AuthenticationRequestStateData buildAuthenticationRequestStateData(
+            final Duration maxAge, final boolean authTimeRequired, 
+            @Nullable final List<String> acrs) {
+        final AuthenticationRequestStateData state = new AuthenticationRequestStateData()
+                .setNonce(ID_TOKEN_NONCE)            
+                .setMaxAge(maxAge)
+                .setAuthTimeRequired(authTimeRequired);        
+        state.setIssuer(CLIENT_ID)
+            .setAuthenticationAuthority(ISSUER)
+            .setResponseLocation(RESPONSE_URL)
+            .setRequestTime(Instant.now());
+        
+        if (acrs != null) {
+            state.setAcrs(acrs);
         }
+        return state;
     }
-    
+
+
     /**
      * Build the authentication request state JSON for the given parameters. This is used for the state value, and 
      * contains important information about the authentication request.
@@ -204,19 +161,8 @@ public final class TestConstants {
      */
     public static String buildAuthenticationState(final Duration maxAge, final boolean authTimeRequired, 
             @Nullable final List<String> acrs) throws URISyntaxException {
-        final AuthenticationRequestStateData state = new AuthenticationRequestStateData()
-                .setClientId(APPLICATION_ID)
-                .setNonce(ID_TOKEN_NONCE)
-                .setAuthenticationAuthority(ISSUER)
-                .setRedirectUri(new URI(RESPONSE_URL))
-                .setMaxAge(maxAge)
-                .setAuthTimeRequired(authTimeRequired)
-                .setAuthnRequestTime(Instant.now());
-        
-        if (acrs != null) {
-            state.setAcrs(acrs);
-        }
         
+        final var state = buildAuthenticationRequestStateData(maxAge, authTimeRequired, acrs);
         final ObjectMapper jsonMapper = JsonMapper.builder()
                 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                 .serializationInclusion(Include.NON_ABSENT)
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
index 2964fcd..9474543 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
@@ -39,8 +39,8 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("idp.home", "classpath:/net/shibboleth/idp/module");
         mock.setProperty("idp.webflows", "classpath*:/flows");
         mock.setProperty("sp.service.agents.resources", "test.sp.oidc.AgentResolverResources");
-        // Use cookie based state managment
-        mock.setProperty("sp.stateToken.Manager","shibboleth.sp.CookieStateTokenManager");
+        // Use cookie based state management
+        mock.setProperty("sp.stateToken.Manager","shibboleth.sp.CookieStateManager");
         // Use a mocked HTTP client
         mock.setProperty("sp.oidc.HttpClient","Mock.HttpClient");
         // Set agent auth to basic
@@ -49,14 +49,12 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("sp.oidc.resolveAttributes", "true");
         // Ensure ACR checks are performed
         mock.setProperty("sp.oidc.idtoken.validateAcrValue", "true");
-        // Turn off the sealing of authentication request state for tests
-        mock.setProperty("sp.oidc.sealAuthenticationState", "false");
-        // Turn off the sealing of the correlation cookie for tests
-        mock.setProperty("sp.oidc.sealCorrelationCookie", "false");
-        // Turn off the sealing of OAuth state for tests
-        mock.setProperty("sp.oidc.sealOAuthState", "false");
+        // Turn off the sealing state data for testing
+        mock.setProperty("sp.stateToken.sealed", "false");;
         // Create a basic default client secret
         mock.setProperty("sp.oidc.defaultClientSecret", "secret");
+        // Turn on the replay cache for testing
+        mock.setProperty("sp.stateToken.checkReplay", "true");
         mock.setProperty("idp.additionalProperties",
                 "/conf/ldap.properties, /conf/saml-nameid.properties, /conf/services.properties, /conf/admin/admin.properties, /conf/authn/authn.properties, /conf/c14n/subject-c14n.properties, /credentials/secrets.properties, /conf/sp/sp.properties, /conf/sp/oidc.properties,  /conf/sp/oidc-test.properties");
         applicationContext.getEnvironment().getPropertySources().addFirst(mock);
diff --git a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService.java
similarity index 75%
copy from sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
copy to sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService.java
index 2964fcd..11f7250 100644
--- a/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializer.java
+++ b/sp-oidc-conf-impl/src/test/java/net/shibboleth/sp/oidc/flows/TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService.java
@@ -22,16 +22,21 @@ import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.mock.env.MockPropertySource;
 
 import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.sp.flows.TestSPEnvironmentApplicationContextInitializer;
 
 /**
 /**
- * An {@link ApplicationContextInitializer} which extends the parent plugin's test initializer.
+ * An {@link ApplicationContextInitializer} which extends the parent plugin's test initializer. Specifically configures
+ * the storage service and cookie backed state manager.
+ * 
+ * TODO combine this and TestSPOIDCEnvironmentApplicationContextInitializer
  */
-public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEnvironmentApplicationContextInitializer {
+public class TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService 
+                                extends TestSPOIDCEnvironmentApplicationContextInitializer {
 
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(TestSPOIDCEnvironmentApplicationContextInitializer.class);
+    @Nonnull private final 
+    Logger log = LoggerFactory.getLogger(TestSPOIDCEnvironmentApplicationContextInitializerWithStorageService.class);
+    
 
     /** {@inheritDoc} */
     @Override public void initialize(@Nonnull final ConfigurableApplicationContext applicationContext) {
@@ -39,8 +44,8 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("idp.home", "classpath:/net/shibboleth/idp/module");
         mock.setProperty("idp.webflows", "classpath*:/flows");
         mock.setProperty("sp.service.agents.resources", "test.sp.oidc.AgentResolverResources");
-        // Use cookie based state managment
-        mock.setProperty("sp.stateToken.Manager","shibboleth.sp.CookieStateTokenManager");
+        // Use storage service and cookie based state managment 
+        mock.setProperty("sp.stateToken.Manager","shibboleth.sp.StorageAndCookieStateManager");
         // Use a mocked HTTP client
         mock.setProperty("sp.oidc.HttpClient","Mock.HttpClient");
         // Set agent auth to basic
@@ -49,14 +54,12 @@ public class TestSPOIDCEnvironmentApplicationContextInitializer extends TestSPEn
         mock.setProperty("sp.oidc.resolveAttributes", "true");
         // Ensure ACR checks are performed
         mock.setProperty("sp.oidc.idtoken.validateAcrValue", "true");
-        // Turn off the sealing of authentication request state for tests
-        mock.setProperty("sp.oidc.sealAuthenticationState", "false");
-        // Turn off the sealing of the correlation cookie for tests
-        mock.setProperty("sp.oidc.sealCorrelationCookie", "false");
-        // Turn off the sealing of OAuth state for tests
-        mock.setProperty("sp.oidc.sealOAuthState", "false");
+        // Turn off the sealing state data for testing
+        mock.setProperty("sp.stateToken.sealed", "false");
         // Create a basic default client secret
         mock.setProperty("sp.oidc.defaultClientSecret", "secret");
+        // Turn on the replay cache for testing
+        mock.setProperty("sp.stateToken.checkReplay", "true");
         mock.setProperty("idp.additionalProperties",
                 "/conf/ldap.properties, /conf/saml-nameid.properties, /conf/services.properties, /conf/admin/admin.properties, /conf/authn/authn.properties, /conf/c14n/subject-c14n.properties, /credentials/secrets.properties, /conf/sp/sp.properties, /conf/sp/oidc.properties,  /conf/sp/oidc-test.properties");
         applicationContext.getEnvironment().getPropertySources().addFirst(mock);
diff --git a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/sp.properties b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/sp.properties
index 0859911..f9ed4c9 100644
--- a/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/sp.properties
+++ b/sp-oidc-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/sp.properties
@@ -23,7 +23,7 @@ sp.service.agents.checkInterval = PT5M
 
 # Default state token management (SAML RelayState, etc.)
 #sp.stateToken.errorsFatal = false
-# Set to shibboleth.sp.CookieStateTokenManager to switch to cookie-based mechanism
+# Set to shibboleth.sp.CookieStateManager to switch to cookie-based mechanism
 #sp.stateToken.Manager = shibboleth.sp.StorageStateTokenManager
 # Controls storage back-end for storage-based state tokens
 #sp.stateToken.StorageService = shibboleth.StorageService
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/metadata/impl/OIDCProviderMetadataLookupHandler.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/metadata/impl/OIDCProviderMetadataLookupHandler.java
index 7fe7e6e..3afe327 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/metadata/impl/OIDCProviderMetadataLookupHandler.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/metadata/impl/OIDCProviderMetadataLookupHandler.java
@@ -80,7 +80,7 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
      */
     public void setContextClassLookupStrategy(
             @Nonnull final Function<MessageContext, ? extends AbstractOIDCEntityContext> strategy) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
         contextClassLookupStrategy = 
                 Constraint.isNotNull(strategy, "Context class lookup strategy can not be null");
     }
@@ -91,7 +91,7 @@ public class OIDCProviderMetadataLookupHandler extends AbstractMessageHandler {
      * @param strategy the strategy function
      */
     public void setCopyContextStrategy(@Nullable final Function<MessageContext, OIDCProviderMetadataContext> strategy) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
 
         copyContextStrategy = strategy;
     }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/DecodeStateAsJsonObjectConsumer.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/DecodeStateAsJsonObjectConsumer.java
index 530e365..71d322f 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/DecodeStateAsJsonObjectConsumer.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/DecodeStateAsJsonObjectConsumer.java
@@ -160,7 +160,7 @@ public class DecodeStateAsJsonObjectConsumer extends AbstractIdentifiableInitial
                 .setState(responseState.getValue())
                 .setStateJson(jsonState);
             
-        } catch (DecodingException | ParseException | DataSealerException e) {
+        } catch (final DecodingException | ParseException | DataSealerException e) {
             log.debug("Unable to decode OAuth state into a JSON token", e);
             return false;
         }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/package-info.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/package-info.java
new file mode 100644
index 0000000..cf16baa
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/decoding/impl/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * OIDC profile decoding implementation classes.
+ */
+package net.shibboleth.sp.oidc.profile.decoding.impl;
\ No newline at end of file
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractHttpOAuthAction.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractHttpOAuthAction.java
index eecb03c..826da8c 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractHttpOAuthAction.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractHttpOAuthAction.java
@@ -162,7 +162,8 @@ public abstract class AbstractHttpOAuthAction<T extends Response>
      * @throws MessageHandlerException on error making the request, or if an error response is returned.
      */
     @Nonnull protected T handleRequest(@Nonnull final ProfileRequestContext profileRequestContext, 
-                @Nullable final AbstractAuthenticatableOIDCContext authenticatableContext) throws MessageHandlerException {
+                @Nullable final AbstractAuthenticatableOIDCContext authenticatableContext) 
+                        throws MessageHandlerException {
         try {              
             final ClassicHttpRequest request = getHttpRequestEncoderStrategy().apply(profileRequestContext);
             if (request == null) {
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractProviderMetadataLookupFunction.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractProviderMetadataLookupFunction.java
index 5cc71fb..e4609d5 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractProviderMetadataLookupFunction.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AbstractProviderMetadataLookupFunction.java
@@ -30,7 +30,7 @@ import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
 import net.shibboleth.shared.logic.Constraint;
 
 /**
- * An abstract base class for pulling out the Provider metadata
+ * An abstract base class for pulling out the OpenID Provider metadata.
  * 
  * @param <F> context type
  * @param <ResultType> result type 
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationCondition.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationCondition.java
index 88a6b6e..c347b08 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationCondition.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationCondition.java
@@ -19,6 +19,7 @@ import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
@@ -28,30 +29,32 @@ import com.nimbusds.jwt.JWTClaimsSet;
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
+import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 
 /**
- * Has the auth_time claim been requested? This is taken from the authentication request state. A failure is treated
- * as a request for auth_time to be checked (for a better security posture).
+ * Has the auth_time claim been requested? This is taken from the authentication request state. Any issues finding
+ * if the auth_time claim was request results in a <em>true</em> response: the claim should be checked.
  */
+ at ThreadSafe
 public final class AuthTimeRequestedActivationCondition implements BiPredicate<ProfileRequestContext, JWTClaimsSet> {
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AuthTimeRequestedActivationCondition.class);
     
-    /** Strategy to find the authentication request.*/
+    /** Strategy to find the state data context.*/
     @Nonnull private final 
-    Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
 
     /**
      * Constructor.
      *
-     * @param strategy strategy to find the authentication request
+     * @param strategy strategy to find the state data context
      */
-    public AuthTimeRequestedActivationCondition(@ParameterName(name = "authenticationRequestLookupStrategy")
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
-                "authenticationRequestLookupStrategy can not be null");
+    public AuthTimeRequestedActivationCondition(@ParameterName(name = "stateDataContextLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
+                "StateDataContextLookupStrategy can not be null");
     }
 
     /**
@@ -61,20 +64,28 @@ public final class AuthTimeRequestedActivationCondition implements BiPredicate<P
      */
     @Override
     public boolean test(@Nullable final ProfileRequestContext prc, @Nullable final JWTClaimsSet claims) {
+        if (prc == null) {
+            log.trace("The profile context was null, 'auth_time' will now be checked"); 
+            return true;
+        }
         
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(prc);
-        final var localAuthnRequest = authnRequestCtx != null ? authnRequestCtx.getAuthnState() : null;
-        if (authnRequestCtx == null || localAuthnRequest == null) {
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(prc);
+        final var localStateData = stateDataCtx != null ? stateDataCtx.getStateData() : null;
+        if (stateDataCtx == null || localStateData == null) {
             // Return true, because if we are not sure, we should default to testing the auth_time as a better security 
             // posture
             log.trace("Authentication request state not found in profile context, is the "
-                    + "authenticationRequestLookupStrategy configured correctly? 'auth_time' will now be checked"); 
+                    + "stateDataContextLookupStrategy configured correctly? 'auth_time' will now be checked"); 
             return true;
         }
-        final boolean authTimeWasRequested = localAuthnRequest.isAuthTimeRequired();
-        log.trace("Authentication time (auth_time) {} request during authentication", 
-                authTimeWasRequested ? "was" : "was not");
-        return authTimeWasRequested;
+        if (localStateData instanceof final AuthenticationRequestStateData authnRequest) {
+            final boolean authTimeWasRequested = authnRequest.isAuthTimeRequired();
+            log.trace("Authentication time (auth_time) {} request during authentication", 
+                    authTimeWasRequested ? "was" : "was not");
+            return authTimeWasRequested;
+        }
+        log.trace("Unexpected StateData type {}, defaulting to auth_time check", localStateData.getClass().getName());
+        return true;
        
     }
     
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategy.java
index d665b10..4ea0178 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategy.java
@@ -15,7 +15,6 @@
 package net.shibboleth.sp.oidc.profile.impl;
 
 import java.util.function.Function;
-import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -23,36 +22,30 @@ import javax.annotation.Nullable;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.shared.security.DataSealerException;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.StateData;
 
 /**
- *  A {@link Function} that produces a serialized JSON string representing authentication request state that is required
+ *  A {@link Function} that produces {@link StateData} representing authentication request state that is required
  *  for validating the authentication response. This can be stored in a way that can be recovered later when the 
  *  response is received.
  *  
  *  <p>It is important that this information is stored in a way that is protected from modification by the client, as 
- *  tampering with this information may allow an attacker to forge authentication responses. This is supported
- *  using the dataSealer—which should only ever be turned off for testing and debugging.</p>
+ *  tampering with this information may allow an attacker to forge authentication responses.</p>
  *  
  *  <p>If an authentication authority is not available, or the authentication request 
- *  is not available, null will be returned. This will ultimately prevent the authentication response from being
- *  validated correctly, and the calling action may wish to terminate the authentication request before a response
+ *  is not available, <code>null</code> will be returned. This will ultimately prevent the authentication response 
+ *  from being validated, therefore the calling action may wish to terminate the authentication process before a request
  *  is made.</p>
  */
 public class AuthenticationRequestStateForStorageStrategy extends AbstractIdentifiableInitializableComponent 
-    implements Function<ProfileRequestContext,String> {
+    implements Function<ProfileRequestContext,AuthenticationRequestStateData> {
     
     /** Class logger. */
     @Nonnull
@@ -61,25 +54,9 @@ public class AuthenticationRequestStateForStorageStrategy extends AbstractIdenti
     /** Strategy to locate authentication authority, that is the issuer of the eventual authentication response. */
     @NonnullAfterInit private Function<ProfileRequestContext,String> authenticationAuthorityLookupStrategy;
     
-    /** JSON object mapper. */
-    @NonnullAfterInit private ObjectMapper objectMapper;
-    
     /** Strategy to locate the authentication request. */
-    @NonnullAfterInit private Function<ProfileRequestContext,OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
-    
-    /** Optional data sealer to use. */
-    @Nullable private DataSealer dataSealer;
-    
-    /** 
-     * If the dataSealer is provided should it be used to seal the authentication request state? Defaults to true, 
-     * that is, if the dataSealer is provided, always seal state.
-     */
-    @Nonnull private Predicate<ProfileRequestContext> sealState;
-    
-    /** Constructor.*/
-    public AuthenticationRequestStateForStorageStrategy() {
-        sealState = PredicateSupport.alwaysTrue();
-    }
+    @NonnullAfterInit 
+    private Function<ProfileRequestContext,OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
     
     /** {@inheritDoc} */
     @Override
@@ -92,53 +69,8 @@ public class AuthenticationRequestStateForStorageStrategy extends AbstractIdenti
         if (authenticationRequestLookupStrategy == null) {
             throw new ComponentInitializationException("Authentication request lookup strategy can not be null");
         }
-        if (objectMapper == null) {
-            throw new ComponentInitializationException("ObjectMapper cannot be null");
-        }
     }
     
-    /**
-     * Sets {@link DataSealer} to use.
-     * 
-     * @param sealer data sealer
-     */
-    public void setDataSealer(@Nullable final DataSealer sealer) {
-        checkSetterPreconditions();
-        
-        dataSealer = sealer;
-    }
-    
-    /**
-     * Set the predicate to determine whether to seal the state.
-     * 
-     * @param predicate the seal state predicate to set.
-     */
-    public void setSealStatePredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
-        checkSetterPreconditions();
-        
-        sealState = Constraint.isNotNull(predicate, "Seal state predicate can not be null");
-    }
-    
-    /**
-     * Set the flag to determine whether to seal the state.
-     * 
-     * @param flag the flag to set.
-     */
-    public void setSealState(final boolean flag) {
-        checkSetterPreconditions();
-        
-        sealState = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
-    }
-    
-    /**
-     * Set the JSON {@link ObjectMapper} to use for serialization.
-     * 
-     * @param mapper object mapper
-     */
-    public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
-        checkSetterPreconditions();
-        objectMapper = Constraint.isNotNull(mapper, "ObjectMapper cannot be null");
-    }
     
     /**
      * Set the authentication request lookup strategy to use.
@@ -157,14 +89,15 @@ public class AuthenticationRequestStateForStorageStrategy extends AbstractIdenti
      * 
      * @param strategy the strategy.
      */
-    public void setAuthenticationAuthorityLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
+    public void setAuthenticationAuthorityLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, String> strategy) {
         checkSetterPreconditions();
         authenticationAuthorityLookupStrategy = Constraint.isNotNull(strategy, 
                 "Authentication Authority Lookup Strategy can not be null");
     }
 
     /** {@inheritDoc} */
-    @Nullable public String apply(@Nullable final ProfileRequestContext prc) {
+    @Nullable public AuthenticationRequestStateData apply(@Nullable final ProfileRequestContext prc) {
         checkComponentActive();
         
 
@@ -173,43 +106,25 @@ public class AuthenticationRequestStateForStorageStrategy extends AbstractIdenti
             return null;
         }
         
-        // Find the relying party or authenticating authority this request has been sent to.
+        // Find the relying party or authenticating authority this request will be issued to.
         final String authenticationAuthority = authenticationAuthorityLookupStrategy.apply(prc);
         // Locate the authentication request to pull out information for saving off.
         final OIDCAuthenticationRequest authnRequest = authenticationRequestLookupStrategy.apply(prc);
         
         if (authenticationAuthority == null) {
-            log.debug("{}: Authentication authority is null, response correlation will fail", getId());
+            log.warn("{}: Authentication authority is null, response correlation will fail", getId());
             return null;
         }        
         if (authnRequest == null) {
-            log.debug("{}: Authentication request is null, response correlation will fail", getId());
+            log.warn("{}: Authentication request is null, response correlation will fail", getId());
             return null;
         }
         // Construct a DTO for saving off the authentication request state
         final AuthenticationRequestStateData state = 
                 AuthenticationRequestStateData.from(authnRequest, authenticationAuthority);
-        log.debug("{}: Created authentication request state data: {}", getId(), state);
+        log.trace("{}: Created authentication request state data: {}", getId(), state);
         
-        try {
-            // Serialize the authentication request state
-            final String serializedState = objectMapper.writeValueAsString(state);
-            if (serializedState == null) {
-                return null;
-            }
-            final DataSealer localDataSealer = dataSealer;
-            if (localDataSealer != null && sealState.test(prc)) {
-                log.debug("{}: Authentication request state was sealed", getId());
-                return localDataSealer.wrap(serializedState);
-            } else {
-                log.warn("{}: Authentication request state was NOT sealed, either DataSealer is not configured or "
-                        + "sealing predicate returned false. Sealing should be enabled in production", getId());
-                return serializedState;
-            }
-        } catch (final JsonProcessingException | DataSealerException e) {
-            log.error("{}: Issue generating authentication request state",getId(),e);
-        }
-        return null;
+        return state;
     }
 
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunction.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunction.java
index 15c9fe4..e1f4d0e 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunction.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunction.java
@@ -25,12 +25,13 @@ import javax.annotation.concurrent.NotThreadSafe;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.StateData;
 
 /** 
  * Lookup function that returns the time at which the RP sent the authentication request, as taken from the 
- * authentication request state object recovered when the response was obtained from the agent. 
+ * authentication request state object recovered when the response delivered via the Agent. 
  * 
  * <p>If the time can not be found, <code>null</code> is returned.</p>
  */
@@ -38,9 +39,9 @@ import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 public class AuthenticationRequestTimeLookupFunction implements Function<ProfileRequestContext, Instant> {
     
     /** 
-     * Strategy used to locate the {@link AuthnRequestStateDataContext}.
+     * Strategy used to locate the {@link StateDataContext}.
      */
-    @Nonnull private Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    @Nonnull private Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
     
     /** 
      * A clock skew to apply to the authentication request time. This accounts for differences between system clocks by
@@ -51,12 +52,12 @@ public class AuthenticationRequestTimeLookupFunction implements Function<Profile
     
     /** Constructor.*/
     public AuthenticationRequestTimeLookupFunction() {
-        authenticationRequestLookupStrategy = prc -> {
+        stateDataContextLookupStrategy = prc -> {
             final var inboundMsgCtx = prc.getInboundMessageContext();
             if (inboundMsgCtx == null) {
                 return null;
             }
-            return inboundMsgCtx.getSubcontext(AuthnRequestStateDataContext.class);
+            return inboundMsgCtx.getSubcontext(StateDataContext.class);
         };   
         final Duration ofSeconds = Duration.ofSeconds(0);
         assert ofSeconds != null;
@@ -73,14 +74,14 @@ public class AuthenticationRequestTimeLookupFunction implements Function<Profile
     }
     
     /**
-     * Set the strategy used to locate the {@link AuthenticationRequestStateData} to use.
+     * Set the strategy used to locate the {@link StateDataContext} to use.
      * 
      * @param strategy lookup strategy
      */
-    public void setAuthenticationRequestLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy =
-                Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+    public void setStateDataContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy =
+                Constraint.isNotNull(strategy, "StateDataContextLookupStrategy lookup strategy cannot be null");
     }
 
     @Override
@@ -88,10 +89,16 @@ public class AuthenticationRequestTimeLookupFunction implements Function<Profile
         if (input == null) {
             return null;
         }
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(input);
-        final AuthenticationRequestStateData authnRequest = authnRequestCtx != null  ? authnRequestCtx.getAuthnState() : null;
-        final Instant authTime = authnRequest != null ? authnRequest.getAuthnRequestTime() : null;
-        return authTime == null ? null : authTime.minus(clockSkew);
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(input);
+        if (stateDataCtx == null) {
+            return null;
+        }
+        final StateData stateData = stateDataCtx.getStateData();
+        if (stateData instanceof final AuthenticationRequestStateData authnRequest) {
+            final Instant authTime = authnRequest.getRequestTime();
+            return authTime == null ? null : authTime.minus(clockSkew);
+        }
+        return null;
     }
 
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/CreateAuthenticationStateData.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/CreateAuthenticationStateData.java
new file mode 100644
index 0000000..790a906
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/CreateAuthenticationStateData.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.StateDataContext;
+import net.shibboleth.sp.profile.AbstractApplicationAction;
+import net.shibboleth.sp.state.StateData;
+
+/**
+ * 
+ * An action that looks up {@link StateData} using a configured lookup strategy 
+ * and adds it to a {@link StateDataContext} obtained from the profile request 
+ * context using a configured creation strategy.
+ * 
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ */
+public class CreateAuthenticationStateData extends AbstractApplicationAction {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(CreateAuthenticationStateData.class);
+
+    /** Lookup strategy for the contents of the state token, as stored in a {@link StateData} or subclass thereof. */
+    @NonnullAfterInit private Function<ProfileRequestContext,StateData> stateDataLookupStrategy;
+    
+    /** Strategy used to create the {@link StateDataContext} to populate. */
+    @Nonnull private Function<ProfileRequestContext,StateDataContext> stateDataContextCreationStrategy;
+    
+    /**
+     * Constructor.
+     */
+    public CreateAuthenticationStateData() {
+        stateDataContextCreationStrategy = new ChildContextLookup<>(StateDataContext.class, true);
+    }
+    
+    /**
+     * Sets the strategy used to create the {@link StateDataContext}.
+     * 
+     * @param strategy creation strategy
+     */
+    public void setStateDataContextCreationStrategy(
+            @Nonnull final Function<ProfileRequestContext,StateDataContext> strategy) {
+        checkSetterPreconditions();
+        stateDataContextCreationStrategy =
+                Constraint.isNotNull(strategy, "StateDataContext creation strategy cannot be null");
+    }
+
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (stateDataLookupStrategy == null) {
+            throw new ComponentInitializationException("StateValueLookupStrategy cannot be null");
+        }
+    }
+    
+    /**
+     * Sets the lookup strategy for obtaining the {@link StateData} of the state token.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setStateDataLookupStrategy(@Nonnull final Function<ProfileRequestContext,StateData> strategy) {
+        checkSetterPreconditions();
+        
+        stateDataLookupStrategy = Constraint.isNotNull(strategy, "State data lookup strategy cannot be null");
+    }
+    
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        final StateDataContext stateDataContext = stateDataContextCreationStrategy.apply(profileRequestContext);
+        
+        if (stateDataContext == null) {
+            log.error("{} Error creating StateDataContext", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        
+        final StateData stateData = stateDataLookupStrategy.apply(profileRequestContext);
+        
+        if (stateData == null) {
+            log.error("{} Error creating StateData", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        
+        stateDataContext.setStateData(stateData);
+        log.debug("{} Created authentication state data for preservation '{}'", getLogPrefix(), stateData);
+        
+    }
+
+}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExchangeCodeForAccessToken.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExchangeCodeForAccessToken.java
index 0a09b00..34c0d1d 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExchangeCodeForAccessToken.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExchangeCodeForAccessToken.java
@@ -109,8 +109,8 @@ public class ExchangeCodeForAccessToken extends AbstractHttpOAuthAction<TokenRes
                   log.trace("{} Token request response was successful '{}'",getLogPrefix(), 
                 		  tokenResponse.indicatesSuccess());
               } else {
-                  throw new MessageHandlerException("Token response was not of the expected format, expected OIDC token "
-                          + "response, got " + responseObject.getClass().getSimpleName());
+                  throw new MessageHandlerException("Token response was not of the expected format, expected OIDC token"
+                          + " response, got " + responseObject.getClass().getSimpleName());
               }           
           } catch (final MessageHandlerException e) {
               log.error("{} Failed to exchange authorisation code for token result",getLogPrefix(), e);
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
index 9ad5e4c..27e8976 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ExtractOIDCClaims.java
@@ -269,7 +269,8 @@ public class ExtractOIDCClaims extends AbstractApplicationAction {
         accumulator.putAll(attributeContext.getIdPAttributes());
         
         // Extract standard attributes.
-        if (standardExtractionStrategy != null && profileConfiguration.isExtractStandardAttributes(profileRequestContext)) {
+        if (standardExtractionStrategy != null && 
+                profileConfiguration.isExtractStandardAttributes(profileRequestContext)) {
             log.debug("{} Extracting standard attributes", getLogPrefix());
             assert standardExtractionStrategy != null;
             final Collection<IdPAttribute> standards = standardExtractionStrategy.apply(endUserClaimsContext);
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeAuthorizationRequest.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeAuthorizationRequest.java
index 6a1b23b..41e5e21 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeAuthorizationRequest.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeAuthorizationRequest.java
@@ -75,7 +75,7 @@ public class InitializeAuthorizationRequest extends AbstractProfileAction {
      */
     public void setOAuth2ClientContextLookupStrategy(
             @Nonnull final Function<ProfileRequestContext, OAuth2ClientContext> strgy) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
 
         oauth2ClientContextLookupStrategy = Constraint.isNotNull(strgy, 
                 "OAuth2 client context lookup strategy cannot be null");
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
index 32c06e2..38e4e79 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
@@ -80,15 +80,15 @@ import net.shibboleth.shared.primitive.StringSupport;
  * TODO split the logic out into commons to share for RP and Proxy
  */
 public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractMessageHandler {
+    
+    /** Lookup function for parent ProfileRequestContext. */
+    @Nonnull private static final ParentProfileRequestContextLookup<MessageContext> PRC_LOOKUP
+        = new ParentProfileRequestContextLookup<>();    
 
     /** Class logger. */
     @Nonnull
     private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientAuthenticationMethodHandler.class);
     
-    /** Lookup function for parent ProfileRequestContext. */
-    @Nonnull private static final ParentProfileRequestContextLookup<MessageContext> PRC_LOOKUP
-        = new ParentProfileRequestContextLookup<>();
-    
     /** 
      * The strategy used to lookup or create the {@link OAuth2ClientAuthenticationContext} 
      * for storing the client authentication.
@@ -175,7 +175,7 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
      * @param expiry the JWT 'exp' claim offset
      */
     public void setJwtBearerExpiryOffset(@Nonnull final Duration expiry) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
 
         jwtBearerExpiryOffset = Constraint.isNotNull(expiry, "jwtBearerExpiryOffset can not be null");
     }
@@ -241,7 +241,7 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
      */
     public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
         checkSetterPreconditions();
-        issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");;
+        issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");
     }
     
     
@@ -262,7 +262,8 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
         
         final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.compose(PRC_LOOKUP).apply(messageContext);     
         if (rpCtx != null && rpCtx.getConfiguration() != null &&
-                rpCtx.getProfileConfig() instanceof final OIDCAuthenticationRelyingPartyProfileConfiguration rpConfig) {
+                rpCtx.getProfileConfig() instanceof final OIDCAuthenticationRelyingPartyProfileConfiguration 
+                rpConfig) {
             profileConfiguration = rpConfig;
         }
         if (profileConfiguration == null) {
@@ -423,7 +424,7 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
     @Nonnull private JWTClaimsSet buildClientAuthenticationJwtClaims(@Nonnull final MessageContext messageContext) 
             throws MessageHandlerException{
         
-        String audience;
+        final String audience;
         if (profileConfiguration.isUseTargetedEndpointAsJWTAudience(PRC_LOOKUP.apply(messageContext))) {
             audience = providerMetadata.getTokenEndpointURI().toString();
         } else {
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientContext.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientContext.java
index 31e8720..770bf53 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientContext.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializeOAuth2ClientContext.java
@@ -104,7 +104,7 @@ public class InitializeOAuth2ClientContext extends AbstractAgentAction {
      */
     public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
         checkSetterPreconditions();
-        issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");;
+        issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy lookup strategy cannot be null");
     }
     
     /**
@@ -114,7 +114,7 @@ public class InitializeOAuth2ClientContext extends AbstractAgentAction {
      */
     public void setRedirectUriOverrideLookupStrategy(
             @Nonnull final Function<ProfileRequestContext, URI> strategy) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
         
         redirectUriOverrideLookupStrategy = 
                 Constraint.isNotNull(strategy, "Redirect URI lookup strategy can not be null");
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializePeerEntityContext.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializePeerEntityContext.java
new file mode 100644
index 0000000..a50e3d0
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/InitializePeerEntityContext.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.StateDataContext;
+import net.shibboleth.sp.state.StateData;
+
+/**
+ * An action that initializes the {@link OIDCPeerEntityContext} based on the issuer value from the {@link StateData}.
+ * 
+ * @event {@link EventIds#INVALID_MSG_CTX} 
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ */
+public class InitializePeerEntityContext  extends AbstractProfileAction {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(InitializePeerEntityContext.class);
+    
+    /** Strategy used to locate the {@link StateDataContext}. */
+    @NonnullAfterInit private Function<ProfileRequestContext,StateDataContext> stateDataContextLookupStrategy;
+    
+    /**
+     * Set the strategy used to locate the {@link StateDataContext} to pull the issuer out of.
+     * 
+     * @param strategy The strategy to set.
+     */
+    public void setStateDataContextLookupStrategy(
+            final Function<ProfileRequestContext, StateDataContext> strategy) {
+        checkSetterPreconditions();
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
+                "stateDataContextLookupStrategy can not be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (null == stateDataContextLookupStrategy) {
+            throw new ComponentInitializationException("StateDataContext lookup strategy cannot be null");
+        }
+    }
+    
+    @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+       
+        final MessageContext messageContext = profileRequestContext.getInboundMessageContext();
+        if (messageContext == null) {
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return;
+        }              
+
+        final OIDCPeerEntityContext peerContext = messageContext.ensureSubcontext(OIDCPeerEntityContext.class);
+        final StateDataContext dataContext = stateDataContextLookupStrategy.apply(profileRequestContext);
+        
+        if (dataContext == null) {
+            log.warn("{} No StateDataContext found, can not proceed", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        final StateData state = dataContext.getStateData();
+        if (state == null) {
+            log.warn("{} No StateData found, can not proceed", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        peerContext.setIdentifier(state.getAuthenticationAuthority());
+
+        log.debug("{} Initialized inbound context for '{}'", getLogPrefix(), peerContext.getIdentifier());
+    }
+    
+
+}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookie.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookie.java
deleted file mode 100644
index d718fda..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookie.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.google.common.escape.Escaper;
-import com.google.common.net.UrlEscapers;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.net.CookieManager;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.context.AgentRequestContext;
-import net.shibboleth.sp.ddf.DDF;
-import net.shibboleth.sp.messaging.RemotedHttpServletRequestResponseContext;
-import net.shibboleth.sp.profile.AbstractApplicationAction;
-import net.shibboleth.sp.profile.SPConstants;
-
-/**
- * Action that issues a cookie used to record state about a request for
- * later enforcement/evaluation.
- * 
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_MESSAGE}
- */
-public class IssueStateCookie extends AbstractApplicationAction {
-    
-    /** Default cookie prefix. */
-    @Nonnull @NotEmpty public static final String DEFAULT_COOKIE_PREFIX = "shibsp_req_";
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(IssueStateCookie.class);
-    
-    /** Cookie manager. */
-    @NonnullAfterInit private CookieManager cookieManager;
-    
-    /** Cookie prefix. */
-    @Nonnull private String cookiePrefix;
-
-    /** Whether an error constructing a correlation cookie is fatal. */
-    private boolean errorFatal;
-    
-    /** Lookup strategy for the contents of the state cookie. */
-    @NonnullAfterInit private Function<ProfileRequestContext,String> stateValueLookupStrategy;
-
-    /** State token value used in cookie name. */
-    @NonnullBeforeExec private String stateToken;
-    
-    /** The value of the state cookie. */
-    @NonnullBeforeExec private String stateValue;
-    
-    /** Constructor. */
-    public IssueStateCookie() {
-        cookiePrefix = DEFAULT_COOKIE_PREFIX;
-    }
-    
-    /**
-     * Sets the {@link CookieManager} to use.
-     * 
-     * @param manager cookie manager instance
-     */
-    public void setCookieManager(@Nonnull final CookieManager manager) {
-        checkSetterPreconditions();
-        
-        cookieManager = Constraint.isNotNull(manager, "CookieManager cannot be null");
-    }
-    
-    /**
-     * Sets the cookie prefix.
-     * 
-     * <p>Defaults to {@link #DEFAULT_COOKIE_PREFIX}.</p>
-     * 
-     * @param prefix cookie prefix
-     */
-    public void setCookiePrefix(@Nonnull @NotEmpty final String prefix) {
-        checkSetterPreconditions();
-        
-        cookiePrefix = Constraint.isNotNull(StringSupport.trimOrNull(prefix), "Cookie prefix cannot be null or empty");
-    }
-    
-    /**
-     * Sets whether an error computing a state token should result in a fatal event.
-     * 
-     * <p>Defaults to false.</p>
-     * 
-     * @param flag flag to set
-     */
-    public void setErrorFatal(final boolean flag) {
-        checkSetterPreconditions();
-        
-        errorFatal = flag;
-    }
-    
-    /**
-     * Sets the lookup strategy for obtaining the contents of the state cookie.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setStateValueLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
-        checkSetterPreconditions();
-        
-        stateValueLookupStrategy = Constraint.isNotNull(strategy, "State value lookup strategy cannot be null");
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (cookieManager == null) {
-            throw new ComponentInitializationException("CookieManager cannot be null");
-        } else if (stateValueLookupStrategy == null) {
-            throw new ComponentInitializationException("State value lookup strategy cannot be null");
-        }
-    }    
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        if (!super.doPreExecute(profileRequestContext)) {
-            return false;
-        }
-
-        stateValue = stateValueLookupStrategy.apply(profileRequestContext);
-        
-        if (stateValue == null) {
-            if (errorFatal) {
-                log.warn("{} State value was missing", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-            } else {
-                log.debug("{} No cookie contents available, skipping creation of state cookie", getLogPrefix());
-            }            
-            return false;
-        }
-        
-        final DDF input = ensureAgentRequestContext().getInput();
-        if (input != null) {
-            stateToken = input.getmember(SPConstants.STATE).string();
-        }
-        
-        if (stateToken == null) {
-            if (stateValue != null) {
-                if (errorFatal) {
-                    log.warn("{} Input was missing {} parameter", getLogPrefix(), SPConstants.STATE);
-                    ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                } else {
-                    log.debug("{} Input was missing {} parameter, skipping creation of state cookie", getLogPrefix(),
-                            SPConstants.STATE);
-                }
-            }
-            return false;
-        }
-        
-        return true;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
-        ensureOutputObjects();
-        final AgentRequestContext agentRequestContext = ensureAgentRequestContext();
-
-        // We do the crazy stuff to accomodate the cookies being set or unset.
-        try {
-            RemotedHttpServletRequestResponseContext.loadCurrent(agentRequestContext.getRemotedHttpServletRequest(),
-                    agentRequestContext.getRemotedHttpServletResponse());
-
-            cookieManager.purgeStaleCookies(cookiePrefix);
-            
-            log.debug("{} Tracking state {} against state token {}", getLogPrefix(), stateValue, stateToken);
-
-            final Escaper escaper = UrlEscapers.urlFormParameterEscaper();
-            final String escapedValue = escaper.escape(stateValue);
-            assert escapedValue != null;
-            cookieManager.addCookie(cookiePrefix + escaper.escape(stateToken), escapedValue);
-        } finally {
-            RemotedHttpServletRequestResponseContext.clearCurrent();
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateTokenToStateValue.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateTokenToStateValue.java
deleted file mode 100644
index 64d64c0..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateTokenToStateValue.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.io.IOException;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.sp.context.AgentRequestContext;
-import net.shibboleth.sp.messaging.RemotedHttpServletRequestResponseContext;
-import net.shibboleth.sp.profile.AbstractApplicationAction;
-
-
-/**
- * Profile action that recovers a state value from a state token and uses a customisable strategy to set it onto the
- * {@link ProfileRequestContext}.
- *
- * <p>Failures encountered during processing may be treated as fatal according to the {@link #errorFatal} flag. If
- * fatal and an error occurs, an {@link EventIds#INVALID_MESSAGE} event is raised.</p>
- *
- * <p>This action does not interpret the state value itself. Instead, the token value consumer is expected to 
- * understand the structure and semantics of the recovered state value and set it on the {@link ProfileRequestContext} 
- * appropriately.</p>
- * 
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_MESSAGE}
- */
-public class MapStateTokenToStateValue extends AbstractApplicationAction {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(MapStateTokenToStateValue.class);
-    
-    /** Lookup strategy for state token. */
-    @NonnullAfterInit private Function<ProfileRequestContext,String> stateTokenLookupStrategy;
-    
-    /** 
-     * A consumer {@link BiFunction} that process the state value and adds it to the profile request context. The 
-     * returned boolean indicates if the consumer function was successful or not. An unsuccessful outcome may trigger 
-     * an error depending on the {@link #errorFatal} flag.
-     */
-    @NonnullAfterInit private BiFunction<ProfileRequestContext, byte[], Boolean> stateValueConsumer;
-    
-    /** Whether an error mapping the state token to a state value is fatal. Defaults to true. */
-    private boolean errorFatal;
-    
-    /**
-     * Constructor.
-     */
-    public MapStateTokenToStateValue() {
-        errorFatal = true;
-    }
-    
-    /**
-     * Sets the lookup strategy to obtain the protocol specific state token. 
-     *
-     * @param strategy lookup strategy
-     */
-    public void setStateTokenLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
-        checkSetterPreconditions();
-        stateTokenLookupStrategy = Constraint.isNotNull(strategy, "State token lookup strategy cannot be null");
-    }
-    
-    /**
-     * Set the state value consumer to add the recovered state value to the profile request context. The return value 
-     * indicates if the consumer function was successful or not. An unsuccessful outcome may trigger an error 
-     * depending on the {@link #errorFatal} flag.
-     * 
-     * @param consumer The consumer to set.
-     */
-    public void setStateValueConsumer(final BiFunction<ProfileRequestContext, byte[], Boolean> consumer) {
-        checkSetterPreconditions();
-        stateValueConsumer = Constraint.isNotNull(consumer, "StateValueConsumer can not be null");
-    }
-    
-    /**
-     * Sets whether an error looking up a state value from a state token should result in a fatal event.
-     * 
-     * <p>Defaults to true.</p>
-     * 
-     * @param flag flag to set
-     */
-    public void setErrorFatal(final boolean flag) {
-        checkSetterPreconditions();
-        
-        errorFatal = flag;
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (stateTokenLookupStrategy == null) {
-            throw new ComponentInitializationException("State token lookup strategy cannot be null");
-        }
-        if (stateValueConsumer == null) {
-            throw new ComponentInitializationException("State value consumer cannot be null");
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
-        ensureOutputObjects();
-        
-        final AgentRequestContext agentRequestContext = ensureAgentRequestContext();
-
-        // We do the crazy stuff to accomodate cookie-backed state management
-        // (and to get the relevant state token in the first place).
-        try {
-            RemotedHttpServletRequestResponseContext.loadCurrent(agentRequestContext.getRemotedHttpServletRequest(),
-                    agentRequestContext.getRemotedHttpServletResponse());
-            
-            final String token = stateTokenLookupStrategy.apply(profileRequestContext);
-            if (token == null) {
-                log.debug("{} No state token returned from lookup strategy", getLogPrefix());
-                if (errorFatal) {
-                    ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                }
-                return;
-            }
-            
-            final byte[] stateValue = ensureApplication().getStateTokenManager().recoverFromStateToken(
-                    ensureAgent(), ensureApplication(), token);     
-
-            if (stateValue == null && !errorFatal) {
-                log.debug("{} No state value found for state token '{}'", getLogPrefix(), token);
-                return;
-            } else if (stateValue == null && errorFatal){
-                log.debug("{} No state value found for state token '{}'", getLogPrefix(), token);
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                return;
-            }
-            
-            final Boolean success = stateValueConsumer.apply(profileRequestContext, stateValue);
-            // Treat null as failure
-            if ((success == null || Boolean.FALSE.equals(success)) && errorFatal) {
-                log.warn("{} Could not set state value", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                return;
-            }
-            
-        } catch (final IOException e) {
-            log.warn("{} Exception recovering state value from state token", getLogPrefix(), e);
-            if (errorFatal) {
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-            }
-        } finally {
-            RemotedHttpServletRequestResponseContext.clearCurrent();
-        }
-    }
-
-}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateValueToStateToken.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateValueToStateToken.java
deleted file mode 100644
index b862d24..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MapStateValueToStateToken.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.sp.context.AgentRequestContext;
-import net.shibboleth.sp.messaging.RemotedHttpServletRequestResponseContext;
-import net.shibboleth.sp.profile.AbstractApplicationAction;
-import net.shibboleth.sp.profile.StateTokenManager;
-
-/**
- * 
- * An action that extracts a state value from the {@link ProfileRequestContext} using a configured lookup strategy 
- * and maps that value into an application-managed state token. The resulting token is then stored via a configured
- * consumer for later use: such as placement into an OAuth 2.0 "state" parameter or other state preserving or 
- * correlation mechanism.
- * 
- * <p>The action delegates the actual token creation and storage to the application's 
- * {@link StateTokenManager}.</p>
- * 
- * <p>Errors may be ignored or result in an {@link EventIds#IO_ERROR} event.</p> 
- * 
- * @event {@link EventIds#IO_ERROR}
- */
-public class MapStateValueToStateToken extends AbstractApplicationAction {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(MapStateValueToStateToken.class);
-
-    /** Whether an error constructing a state token is fatal. Default is true.*/
-    private boolean errorFatal;
-    
-    /** Lookup strategy for the contents of the state token. */
-    @NonnullAfterInit private Function<ProfileRequestContext,String> stateValueLookupStrategy;
-    
-    /** A consumer that adds the state token to the profile request context.*/
-    @NonnullAfterInit private BiConsumer<ProfileRequestContext, String> stateTokenConsumer;
-    
-    /**
-     * Constructor.
-     */
-    public MapStateValueToStateToken() {
-        errorFatal = true;
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (stateValueLookupStrategy == null) {
-            throw new ComponentInitializationException("StateValueLookupStrategy cannot be null");
-        }
-        if (stateTokenConsumer == null) {
-            throw new ComponentInitializationException("StateTokenConsumer cannot be null");
-        }
-    }
-    
-    
-    /**
-     * Sets whether an error computing a state token should result in a fatal event.
-     * 
-     * <p>Defaults to true.</p>
-     * 
-     * @param flag flag to set
-     */
-    public void setErrorFatal(final boolean flag) {
-        checkSetterPreconditions();
-        
-        errorFatal = flag;
-    }
-    
-    /**
-     * Sets the lookup strategy for obtaining the contents of the state token.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setStateValueLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
-        checkSetterPreconditions();
-        
-        stateValueLookupStrategy = Constraint.isNotNull(strategy, "State value lookup strategy cannot be null");
-    }
-    
-    /**
-     * Set the consumer used to store off the state token onto the profile request context.
-     * 
-     * @param consumer The stateTokenConsumer to set.
-     */
-    public void setStateTokenConsumer(@Nonnull final BiConsumer<ProfileRequestContext, String> consumer) {
-        checkSetterPreconditions();
-        
-        stateTokenConsumer = Constraint.isNotNull(consumer, "StateTokenConsumer can not be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
-        ensureOutputObjects();
-        
-        final AgentRequestContext agentRequestContext = ensureAgentRequestContext();
-
-        // We do the crazy stuff to accomodate cookie-backed state management.
-        try {
-            RemotedHttpServletRequestResponseContext.loadCurrent(agentRequestContext.getRemotedHttpServletRequest(),
-                    agentRequestContext.getRemotedHttpServletResponse());
-            
-            try {
-                final String stateValue = stateValueLookupStrategy.apply(profileRequestContext);
-                if (stateValue != null) {
-                    final byte[] stateValueBytes = stateValue.getBytes(StandardCharsets.UTF_8);
-                    // b64 encode is handled by the state manager
-                    assert stateValueBytes != null;
-                    final String token = ensureApplication().getStateTokenManager().preserveToStateToken(
-                            ensureAgent(), ensureApplication(), stateValueBytes);
-                    
-                    stateTokenConsumer.accept(profileRequestContext, token);
-                    
-                    log.debug("{} State preserved to state token: {}", getLogPrefix(), token);
-                } else{
-                    log.debug("{} State value was null, state not preserved", getLogPrefix());
-                    if (errorFatal) {
-                        ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
-                        return;
-                    }
-                }
-            } catch (final IOException | RuntimeException e) {
-                log.warn("{} Exception preserving state to token", getLogPrefix(), e);
-                if (errorFatal) {
-                    ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
-                }
-            }
-        } finally {
-            RemotedHttpServletRequestResponseContext.clearCurrent();
-        }
-    }
-
-}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunction.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunction.java
index cf675d1..bfddd25 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunction.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunction.java
@@ -29,13 +29,14 @@ import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.StateData;
 
 /** 
- * Locate the maximum authentication age from the authentication request. If not present in the request, it is taken
- * from the profile configuration associated with the relying party context. Returning a default value if neither are 
- * found.
+ * Locate the maximum authentication age from the authentication request state. If not present in the request, it is 
+ * taken from the profile configuration associated with the relying party context. Returning a default value if neither 
+ * are found.
  */
 @NotThreadSafe
 public class MaxAgeFromAuthenticationRequestLookupFunction extends AbstractRelyingPartyLookupFunction<Duration> {
@@ -44,9 +45,9 @@ public class MaxAgeFromAuthenticationRequestLookupFunction extends AbstractRelyi
     @Nonnull private final Duration maxAgeDefault;
     
     /** 
-     * Strategy used to locate the {@link AuthnRequestStateDataContext}. Defaults to under the inbound message context.
+     * Strategy used to locate the {@link StateDataContext}. Defaults to under the inbound message context.
      */
-    @Nonnull private Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    @Nonnull private Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
     
     /**
      * Constructor.
@@ -57,12 +58,12 @@ public class MaxAgeFromAuthenticationRequestLookupFunction extends AbstractRelyi
             @ParameterName(name = "maxAgeDefault") @Nonnull final Duration defaultAge) {
         maxAgeDefault = Constraint.isNotNull(defaultAge, "Max Age default can not be null");
         
-        authenticationRequestLookupStrategy = prc -> {
+        stateDataContextLookupStrategy = prc -> {
             final var inboundMsgCtx = prc.getInboundMessageContext();
             if (inboundMsgCtx == null) {
                 return null;
             }
-            return inboundMsgCtx.getSubcontext(AuthnRequestStateDataContext.class);
+            return inboundMsgCtx.getSubcontext(StateDataContext.class);
         };  
     }
     
@@ -71,10 +72,10 @@ public class MaxAgeFromAuthenticationRequestLookupFunction extends AbstractRelyi
      * 
      * @param strategy the strategy
      */
-    public void setAuthenticationRequestLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
-                "AuthenticationRequestLookupStrategy can not be null");
+    public void setStateDataContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
+                "StateDataContextLookupStrategy can not be null");
     }
 
     @Override
@@ -87,10 +88,10 @@ public class MaxAgeFromAuthenticationRequestLookupFunction extends AbstractRelyi
         
         // Max_age from authentication request is authoritative over that from the profile config.
         
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(input);
-        final AuthenticationRequestStateData authnRequest = 
-                authnRequestCtx != null  ? authnRequestCtx.getAuthnState() : null;
-        if (authnRequest != null) {
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(input);
+        final StateData stateData = stateDataCtx != null  ? stateDataCtx.getStateData() : null;
+        
+        if (stateData instanceof final AuthenticationRequestStateData authnRequest) {
             final Duration authnRequestMaxAge = authnRequest.getMaxAge();
             if (authnRequestMaxAge != null) {
                 return authnRequestMaxAge;
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/NonceFromAuthenticationRequestStateLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/NonceFromAuthenticationRequestStateLookupStrategy.java
index a55639e..ebdf8f9 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/NonceFromAuthenticationRequestStateLookupStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/NonceFromAuthenticationRequestStateLookupStrategy.java
@@ -26,9 +26,10 @@ import org.opensaml.profile.context.ProfileRequestContext;
 
 import com.nimbusds.jwt.JWTClaimsSet;
 
+import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 
 /** 
@@ -40,8 +41,8 @@ public final class NonceFromAuthenticationRequestStateLookupStrategy
                         implements BiFunction<ProfileRequestContext,JWTClaimsSet, String> {
     
     /** The strategy used to lookup the {@link AuthenticationRequestStateData}.*/
-    @Nonnull 
-    private final Function<ProfileRequestContext, AuthenticationRequestStateData> authenticationStateDataLookupStrategy; 
+    @Nonnull private final 
+    Function<ProfileRequestContext, AuthenticationRequestStateData> authenticationStateDataLookupStrategy; 
     
     /**
      * 
@@ -50,6 +51,7 @@ public final class NonceFromAuthenticationRequestStateLookupStrategy
      * @param strategy the lookup strategy to use
      */
     public NonceFromAuthenticationRequestStateLookupStrategy(
+            @ParameterName(name="authenticationStateDataLookupStrategy")
             @Nonnull final Function<ProfileRequestContext, AuthenticationRequestStateData> strategy) {
         authenticationStateDataLookupStrategy = 
                 Constraint.isNotNull(strategy, "Authentication state data lookup strategy can not be null");
@@ -65,9 +67,10 @@ public final class NonceFromAuthenticationRequestStateLookupStrategy
             if (messageContext == null) {
                 return null;
             }
-            final AuthnRequestStateDataContext context = messageContext.getSubcontext(AuthnRequestStateDataContext.class);
-            if (context != null) {
-                return context.getAuthnState();
+            final StateDataContext context = messageContext.getSubcontext(StateDataContext.class);
+            if (context != null && context.getStateData() 
+                    instanceof final AuthenticationRequestStateData authnRequest) {
+                return authnRequest;
             }
             return null;
         };
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PKCECodeVerifierFromStateLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PKCECodeVerifierFromStateLookupStrategy.java
index bc873bb..8bc15d8 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PKCECodeVerifierFromStateLookupStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PKCECodeVerifierFromStateLookupStrategy.java
@@ -24,48 +24,53 @@ import org.opensaml.profile.context.ProfileRequestContext;
 
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 
 
 /**
- * A lookup strategy that retrieves the redirect_uri from the {@link AuthenticationRequestStateData} stored in the
- * {@link AuthnRequestStateDataContext} associated with a {@link ProfileRequestContext}.
+ * A lookup strategy that retrieves the PKCE Code verifier from the {@link AuthenticationRequestStateData} stored in the
+ * {@link StateDataContext} associated with a {@link ProfileRequestContext}.
  */
 @ThreadSafe
-public class PKCECodeVerifierFromStateLookupStrategy implements Function<ProfileRequestContext, String> {
+public final class PKCECodeVerifierFromStateLookupStrategy implements Function<ProfileRequestContext, String> {
     
     /** 
-     * Strategy used to locate the {@link AuthnRequestStateDataContext}.
+     * Strategy used to locate the {@link StateDataContext}.
      */
     @Nonnull 
-    private final Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    private final Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
 
     /**
      * Constructor.
      *
      * @param strategy strategy to find the authentication request state context
      */
-    public PKCECodeVerifierFromStateLookupStrategy(@ParameterName(name = "authenticationRequestLookupStrategy")
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
-                "authenticationRequestLookupStrategy can not be null");
+    public PKCECodeVerifierFromStateLookupStrategy(@ParameterName(name = "stateDataContextLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
+                "StateDataContextLookupStrategy can not be null");
     }
     
 
     @Override
-    @Nullable public String apply(final ProfileRequestContext prc) {
-        
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(prc);
-        
-        if (authnRequestCtx == null) {
+    @Nullable public String apply(@Nullable final ProfileRequestContext prc) {
+        if (prc == null) {
             return null;
         }
-        final AuthenticationRequestStateData authnRequest = authnRequestCtx.getAuthnState();
-        if (authnRequest == null) {
-            return null;
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(prc);        
+        if (stateDataCtx != null && 
+                stateDataCtx.getStateData() instanceof final AuthenticationRequestStateData authnRequest) {
+            
+            final String verifier = StringSupport.trimOrNull(authnRequest.getPkceCodeVerifier());
+            if (verifier != null) {
+                return verifier;
+            }
+            
         }
-        return authnRequest.getPkceCodeVerifier();
+        return null;
+        
     }
 
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareOIDCInboundMessageContext.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareOIDCInboundMessageContext.java
index bfcff9c..88f4359 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareOIDCInboundMessageContext.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareOIDCInboundMessageContext.java
@@ -84,7 +84,7 @@ public class PrepareOIDCInboundMessageContext extends AbstractProfileAction {
      * @param flag the flag to set.
      */
     public void setAddToExistingInboundMessageContext(final boolean flag) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
         
         addToExistingInboundMessageContextPredicate = 
                 flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RedirectUriFromStateLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RedirectUriFromStateLookupStrategy.java
index e6b0060..8739d86 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RedirectUriFromStateLookupStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RedirectUriFromStateLookupStrategy.java
@@ -15,6 +15,7 @@
 package net.shibboleth.sp.oidc.profile.impl;
 
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -22,34 +23,41 @@ import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
 
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
 
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.StateData;
 
 
 /**
- * A lookup strategy that retrieves the redirect_uri from the {@link AuthenticationRequestStateData} stored in the
- * {@link AuthnRequestStateDataContext} associated with a {@link ProfileRequestContext}.
+ * A lookup strategy that retrieves the response location from the {@link AuthenticationRequestStateData} stored in the
+ * {@link StateDataContext} associated with a {@link ProfileRequestContext}, converts it to a redirect_uri
+ * and returns it. Any failure returns a <code>null</code> URI.
  */
 @ThreadSafe
 public class RedirectUriFromStateLookupStrategy implements Function<ProfileRequestContext, URI> {
     
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(RedirectUriFromStateLookupStrategy.class);
+    
     /** 
-     * Strategy used to locate the {@link AuthnRequestStateDataContext}.
+     * Strategy used to locate the {@link StateDataContext}.
      */
     @Nonnull 
-    private final Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    private final Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
 
     /**
      * Constructor.
      *
      * @param strategy strategy to find the authentication request state context
      */
-    public RedirectUriFromStateLookupStrategy(@ParameterName(name = "authenticationRequestLookupStrategy")
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
+    public RedirectUriFromStateLookupStrategy(@ParameterName(name = "stateDataContextLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
                 "authenticationRequestLookupStrategy can not be null");
     }
     
@@ -57,16 +65,26 @@ public class RedirectUriFromStateLookupStrategy implements Function<ProfileReque
     @Override
     @Nullable public URI apply(final ProfileRequestContext prc) {
         
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(prc);
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(prc);
         
-        if (authnRequestCtx == null) {
+        if (stateDataCtx == null) {
             return null;
         }
-        final AuthenticationRequestStateData authnRequest = authnRequestCtx.getAuthnState();
-        if (authnRequest == null) {
-            return null;
+        final StateData stateData = stateDataCtx.getStateData();
+        if (stateData instanceof final AuthenticationRequestStateData authnRequest) {
+            if (authnRequest.getResponseLocation() == null) {
+                log.warn("Unable to find redirect_uri from authentication request state data");
+                return null;
+            }
+            try {
+                return new URI(authnRequest.getResponseLocation());
+            } catch (final URISyntaxException e) {
+                log.error("Unable to convert response location '{}' to redirect_uri",
+                        authnRequest.getResponseLocation(), e);
+                return null;
+            }
         }
-        return authnRequest.getRedirectUri();
+        return null;
     }
 
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java
index bbf1b53..232e43b 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategy.java
@@ -30,11 +30,12 @@ import com.nimbusds.jwt.JWTClaimsSet;
 import net.shibboleth.shared.annotation.ParameterName;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
+import net.shibboleth.sp.state.StateData;
 
 /**
- * Strategy that looks up any requested ACR claims from the saved off authentication request state. Not judgement is
+ * Strategy that looks up any requested ACR claims from the saved off authentication request state. No judgement is
  * made over how these ACR values were requested, for example, either as essential or optional.
  */
 @ThreadSafe
@@ -42,18 +43,18 @@ public class RequestedACRClaimsFromStateLookupStrategy
             implements BiFunction<ProfileRequestContext,JWTClaimsSet, List<String>> {
     
     /** 
-     * Strategy used to locate the {@link AuthnRequestStateDataContext}.
+     * Strategy used to locate the {@link StateDataContext}.
      */
-    @Nonnull private final Function<ProfileRequestContext, AuthnRequestStateDataContext> authenticationRequestLookupStrategy;
+    @Nonnull private final Function<ProfileRequestContext, StateDataContext> stateDataContextLookupStrategy;
 
     /**
      * Constructor.
      *
      * @param strategy strategy to find the authentication request state context
      */
-    public RequestedACRClaimsFromStateLookupStrategy(@ParameterName(name = "authenticationRequestLookupStrategy")
-            @Nonnull final Function<ProfileRequestContext, AuthnRequestStateDataContext> strategy) {
-        authenticationRequestLookupStrategy = Constraint.isNotNull(strategy,
+    public RequestedACRClaimsFromStateLookupStrategy(@ParameterName(name = "stateDataContextLookupStrategy")
+            @Nonnull final Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = Constraint.isNotNull(strategy,
                 "authenticationRequestLookupStrategy can not be null");
     }
     
@@ -61,20 +62,20 @@ public class RequestedACRClaimsFromStateLookupStrategy
     @Override
     @Nonnull public List<String> apply(final ProfileRequestContext prc, final JWTClaimsSet claims) {
         
-        final AuthnRequestStateDataContext authnRequestCtx = authenticationRequestLookupStrategy.apply(prc);
+        final StateDataContext stateDataCtx = stateDataContextLookupStrategy.apply(prc);
         
-        if (authnRequestCtx == null) {
+        if (stateDataCtx == null) {
             return CollectionSupport.emptyList();
         }
-        final AuthenticationRequestStateData authnRequest = authnRequestCtx.getAuthnState();
-        if (authnRequest == null) {
-            return CollectionSupport.emptyList();
-        }
-        return authnRequest.getAcrs().stream()
-                .filter(Objects::nonNull)
-                .map(String::trim)
-                .filter(s -> !s.isEmpty()).collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
+        final StateData stateData = stateDataCtx.getStateData();
+        if (stateData instanceof final AuthenticationRequestStateData authnRequest) {
+            return authnRequest.getAcrs().stream()
+                    .filter(Objects::nonNull)
+                    .map(String::trim)
+                    .filter(s -> !s.isEmpty()).collect(CollectionSupport.nonnullCollector(Collectors.toList())).get();
 
+        }        
+        return CollectionSupport.emptyList();       
     }
 
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java
index 01234b6..02f38b3 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationCondition.java
@@ -78,6 +78,6 @@ public class RequestedACRValidationActivationCondition implements BiPredicate<Pr
             log.debug("No profileConfiguration available, activating ACR validation");
             return true;
         }
-        return profileConfiguration.shouldValidateAcrValue(prc);
+        return profileConfiguration.isValidateAcrValue(prc);
     }
 }
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookie.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookie.java
deleted file mode 100644
index cfcb356..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookie.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.google.common.escape.Escaper;
-import com.google.common.net.UrlEscapers;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.net.CookieManager;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.sp.context.AgentRequestContext;
-import net.shibboleth.sp.messaging.RemotedHttpServletRequestResponseContext;
-import net.shibboleth.sp.profile.AbstractApplicationAction;
-
-/**
- * Action that processes a previously issued state cookie, referenced by a state token, submitted with the request and
- * extracts the value for use by subsequent validation steps. The cookie is immediately unset once retrieved.
- * 
- * <p>The value is set onto the context tree using a custom consumer strategy.</p>
- * 
- * <p>The absence of either state value or cookie value can either be treated as fatal or not fatal depending on the 
- * errorFatal flag.</p>
- * 
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_MESSAGE}
- */
-public class ResolveStateCookie extends AbstractApplicationAction {
-    
-    /** Default cookie prefix. */
-    @Nonnull @NotEmpty public static final String DEFAULT_COOKIE_PREFIX = "_shibsp_req_";
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ResolveStateCookie.class);
-    
-    /** Cookie manager. */
-    @NonnullAfterInit private CookieManager cookieManager;
-    
-    /** Cookie prefix. */
-    @Nonnull private String cookiePrefix;
-        
-    /** Lookup strategy for state token value. */
-    @NonnullAfterInit private Function<ProfileRequestContext,String> stateTokenLookupStrategy;
-    
-    /** 
-     * A consumer (as a function) to set the cookie value into the appropriate context in the tree. The return boolean 
-     * indicates if the consumer function was successful or not. An unsuccessful outcome may trigger an error depending 
-     * on the {@link #errorFatal} flag.
-     */
-    @Nullable private BiFunction<ProfileRequestContext, String, Boolean> cookieValueConsumerStrategy;
-    
-    /** Whether an error recovering a correlation cookie is fatal. */
-    private boolean errorFatal;
-    
-    /** State token accompanying request. */
-    @NonnullBeforeExec private String stateToken;
-    
-    /** Constructor. */
-    public ResolveStateCookie() {
-        cookiePrefix = DEFAULT_COOKIE_PREFIX;
-    }
-    
-    /**
-     * Sets the {@link CookieManager} to use.
-     * 
-     * @param manager cookie manager instance
-     */
-    public void setCookieManager(@Nonnull final CookieManager manager) {
-        checkSetterPreconditions();
-        
-        cookieManager = Constraint.isNotNull(manager, "CookieManager cannot be null");
-    }
-            
-    /**
-     * Set the consumer function strategy to use to set the cookie value into the context tree. The return value 
-     * indicates if the consumer function was successful or not. An unsuccessful outcome may trigger an error 
-     * depending on the errorFatal flag.
-     * 
-     * @param strategy The strategy to set.
-     */
-    public void setCookieValueConsumerStrategy(final BiFunction<ProfileRequestContext, String, Boolean> strategy) {
-        checkSetterPreconditions();
-        cookieValueConsumerStrategy = Constraint.isNotNull(strategy,
-                "cookieValueConsumerStrategy can not be null");
-    }       
-    
-    /**
-     * Sets the cookie prefix.
-     * 
-     * <p>Defaults to {@link ResolveStateCookie#DEFAULT_COOKIE_PREFIX}.</p>
-     * 
-     * @param prefix cookie prefix
-     */
-    public void setCookiePrefix(@Nonnull @NotEmpty final String prefix) {
-        checkSetterPreconditions();
-        
-        cookiePrefix = Constraint.isNotNull(StringSupport.trimOrNull(prefix), "Cookie prefix cannot be null or empty");
-    }
-    
-    /**
-     * Sets whether an error recovering a state token should result in a fatal event.
-     * 
-     * <p>Defaults to false.</p>
-     * 
-     * @param flag flag to set
-     */
-    public void setErrorFatal(final boolean flag) {
-        checkSetterPreconditions();
-        
-        errorFatal = flag;
-    }
-    
-    /**
-     * Sets the lookup strategy for obtaining the state token.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setStateTokenLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
-        checkSetterPreconditions();
-        
-        stateTokenLookupStrategy = Constraint.isNotNull(strategy, "State token lookup strategy cannot be null");
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (cookieManager == null) {
-            throw new ComponentInitializationException("CookieManager cannot be null");
-        } else if (stateTokenLookupStrategy == null) {
-            throw new ComponentInitializationException("State token lookup strategy cannot be null");
-        }
-    }    
-
-    /** {@inheritDoc} */
-    @Override
-    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        if (!super.doPreExecute(profileRequestContext)) {
-            return false;
-        }
-
-        stateToken = stateTokenLookupStrategy.apply(profileRequestContext);
-        if (stateToken == null) {
-            if (!errorFatal) {
-                log.debug("{} No state token found in request, non-fatal skipping correlation cookie processing", 
-                        getLogPrefix());
-                return false;
-            } else {
-                log.warn("{} No state token found in request, fatal error", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                return false;
-            }
-        }
-        // If error is fatal, we do not skip processing here.
-        return true;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
-        ensureOutputObjects();
-        final AgentRequestContext agentRequestContext = ensureAgentRequestContext();
-
-        // We do the crazy stuff to accomodate the cookies being read and unset.
-        try {
-            RemotedHttpServletRequestResponseContext.loadCurrent(agentRequestContext.getRemotedHttpServletRequest(),
-                    agentRequestContext.getRemotedHttpServletResponse());
-
-            final Escaper escaper = UrlEscapers.urlFormParameterEscaper();
-            final String cookieName = cookiePrefix + escaper.escape(stateToken);
-
-            final String value = cookieManager.getCookieValue(cookieName, null);
-            cookieManager.unsetCookie(cookieName);
-            
-            if (value == null && !errorFatal) {
-                log.debug("{} No cookie found for state token '{}'", getLogPrefix(), stateToken);
-                return;
-            } else if (value == null){
-                log.debug("{} No cookie found for state token '{}'", getLogPrefix(), stateToken);
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                return;
-            }
-
-            if (cookieValueConsumerStrategy != null) {
-                final Boolean success = cookieValueConsumerStrategy.apply(profileRequestContext, value);
-                // Treat null as failure
-                if ((success == null || Boolean.FALSE.equals(success)) && errorFatal) {
-                    log.warn("{} Could not set correlation cookie", getLogPrefix());
-                    ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
-                    return;
-                }
-                log.debug("{} Resolved cookie for state token '{}'", getLogPrefix(), stateToken);
-            } else {
-                log.debug("{} No cookie value consumer strategy defined, cookie value not processed", getLogPrefix());
-            }
-        } finally {
-            RemotedHttpServletRequestResponseContext.clearCurrent();
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumer.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumer.java
deleted file mode 100644
index 4a3cce5..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumer.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.nio.charset.StandardCharsets;
-import java.util.function.BiFunction;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.logic.PredicateSupport;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.primitive.StringSupport;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.shared.security.DataSealerException;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
-import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
-
-/**
- * A consumer function that converts the authentication request state data JSON into an 
- * {@link AuthenticationRequestStateData} object and sets it onto an {@link AuthnRequestStateDataContext} within the 
- * inbound message context, along with setting the issuer onto the {@link OIDCPeerEntityContext}.
- * 
- * <p>The supplied byte array is assumed to represent UTF‑8 JSON, already Base64‑decoded upstream.</p>
- * 
- * <p>In production use, you should always attempt to unseal the authentication request state, such that it will fail if
- * it was not sealed. Allowing unsealed authentication request state should only be used for testing.</p>
- */
-//TODO this class is complex and more fitting a decoder type mechanism
-public class SetAuthenticationRequestToPeerContextConsumer extends AbstractIdentifiableInitializableComponent 
-    implements BiFunction<ProfileRequestContext, byte[], Boolean> {
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SetAuthenticationRequestToPeerContextConsumer.class);
-    
-    /** JSON object mapper. */
-    @NonnullAfterInit private ObjectMapper objectMapper;
-    
-    /** Optional data sealer to use. */
-    @Nullable private DataSealer dataSealer;
-    
-    /** 
-     * If the dataSealer is provided should it be used to unwrap the authentication request state? Defaults to true, 
-     * that is, if the dataSealer is provided, always attempt to unwrap the state.
-     */
-    @Nonnull private Predicate<ProfileRequestContext> unsealState;
-    
-    /** Constructor.*/
-    public SetAuthenticationRequestToPeerContextConsumer() {
-        unsealState = PredicateSupport.alwaysTrue();
-    }
-    
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (objectMapper == null) {
-            throw new ComponentInitializationException("ObjectMapper cannot be null");
-        }
-    }
-    
-    /**
-     * Set the JSON {@link ObjectMapper} to use for serialization.
-     * 
-     * @param mapper object mapper
-     */
-    public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
-        checkSetterPreconditions();
-        
-        objectMapper = Constraint.isNotNull(mapper, "ObjectMapper cannot be null");
-    }
-    
-    /**
-     * Sets {@link DataSealer} to use.
-     * 
-     * @param sealer data sealer
-     */
-    public void setDataSealer(@Nullable final DataSealer sealer) {
-        checkSetterPreconditions();
-        
-        dataSealer = sealer;
-    }
-    
-    /**
-     * Set the predicate to determine whether to unseal the state.
-     * 
-     * @param predicate the seal state predicate to set.
-     */
-    public void setUnsealStatePredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
-        checkSetterPreconditions();
-        
-        unsealState = Constraint.isNotNull(predicate, "Seal state predicate can not be null");
-    }
-    
-    /**
-     * Set the flag to determine whether to unseal the state.
-     * 
-     * @param flag the flag to set.
-     */
-    public void setUnsealState(final boolean flag) {
-        checkSetterPreconditions();
-        
-        unsealState = flag ? PredicateSupport.alwaysTrue() : PredicateSupport.alwaysFalse();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Boolean apply(@Nullable final ProfileRequestContext profileRequestContext, @Nullable final byte[] value) {
-        checkComponentActive();
-        if (profileRequestContext == null || value == null) {
-            log.trace("Authentication request value could not be decoded, the input value was null");
-            return false;
-        }
-        // The value is already base64 decoded by the token manager
-        String valueString = new String(value, StandardCharsets.UTF_8);
-        // Possibly unseal and convert to class
-        try {
-            if (unsealState.test(profileRequestContext) && dataSealer != null) {
-                valueString = dataSealer.unwrap(valueString);
-            }
-            final AuthenticationRequestStateData authnState = 
-                    objectMapper.readValue(valueString, AuthenticationRequestStateData.class);
-            log.debug("Recovered authentication request state '{}'", authnState);
-            final MessageContext inboundCtx = profileRequestContext.getInboundMessageContext();
-            if (inboundCtx == null) {
-                log.trace("There is no Inbound Context, cannot set authentication request data");
-                return false;
-            }
-            if (StringSupport.trimOrNull(authnState.getAuthenticationAuthority()) == null) {
-                // Fail early as this is usually terminal
-                log.debug("Authenticating authority could not be recovered");
-                return false;
-            }
-            // Add identifier to the peer context now we know it
-            inboundCtx.ensureSubcontext(OIDCPeerEntityContext.class)
-                    .setIdentifier(authnState.getAuthenticationAuthority());
-            // Attach the recovered authentication request state information to the inbound context for later use
-            inboundCtx.ensureSubcontext(AuthnRequestStateDataContext.class).setAuthnState(authnState);
-            return true;
-            
-        } catch (final JsonProcessingException | DataSealerException e) {
-            log.error("Unable to process authentication request state data, was it sealed and you are not unsealing it?"
-                    , e);
-            return false;
-        }     
-    }
-
-}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationStateTokenConsumer.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationStateTokenConsumer.java
deleted file mode 100644
index 80d0357..0000000
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationStateTokenConsumer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.util.function.BiConsumer;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
-
-/**
- * A consumer that sets the authentication state token on the {@link AuthnRequestStateDataContext} in the outbound 
- * message context of the {@link ProfileRequestContext}.
- */
-public class SetAuthenticationStateTokenConsumer implements BiConsumer<ProfileRequestContext, String>{
-    
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(SetAuthenticationStateTokenConsumer.class);
-
-    /** {@inheritDoc} */
-    @Override
-    public void accept(final ProfileRequestContext prc, final String token) {        
-        final MessageContext outbound = prc.getOutboundMessageContext();
-        if (outbound == null) {
-            return;
-        }
-        log.trace("Setting authentication state token '{}' onto token context", token);
-        final AuthnRequestStateDataContext stateCtx = outbound.ensureSubcontext(AuthnRequestStateDataContext.class);
-        stateCtx.setToken(token);
-        
-    }
-
-}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StandardExtractionStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StandardExtractionStrategy.java
index e955b10..2fdcd7f 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StandardExtractionStrategy.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StandardExtractionStrategy.java
@@ -81,6 +81,7 @@ public class StandardExtractionStrategy implements Function<EndUserClaimsContext
         authnContextClassRefAttributeId = StringSupport.trimOrNull(id);
     }
     
+    /** {@inheritDoc} */
     @Nullable public Collection<IdPAttribute> apply(@Nullable final EndUserClaimsContext claimsContext) {
         
         final JWTClaimsSet idTokenClaims = claimsContext != null ? claimsContext.getUnprocessedIdTokenClaims() :  null;
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StateTokenLookupStrategy.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StateTokenLookupStrategy.java
new file mode 100644
index 0000000..f906585
--- /dev/null
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/StateTokenLookupStrategy.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.ParentProfileRequestContextLookup;
+
+import net.shibboleth.oidc.profile.core.StateToken;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.sp.context.StateDataContext;
+
+/**
+ * A strategy function that looks up the state value from the token stored in the {@link StateDataContext}. This 
+ * keys/links the OAuth 2.0 state to the stored authentication request data.
+*/
+public class StateTokenLookupStrategy implements Function<MessageContext, StateToken> {
+    
+    /** Lookup function for parent ProfileRequestContext. */
+    @Nonnull private static final ParentProfileRequestContextLookup<MessageContext> PRC_LOOKUP
+        = new ParentProfileRequestContextLookup<>();
+    
+    
+    /** Strategy used to locate the {@link StateDataContext}. */
+    @Nonnull private final Function<ProfileRequestContext,StateDataContext> stateDataContextLookupStrategy;
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param strategy the lookup strategy used to locate the state data context
+     */
+    public StateTokenLookupStrategy(@Nonnull @ParameterName(name="stateDataContextLookupStrategy") final 
+        Function<ProfileRequestContext, StateDataContext> strategy) {
+        stateDataContextLookupStrategy = 
+                Constraint.isNotNull(strategy, "StateDataContextLookupStrategy can not be null");
+    }
+        
+
+    /** {@inheritDoc} */
+    @Override
+    public StateToken apply(final MessageContext messageCtx) {  
+        if (messageCtx == null) {
+            return null;
+        }
+        final StateDataContext dataContext = stateDataContextLookupStrategy.apply(PRC_LOOKUP.apply(messageCtx));
+        if (dataContext == null) {
+            return null;
+        }
+        
+        return new StateToken(dataContext.getStateToken(), null);
+        
+    }
+
+}
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateUserInfoJSONObjectClaims.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateUserInfoJSONObjectClaims.java
index 627b95c..9283bb2 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateUserInfoJSONObjectClaims.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/ValidateUserInfoJSONObjectClaims.java
@@ -87,7 +87,7 @@ public class ValidateUserInfoJSONObjectClaims extends AbstractProfileAction {
      */
     public void setTokenResponseContextLookupStrategy(
             @Nonnull final Function<ProfileRequestContext, AccessTokenResponseContext> strategy) {
-    	checkSetterPreconditions();
+        checkSetterPreconditions();
         
         tokenResponseContextLookupStrategy = Constraint.isNotNull(strategy,
                 "TokenResponseContext lookup strategy cannot be null");
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationConditionTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationConditionTest.java
index c483f0f..8c442e5 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationConditionTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthTimeRequestedActivationConditionTest.java
@@ -17,11 +17,13 @@ package net.shibboleth.sp.oidc.profile.impl;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.nimbusds.jwt.JWTClaimsSet;
 
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
 
@@ -33,13 +35,18 @@ public class AuthTimeRequestedActivationConditionTest extends BaseApplicationAct
     /** The condition to test.*/
     private AuthTimeRequestedActivationCondition condition;
     
+    @BeforeMethod
+    public void setup() throws ComponentInitializationException {
+        super.beforeMethod();
+    }
+    
 
     @Test
     public void testAuthTimeRequested() {
         final AuthenticationRequestStateData state = new AuthenticationRequestStateData()
                 .setAuthTimeRequired(true);
         condition = new AuthTimeRequestedActivationCondition(prc -> 
-             new AuthnRequestStateDataContext().setAuthnState(state));        
+             new StateDataContext().setStateData(state));        
         
         final boolean requested = condition.test(prc, null);
         assertTrue(requested);
@@ -53,7 +60,7 @@ public class AuthTimeRequestedActivationConditionTest extends BaseApplicationAct
                 .setAuthTimeRequired(false);
 
         condition = new AuthTimeRequestedActivationCondition(
-            prcArg -> new AuthnRequestStateDataContext().setAuthnState(state)
+            prcArg -> new StateDataContext().setStateData(state)
         );
 
         final boolean requested = condition.test(prc, null);
@@ -75,7 +82,7 @@ public class AuthTimeRequestedActivationConditionTest extends BaseApplicationAct
     public void testFailSecureWhenAuthnStateIsNull() {
         // Context present but authnState is null -> should default to true (fail-secure)
         condition = new AuthTimeRequestedActivationCondition(
-            prcArg -> new AuthnRequestStateDataContext().setAuthnState(null)
+            prcArg -> new StateDataContext().setStateData(null)
         );
 
         final boolean requested = condition.test(prc, null);
@@ -88,7 +95,7 @@ public class AuthTimeRequestedActivationConditionTest extends BaseApplicationAct
                 .setAuthTimeRequired(true);
 
         condition = new AuthTimeRequestedActivationCondition(
-            prcArg -> new AuthnRequestStateDataContext().setAuthnState(state)
+            prcArg -> new StateDataContext().setStateData(state)
         );
 
         final JWTClaimsSet claims1 = new JWTClaimsSet.Builder().claim("foo", "bar").build();
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategyTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategyTest.java
index 11b3cc3..b2edbb6 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategyTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestStateForStorageStrategyTest.java
@@ -14,22 +14,18 @@
 
 package net.shibboleth.sp.oidc.profile.impl;
 
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import java.time.Duration;
 import java.time.Instant;
 
-import org.mockito.Mockito;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.core.Base64Variants;
 import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.json.JsonMapper;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
@@ -39,7 +35,7 @@ import com.nimbusds.openid.connect.sdk.Nonce;
 
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
 import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.security.DataSealer;
+import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
 
 /**
@@ -86,39 +82,28 @@ public class AuthenticationRequestStateForStorageStrategyTest extends BaseApplic
         maxAge = Duration.ofMinutes(1);
         authnRequest.setMaxAge(maxAge);
         assert mapper != null;
-        strategy.setAuthenticationRequestLookupStrategy(prc -> authnRequest);
-        strategy.setObjectMapper(mapper);        
+        strategy.setAuthenticationRequestLookupStrategy(prc -> authnRequest);       
     }
     
     @Test
     public void testSuccessfulConstruction() throws Exception {
         strategy.initialize();
-        final String requestState = strategy.apply(prc);        
-
-        final JsonNode root = mapper.readTree(requestState);
-        assertTrue(root.hasNonNull("nonce"), "Nonce should be present and non-null");
-        assertTrue(root.hasNonNull("authority"), "Issuer should be present and non-null");
-        assertTrue(root.hasNonNull("pkce_code_verifier"), "Pkce_code_verifier should be present and non-null");
-        assertTrue(root.hasNonNull("authn_request_time"), "authn_request_time should be present and non-null");
-        assertTrue(root.hasNonNull("max_age"), "max_age should be present and non-null");
-        assertTrue(root.hasNonNull("is_auth_time_required"), "is_auth_time_required should be present and non-null");
-        assertTrue(root.get("is_auth_time_required").asBoolean(),"is_auth_time_required should be required");
-    }
-    
-    @Test
-    public void testFailedConstruction_NoAuthenticatingAuthority() throws Exception {
-        strategy.setAuthenticationAuthorityLookupStrategy(prc -> null);
-        strategy.initialize();
-        final String requestState = strategy.apply(prc);        
-
-        assertNull(requestState, "no authenticating authority should result in a null construction");
+        final AuthenticationRequestStateData requestState = strategy.apply(prc);        
+        assert requestState != null;
+        assertTrue(requestState.getNonce()!=null, "Nonce should be present and non-null");
+        assertTrue(requestState.getAuthenticationAuthority()!=null, 
+                "Authenitcation authority should be present and non-null");
+        assertTrue(requestState.getPkceCodeVerifier()!=null, "Pkce_code_verifier should be present and non-null");
+        assertTrue(requestState.getRequestTime()!=null, "authn_request_time should be present and non-null");
+        assertTrue(requestState.getMaxAge()!=null, "max_age should be present and non-null");
+        assertTrue(requestState.isAuthTimeRequired(), "is_auth_time_required should be present and non-null");
     }
     
     @Test
     public void testFailedConstruction_NoAuthenticationRequest() throws Exception {
         strategy.setAuthenticationRequestLookupStrategy(prc -> null);
         strategy.initialize();
-        final String requestState = strategy.apply(prc);        
+        final AuthenticationRequestStateData requestState = strategy.apply(prc);        
 
         assertNull(requestState, "no authentication request should result in a null construction");
     }
@@ -127,56 +112,16 @@ public class AuthenticationRequestStateForStorageStrategyTest extends BaseApplic
     public void testSuccessfulConstruction_NoPKCE() throws Exception {
         authnRequest.setCodeVerifier(null);
         strategy.initialize();
-        final String requestState = strategy.apply(prc);        
-
-        final JsonNode root = mapper.readTree(requestState);
-        assertTrue(root.hasNonNull("nonce"), "Nonce should be present and non-null");
-        assertTrue(root.hasNonNull("authority"), "Issuer should be present and non-null");
-        assertNull(root.get("pkce_code_verifier"), "Pkce_code_verifier should be null");
-        assertTrue(root.hasNonNull("authn_request_time"), "authn_request_time should be present and non-null");
-        assertTrue(root.hasNonNull("max_age"), "max_age should be present and non-null");
-        assertTrue(root.hasNonNull("is_auth_time_required"), "is_auth_time_required should be present and non-null");
-        assertTrue(root.get("is_auth_time_required").asBoolean(),"is_auth_time_required should be required");
-
-    }
-    
-    @SuppressWarnings("null")
-    @Test
-    public void testSuccessfulConstruction_WithDataSealer() throws Exception {
-        final DataSealer sealer = Mockito.mock(DataSealer.class);
-        Mockito.when(sealer.wrap(Mockito.anyString())).thenReturn("sealed");
-        strategy.setDataSealer(sealer);
-        strategy.initialize();
-        final String requestState = strategy.apply(prc);        
-        assertNotNull(requestState, "request state should not be null");
-        assert requestState != null;
-        assertTrue(requestState.contains("sealed"), "request state should be sealed");
-
-    }
-    
-    @SuppressWarnings("null")
-    @Test
-    public void testSuccessfulConstruction_WithDataSealerButSealedPredicateIsFalse() throws Exception {
-        final DataSealer sealer = Mockito.mock(DataSealer.class);
-        Mockito.when(sealer.wrap(Mockito.anyString())).thenReturn("sealed");
-        strategy.setDataSealer(sealer);
-        strategy.setSealStatePredicate(prc -> false);
-        strategy.initialize();
+        final AuthenticationRequestStateData requestState = strategy.apply(prc);    
         
-        final String requestState = strategy.apply(prc);        
-        
-        assertNotNull(requestState, "request state should not be null");
         assert requestState != null;
-        assertFalse(requestState.contains("sealed"), "request state should be sealed");
-        
-        final JsonNode root = mapper.readTree(requestState);
-        assertTrue(root.hasNonNull("nonce"), "Nonce should be present and non-null");
-        assertTrue(root.hasNonNull("authority"), "Issuer should be present and non-null");
-        assertTrue(root.hasNonNull("pkce_code_verifier"), "Pkce_code_verifier should be present and non-null");
-        assertTrue(root.hasNonNull("authn_request_time"), "authn_request_time should be present and non-null");
-        assertTrue(root.hasNonNull("max_age"), "max_age should be present and non-null");
-        assertTrue(root.hasNonNull("is_auth_time_required"), "is_auth_time_required should be present and non-null");
-        assertTrue(root.get("is_auth_time_required").asBoolean(),"is_auth_time_required should be required");
+        assertTrue(requestState.getNonce()!=null, "Nonce should be present and non-null");
+        assertTrue(requestState.getAuthenticationAuthority()!=null, 
+                "Authenitcation authority should be present and non-null");
+        assertTrue(requestState.getPkceCodeVerifier()==null, "Pkce_code_verifier should be present and non-null");
+        assertTrue(requestState.getRequestTime()!=null, "authn_request_time should be present and non-null");
+        assertTrue(requestState.getMaxAge()!=null, "max_age should be present and non-null");
+        assertTrue(requestState.isAuthTimeRequired(), "is_auth_time_required should be present and non-null");
 
     }
 
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunctionTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunctionTest.java
index d697468..91b862f 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunctionTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/AuthenticationRequestTimeLookupFunctionTest.java
@@ -23,7 +23,7 @@ import java.time.Instant;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.testng.annotations.Test;
 
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 
 /**
@@ -38,11 +38,12 @@ public class AuthenticationRequestTimeLookupFunctionTest {
     public void testAuthTimeReturned() {
         final Instant now = Instant.now();
 
-        final AuthenticationRequestStateData state = new AuthenticationRequestStateData().setAuthnRequestTime(now);
+        final AuthenticationRequestStateData state = (AuthenticationRequestStateData) 
+                new AuthenticationRequestStateData().setRequestTime(now);
 
         final AuthenticationRequestTimeLookupFunction function = new AuthenticationRequestTimeLookupFunction();
 
-        function.setAuthenticationRequestLookupStrategy(prc -> new AuthnRequestStateDataContext().setAuthnState(state));
+        function.setStateDataContextLookupStrategy(prc -> new StateDataContext().setStateData(state));
 
         final Instant result = function.apply(new ProfileRequestContext());
         assertEquals(result, now);
@@ -56,13 +57,14 @@ public class AuthenticationRequestTimeLookupFunctionTest {
         final Instant now = Instant.now();
         final Duration skew = Duration.ofSeconds(10);
 
-        final AuthenticationRequestStateData state = new AuthenticationRequestStateData().setAuthnRequestTime(now);
+        final AuthenticationRequestStateData state = (AuthenticationRequestStateData)
+                new AuthenticationRequestStateData().setRequestTime(now);
 
         final AuthenticationRequestTimeLookupFunction function = new AuthenticationRequestTimeLookupFunction();
         assert skew != null;
         function.setClockSkew(skew);
 
-        function.setAuthenticationRequestLookupStrategy(prc -> new AuthnRequestStateDataContext().setAuthnState(state));
+        function.setStateDataContextLookupStrategy(prc -> new StateDataContext().setStateData(state));
 
         final Instant result = function.apply(new ProfileRequestContext());
         assertEquals(result, now.minus(skew));
@@ -77,7 +79,7 @@ public class AuthenticationRequestTimeLookupFunctionTest {
 
         final AuthenticationRequestTimeLookupFunction function = new AuthenticationRequestTimeLookupFunction();
 
-        function.setAuthenticationRequestLookupStrategy(prc -> new AuthnRequestStateDataContext().setAuthnState(state));
+        function.setStateDataContextLookupStrategy(prc -> new StateDataContext().setStateData(state));
 
         final Instant result = function.apply(new ProfileRequestContext());
         assertNull(result);
@@ -90,7 +92,7 @@ public class AuthenticationRequestTimeLookupFunctionTest {
     public void testNoAuthnContextReturnsNull() {
         final AuthenticationRequestTimeLookupFunction function = new AuthenticationRequestTimeLookupFunction();
 
-        function.setAuthenticationRequestLookupStrategy(prc -> null);
+        function.setStateDataContextLookupStrategy(prc -> null);
 
         final Instant result = function.apply(new ProfileRequestContext());
         assertNull(result);
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookieTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookieTest.java
deleted file mode 100644
index 9f0a1f1..0000000
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/IssueStateCookieTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.webflow.execution.Event;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import jakarta.servlet.http.Cookie;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.codec.DecodingException;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.net.CookieManager;
-import net.shibboleth.shared.net.CookieManager.SameSiteValue;
-import net.shibboleth.shared.primitive.NonnullSupplier;
-import net.shibboleth.sp.ddf.DDF;
-import net.shibboleth.sp.profile.SPConstants;
-import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
-
-
-
-/**
- * Unit test for {@link IssueStateCookie} action.
- */
- at SuppressWarnings("javadoc")
-public class IssueStateCookieTest extends BaseApplicationActionTest {
-
-    @Nonnull @NotEmpty private static final String TEST_STATE = "foo";
-    @Nonnull @NotEmpty private static final String TEST_VALUE = "123456789";
-
-    private String stateValue;
-    
-    private DDF input;
-    private MockHttpServletRequest request;
-    private MockHttpServletResponse response;
-    
-    private CookieManager cookieManager;
-    private IssueStateCookie action;
-        
-    /**
-     * Set up test.
-     * 
-     * @throws ComponentInitializationException
-     */
-    @SuppressWarnings("null")
-    @BeforeMethod
-    public void setUp() throws ComponentInitializationException {
-        super.beforeMethod();
-        
-        request = new MockHttpServletRequest();
-        response = new MockHttpServletResponse();
-        
-        cookieManager = new CookieManager();
-        cookieManager.setHttpServletRequestSupplier(NonnullSupplier.of(request));
-        cookieManager.setHttpServletResponseSupplier(NonnullSupplier.of(response));
-        cookieManager.setCookieLimit(10);
-        cookieManager.setSameSite(SameSiteValue.None);
-        cookieManager.setMaxAge(-1);
-        cookieManager.initialize();
-        
-        action = new IssueStateCookie();
-        action.setCookieManager(cookieManager);
-        action.setStateValueLookupStrategy(new Function<>() {
-            public String apply(final ProfileRequestContext t) {
-                return stateValue;
-            }
-        });
-        
-        action.setErrorFatal(true);        
-
-        input = new DDF(null).structure();
-        arc.setInput(input);
-    }
-    
-    /**
-     * Tear down test.
-     */
-    @AfterMethod
-    public void tearDown() {
-        action.destroy();
-        cookieManager.destroy();
-    }
-
-    @DataProvider
-    Object[][] correlationData() {
-        return new Object[][] {
-            new Object[] { null, TEST_VALUE},            
-            new Object[] { TEST_STATE, TEST_VALUE},
-        };
-    }
-        
-    @Test(dataProvider="correlationData")
-    public void testAction(final String state, final String id) throws ComponentInitializationException {
-        action.initialize();
-        evaluateAction(state, id);
-    }
-    
-    @Test
-    public void testAction_NullValue() throws ComponentInitializationException {
-        action.setErrorFatal(false);
-        action.initialize();
-        evaluateAction(TEST_STATE, null);
-    }
-    
-    private void evaluateAction(final String state, final String id) {
-        stateValue = id;
-        if (state != null) {
-            input.addmember(SPConstants.STATE).string(state);
-        }
-        
-        final Event event = action.execute(src);
-        
-        if (state != null) {
-            ActionTestingSupport.assertProceedEvent(event);
-        } else {
-            ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
-            return;
-        }
-        
-        if (id == null) {
-            Assert.assertEquals(response.getCookies().length, 0);
-            return;
-        }
-        
-        final Cookie cookie = response.getCookie(IssueStateCookie.DEFAULT_COOKIE_PREFIX + state);
-        assert cookie != null;
-        Assert.assertEquals(cookie.getValue(), stateValue);
-        Assert.assertEquals(cookie.getMaxAge(), -1);
-        Assert.assertEquals(cookie.getAttribute("SameSite"), SameSiteValue.None.getValue());
-    }
-    
-    @Test
-    public void testPurge() throws ComponentInitializationException, DecodingException, InterruptedException {
-        action.initialize();
-        final List<Cookie> cookies = new ArrayList<>(12);
-        for (int i = 0; i < 12; ++i) {
-            cookies.add(new Cookie(IssueStateCookie.DEFAULT_COOKIE_PREFIX + i, "foo" + i));
-            Thread.sleep(250);
-        }
-        request.setCookies(cookies.toArray(new Cookie[12]));
-        
-        evaluateAction(TEST_STATE, TEST_VALUE);
-        
-        Assert.assertEquals(response.getCookies().length, 3);
-    }
-
-}
\ No newline at end of file
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunctionTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunctionTest.java
index 077147c..7148fe1 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunctionTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/MaxAgeFromAuthenticationRequestLookupFunctionTest.java
@@ -23,7 +23,7 @@ import org.testng.annotations.Test;
 
 import net.shibboleth.oidc.profile.config.impl.DefaultOIDCAuthorizationConfiguration;
 import net.shibboleth.profile.context.RelyingPartyContext;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 
 /**
@@ -44,7 +44,7 @@ public class MaxAgeFromAuthenticationRequestLookupFunctionTest {
         final MaxAgeFromAuthenticationRequestLookupFunction function = 
                 new MaxAgeFromAuthenticationRequestLookupFunction(Duration.ofSeconds(120));
 
-        function.setAuthenticationRequestLookupStrategy(prc -> new AuthnRequestStateDataContext().setAuthnState(state));
+        function.setStateDataContextLookupStrategy(prc -> new StateDataContext().setStateData(state));
 
         final Duration result = function.apply(new ProfileRequestContext());
         assertEquals(result, expected);
@@ -62,7 +62,7 @@ public class MaxAgeFromAuthenticationRequestLookupFunctionTest {
         final MaxAgeFromAuthenticationRequestLookupFunction func = new MaxAgeFromAuthenticationRequestLookupFunction(
                 Duration.ofSeconds(999));
 
-        func.setAuthenticationRequestLookupStrategy(prc -> null); // no authn request → fallback path
+        func.setStateDataContextLookupStrategy(prc -> null); // no authn request -- fallback path
 
         final ProfileRequestContext prc = new ProfileRequestContext();
 
@@ -84,7 +84,7 @@ public class MaxAgeFromAuthenticationRequestLookupFunctionTest {
                 new MaxAgeFromAuthenticationRequestLookupFunction(
                 defaultValue);
 
-        function.setAuthenticationRequestLookupStrategy(prc -> null);
+        function.setStateDataContextLookupStrategy(prc -> null);
 
         final Duration result = function.apply(new ProfileRequestContext());
         assertEquals(result, defaultValue);
@@ -104,7 +104,7 @@ public class MaxAgeFromAuthenticationRequestLookupFunctionTest {
                 new MaxAgeFromAuthenticationRequestLookupFunction(
                 defaultValue);
 
-        function.setAuthenticationRequestLookupStrategy(prc -> new AuthnRequestStateDataContext().setAuthnState(state));
+        function.setStateDataContextLookupStrategy(prc -> new StateDataContext().setStateData(state));
 
         final ProfileRequestContext prc = new ProfileRequestContext();
 
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java
index 7eeaace..aabcfb9 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRClaimsFromStateLookupStrategyTest.java
@@ -25,7 +25,7 @@ import org.mockito.Mockito;
 import org.testng.annotations.Test;
 
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
+import net.shibboleth.sp.context.StateDataContext;
 import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
 import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
 
@@ -46,10 +46,10 @@ public class RequestedACRClaimsFromStateLookupStrategyTest extends BaseApplicati
     public void testSuccess_AcrReturnedNullRemoved() {
         
         // Arrange
-        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+        final StateDataContext ctx = Mockito.mock(StateDataContext.class);
         final AuthenticationRequestStateData state = Mockito.mock(AuthenticationRequestStateData.class);
 
-        Mockito.when(ctx.getAuthnState()).thenReturn(state);
+        Mockito.when(ctx.getStateData()).thenReturn(state);
         Mockito.when(state.getAcrs()).thenReturn(Arrays.asList("loa1", null));
         
         final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
@@ -64,10 +64,10 @@ public class RequestedACRClaimsFromStateLookupStrategyTest extends BaseApplicati
     public void testSuccess_EmptyAcrList() {
         
         // Arrange
-        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+        final StateDataContext ctx = Mockito.mock(StateDataContext.class);
         final AuthenticationRequestStateData state = Mockito.mock(AuthenticationRequestStateData.class);
 
-        Mockito.when(ctx.getAuthnState()).thenReturn(state);
+        Mockito.when(ctx.getStateData()).thenReturn(state);
         Mockito.when(state.getAcrs()).thenReturn(CollectionSupport.emptyList());
         
         final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
@@ -82,9 +82,9 @@ public class RequestedACRClaimsFromStateLookupStrategyTest extends BaseApplicati
     public void testSuccess_NullRequestState() {
         
         // Arrange
-        final AuthnRequestStateDataContext ctx = Mockito.mock(AuthnRequestStateDataContext.class);
+        final StateDataContext ctx = Mockito.mock(StateDataContext.class);
 
-        Mockito.when(ctx.getAuthnState()).thenReturn(null);
+        Mockito.when(ctx.getStateData()).thenReturn(null);
         
         final var strategy = new RequestedACRClaimsFromStateLookupStrategy(prc -> ctx);
 
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java
index fab748f..a2ba5aa 100644
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/RequestedACRValidationActivationConditionTest.java
@@ -51,14 +51,14 @@ public class RequestedACRValidationActivationConditionTest extends BaseApplicati
     
     @Test
     public void testShouldValidateAcr_fromProfileConfig() {        
-        rpConfig.setShouldValidateAcrValue(true);
+        rpConfig.setValidateAcrValue(true);
         final boolean activate = condition.test(prc, null);
         assertTrue(activate);
     }
     
     @Test
     public void testShouldNotValidateAcr_fromProfileConfig() {        
-        rpConfig.setShouldValidateAcrValue(false);
+        rpConfig.setValidateAcrValue(false);
         final boolean activate = condition.test(prc, null);
         assertFalse(activate);
     }
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookieTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookieTest.java
deleted file mode 100644
index dd8f95f..0000000
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/ResolveStateCookieTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import org.mockito.Mockito;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.EventContext;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.net.CookieManager;
-import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
-
-/**
- * Tests for {@link ResolveStateCookie}.
- */
-public class ResolveStateCookieTest extends BaseApplicationActionTest {
-
-    private ResolveStateCookie action;
-    private CookieManager cookieManager;
-
-
-    @SuppressWarnings("null")
-    @BeforeMethod
-    public void BeforeMethod() throws ComponentInitializationException  {
-        super.beforeMethod();
-        action = new ResolveStateCookie();
-        cookieManager = Mockito.mock(CookieManager.class);
-        action.setCookieManager(cookieManager);        
-    }
-
-    @Test
-    public void testMissingStateTokenNonFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> null);
-        action.setErrorFatal(false);
-        action.initialize();
-
-        @SuppressWarnings("null")
-        final boolean result = action.doPreExecute(prc);
-        assertFalse(result);
-    }
-
-    /* Missing state token is fatal.*/
-    @Test
-    public void testMissingStateTokenFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> null);
-        action.setErrorFatal(true);
-        action.initialize();
-
-        @SuppressWarnings("null")
-        final boolean result = action.doPreExecute(prc);
-        assertFalse(result);
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNotNull(ec);
-        assert ec != null;
-        assertEquals(ec.getEvent(), EventIds.INVALID_MESSAGE);
-    }
-
-    /* The cookie is missing, but this is not fatal.*/
-    @SuppressWarnings("null")
-    @Test
-    public void testMissingCookieNonFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> "state");
-        action.setErrorFatal(false);
-        action.initialize();
-
-        Mockito.when(cookieManager.getCookieValue(Mockito.anyString(), Mockito.any())).thenReturn(null);
-
-        assertTrue(action.doPreExecute(prc));
-        action.doExecute(prc);
-        
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNull(ec);
-    }
-
-    /* The cookie is missing, and this is fatal.*/
-    @SuppressWarnings("null")
-    @Test
-    public void testMissingCookieFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> "state");
-        action.setErrorFatal(true);
-        action.initialize();
-
-        Mockito.when(cookieManager.getCookieValue(Mockito.anyString(), Mockito.any())).thenReturn(null);
-
-        assertTrue(action.doPreExecute(prc));
-        action.doExecute(prc);
-        
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNotNull(ec);
-        assert ec != null;
-        assertEquals(ec.getEvent(), EventIds.INVALID_MESSAGE);
-    }
-
-    /* Test cookie value found and set.*/
-    @SuppressWarnings("null")
-    @Test
-    public void testConsumerSuccess() throws ComponentInitializationException {
-        
-        action.setStateTokenLookupStrategy(prcFunct -> "state");
-        action.setCookieValueConsumerStrategy((prcFunct, value) -> Boolean.TRUE);
-        action.setErrorFatal(true);
-        action.initialize();
-
-        Mockito.when(cookieManager.getCookieValue(Mockito.anyString(), Mockito.any())).thenReturn("cookie-value");
-
-        assertTrue(action.doPreExecute(prc));
-        action.doExecute(prc);
-
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNull(ec);
-
-    }
-
-    /* Could not set cookie back to context, but not fatal.*/
-    @SuppressWarnings("null")
-    @Test
-    public void testConsumerFailureNonFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> "state");
-        action.setCookieValueConsumerStrategy((prcFunct, value) -> Boolean.FALSE);
-        action.setErrorFatal(false);
-        action.initialize();
-
-        Mockito.when(cookieManager.getCookieValue(Mockito.anyString(), Mockito.any())).thenReturn("cookie-value");
-
-        assertTrue(action.doPreExecute(prc));
-        action.doExecute(prc);
-
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNull(ec);
-    }
-
-    /* Could not set cookie back to context, and fatal.*/
-    @SuppressWarnings("null")
-    @Test
-    public void testConsumerFailureFatal() throws ComponentInitializationException {
-
-        action.setStateTokenLookupStrategy(prcFunct -> "state");
-        action.setCookieValueConsumerStrategy((prcFunct, value) -> Boolean.FALSE);
-        action.setErrorFatal(true);
-        action.initialize();
-
-        Mockito.when(cookieManager.getCookieValue(Mockito.anyString(), Mockito.any())).thenReturn("cookie-value");
-
-        assertTrue(action.doPreExecute(prc));
-        action.doExecute(prc);
-
-        final var ec = prc.getSubcontext(EventContext.class);
-        assertNotNull(ec);
-        assert ec != null;
-        assertEquals(ec.getEvent(), EventIds.INVALID_MESSAGE);
-    }
-
-}
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumerTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumerTest.java
deleted file mode 100644
index b262a84..0000000
--- a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/SetAuthenticationRequestToPeerContextConsumerTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed 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 net.shibboleth.sp.oidc.profile.impl;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-
-import java.nio.charset.StandardCharsets;
-
-import org.mockito.Mockito;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.security.DataSealer;
-import net.shibboleth.sp.oidc.context.AuthnRequestStateDataContext;
-import net.shibboleth.sp.oidc.profile.AuthenticationRequestStateData;
-import net.shibboleth.sp.profile.impl.BaseApplicationActionTest;
-
-/**
- * Tests for {@link SetAuthenticationRequestToPeerContextConsumer}.
- */
-public class SetAuthenticationRequestToPeerContextConsumerTest extends BaseApplicationActionTest{
-    
-    /** The Consumer, which is a function, to test.*/
-    private SetAuthenticationRequestToPeerContextConsumer function;
-    
-    /** The authentication state data used to construct and suitable JSON input string for deserialising.*/
-    private AuthenticationRequestStateData authnState;
-    
-    /** The serialized form of the authnState.*/
-    private String authnStateSerialized;
-    
-    /** The serialized form of the authnState as UTF-8 encoded bytes.*/
-    private byte[] authnStateSerializedAsBytes;
-    
-    @Override
-    @BeforeMethod
-    public void beforeMethod() throws ComponentInitializationException {
-        super.beforeMethod();
-        
-        function = new SetAuthenticationRequestToPeerContextConsumer();
-        function.setId("TestComponent");
-        
-        authnState = new AuthenticationRequestStateData();
-        authnState.setAuthenticationAuthority("https://op.example.org");
-        authnState.setPkceCodeVerifier("PKCECodeVerifier");
-        authnState.setNonce("nonce");
-        
-        final ObjectMapper mapper = new ObjectMapper();
-        function.setObjectMapper(mapper);
-        try {
-            authnStateSerialized = mapper.writeValueAsString(authnState);
-            authnStateSerializedAsBytes = authnStateSerialized.getBytes(StandardCharsets.UTF_8);
-        } catch (final JsonProcessingException e) {
-           throw new ComponentInitializationException(e);
-        }       
-        
-    }
-    
-    @Test
-    public void testSuccess() throws Exception {
-        function.initialize();
-        
-        function.apply(prc, authnStateSerializedAsBytes);
-        
-        final var inboundMsgCtx = prc.getInboundMessageContext();
-        assertNotNull(inboundMsgCtx);
-        assert inboundMsgCtx != null;
-        final var peerCtx = inboundMsgCtx.getSubcontext(OIDCPeerEntityContext.class);
-        assertNotNull(peerCtx);
-        assert peerCtx != null;
-        assertEquals(peerCtx.getIdentifier(), "https://op.example.org");
-        final var authnStateCtx = inboundMsgCtx.getSubcontext(AuthnRequestStateDataContext.class);
-        assertNotNull(authnStateCtx);
-        assert authnStateCtx != null;
-        assertNotNull(authnStateCtx.getAuthnState());
-        final var authnStateFromCtx = authnStateCtx.getAuthnState();
-        assertNotNull(authnStateFromCtx);
-        assert authnStateFromCtx != null;
-        assertEquals(authnStateFromCtx.getNonce(), "nonce");
-        assertEquals(authnStateFromCtx.getPkceCodeVerifier(), "PKCECodeVerifier");        
-    }   
-    
-    @SuppressWarnings("null")
-    @Test
-    public void testSuccess_SealedWithDataSealer() throws Exception {
-        final DataSealer sealer = Mockito.mock(DataSealer.class);
-        Mockito.when(sealer.unwrap(Mockito.anyString())).thenReturn(authnStateSerialized);
-        function.setDataSealer(sealer);
-        function.initialize();
-        
-        function.apply(prc, authnStateSerializedAsBytes);
-        
-        final var inboundMsgCtx = prc.getInboundMessageContext();
-        assertNotNull(inboundMsgCtx);
-        assert inboundMsgCtx != null;
-        final var peerCtx = inboundMsgCtx.getSubcontext(OIDCPeerEntityContext.class);
-        assertNotNull(peerCtx);
-        assert peerCtx != null;
-        assertEquals(peerCtx.getIdentifier(), "https://op.example.org");
-        final var authnStateCtx = inboundMsgCtx.getSubcontext(AuthnRequestStateDataContext.class);
-        assertNotNull(authnStateCtx);
-        assert authnStateCtx != null;
-        assertNotNull(authnStateCtx.getAuthnState());
-        final var authnStateFromCtx = authnStateCtx.getAuthnState();
-        assertNotNull(authnStateFromCtx);
-        assert authnStateFromCtx != null;
-        assertEquals(authnStateFromCtx.getNonce(), "nonce");
-        assertEquals(authnStateFromCtx.getPkceCodeVerifier(), "PKCECodeVerifier");        
-    }   
-    
-    @Test
-    public void testFail_BadJSONAuthnState() throws Exception {
-        function.initialize();
-        
-        function.apply(prc, "bad".getBytes(StandardCharsets.UTF_8));
-        
-        final var inboundMsgCtx = prc.getInboundMessageContext();
-        assertNotNull(inboundMsgCtx);
-        assert inboundMsgCtx != null;
-        final var peerCtx = inboundMsgCtx.getSubcontext(OIDCPeerEntityContext.class);
-        assertNull(peerCtx);
-        final var authnStateCtx = inboundMsgCtx.getSubcontext(AuthnRequestStateDataContext.class);
-        assertNull(authnStateCtx);
-           
-    } 
-
-}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list