[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