[java-idp-plugin-oidc-rp] branch main updated: Add predicates to express conditions in the flow

Phil Smart philip.smart at jisc.ac.uk
Thu Feb 17 17:02:32 UTC 2022


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=3f892d215e19feb016719242185d914ecdc89379

The following commit(s) were added to refs/heads/main by this push:
     new 3f892d2  Add predicates to express conditions in the flow
3f892d2 is described below

commit 3f892d215e19feb016719242185d914ecdc89379
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Feb 17 17:02:26 2022 +0000

    Add predicates to express conditions in the flow
---
 idp-oidc-rp-api/pom.xml                            |   5 +
 .../AbstractUserInfoResponseTypeCondition.java     |  87 +++++++++++++++++
 .../UserInfoEncryptedJWTResponseTypeCondition.java |  38 ++++++++
 .../context/logic}/UserInfoLookupCondition.java    |   2 +-
 .../UserInfoSignedJWTResponseTypeCondition.java    |  38 ++++++++
 .../plugin/authn/oidc/rp/impl/AddAuthzRequest.java |   9 +-
 .../authn/oidc/rp/impl/ProcessEndUserClaims.java   |   3 +-
 .../oidc-relying-party-authn-beans.xml             |  10 +-
 .../oidc-relying-party-authn-flow.xml              |  24 ++---
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  | 104 +++++++++++++++++++++
 10 files changed, 297 insertions(+), 23 deletions(-)

diff --git a/idp-oidc-rp-api/pom.xml b/idp-oidc-rp-api/pom.xml
index 67d9a6d..2a8126f 100644
--- a/idp-oidc-rp-api/pom.xml
+++ b/idp-oidc-rp-api/pom.xml
@@ -40,6 +40,11 @@
             <groupId>${idp.groupId}</groupId>
             <artifactId>idp-authn-api</artifactId>
             <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>net.shibboleth.oidc</groupId>
