[java-idp-plugin-oidc-rp] branch main updated: Improve and add more isolated flow tests

Phil Smart philip.smart at jisc.ac.uk
Mon Feb 13 15:10:14 UTC 2023


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

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=90a26035690c1fcc6022ceef6a4b3d245e1c17e9

The following commit(s) were added to refs/heads/main by this push:
     new 90a2603  Improve and add more isolated flow tests
90a2603 is described below

commit 90a26035690c1fcc6022ceef6a4b3d245e1c17e9
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Feb 13 15:10:16 2023 +0000

    Improve and add more isolated flow tests
---
 ...RelyingPartyProxySigningParametersResolver.java |   9 +-
 .../OIDCRPFlowFromAuthenticationResponseTest.java  | 636 ++++++++++++++
 .../oidc/rp/impl/OIDCRPFlowPreRedirectTest.java    | 396 +++++++++
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  | 934 ++-------------------
 .../conf/test-relying-party-postconfig.xml         |   8 -
 .../resources/conf/test-relying-party-system.xml   |  15 +-
 ...test-provider-requestobject-HS512-only-sig.json |  62 ++
 7 files changed, 1194 insertions(+), 866 deletions(-)

diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
index f2bf391..a7b08a5 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/RelyingPartyProxySigningParametersResolver.java
@@ -49,10 +49,10 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
 
 /**
  * A specialization of {@link BasicSignatureSigningParametersResolver} which supports selecting signing credentials
- * from client secret credential criterion (e.g. from the relying party configuration) in addition to the configured 
+ * from client_secret credential criterion (e.g. from the relying party configuration) in addition to the configured 
  * signing credentials inside the signing configuration (determined by the superclass). 
  * 
- * <p>The upstream OP's metadata is also used to filter for those algorithms supported by the OP in addition to
+ * <p>The OpenID Providers's metadata is also used to filter for those algorithms supported by the OP in addition to
  * those supported by the security configuration.</p>
  * 
  *  * <p>
@@ -105,9 +105,8 @@ public class RelyingPartyProxySigningParametersResolver extends BasicSignatureSi
         // Add any static credentials from the criteria
         if (criteria.contains(ClientSecretCredentialCriterion.class)) {
             final ClientSecretCredential staticCred = 
-                    criteria.get(ClientSecretCredentialCriterion.class).getCredential();    
-
-            log.trace("Client Secret signing credential found in criterion");
+                    criteria.get(ClientSecretCredentialCriterion.class).getCredential();
+            log.trace("Client secret signing credential found in criterion");
             // Extract a key suitable for creating and validating MACs
             allCredentials.add(staticCred.toSigningCredential());
         }
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
new file mode 100644
index 0000000..83eb922
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowFromAuthenticationResponseTest.java
@@ -0,0 +1,636 @@
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.credential.Credential;
+import org.springframework.webflow.engine.impl.FlowExecutionImpl;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.EndUserClaimsContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
+import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
+import net.shibboleth.oidc.profile.config.JSONSecurityConfiguration;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
+import net.shibboleth.oidc.security.credential.impl.BasicJOSEObjectCredentialResolver;
+import net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver;
+import net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver;
+import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
+import net.shibboleth.oidc.security.jose.impl.BasicDecryptionConfiguration;
+import net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationConfiguration;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+/**
+ * RP flow tests that start after the OpenID Provider redirect back to the RP's callback endpoint. As the initial 
+ * actions are not run by these tests, most the context setup needs to be mocked. 
+ */
+public class OIDCRPFlowFromAuthenticationResponseTest extends OIDCRPFlowTest {
+    
+    /** 
+     * Test the flow from the external authorization request to the end of the flow.
+     * Using a MAC signed id_token and plain UserInfo JSON Response.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_PlainUserInfo() throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(  
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is plain userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(USERINFO_RESPONSE));
+        mockOPServer.start(9918);
+        
+
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
+                        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        assertStandardEndFlowSuccessConditions(prc);
+         
+    }
+    
+    /** 
+     * Test the flow from the external authorization request to the end of the flow.
+     * Using a MAC signed id_token and an invalid UserInfo JSON Response - it has not subject.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_InvalidPlainUserInfo() throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(   
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is plain userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(USERINFO_RESPONSE_NO_SUB));
+        mockOPServer.start(9918);
+        
+
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
+                        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        //assert success conditions
+        assertFlowExecutionEnded();
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));      
+
+    }
+
+    
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response() throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/");
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is userInfo
+        final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
+                OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/jwt")
+                .setBody(userInfoToken.serialize()));
+        mockOPServer.start(9918);
+        
+
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);  
+                        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        assertStandardEndFlowSuccessConditions(prc);   
+        
+    }
+    
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSignedIDToken_And_AsymetricSignedAndEncryptedUserInfoResponse() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONIDTokenSignedAndDirEncrypted(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is userInfo
+        final var userInfoTokenAndKey =
+                TestTokenHelper.createAsymetricSignedAndAsymetricEncryptedUserInfoJWTResponse(
+                        OP_ISSUER_ID,List.of(CLIENT_ID), CLIENT_ID);
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/jwt")
+                .setBody(userInfoTokenAndKey.getSecond().serialize()));
+        mockOPServer.start(9918);
+        
+
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
+        
+        final OIDCAuthorizationConfiguration partyConfig =
+                (OIDCAuthorizationConfiguration) prc.getSubcontext(AuthenticationContext.class)
+                    .getSubcontext(ProfileRequestContext.class)
+                    .getSubcontext(RelyingPartyContext.class)
+                    .getProfileConfig();
+        
+        partyConfig.setClientCredential(
+                TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
+        // Set a default security config for the profile config
+        final JSONSecurityConfiguration secConfig = new JSONSecurityConfiguration();
+        
+        final var idTokenDecryptConfig = new BasicDecryptionConfiguration();        
+        idTokenDecryptConfig.setContentEncryptionKeyCredentialResolver(
+                new ClientSecretCriterionCredentialResolver());
+        
+        final var decryptConfig = new BasicDecryptionConfiguration();        
+        decryptConfig.setContentEncryptionKeyCredentialResolver(
+                new ClientSecretCriterionCredentialResolver());
+        decryptConfig.setKEKCredentialResolver(new JOSEObjectCredentialResolver() {
+            
+            @Override
+            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
+                jwkCredential.setKid(userInfoTokenAndKey.getFirst().getSecond().getKeyID());                
+                try {
+                    jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getSecond().toPrivateKey());
+                    jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getSecond().toPublicKey());
+                } catch (final JOSEException e) {
+                    fail();
+                }                
+                return jwkCredential;
+            }            
+            @Override
+            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        });      
+        secConfig.setJwtDecryptionConfiguration(decryptConfig);    
+        
+        //Signature config for userinfo token
+        final BasicSignatureValidationConfiguration sigValidation = 
+                new BasicSignatureValidationConfiguration();
+        sigValidation.setSignatureTrustEngine(
+                new ExplicitKeySignedJWTTrustEngine(
+                new ChainingJOSEObjectCredentialResolver(List.of(
+                        new ClientSecretCriterionCredentialResolver(),
+                new JOSEObjectCredentialResolver() {
+                    
+                    @Override
+                    public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                        final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                        jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
+                        jwkCredential.setKid(userInfoTokenAndKey.getFirst().getFirst().getKeyID());                
+                        try {
+                            jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getFirst().toPrivateKey());
+                            jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getFirst().toPublicKey());
+                        } catch (final JOSEException e) {
+                            fail();
+                        }                
+                        return jwkCredential;
+                    }            
+                    @Override
+                    public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                        return List.of(resolveSingle(criteria));
+                    }
+                })), new BasicJOSEObjectCredentialResolver()));
+        
+        secConfig.setJwtSignatureValidationConfiguration(sigValidation);  
+        
+        partyConfig.setSecurityConfiguration(secConfig);             
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        assertStandardEndFlowSuccessConditions(prc);
+               
+    }
+    
+    /**
+     * Uses symmetric MAC and asymmetric encryption. Plain UserInfo response.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSigned_And_AsymetricEncryptedIDToken() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final Pair<String, RSAKey> accessTokenAndKey = 
+                TestTokenHelper.createAccessTokenResponseJSONWithSignedAndAsymmetricEncryptedIDToken(
+                OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(accessTokenAndKey.getFirst()));
+        // Second is userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(USERINFO_RESPONSE));
+        mockOPServer.start(9918);
+        
+
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);        
+        
+        final OIDCAuthorizationConfiguration partyConfig =
+                (OIDCAuthorizationConfiguration) prc.getSubcontext(AuthenticationContext.class)
+                    .getSubcontext(ProfileRequestContext.class)
+                    .getSubcontext(RelyingPartyContext.class)
+                    .getProfileConfig();        
+      
+        final JSONSecurityConfiguration secConfig = new JSONSecurityConfiguration();
+        
+        final BasicSignatureValidationConfiguration sigValidation = 
+                new BasicSignatureValidationConfiguration();
+        sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
+                new ClientSecretCriterionCredentialResolver(), 
+                new BasicJOSEObjectCredentialResolver()));
+        
+        secConfig.setJwtSignatureValidationConfiguration(sigValidation);   
+        
+        final var decryptConfig = new BasicDecryptionConfiguration();
+        decryptConfig.setKEKCredentialResolver(new JOSEObjectCredentialResolver() {
+            
+            @Override
+            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+                jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
+                jwkCredential.setKid(accessTokenAndKey.getSecond().getKeyID());                
+                try {
+                    jwkCredential.setPrivateKey(accessTokenAndKey.getSecond().toPrivateKey());
+                    jwkCredential.setPublicKey(accessTokenAndKey.getSecond().toPublicKey());
+                } catch (final JOSEException e) {
+                    fail();
+                }                
+                return jwkCredential;
+            }            
+            @Override
+            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+                return List.of(resolveSingle(criteria));
+            }
+        });
+
+        secConfig.setJwtDecryptionConfiguration(decryptConfig); 
+        partyConfig.setSecurityConfiguration(secConfig);
+
+                             
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        assertStandardEndFlowSuccessConditions(prc);
+      
+       
+    }
+    
+    /**
+     * Test a plain UserInfo JWT type. This can not happen, and should trigger an error.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_UsingPlainJSONObjectUserInfoResponse() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/jwt")
+                .setBody(TestTokenHelper.createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
+                        .serialize()));
+        mockOPServer.start(9918);
+        
+
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);           
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        //assert success conditions
+        assertFlowExecutionEnded();
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
+           
+    }
+    
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response_WithACRAMRTranslation() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/");
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is token exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
+                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
+        // Second is userInfo
+        final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
+                OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/jwt")
+                .setBody(userInfoToken.serialize()));
+        mockOPServer.start(9918);
+        
+
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);  
+        // Add mapping functions
+        addACRAndAMRFunctions(prc.getSubcontext(AuthenticationContext.class)
+                .getSubcontext(ProfileRequestContext.class));                
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        assertStandardEndFlowSuccessConditions(prc);  
+        // Add checks for added ACRs and AMRs
+        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class).getSubject()
+                .getPrincipals(AuthenticationContextClassReferencePrincipal.class));
+        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class).getSubject()
+                .getPrincipals(AuthenticationMethodPrincipal.class));
+        
+    }
+    
+    /** 
+     * Test the flow terminates correctly when an unsupported flow is used - which is unlikely to get
+     * this far.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_UnsupportedOIDCFlow() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(  
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        
+        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
+                        
+        // Add a response type that suggests this flow was triggered by the IMPLICIT grant.
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest)prc.getSubcontext(AuthenticationContext.class)
+                .getSubcontext(ProfileRequestContext.class).getOutboundMessageContext().getMessage();
+        
+        request.setResponseType(ResponseType.IDTOKEN);
+        
+        assertNotNull(request);
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+  
+        assertFlowExecutionEnded();
+        // Flow did not produce an end-user message context
+        assertNull(prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class)
+                .getInboundMessageContext().getSubcontext(EndUserClaimsContext.class));
+
+    }
+    
+    /** 
+     * Test the flow from the external authorization request to the end of the flow when an error
+     * is returned from the downstream OP.
+     * 
+     * @throws Exception on error.
+     */
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_ErrorAuthenticationResponse() 
+            throws Exception {
+        
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);        
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc =  buildProfileRequestContext("authn/OIDCRelyingParty", false,false);
+
+        prc.getSubcontext(AuthenticationContext.class)
+                    .addSubcontext(new ExternalAuthenticationContext(new ExternalAuthenticationImpl(false)));
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        
+        // create a nested PRC under the authentication context
+        final ProfileRequestContext nestPrc = (ProfileRequestContext) 
+                prc.getSubcontext(AuthenticationContext.class).addSubcontext(new ProfileRequestContext(), true);       
+       
+        final MessageContext outMsgCtx = new MessageContext();
+        final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID(OP_ISSUER_ID));
+        request.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
+        outMsgCtx.setMessage(request);
+        nestPrc.setOutboundMessageContext(outMsgCtx);
+        
+        final MessageContext inMsgCtx = new MessageContext();
+        inMsgCtx.setMessage(AuthenticationResponseParser.parse(
+                new URI("/idp/profile/Authn/OIDC/RP/callback?"
+                        + "error=login_required&error_description=Login%20required&"
+                        + "state=d0c455126e9078aaf5a8e84c0e1910ad.65317332")));
+        nestPrc.setInboundMessageContext(inMsgCtx);
+        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthnRequest");       
+        resumeFlow(externalContext);
+        
+        //assert success conditions
+        assertFlowExecutionEnded();   
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));      
+        
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java
new file mode 100644
index 0000000..c3f6932
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowPreRedirectTest.java
@@ -0,0 +1,396 @@
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.junit.Test;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.webflow.core.collection.LocalAttributeMap;
+import org.springframework.webflow.execution.FlowExecution;
+
+import com.nimbusds.oauth2.sdk.ResponseMode;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.security.jose.context.SecurityParametersContext;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+/**
+ * RP Flow tests for flows actions that built the authentication request before being redirected to the OP. Uses
+ * configuration from the loaded XML configuration files — unlike {@link OIDCRPFlowFromAuthenticationResponseTest}
+ * which needs to build the configuration programatically because the initial seeding of the configuration only happens
+ * pre-authn redirect. 
+ * 
+ */
+public class OIDCRPFlowPreRedirectTest extends OIDCRPFlowTest {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCRPFlowPreRedirectTest.class);
+    
+    
+    /**
+     * Test the flow running to the authorization redirect using default, basic, settings. 
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect() throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO)));
+       
+        mockOPServer.start(9918);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition()); 
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext", prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext);   
+        
+        mockOPServer.shutdown();
+        
+        final var nestedPrc = assertStandardAuthenticationRedirectFlowSuccessConditions(prc);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        assertTrue(request.getAcrs().isEmpty());
+        assertEquals(request.getRedirectURI().toASCIIString(), "https://localhost/callback");
+        assertEquals(request.getResponseMode(), ResponseMode.QUERY);
+        
+    }
+    
+    /**
+     * Test the flow to the authorization redirect using an OP whose RP config in XML is set to use a request object.
+     * The request object will be signed by not encrypted by default.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect_UsingRequestObject() throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT)));
+       
+        mockOPServer.start(9919);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+        
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext); 
+        
+        mockOPServer.shutdown();
+        
+        final var nestedPrc = assertStandardAuthenticationRedirectFlowSuccessConditions(prc);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNotNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        assertTrue(request.getAcrs().isEmpty());      
+
+        // Test request object was built correctly.
+        assertStandardRequestObjectSuccessConditions(request.getRequestObjectClaimsSet(), "https://localhost:9919");
+       
+    }
+    
+    /**
+     * Test the flow to the authorization redirect using an OP whose RP config in XML is set to use a request object,
+     * and the RP is configured to create a JWE request object.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect_UsingRequestObject_WithEncryption() throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE_ENCRYPT);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_ENCRYPT)));
+        
+        // Second is JWKSet lookup
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(REMOTE_JWKSET_RESPONSE)));
+       
+        mockOPServer.start(9921);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+        
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext);   
+        
+        mockOPServer.shutdown();
+        
+        final var nestedPrc = assertStandardAuthenticationRedirectFlowSuccessConditions(prc);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNotNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        assertTrue(request.getAcrs().isEmpty());  
+       
+        // Test request object was built correctly.
+        assertStandardRequestObjectSuccessConditions(request.getRequestObjectClaimsSet(), "https://localhost:9921");
+        
+        // Check security context
+        assertNotNull(nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class));
+        final SecurityParametersContext secContext = 
+                nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class);
+        
+        // Check signing params are set correctly
+        assertNotNull(secContext.getSignatureSigningParameters());
+        assertEquals("HS256",secContext.getSignatureSigningParameters().getSignatureAlgorithm()); 
+        assertNotNull(secContext.getSignatureSigningParameters().getSigningCredential());      
+        
+        // Check encryption params are set correctly
+        assertNotNull(secContext.getEncryptionParameters());
+        assertEquals("A128CBC-HS256",secContext.getEncryptionParameters().getDataEncryptionAlgorithm());  
+        assertEquals("RSA-OAEP",secContext.getEncryptionParameters().getKeyTransportEncryptionAlgorithm());
+        // No data enc credential, as that is derived once key encrypted. 
+        assertNull(secContext.getEncryptionParameters().getDataEncryptionCredential());
+        assertNotNull(secContext.getEncryptionParameters().getKeyTransportEncryptionCredential());
+    }
+    
+    /**
+     * Test the flow to the authorization redirect using an OP who's RP config in XML is set to use a request object.
+     * Configuration restricts the OP to only accepting RS256 signed request objects. So the RP must choose that.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect_UsingRequestObject_RSA256Signature() throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE_RSA256_SIG);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_RSA256_SIG)));
+       
+        mockOPServer.start(9920);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+        
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext);   
+        
+        mockOPServer.shutdown();
+        
+        final var nestedPrc = assertStandardAuthenticationRedirectFlowSuccessConditions(prc);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNotNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        assertTrue(request.getAcrs().isEmpty());  
+       
+        // Test request object was built correctly.
+        assertStandardRequestObjectSuccessConditions(request.getRequestObjectClaimsSet(), "https://localhost:9920");
+        
+        // Check security context
+        assertNotNull(nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class));
+        final SecurityParametersContext secContext = 
+                nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class);
+        
+        // Check signing params are set correctly
+        assertNotNull(secContext.getSignatureSigningParameters());
+        assertEquals("RS256",secContext.getSignatureSigningParameters().getSignatureAlgorithm()); 
+        assertNotNull(secContext.getSignatureSigningParameters().getSigningCredential());
+        
+    }
+    
+    /**
+     * Test the flow to the authorization redirect using an OP who's RP config in XML is set to use a request object.
+     * The OP and RP do not support the same set of signature algorithms, so this should fail.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect_Fail_UsingRequestObject_UnsupportedSignatureAlgorithm()
+            throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE_HS512_SIG);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile
+                        (GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_HS512_SIG)));
+       
+        mockOPServer.start(9923);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+        
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext);   
+        
+        mockOPServer.shutdown();
+        
+        assertFlowExecutionEnded();
+        
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class));
+        final var nestedPrc = prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        assertTrue(request.getAcrs().isEmpty());  
+        
+        // Check security context does not contain credentials and algorithms
+        assertNotNull(nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class));
+        final SecurityParametersContext secContext = 
+                nestedPrc.getOutboundMessageContext().getSubcontext(SecurityParametersContext.class);
+        
+        // Check signing params have not been built as signature algorithms did not match
+        assertNull(secContext.getSignatureSigningParameters());
+        
+    }
+    
+    /**
+     * Test the flow to the authorization redirect and add an authentication context class requested principal.
+     * 
+     * @throws Exception on error.
+     */
+    @Test
+    public void testFlowToAuthorizationRedirect_WithACRs() throws Exception {
+        setFlowPath(FLOW);
+        setFlowModelResources(flowResources);
+        setSubflows(subflows);
+        
+        final Map<String,String> mockProperties = Map.of(
+                "idp.service.clientinfo.failFast","false",
+                "idp.entityID", "http://idp.example.com/",
+                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
+        
+        setMockProperties(mockProperties);
+        
+        final MockWebServer mockOPServer = createSimpleServer();
+        // First is metadata exchange
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/json")
+                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO)));
+       
+        mockOPServer.start(9918);
+        
+        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
+        inputMap.put("calledAsSubflow", true);
+
+        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
+        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
+        
+        final RequestedPrincipalContext rpc = new RequestedPrincipalContext();
+        final List<Principal> requestedPrincipals = 
+                List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"));
+        rpc.setRequestedPrincipals(requestedPrincipals);
+        rpc.setOperator("exact");
+        prc.getSubcontext(AuthenticationContext.class).addSubcontext(rpc);
+        
+        flowExecution.getConversationScope().put("opensamlProfileRequestContext", prc);
+        updateFlowExecution(flowExecution);
+        flowExecution.start(inputMap, externalContext);    
+        
+        mockOPServer.shutdown();
+        
+        final var nestedPrc = assertStandardAuthenticationRedirectFlowSuccessConditions(prc);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) nestedPrc.getOutboundMessageContext().getMessage();
+        assertNull(request.getRequestObject());
+        assertNull(request.getRequestObjectClaimsSet());
+        assertNull(request.getRequestedClaims());
+        // Check ACR exists
+        assertEquals(request.getAcrs().size(), 1);  
+        
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index 3c4c4c9..c8d9816 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -34,43 +34,36 @@ import org.apache.http.conn.ssl.NoopHostnameVerifier;
 import org.apache.http.conn.ssl.TrustAllStrategy;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.ssl.SSLContextBuilder;
-import org.junit.Test;
 import org.opensaml.core.config.InitializationException;
 import org.opensaml.core.metrics.impl.MetricRegistryInitializer;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.security.credential.Credential;
 import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.core.io.ClassPathResource;
-import org.springframework.webflow.core.collection.LocalAttributeMap;
 import org.springframework.webflow.engine.Flow;
 import org.springframework.webflow.engine.impl.FlowExecutionImpl;
-import org.springframework.webflow.execution.FlowExecution;
 import org.springframework.webflow.test.MockFlowBuilderContext;
 
-import com.nimbusds.jose.JOSEException;
-import com.nimbusds.jose.JWEAlgorithm;
-import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.ResponseMode;
 import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.Audience;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
 import com.nimbusds.openid.connect.sdk.Nonce;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
 import net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
-import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
 import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
 import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.EndUserClaimsContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.principal.OIDCSubjectIdentifierPrincipal;
@@ -80,15 +73,11 @@ import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
 import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
 import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
-import net.shibboleth.oidc.authn.principal.AuthenticationContextClassReferencePrincipal;
 import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
 import net.shibboleth.oidc.profile.config.JSONSecurityConfiguration;
 import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
 import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
-import net.shibboleth.oidc.security.credential.BasicJWKCredential;
-import net.shibboleth.oidc.security.credential.JOSEObjectCredentialResolver;
 import net.shibboleth.oidc.security.credential.impl.BasicJOSEObjectCredentialResolver;