+            <artifactId>oidc-common-profile-api</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>com.nimbusds</groupId>
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/AbstractUserInfoResponseTypeCondition.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/AbstractUserInfoResponseTypeCondition.java
new file mode 100644
index 0000000..51f0e15
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/AbstractUserInfoResponseTypeCondition.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Abstract predicate for pulling out the {@link UserInfoResponseContext}. If either the
+ * profile request context or extracted UserInfo response context are null, false is returned
+ * immediately. 
+ */
+public abstract class AbstractUserInfoResponseTypeCondition implements Predicate<ProfileRequestContext> {
+    
+    /** Strategy used to look up the {@link UserInfoResponseContext}. */
+    @Nonnull private final Function<ProfileRequestContext, UserInfoResponseContext> 
+            userInfoResponseContextLookupStrategy; 
+    
+    /** Constructor.*/
+    protected AbstractUserInfoResponseTypeCondition() {
+        userInfoResponseContextLookupStrategy =
+                new ChildContextLookup<>(UserInfoResponseContext.class).compose(
+                        new InboundMessageContextLookup());
+    }
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param strategy the UserInfo response context lookup strategy to use.
+     */
+    protected AbstractUserInfoResponseTypeCondition(
+            @Nonnull final Function<ProfileRequestContext, UserInfoResponseContext> strategy) {
+        userInfoResponseContextLookupStrategy = 
+                Constraint.isNotNull(strategy, "UserInfoResponseContext lookup strategy can not be null");
+    }
+    
+    @Override
+    public boolean test(@Nullable final ProfileRequestContext prc) {
+        if (prc == null) {
+            return false;
+        } 
+        final UserInfoResponseContext context = userInfoResponseContextLookupStrategy.apply(prc);
+        if (context == null) {
+            return false;
+        } 
+        return doTest(prc, context);
+        
+    }
+
+    /** 
+     * Implementations should override this method to provide necessary logic.
+     * 
+     * @param prc the profile request context
+     * @param context the UserInfo response context
+     * 
+     * @return the result of this test
+     */
+    protected abstract boolean doTest(@Nonnull final ProfileRequestContext prc, 
+            @Nonnull final UserInfoResponseContext context);
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoEncryptedJWTResponseTypeCondition.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoEncryptedJWTResponseTypeCondition.java
new file mode 100644
index 0000000..04cfca2
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoEncryptedJWTResponseTypeCondition.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext;
+
+/**
+ * Return true if the UserInfo response was an encrypted JWT type.
+ */
+public class UserInfoEncryptedJWTResponseTypeCondition extends AbstractUserInfoResponseTypeCondition {
+
+    @Override
+    protected boolean doTest(@Nonnull final ProfileRequestContext prc, 
+            @Nonnull final UserInfoResponseContext context) {
+        if (context.getUserInfo() == null) {
+            return false;
+        }
+        return context.getUserInfo().isEncrypted();
+    }}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoLookupCondition.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoLookupCondition.java
similarity index 96%
rename from idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoLookupCondition.java
rename to idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoLookupCondition.java
index e2e0e24..3329b56 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/UserInfoLookupCondition.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoLookupCondition.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic;
 
 import java.util.function.Predicate;
 
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoSignedJWTResponseTypeCondition.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoSignedJWTResponseTypeCondition.java
new file mode 100644
index 0000000..d1322b8
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/UserInfoSignedJWTResponseTypeCondition.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext;
+
+/**
+ * Return true if the UserInfo response was a signed JWT type.
+ */
+public class UserInfoSignedJWTResponseTypeCondition extends AbstractUserInfoResponseTypeCondition {
+
+    @Override
+    protected boolean doTest(@Nonnull final ProfileRequestContext prc, 
+            @Nonnull final UserInfoResponseContext context) {
+        if (context.getUserInfo() == null) {
+            return false;
+        }
+        return context.getUserInfo().isSigned();
+    }}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
index 7cd1a66..0665f4b 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddAuthzRequest.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
+import java.util.List;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
@@ -65,7 +66,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 public class AddAuthzRequest extends AbstractAuthenticationAction {
     
     /** Class logger. */
-    @Nonnull private Logger log = LoggerFactory.getLogger(AddAuthzRequest.class);    
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AddAuthzRequest.class);    
     
     /** Overwrite an existing message? */
     private boolean overwriteExisting;
@@ -230,7 +231,10 @@ public class AddAuthzRequest extends AbstractAuthenticationAction {
         request.setEndpointURI(providerMetadata.getProviderInformation().getAuthorizationEndpointURI());
         request.setRedirectURI(clientMetadata.getClientInformation().getMetadata().getRedirectionURI());
         
-        request.getScope().add("profile");
+        // Add scopes
+        final List<String> scopes = 
+                clientMetadata.getClientInformation().getMetadata().getScope().toStringList();
+        scopes.forEach(s -> request.getScope().add(s));
         
         //TODO use strategy with injectable secure random implementation?
         request.setNonce(new Nonce(OIDCProxySupport.generateNonce(16)));
@@ -248,4 +252,5 @@ public class AddAuthzRequest extends AbstractAuthenticationAction {
         
     }
 
+
 }
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProcessEndUserClaims.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProcessEndUserClaims.java
index cd65321..51cabc8 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProcessEndUserClaims.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ProcessEndUserClaims.java
@@ -40,7 +40,6 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.AccessTokenResponseContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.EndUserClaimsContext;
 import net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext;
-import net.shibboleth.oidc.profile.core.OidcEventIds;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
@@ -48,7 +47,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * Process the end-user claims from the id_token and possible UserInfo claims sets.
  *  
  * <p>Sanitized both claims sets using a replaceable strategy. For example, 
- * by default to remove 'validation claims' that should not be exposed further by the system.</p>
+ * by default to remove standard JWT 'validation' claims that should not be exposed further by the system.</p>
  * 
  * <p>Merge the claims sets together to produce an aggregate claims set. The UserInfo claims can 
  * be empty i.e. claims from the UserInfo endpoint were not requested.</p>
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index b6492b9..d49e603 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -400,8 +400,8 @@
     
     
     <bean id="CheckUserInfoRequiredCondition"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.UserInfoLookupCondition"/>
-    
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoLookupCondition"/>
+        
     
     <!-- UserInfo endpoint beans -->
     
@@ -432,6 +432,12 @@
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ProcessEndUserClaims" scope="prototype" 
         p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
         p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
+        
+    <bean id="CheckUserInfoSignedJWTResponseTypeCondition"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoSignedJWTResponseTypeCondition"/>
+        
+    <bean id="CheckUserInfoEncryptedJWTResponseTypeCondition"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.UserInfoEncryptedJWTResponseTypeCondition"/>
     
     
     <!-- UserInfo response JWT validation -->
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index 8b31a5d..44e1bd6 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -102,14 +102,12 @@
         <!--  <evaluate expression="PopulateTokenEncryptionParameters" /> -->
          <evaluate expression="ValidateIDTokenClaims" />
          <evaluate expression="'proceed'" />
-         <transition on="proceed" to="CheckUserInfoRequired" />
+         <transition on="proceed" to="CheckUserInfoClaimsRequired" />
     </action-state>
     
-    <!-- Should we request information from the UserInfo endpoint based on profile config -->
-    <decision-state id="CheckUserInfoRequired">
+    <decision-state id="CheckUserInfoClaimsRequired">
         <if test="CheckUserInfoRequiredCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
-            then="UserInfoRequest"
-            else="FinalizeResponse" />
+            then="UserInfoRequest" else="FinalizeResponse" />
        <!-- if else here, we need to set id_token claims into EndUserClaimsContext -->
     </decision-state>
      
@@ -120,18 +118,11 @@
         <transition on="proceed" to="CheckUserInfoResponseType" />
      </action-state>
      
-     <!-- Make strategy for these conditions 
-     Also, what about a plain JWT? need to extract out the claims for those too -->
+     <!-- A plain JWT will skip token validation and go straight to claim validation -->
      <decision-state id="CheckUserInfoResponseType">
-        <if test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getInboundMessageContext()
-                .getSubcontext('net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext')
-                .getUserInfo().isSigned()"
+        <if test="CheckUserInfoSignedJWTResponseTypeCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
             then="ValidateSignedUserInfoJWT" />
-        <if test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
-                 .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getInboundMessageContext()
-                 .getSubcontext('net.shibboleth.idp.plugin.authn.oidc.rp.context.UserInfoResponseContext')
-                 .getUserInfo().isEncrypted()"
+        <if test="CheckUserInfoEncryptedJWTResponseTypeCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
             then="DecryptUserInfoJWT" 
             else="ValidateUserInfoClaimsSet"/>       
      </decision-state>
@@ -143,6 +134,7 @@
         <transition on="proceed" to="ValidateUserInfoClaimsSet" />
     </action-state>
     
+    <!-- TODO decrypt then check signature if signed, then check claims if signed! see 5.3.2 -->
     <action-state id="DecryptUserInfoJWT">
         <!--  <evaluate expression="PopulateTokenEncryptionParameters" /> -->
         <evaluate expression="ValidateUserInfoToken" /> <!--  Will die if not decrypted properly first -->
@@ -157,7 +149,7 @@
     </action-state>
 
     <action-state id="FinalizeResponse">
-        <evaluate expression="ProcessEndUserClaims" /> <!-- check this works if no userInfo -->
+        <evaluate expression="ProcessEndUserClaims" />
         <evaluate expression="ValidateOIDCAuthentication" />
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="proceed" />
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 df03349..1f5537f 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
@@ -64,6 +64,7 @@ import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
 import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
 import com.nimbusds.jwt.EncryptedJWT;
 import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
 import com.nimbusds.jwt.SignedJWT;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.ResponseMode;
@@ -356,6 +357,29 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         return signedJWT;
     }
     