-import net.shibboleth.oidc.security.credential.impl.ChainingJOSEObjectCredentialResolver;
 import net.shibboleth.oidc.security.credential.impl.ClientSecretCriterionCredentialResolver;
 import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
 import net.shibboleth.oidc.security.jose.impl.BasicDecryptionConfiguration;
@@ -96,58 +85,54 @@ import net.shibboleth.oidc.security.jose.impl.BasicSignatureValidationConfigurat
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
-import net.shibboleth.utilities.java.support.collection.Pair;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
-import okhttp3.mockwebserver.MockResponse;
 import okhttp3.mockwebserver.MockWebServer;
 import okhttp3.tls.HandshakeCertificates;
 import okhttp3.tls.HeldCertificate;
 
 /** 
- * Test the OIDC relying party flow.
- * 
- * <p>Any test which tests flow execution up to the authentication request controller will use config
- * in the various XML configuration files. Any test which tests flow execution from the authentication 
- * request controller will need to setup all required contexts programatically. </p>
+ * Abstract class to test the OIDC relying party flow.
  * 
  * <p>Note, the profile configuration which normal exists in oidc-commons i.e.
  * inside the relying-party/postconfig.xml, is in the test resources tree in the /conf directory.</p>
  * */
 public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     
-    /** The OP Issuer to use.*/
-    private static final String OP_ISSUER_ID = "https://localhost:9918";
+    /** The Default OP Issuer to use.*/
+    protected static final String OP_ISSUER_ID = "https://localhost:9918";
     
     /** The OP Issuer to use with an override in the config to use the request object authn param.*/
-    private static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE = "https://localhost:9919";
+    protected static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE = "https://localhost:9919";
     
     /** The OP Issuer to use with an override in the config to use the request object authn param
      * signed using RS256.*/
-    private static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE_RSA256_SIG = "https://localhost:9920";
+    protected static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE_RSA256_SIG = "https://localhost:9920";
+    
+    /** The OP Issuer to use with an override in the config to use the request object authn param
+     * signed using HS512.*/
+    protected static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE_HS512_SIG = "https://localhost:9923";
     
     /** The OP Issuer to use with an override in the config to use the request object authn param
      * which is to be encrypted.*/
-    private static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE_ENCRYPT= "https://localhost:9921";
+    protected static final String OP_ISSUER_ID_REQUESTOBJECT_TRUE_ENCRYPT= "https://localhost:9921";
     
     /** A redirect_uri override.*/
-    private static final String REDIRECT_URI_OVERRIDE = "https://localhost/callback";
+    protected static final String REDIRECT_URI_OVERRIDE = "https://localhost/callback";
     
     /** The client_id.*/
-    private static final String CLIENT_ID = "demo_rp";
+    protected static final String CLIENT_ID = "demo_rp";
     
     /** The client_secret.*/
-    private static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+    protected static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
     
     /** A JWKSet resource.*/
-    private static final ClassPathResource REMOTE_JWKSET_RESPONSE = 
+    protected static final ClassPathResource REMOTE_JWKSET_RESPONSE = 
             new ClassPathResource("/conf/credentials/remote-jwkset-response.jwk");
 
     /**
      * Example of good provider metadata. Endpoints are localhost to support the 
      * mock server that is started. 
      */
-    private static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO = 
+    protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO = 
             new ClassPathResource("/metadata/test-provider-standard.json");
    
     
@@ -155,27 +140,32 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
      * Example of good provider metadata. Endpoints are localhost to support the 
      * mock server that is started. This OP supports the use of the request object.
      */
-    private static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT = 
+    protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT = 
             new ClassPathResource("/metadata/test-provider-requestobject.json");
     
     /**
      * Example of good provider metadata. Endpoints are localhost to support the 
      * mock server that is started. This OP supports the use of the request object.
      */
-    private static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_ENCRYPT = 
+    protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_ENCRYPT = 
             new ClassPathResource("/metadata/test-provider-requestobject-encrypt.json");;
     
     /**
-     * Example of good provider metadata. Endpoints are localhost to support the 
-     * mock server that is started. This OP supports the use of the request object.
+     * Example of good provider metadata. Only supports RS256 signature alg for request object.
      */
-    private static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_RSA256_SIG = 
+     protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_RSA256_SIG = 
             new ClassPathResource("/metadata/test-provider-requestobject-rs256-sig.json");
+     
+     /**
+      * Example of good provider metadata. Only supports HS512 signature alg for request object.
+      */
+      protected static final ClassPathResource GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_HS512_SIG = 
+             new ClassPathResource("/metadata/test-provider-requestobject-HS512-only-sig.json");
    
 
     /** Mock JSON Object response from the UserInfo endpoint.*/
     @Nonnull @NotEmpty
-    private static final String USERINFO_RESPONSE ="{\n"
+    protected static final String USERINFO_RESPONSE ="{\n"
             + "  \"sub\": \"jdoe\",\n"
             + "  \"website\": \"https://openid.net/\",\n"
             + "  \"zoneinfo\": \"America/Los_Angeles\",\n"
@@ -193,7 +183,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     
     /** Mock JSON Object response from the UserInfo endpoint.*/
     @Nonnull @NotEmpty