+    /**
+     * Create a Plain UserInfo response JWT.
+     * 
+     * @param issuer the issuer
+     * @param audience the audience
+     * @return the signed JWT
+     * @throws JOSEException on error
+     */
+    private PlainJWT createPlainUserInfoJWTResponseJSON(final String issuer, final String audience) 
+            throws JOSEException {
+        
+        
+        final var payload = new JWTClaimsSet.Builder()
+                .issuer(issuer)
+                .audience(audience)
+                .subject("jdoe")
+                .claim("preferred_username", "jdoe")
+                .claim("name", "J Doe")
+                .build();
+        
+        return new PlainJWT(payload);
+    }
+    
     /**
      * Create a signed and encrypted UserInfo response JWT.
      * 
@@ -754,6 +778,86 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
       
        
+    }
+    
+    @Test 
+    public void testAuthnFlowFromAuthorizationCallback_UsingPlainJWTUserInfoResponse() 
+            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.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(createAccessTokenResponseJSON()));
+        // Second is userInfo
+        mockOPServer.enqueue(new MockResponse().setResponseCode(200)
+                .setHeader("content-type", "application/jwt")
+                .setBody(createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp")
+                        .serialize()));
+        mockOPServer.start(9918);
+        
+
+        final FlowExecutionImpl flowExecution = (FlowExecutionImpl)getFlowExecutionFactory()
+                .createFlowExecution(getFlowDefinition());
+        final ProfileRequestContext prc =  buildProfileRequestContext("authn/OIDCRelyingParty", false,false);
+        prc.getSubcontext(AuthenticationContext.class).setAuthenticatingAuthority(OP_ISSUER_ID);
+        
+        // create a nested PRC under the authentication context
+        final ProfileRequestContext nestPrc = (ProfileRequestContext) 
+                prc.getSubcontext(AuthenticationContext.class).addSubcontext(new ProfileRequestContext(), true);  
+        
+        // Add under nest PRC
+        final RelyingPartyContext partyContext = new RelyingPartyContext();
+        final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();        
+        partyContext.setProfileConfig(partyConfig);
+        nestPrc.addSubcontext(partyContext);
+       
+        // Setup outbound context
+        final MessageContext outMsgCtx = new MessageContext();        
+        outMsgCtx.setMessage(createAuthenticationRequest());        
+        outMsgCtx.addSubcontext(createPeerContext());
+        outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
+        outMsgCtx.addSubcontext(createClientMedataContext());
+        nestPrc.setOutboundMessageContext(outMsgCtx);    
+        
+        // Setup inbound context.
+        final MessageContext inMsgCtx = new MessageContext();
+        inMsgCtx.setMessage(createAuthenticationResponse());
+        nestPrc.setInboundMessageContext(inMsgCtx);
+        
+        // Add prc to flow.
+        prc.getSubcontext(AuthenticationContext.class)
+        .addSubcontext(new ExternalAuthenticationContext(new ExternalAuthenticationImpl(false)));
+                        flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
+                        
+        
+        updateFlowExecution(flowExecution);
+        
+        //set start view and ending event to transition on.
+        externalContext.setEventId("proceed");
+        setCurrentState("AuthRequest");       
+        resumeFlow(externalContext);
+        
+        mockOPServer.shutdown();
+        
+        //assert success conditions
+        assertFlowExecutionEnded();
+        assertNotNull(prc.getSubcontext(AuthenticationContext.class));
+        assertNotNull(prc.getSubcontext(SubjectCanonicalizationContext.class));
+        assertEquals(prc.getSubcontext(SubjectCanonicalizationContext.class).getPrincipalName(),"jdoe");
+      
+       
     }
     
     

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


More information about the commits mailing list