-    private static final String USERINFO_RESPONSE_NO_SUB ="{\n"
+    protected static final String USERINFO_RESPONSE_NO_SUB ="{\n"
             + "  \"website\": \"https://openid.net/\",\n"
             + "  \"zoneinfo\": \"America/Los_Angeles\",\n"
             + "  \"birthdate\": \"2000-02-03\",\n"
@@ -210,7 +200,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
 
     
     /** Path to the flow to be tested.*/
-    @Nonnull private static final String FLOW = 
+    @Nonnull protected static final String FLOW = 
             "/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml";
 
     
@@ -219,14 +209,14 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     @Nonnull private final Logger log = LoggerFactory.getLogger(OIDCRPFlowTest.class);
     
     /** List of mocked subflows.*/
-    @Nonnull @NonnullElements @Unmodifiable private final List<Flow> subflows = 
+    @Nonnull @NonnullElements @Unmodifiable protected final List<Flow> subflows = 
             List.of(MockFlowBuilder.MockNoOpFlow("c14n"));
     
     /** 
      * Map of flow resources that support building the flow to test.
      * These are only for parent flows, not subflows.
      */
-    @Nonnull @NonnullElements @Unmodifiable private final Map<String,String> flowResources = 
+    @Nonnull @NonnullElements @Unmodifiable protected final Map<String,String> flowResources = 
             Map.of(
             "classpath:/net/shibboleth/idp/flows/authn/authn-abstract-flow.xml","authn.abstract",
             "classpath:/flows/authn/conditions/conditions-flow.xml","authn/conditions",
@@ -351,7 +341,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
      * 
      * @throws UnknownHostException on error.
      */
-    private MockWebServer createSimpleServer() throws UnknownHostException {
+    protected MockWebServer createSimpleServer() throws UnknownHostException {
         //start mock server
         final MockWebServer mockServer = new MockWebServer();
         final String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
@@ -373,7 +363,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
      * 
      * @throws ParseException on error.
      */
-    private OIDCPeerEntityContext createPeerContext() throws ParseException {
+    protected OIDCPeerEntityContext createPeerContext() throws ParseException {
         final OIDCPeerEntityContext peerCtx = new OIDCPeerEntityContext();
         final OIDCProviderMetadata providerMetadata = 
                 OIDCProviderMetadata.parse(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO));
@@ -388,7 +378,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
      * 
      * @return the authentication request.
      */
-    private OIDCAuthenticationRequest createAuthenticationRequest() {
+    protected OIDCAuthenticationRequest createAuthenticationRequest() {
         final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID(OP_ISSUER_ID));
         request.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
         request.setNonce(new Nonce("abadnonce"));
@@ -397,7 +387,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         return request;
     }
     
-    private OAuth2ClientContext createOAuth2ClientContext(@Nonnull final String clientId, 
+    protected OAuth2ClientContext createOAuth2ClientContext(@Nonnull final String clientId, 
             @Nullable final URI redirectOverride) {
         final OAuth2ClientContext context = new OAuth2ClientContext();
         context.setClientId(clientId);
@@ -411,7 +401,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
 
      * @return the basic security configuration.
      */
-    private JSONSecurityConfiguration createBasicSecurityConfigAndValidationParams() {
+    protected JSONSecurityConfiguration createBasicSecurityConfigAndValidationParams() {
 
         final var securityConfig = new JSONSecurityConfiguration();
         
@@ -430,7 +420,12 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         return securityConfig;
     }
     
-    private void assertStandardSuccessConditions(final ProfileRequestContext prc) {
+    /**
+     * Assert the basic set of conditions expected at the end of the entire authentication flow.
+     * 
+     * @param prc the profile request context.
+     */
+    protected void assertStandardEndFlowSuccessConditions(final ProfileRequestContext prc) {
         //assert success conditions. 
         assertFlowExecutionEnded();
         assertNotNull(prc.getSubcontext(AuthenticationContext.class));
@@ -444,7 +439,40 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         assertEquals(subjectIndentifierPrincipal.getName(),"jdoe");
     }
     
-   
+    /**
+     * Assert the basic set of conditions expected when the authentication flow is at the authentication redirect.
+     * 
+     * @param rootPrc the root profile request context.
+     * 
+     * @return the nested profile request context to perform further checks over.
+     */
+    protected ProfileRequestContext assertStandardAuthenticationRedirectFlowSuccessConditions(
+            final ProfileRequestContext rootPrc) {
+        //assert success conditions. 
+        assertCurrentStateEquals("AuthnRequest");
+        assertNotNull(rootPrc.getSubcontext(AuthenticationContext.class));
+        assertNotNull(rootPrc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class));
+        assertNotNull(rootPrc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class)
+                .getOutboundMessageContext());
+        assertTrue(rootPrc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class)
+                .getOutboundMessageContext().getMessage() instanceof OIDCAuthenticationRequest);
+        final OIDCAuthenticationRequest request = 
+                (OIDCAuthenticationRequest) rootPrc.getSubcontext(AuthenticationContext.class)
+                .getSubcontext(ProfileRequestContext.class).getOutboundMessageContext().getMessage();
+        assertEquals(request.getResponseType(), ResponseType.CODE);
+        assertEquals(request.getRedirectURI().toASCIIString(), "https://localhost/callback");
+        assertEquals(request.getResponseMode(), ResponseMode.QUERY);
+        
+        return rootPrc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class);
+    }
+    
+    protected void assertStandardRequestObjectSuccessConditions(final ClaimsSet roClaims, final String audience) {
+        assertEquals("https://localhost/callback",roClaims.getStringClaim("redirect_uri"));
+        assertEquals("openid", roClaims.getStringClaim("scope"));
+        assertEquals("demo_rp", roClaims.getStringClaim("client_id"));
+        assertEquals("code", roClaims.getStringClaim("response_type"));
+        assertTrue(roClaims.getAudience().contains(new Audience(audience)));
+    }
     
     /** 
      * Create an authentication response.
@@ -453,7 +481,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
      * 
      * @throws Exception on error.
      */
-    private AuthenticationResponse createAuthenticationResponse() throws Exception {
+    protected AuthenticationResponse createAuthenticationResponse() throws Exception {
         return AuthenticationResponseParser.parse(
                 new URI("/idp/profile/Authn/OIDC/RP/callback"
                         + "?state=8df98fd63a53fa5b5433d6f8754bca5d.65317332&code=z8C2DCp6sn0D9aGbEqlrFesdPVRXPtDX"));
@@ -466,696 +494,9 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
             @Nonnull final boolean addC14Context) {
         return super.buildProfileRequestContext(flowId, forceAuthn, addC14Context);        
     }
-
-    @Test
-    public void testFlowToAuthorizationRedirect() throws Exception {
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is metadata exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO)));
-       
-        mockOPServer.start(9918);
-        
-        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
-        inputMap.put("calledAsSubflow", true);
-
-        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
-        flowExecution.getConversationScope()
-                    .put("opensamlProfileRequestContext", 
-                            buildProfileRequestContext("authn/OIDCRelyingParty",false,true));
-        updateFlowExecution(flowExecution);
-        flowExecution.start(inputMap, externalContext);   
-        
-        mockOPServer.shutdown();
-        
-        assertCurrentStateEquals("AuthnRequest");
-    }
-    
-    /**
-     * Test to the authorization redirect using an OP who's RP config in XML is set to use a request object.
-     * 
-     * @throws Exception on error.
-     */
-    @Test
-    public void testFlowToAuthorizationRedirect_UsingRequestObject() throws Exception {
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is metadata exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT)));
-       
-        mockOPServer.start(9919);
-        
-        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
-        inputMap.put("calledAsSubflow", true);
-        
-        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
-        
-        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
-        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
-        updateFlowExecution(flowExecution);
-        flowExecution.start(inputMap, externalContext); 
-        
-        mockOPServer.shutdown();
-        
-        assertCurrentStateEquals("AuthnRequest");
-    }
-    
-    /**
-     * Test to the authorization redirect using an OP who's RP config in XML is set to use a request object.
-     * And encryption is enabled
-     * 
-     * @throws Exception on error.
-     */
-    @Test
-    public void testFlowToAuthorizationRedirect_UsingRequestObject_WithEncryption() throws Exception {
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE_ENCRYPT);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is metadata exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_ENCRYPT)));
-        
-        // Second is JWKSet lookup
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(REMOTE_JWKSET_RESPONSE)));
-       
-        mockOPServer.start(9921);
-        
-        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
-        inputMap.put("calledAsSubflow", true);
-        
-        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
-        
-        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
-        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
-        updateFlowExecution(flowExecution);
-        flowExecution.start(inputMap, externalContext);   
-        
-        mockOPServer.shutdown();
-        
-        assertCurrentStateEquals("AuthnRequest");
-        
-    }
-    
-    /**
-     * Test to the authorization redirect using an OP who's RP config in XML is set to use a request object.
-     * 
-     * @throws Exception on error.
-     */
-    @Test
-    public void testFlowToAuthorizationRedirect_UsingRequestObject_RSA256Signature() throws Exception {
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID_REQUESTOBJECT_TRUE_RSA256_SIG);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is metadata exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO_USE_REQUEST_OBJECT_RSA256_SIG)));
-       
-        mockOPServer.start(9920);
-        
-        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
-        inputMap.put("calledAsSubflow", true);
-        
-        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
-        
-        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
-        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
-        updateFlowExecution(flowExecution);
-        flowExecution.start(inputMap, externalContext);   
-        
-        mockOPServer.shutdown();
-        
-        assertCurrentStateEquals("AuthnRequest");
-    }
-    
-    @Test
-    public void testFlowToAuthorizationRedirect_WithACRs() throws Exception {
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is metadata exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestJsonHelper.readJsonFromFile(GOOD_PROVIDER_CONFIGURATION_INFO)));
-       
-        mockOPServer.start(9918);
-        
-        final LocalAttributeMap<Object> inputMap = new LocalAttributeMap<>();
-        inputMap.put("calledAsSubflow", true);
-
-        final FlowExecution flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());  
-        final ProfileRequestContext prc = buildProfileRequestContext("authn/OIDCRelyingParty",false,true);
-        
-        final RequestedPrincipalContext rpc = new RequestedPrincipalContext();
-        final List<Principal> requestedPrincipals = 
-                List.of(new AuthnContextClassRefPrincipal("http://example.org/ac/classes/mfa"));
-        rpc.setRequestedPrincipals(requestedPrincipals);
-        rpc.setOperator("exact");
-        prc.getSubcontext(AuthenticationContext.class).addSubcontext(rpc);
-        
-        flowExecution.getConversationScope().put("opensamlProfileRequestContext", prc);
-        updateFlowExecution(flowExecution);
-        flowExecution.start(inputMap, externalContext);    
-        
-        mockOPServer.shutdown();
-        
-        assertCurrentStateEquals("AuthnRequest");
-        
-        
-    }
-    
-    
-    /** 
-     * Test the flow from the external authorization request to the end of the flow.
-     * Using a MAC signed id_token and plain UserInfo JSON Response.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_PlainUserInfo() throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(  
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is plain userInfo
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(USERINFO_RESPONSE));
-        mockOPServer.start(9918);
-        
-
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
-                        
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        assertStandardSuccessConditions(prc);
-         
-    }
-    
-    /** 
-     * Test the flow from the external authorization request to the end of the flow.
-     * Using a MAC signed id_token and an invalid UserInfo JSON Response - it has not subject.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_MACSignedIDToken_InvalidPlainUserInfo() throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(   
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is plain userInfo
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(USERINFO_RESPONSE_NO_SUB));
-        mockOPServer.start(9918);
-        
-
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
-                        
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        //assert success conditions
-        assertFlowExecutionEnded();
-        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
-        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));      
-
-    }
-    
-    
-    
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response() throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/");
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is userInfo
-        final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
-                OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/jwt")
-                .setBody(userInfoToken.serialize()));
-        mockOPServer.start(9918);
-        
-
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);  
-                        
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        assertStandardSuccessConditions(prc);   
-        
-    }
-    
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSignedIDToken_And_AsymetricSignedAndEncryptedUserInfoResponse() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONIDTokenSignedAndDirEncrypted(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is userInfo
-        final var userInfoTokenAndKey =
-                TestTokenHelper.createAsymetricSignedAndAsymetricEncryptedUserInfoJWTResponse(
-                        OP_ISSUER_ID,List.of(CLIENT_ID), CLIENT_ID);
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/jwt")
-                .setBody(userInfoTokenAndKey.getSecond().serialize()));
-        mockOPServer.start(9918);
-        
-
-        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
-        
-        final OIDCAuthorizationConfiguration partyConfig =
-                (OIDCAuthorizationConfiguration) prc.getSubcontext(AuthenticationContext.class)
-                    .getSubcontext(ProfileRequestContext.class)
-                    .getSubcontext(RelyingPartyContext.class)
-                    .getProfileConfig();
-        
-        partyConfig.setClientCredential(
-                TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
-        // Set a default security config for the profile config
-        final JSONSecurityConfiguration secConfig = new JSONSecurityConfiguration();
-        
-        final var idTokenDecryptConfig = new BasicDecryptionConfiguration();        
-        idTokenDecryptConfig.setContentEncryptionKeyCredentialResolver(
-                new ClientSecretCriterionCredentialResolver());
-        
-        final var decryptConfig = new BasicDecryptionConfiguration();        
-        decryptConfig.setContentEncryptionKeyCredentialResolver(
-                new ClientSecretCriterionCredentialResolver());
-        decryptConfig.setKEKCredentialResolver(new JOSEObjectCredentialResolver() {
-            
-            @Override
-            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
-                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
-                jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
-                jwkCredential.setKid(userInfoTokenAndKey.getFirst().getSecond().getKeyID());                
-                try {
-                    jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getSecond().toPrivateKey());
-                    jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getSecond().toPublicKey());
-                } catch (final JOSEException e) {
-                    fail();
-                }                
-                return jwkCredential;
-            }            
-            @Override
-            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
-                return List.of(resolveSingle(criteria));
-            }
-        });      
-        secConfig.setJwtDecryptionConfiguration(decryptConfig);    
-        
-        //Signature config for userinfo token
-        final BasicSignatureValidationConfiguration sigValidation = 
-                new BasicSignatureValidationConfiguration();
-        sigValidation.setSignatureTrustEngine(
-                new ExplicitKeySignedJWTTrustEngine(
-                new ChainingJOSEObjectCredentialResolver(List.of(
-                        new ClientSecretCriterionCredentialResolver(),
-                new JOSEObjectCredentialResolver() {
-                    
-                    @Override
-                    public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
-                        final BasicJWKCredential jwkCredential = new BasicJWKCredential();
-                        jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
-                        jwkCredential.setKid(userInfoTokenAndKey.getFirst().getFirst().getKeyID());                
-                        try {
-                            jwkCredential.setPrivateKey(userInfoTokenAndKey.getFirst().getFirst().toPrivateKey());
-                            jwkCredential.setPublicKey(userInfoTokenAndKey.getFirst().getFirst().toPublicKey());
-                        } catch (final JOSEException e) {
-                            fail();
-                        }                
-                        return jwkCredential;
-                    }            
-                    @Override
-                    public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
-                        return List.of(resolveSingle(criteria));
-                    }
-                })), new BasicJOSEObjectCredentialResolver()));
-        
-        secConfig.setJwtSignatureValidationConfiguration(sigValidation);  
-        
-        partyConfig.setSecurityConfiguration(secConfig);             
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        assertStandardSuccessConditions(prc);
-               
-    }
-    
-    /**
-     * Uses symmetric MAC and asymmetric encryption. Plain UserInfo response.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_SymetricSigned_And_AsymetricEncryptedIDToken() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final Pair<String, RSAKey> accessTokenAndKey = 
-                TestTokenHelper.createAccessTokenResponseJSONWithSignedAndAsymmetricEncryptedIDToken(
-                OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(accessTokenAndKey.getFirst()));
-        // Second is userInfo
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(USERINFO_RESPONSE));
-        mockOPServer.start(9918);
-        
-
-        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);        
-        
-        final OIDCAuthorizationConfiguration partyConfig =
-                (OIDCAuthorizationConfiguration) prc.getSubcontext(AuthenticationContext.class)
-                    .getSubcontext(ProfileRequestContext.class)
-                    .getSubcontext(RelyingPartyContext.class)
-                    .getProfileConfig();        
-      
-        final JSONSecurityConfiguration secConfig = new JSONSecurityConfiguration();
-        
-        final BasicSignatureValidationConfiguration sigValidation = 
-                new BasicSignatureValidationConfiguration();
-        sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
-                new ClientSecretCriterionCredentialResolver(), 
-                new BasicJOSEObjectCredentialResolver()));
-        
-        secConfig.setJwtSignatureValidationConfiguration(sigValidation);   
-        
-        final var decryptConfig = new BasicDecryptionConfiguration();
-        decryptConfig.setKEKCredentialResolver(new JOSEObjectCredentialResolver() {
-            
-            @Override
-            public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
-                final BasicJWKCredential jwkCredential = new BasicJWKCredential();
-                jwkCredential.setAlgorithm(JWEAlgorithm.RSA_OAEP_256);
-                jwkCredential.setKid(accessTokenAndKey.getSecond().getKeyID());                
-                try {
-                    jwkCredential.setPrivateKey(accessTokenAndKey.getSecond().toPrivateKey());
-                    jwkCredential.setPublicKey(accessTokenAndKey.getSecond().toPublicKey());
-                } catch (final JOSEException e) {
-                    fail();
-                }                
-                return jwkCredential;
-            }            
-            @Override
-            public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
-                return List.of(resolveSingle(criteria));
-            }
-        });
-
-        secConfig.setJwtDecryptionConfiguration(decryptConfig); 
-        partyConfig.setSecurityConfiguration(secConfig);
-
-                             
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        assertStandardSuccessConditions(prc);
-      
-       
-    }
-    
-    /**
-     * Test a plain UserInfo JWT type. This can not happen, and should trigger an error.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_UsingPlainJSONObjectUserInfoResponse() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is userInfo
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/jwt")
-                .setBody(TestTokenHelper.createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
-                        .serialize()));
-        mockOPServer.start(9918);
-        
-
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);           
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        //assert success conditions
-        assertFlowExecutionEnded();
-        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
-        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
-           
-    }
-    
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_Using_HMAC_UserInfo_And_IDToken_Response_WithACRAMRTranslation() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/");
-        
-        setMockProperties(mockProperties);
-        
-        final MockWebServer mockOPServer = createSimpleServer();
-        // First is token exchange
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/json")
-                .setBody(TestTokenHelper.createAccessTokenResponseJSONWithHMACIDToken(
-                        OP_ISSUER_ID, CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET)));
-        // Second is userInfo
-        final var userInfoToken = TestTokenHelper.createHMACSignedUserInfoJWTResponseJSON(
-                OP_ISSUER_ID,CLIENT_ID, List.of(CLIENT_ID), CLIENT_SECRET);
-        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
-                .setHeader("content-type", "application/jwt")
-                .setBody(userInfoToken.serialize()));
-        mockOPServer.start(9918);
-        
-
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution);  
-        // Add mapping functions
-        addACRAndAMRFunctions(prc.getSubcontext(AuthenticationContext.class)
-                .getSubcontext(ProfileRequestContext.class));                
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        mockOPServer.shutdown();
-        
-        assertStandardSuccessConditions(prc);  
-        // Add checks for added ACRs and AMRs
-        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class).getSubject()
-                .getPrincipals(AuthenticationContextClassReferencePrincipal.class));
-        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class).getSubject()
-                .getPrincipals(AuthenticationMethodPrincipal.class));
-        
-    }
+   
     
-    private ProfileRequestContext populateBasicContextTreeFromAuthnResponse(
+    protected ProfileRequestContext populateBasicContextTreeFromAuthnResponse(
             final FlowExecutionImpl flowExecution) throws Exception {
         
         final ProfileRequestContext prc =  buildProfileRequestContext("authn/OIDCRelyingParty", false,false);
@@ -1201,7 +542,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
     }
     
     /* Add functions to convert ACR and AMR in id_token responses to principals.*/
-    private void addACRAndAMRFunctions(@Nonnull final ProfileRequestContext prc) {
+    protected void addACRAndAMRFunctions(@Nonnull final ProfileRequestContext prc) {
         final OIDCAuthorizationConfiguration partyConfig = 
                 (OIDCAuthorizationConfiguration) prc.getSubcontext(RelyingPartyContext.class).getProfileConfig();
         
@@ -1221,119 +562,12 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                     for (final String amr : amrs) {
                         if ("pwd".equals(amr)) {
                             principals.add(new
-                                    AuthenticationMethodPrincipal("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"));
+                                    AuthenticationMethodPrincipal(
+                                            "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"));
                         }
                     }
                     return principals;
                 });
     }
-
-    
-    /** 
-     * Test the flow from the external authorization request to the end of the flow when an error
-     * is returned from the downstream OP.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_ErrorAuthenticationResponse() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(
-                "idp.service.clientinfo.failFast","false",
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-
-        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        final ProfileRequestContext prc =  buildProfileRequestContext("authn/OIDCRelyingParty", false,false);
-
-        prc.getSubcontext(AuthenticationContext.class)
-                    .addSubcontext(new ExternalAuthenticationContext(new ExternalAuthenticationImpl(false)));
-        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
-        
-        // create a nested PRC under the authentication context
-        final ProfileRequestContext nestPrc = (ProfileRequestContext) 
-                prc.getSubcontext(AuthenticationContext.class).addSubcontext(new ProfileRequestContext(), true);       
-       
-        final MessageContext outMsgCtx = new MessageContext();
-        final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID(OP_ISSUER_ID));
-        request.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
-        outMsgCtx.setMessage(request);
-        nestPrc.setOutboundMessageContext(outMsgCtx);
-        
-        final MessageContext inMsgCtx = new MessageContext();
-        inMsgCtx.setMessage(AuthenticationResponseParser.parse(
-                new URI("/idp/profile/Authn/OIDC/RP/callback?"
-                        + "error=login_required&error_description=Login%20required&"
-                        + "state=d0c455126e9078aaf5a8e84c0e1910ad.65317332")));
-        nestPrc.setInboundMessageContext(inMsgCtx);
-        
-        
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-        
-        //assert success conditions
-        assertFlowExecutionEnded();   
-        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
-        assertNull(prc.getSubcontext(SubjectCanonicalizationContext.class));      
-        
-    }
-    
-    /** 
-     * Test the flow terminates correctly when an unsupported flow is used - which is unlikely to get
-     * this far.
-     * 
-     * @throws Exception on error.
-     */
-    @Test 
-    public void testAuthnFlowFromAuthorizationCallback_UnsupportedOIDCFlow() 
-            throws Exception {
-        
-        setFlowPath(FLOW);
-        setFlowModelResources(flowResources);
-        setSubflows(subflows);        
-        
-        final Map<String,String> mockProperties = Map.of(  
-                "idp.entityID", "http://idp.example.com/",
-                "idp.authn.oidc.rp.provider.proxyIssuer",OP_ISSUER_ID);
-        
-        setMockProperties(mockProperties);
-        
-        final var flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
-                .createFlowExecution(getFlowDefinition());
-        
-        final ProfileRequestContext prc = populateBasicContextTreeFromAuthnResponse(flowExecution); 
-                        
-        // Add a response type that suggests this flow was triggered by the IMPLICIT grant.
-        final OIDCAuthenticationRequest request = 
-                (OIDCAuthenticationRequest)prc.getSubcontext(AuthenticationContext.class)
-                .getSubcontext(ProfileRequestContext.class).getOutboundMessageContext().getMessage();
-        
-        request.setResponseType(ResponseType.IDTOKEN);
-        
-        assertNotNull(request);
-        updateFlowExecution(flowExecution);
-        
-        //set start view and ending event to transition on.
-        externalContext.setEventId("proceed");
-        setCurrentState("AuthnRequest");       
-        resumeFlow(externalContext);
-  
-        assertFlowExecutionEnded();
-        // Flow did not produce an end-user message context
-        assertNull(prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class)
-                .getInboundMessageContext().getSubcontext(EndUserClaimsContext.class));
-
-    }
+   
 }
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-postconfig.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-postconfig.xml
index 67fa032..ff0a19c 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-postconfig.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-postconfig.xml
@@ -147,16 +147,8 @@
             <list>
                 <util:constant
                     static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_256" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_384" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_RS_512" />
                 <util:constant
                     static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_256" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_384" />
-                <util:constant
-                    static-field="net.shibboleth.oidc.jwa.support.SignatureConstants.ALGO_ID_SIGNATURE_HS_512" />
             </list>
         </property>
     </bean>
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index aa6c8ca..b334ee7 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -44,7 +44,7 @@
     </bean>
 
     <util:list id="shibboleth.RelyingPartyOverrides">
-        <!-- This override is used in the OIDCRPFlowTest#testFlowToAuthorizationRedirect_UsingRequestObject test -->
+        <!-- This override is used in the testFlowToAuthorizationRedirect_UsingRequestObject test -->
         <bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9919">
             <property name="profileConfigurations">
                 <list>
@@ -53,7 +53,16 @@
                 </list>
             </property>
         </bean>
-        <!-- This override is used in the OIDCRPFlowTest#testFlowToAuthorizationRedirect_UsingRequestObject_RSA256_Signature test -->
+        <!-- This override is used in the testFlowToAuthorizationRedirect_Fail_UsingRequestObject_UnsupportedSignatureAlgorithm test -->
+        <bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9923">
+            <property name="profileConfigurations">
+                <list>
+                    <bean parent="OIDC.SSO" p:useRequestObject="true" p:signRequestObject="true"
+                    p:encryptRequestObject="false"/>
+                </list>
+            </property>
+        </bean>
+        <!-- This override is used in the testFlowToAuthorizationRedirect_UsingRequestObject_RSA256_Signature test -->
         <bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9920">
             <property name="profileConfigurations">
                 <list>
@@ -62,7 +71,7 @@
                 </list>
             </property>
         </bean>
-         <!-- This override is used in the OIDCRPFlowTest#testFlowToAuthorizationRedirect_UsingRequestObject_WithEncryption test -->
+         <!-- This override is used in the testFlowToAuthorizationRedirect_UsingRequestObject_WithEncryption test -->
         <bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9921">
             <property name="profileConfigurations">
                 <list>
diff --git a/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-HS512-only-sig.json b/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-HS512-only-sig.json
new file mode 100644
index 0000000..e75ed49
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/resources/metadata/test-provider-requestobject-HS512-only-sig.json
@@ -0,0 +1,62 @@
+{
+   "issuer":"https://localhost:9923",
+   "authorization_endpoint":"https://localhost:9923/o/oauth2/v2/auth",
+   "device_authorization_endpoint":"https://localhost:9923/device/code",
+   "token_endpoint":"https://localhost:9923/token",
+   "userinfo_endpoint":"https://localhost:9923/v1/userinfo",
+   "revocation_endpoint":"https://localhost:9923/revoke",
+   "jwks_uri":"https://localhost:9923/oauth2/v3/certs",
+   "request_parameter_supported":true,
+   "response_types_supported":[
+      "code",
+      "token",
+      "id_token",
+      "code token",
+      "code id_token",
+      "token id_token",
+      "code token id_token",
+      "none"
+   ],
+   "subject_types_supported":[
+      "public"
+   ],
+   "id_token_signing_alg_values_supported":[
+      "RS256"
+   ],
+   "request_object_signing_alg_values_supported":[
+      "HS512"
+   ],
+   "scopes_supported":[
+      "openid",
+      "email",
+      "profile"
+   ],
+   "token_endpoint_auth_methods_supported":[
+      "client_secret_post",
+      "client_secret_basic"
+   ],
+   "claims_supported":[
+      "aud",
+      "email",
+      "email_verified",
+      "exp",
+      "family_name",
+      "given_name",
+      "iat",
+      "iss",
+      "locale",
+      "name",
+      "picture",
+      "sub"
+   ],
+   "code_challenge_methods_supported":[
+      "plain",
+      "S256"
+   ],
+   "grant_types_supported":[
+      "authorization_code",
+      "refresh_token",
+      "urn:ietf:params:oauth:grant-type:device_code",
+      "urn:ietf:params:oauth:grant-type:jwt-bearer"
+   ]
+}
\ No newline at end of file

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


More information about the commits mailing list