[java-idp-oidc] branch main updated: JOIDC-231 - Multiple resource parameters cause MessageDecodingException

Henri Mikkonen henri.mikkonen at iki.fi
Thu Oct 3 10:51:27 UTC 2024


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

hjmikkon pushed a commit to branch main
in repository java-idp-oidc.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=6eda016b099559f148d666a5ea13a7c5081da8d7

The following commit(s) were added to refs/heads/main by this push:
     new 6eda016b JOIDC-231 - Multiple resource parameters cause MessageDecodingException
6eda016b is described below

commit 6eda016b099559f148d666a5ea13a7c5081da8d7
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Oct 3 13:51:05 2024 +0300

    JOIDC-231 - Multiple resource parameters cause MessageDecodingException
    
    https://shibboleth.atlassian.net/browse/JOIDC-231
    
    Switched the approach of using custom parameter name for 'resource' into using custom prefix for its values.
    The parameter values in authorization/authentication/token requests are now prefixed with 'urn:shibboleth.oidc.op.resource:'
    before feeding for Nimbus to be parsed. That makes the values to fulfill the strict URI-requirement. The lookup functions
    remove the prefix before returning the parameter value.
---
 .../DefaultRequestAudienceLookupFunction.java      | 23 ++++++++---
 .../TokenRequestAudienceLookupFunction.java        | 17 ++++++--
 .../decoding/impl/BaseOAuth2RequestDecoder.java    | 18 ++++----
 .../CustomResourceHttpServletRequestWrapper.java   | 15 ++++---
 .../impl/OIDCAuthenticationRequestDecoderTest.java | 27 ++++++------
 .../decoding/impl/OIDCTokenRequestDecoderTest.java | 35 +++++++++++++---
 .../OAuth2AuthorizationRequestDecoderTest.java     | 48 ++++++++++++++++------
 .../oauth2/profile/impl/ValidateAudienceTest.java  | 20 ++++-----
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    |  4 +-
 9 files changed, 139 insertions(+), 68 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestAudienceLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestAudienceLookupFunction.java
index 5efd1749..2116ec85 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestAudienceLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/DefaultRequestAudienceLookupFunction.java
@@ -14,9 +14,11 @@
 
 package net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate;
 
+import java.net.URI;
 import java.text.ParseException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -33,17 +35,18 @@ import net.shibboleth.shared.primitive.LoggerFactory;
 /**
  * A function that returns resource value of the authorization request.
  * 
- * The resource value is fetched via custom parameter name {@link #CUSTOM_RESOURCE_PARAM_NAME}. The message decoders
- * switches the incoming resource -parameter names into that. The request objects contain the standard resource
- * parameter name.
+ * The resource value is expected to be prefixed with 
+ * {@link DefaultRequestAudienceLookupFunction#CUSTOM_RESOURCE_PARAM_PREFIX}. The authorization request message decoder
+ * switches the incoming resource -parameter values to have that prefix. This function returns the value without the
+ * prefix.
  * 
  * @since 4.2.0
  */
 @ThreadSafeAfterInit
 public class DefaultRequestAudienceLookupFunction extends AbstractAuthorizationRequestLookupFunction<List<String>> {
 
-    /** Custom name for the resource parameter that allows non-uri values. */
-    public static final String CUSTOM_RESOURCE_PARAM_NAME = "shibboleth.oidc.op.resource";
+    /** Custom prefix for the resource parameter values to allow non-uri values. */
+    public static final String CUSTOM_RESOURCE_PARAM_PREFIX = "urn:shibboleth.oidc.op.resource:";
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(DefaultRequestAudienceLookupFunction.class);
@@ -71,8 +74,16 @@ public class DefaultRequestAudienceLookupFunction extends AbstractAuthorizationR
             return null;
         }
 
+        final List<URI> resources = req.getResources();
+        if (resources == null) {
+            return null;
+        }
         return checkRequestObject(req, isRequestObjectFromPar, "resource",
-                req.getCustomParameter(CUSTOM_RESOURCE_PARAM_NAME), null);
+                resources.stream()
+                    .filter(Objects::nonNull)
+                    .map(URI::toString)
+                    .map(str -> str.substring(CUSTOM_RESOURCE_PARAM_PREFIX.length()))
+                    .toList(), null);
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/TokenRequestAudienceLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/TokenRequestAudienceLookupFunction.java
index 9579df08..a5c1e9f2 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/TokenRequestAudienceLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/TokenRequestAudienceLookupFunction.java
@@ -14,7 +14,9 @@
 
 package net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate;
 
+import java.net.URI;
 import java.util.List;
+import java.util.Objects;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -23,9 +25,10 @@ import javax.annotation.concurrent.ThreadSafe;
 import com.nimbusds.oauth2.sdk.TokenRequest;
 
 /**
- * A function that returns a copy of requested resource list from a {@link TokenRequest}. The resource value is fetched
- * via custom parameter name {@link DefaultRequestAudienceLookupFunction#CUSTOM_RESOURCE_PARAM_NAME}. The token request
- * message decoder switches the incoming resource -parameter names into that.
+ * A function that returns a copy of requested resource list from a {@link TokenRequest}. The resource value is
+ * expected to be prefixed with the {@link DefaultRequestAudienceLookupFunction#CUSTOM_RESOURCE_PARAM_PREFIX}. The token
+ * request message decoder switches the incoming resource -parameter values to have that prefix. This function returns
+ * the value without the prefix.
  * 
  * @since 4.2.0
  */
@@ -35,7 +38,13 @@ public class TokenRequestAudienceLookupFunction extends AbstractTokenRequestLook
     /** {@inheritDoc} */
     @Override
     @Nullable List<String> doLookup(@Nonnull final TokenRequest req) {
-        return req.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME);
+        final List<URI> resources = req.getResources();
+        return resources == null ? null :
+            resources.stream()
+                .filter(Objects::nonNull)
+                .map(URI::toString)
+                .map(str -> str.substring(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX.length()))
+                .toList();
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/BaseOAuth2RequestDecoder.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/BaseOAuth2RequestDecoder.java
index 67230735..290a44da 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/BaseOAuth2RequestDecoder.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/BaseOAuth2RequestDecoder.java
@@ -135,10 +135,10 @@ public abstract class BaseOAuth2RequestDecoder<T extends Request> extends Abstra
     }
 
     /**
-     * Switches the 'resource' parameter names in the given HTTP request body into a custom parameter name. This allows
-     * us to avoid Nimbus library's control over the values of the parameter.
+     * Switches the 'resource' parameter values in the given HTTP request body to contain a custom parameter prefix.
+     * This allows us to avoid Nimbus library's strict requirement of URI values for the parameter.
      * 
-     * @param httpRequest The HTTP request object where the parameter names are switched
+     * @param httpRequest The HTTP request object where the parameter values are prefixed
      */
     protected void switchIntoCustomResource(@Nonnull final HTTPRequest httpRequest) {
         final String body = httpRequest.getQuery();
@@ -148,22 +148,22 @@ public abstract class BaseOAuth2RequestDecoder<T extends Request> extends Abstra
     }
 
     /**
-     * Transforms the 'resource' parameter names in the given input with a custom one defined by {@link
-     * DefaultRequestAudienceLookupFunction#CUSTOM_RESOURCE_PARAM_NAME}.
+     * Transforms the 'resource' parameter values in the given input to contain a prefix defined by {@link
+     * DefaultRequestAudienceLookupFunction#CUSTOM_RESOURCE_PARAM_PREFIX}.
      * 
      * @param string the input
-     * @return the input with resource parameter names transformed into custom ones.
+     * @return the input with resource parameter values transformed having the custom prefix.
      */
     @Nullable public static String transformResourceParameter(@Nullable final String string) {
         String input = string;
         if (input == null || !input.contains("resource=")) {
             return input;
         }
-        final String customParameterName = DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME;
+        final String customParameterPrefix = DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX;
         if (input.startsWith("resource=")) {
-            input = customParameterName + "=" + input.substring("resource=".length());
+            input = "resource=" + customParameterPrefix + input.substring("resource=".length());
         }
-        input = input.replace("&resource=", "&" + customParameterName + "=");
+        input = input.replace("&resource=", "&resource=" + customParameterPrefix);
         log.debug("Returning modified string {}", input);
         return input;
     }
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomResourceHttpServletRequestWrapper.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomResourceHttpServletRequestWrapper.java
index bca19215..0202a2a5 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomResourceHttpServletRequestWrapper.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomResourceHttpServletRequestWrapper.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl;
 
+import java.util.Arrays;
 import java.util.Map;
 import java.util.stream.Collectors;
 
@@ -24,8 +25,8 @@ import jakarta.servlet.http.HttpServletRequestWrapper;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.DefaultRequestAudienceLookupFunction;
 
 /**
- * A custom extension to {@link HttpServletRequestWrapper} overriding the resource parameter with a custom one in the
- * query string (for GET) and parameter map (for POST).
+ * A custom extension to {@link HttpServletRequestWrapper} overriding the resource parameter values with a custom prefix
+ * in the query string (for GET) and parameter map (for POST).
  */
 public class CustomResourceHttpServletRequestWrapper extends HttpServletRequestWrapper {
 
@@ -48,8 +49,12 @@ public class CustomResourceHttpServletRequestWrapper extends HttpServletRequestW
     @Override
     public Map<String, String[]> getParameterMap() {
         return super.getParameterMap().entrySet().stream()
-                .collect(Collectors.toMap(entry -> "resource".equals(entry.getKey()) ?
-                        DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME : entry.getKey(),
-                        Map.Entry::getValue));
+                .collect(Collectors.toMap(Map.Entry::getKey,
+                        entry -> entry.getValue() != null && "resource".equals(entry.getKey())
+                                ? Arrays.stream(entry.getValue())
+                                        .map(str -> DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX
+                                                + str)
+                                        .toArray(String[]::new) 
+                                : entry.getValue()));
     }
 }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCAuthenticationRequestDecoderTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCAuthenticationRequestDecoderTest.java
index 6ec49f0d..f3fd8980 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCAuthenticationRequestDecoderTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCAuthenticationRequestDecoderTest.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.decoding.impl;
 
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Map;
 
@@ -81,8 +82,8 @@ public class OIDCAuthenticationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("https://resource.example.org/"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX +("https://resource.example.org/"))));
     }
 
     @Test
@@ -99,12 +100,12 @@ public class OIDCAuthenticationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("https://resource.example.org/"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX +("https://resource.example.org/"))));
     }
 
     @Test
-    public void testRequestDecodingWithNonURIResource() throws MessageDecodingException {
+    public void testRequestDecodingWithNonURIResource() throws MessageDecodingException, URISyntaxException {
         httpRequest
                 .setQueryString("response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=openid%20profile&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj&resource=mockResourceId");
         decoder.decode();
@@ -117,12 +118,12 @@ public class OIDCAuthenticationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("mockResourceId"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
     }
 
     @Test
-    public void testRequestDecodingWithNonURIResourceFirst() throws MessageDecodingException {
+    public void testRequestDecodingWithNonURIResourceFirst() throws MessageDecodingException, URISyntaxException {
         httpRequest
                 .setQueryString("resource=mockResourceId&response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=openid%20profile&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj");
         decoder.decode();
@@ -135,13 +136,13 @@ public class OIDCAuthenticationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("mockResourceId"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
     }
 
     @SuppressWarnings("null")
     @Test
-    public void testPostRequestDecodingWithNonURIResource() throws MessageDecodingException {
+    public void testPostRequestDecodingWithNonURIResource() throws MessageDecodingException, URISyntaxException {
         httpRequest.setMethod("POST");
         httpRequest.setContentType(ContentType.APPLICATION_URLENCODED.toString());
         httpRequest.setParameters(CollectionSupport.copyToMap(Map.of("response_type", "code", "client_id", "s6BhdRkqt3",
@@ -156,8 +157,8 @@ public class OIDCAuthenticationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("mockResourceId"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
     }
 
     @Test(expectedExceptions = MessageDecodingException.class)
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCTokenRequestDecoderTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCTokenRequestDecoderTest.java
index 9497899e..cd7605a9 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCTokenRequestDecoderTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/decoding/impl/OIDCTokenRequestDecoderTest.java
@@ -15,6 +15,8 @@
 package net.shibboleth.idp.plugin.oidc.op.decoding.impl;
 
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.List;
 import java.util.Map;
 
@@ -82,7 +84,7 @@ public class OIDCTokenRequestDecoderTest {
     }
 
     @Test
-    public void testRequestDecodingWithUriResource() throws MessageDecodingException, IOException {
+    public void testRequestDecodingWithUriResource() throws MessageDecodingException, IOException, URISyntaxException {
         httpRequest.addHeader("Authorization", "Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW");
         httpRequest.setContentType("application/x-www-form-urlencoded");
         httpRequest.addParameter("grant_type", "authorization_code");
@@ -95,12 +97,12 @@ public class OIDCTokenRequestDecoderTest {
         final TokenRequest message = (TokenRequest) messageContext.getMessage();
         assert message != null;
         Assert.assertEquals(message.getAuthorizationGrant().getType().getValue(), "authorization_code");
-        Assert.assertEquals(message.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME),
-                List.of("https://resource.example.org"));
+        Assert.assertEquals(message.getResources(),
+                List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://resource.example.org")));
     }
 
     @Test
-    public void testRequestDecodingWithNonUriResource() throws MessageDecodingException, IOException {
+    public void testRequestDecodingWithNonUriResource() throws MessageDecodingException, IOException, URISyntaxException {
         httpRequest.addHeader("Authorization", "Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW");
         httpRequest.setContentType("application/x-www-form-urlencoded");
         httpRequest.addParameter("grant_type", "authorization_code");
@@ -113,8 +115,29 @@ public class OIDCTokenRequestDecoderTest {
         final TokenRequest message = (TokenRequest) messageContext.getMessage();
         assert message != null;
         Assert.assertEquals(message.getAuthorizationGrant().getType().getValue(), "authorization_code");
-        Assert.assertEquals(message.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME),
-                List.of("resource.example.org"));
+        Assert.assertEquals(message.getResources(),
+                List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "resource.example.org")));
+    }
+
+    @Test
+    public void testRequestDecodingWithTwoResources() throws MessageDecodingException, IOException, URISyntaxException {
+        httpRequest.addHeader("Authorization", "Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW");
+        httpRequest.setContentType("application/x-www-form-urlencoded");
+        httpRequest.addParameter("grant_type", "authorization_code");
+        httpRequest.addParameter("code", "SplxlOBeZQQYbYS6WxSbIA");
+        httpRequest.addParameter("redirect_uri", "https://client.example.org/cb");
+        httpRequest.addParameter("resource", "https://resource.example.org", "resource.example.org");
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        assert messageContext != null;
+        final TokenRequest message = (TokenRequest) messageContext.getMessage();
+        assert message != null;
+        Assert.assertEquals(message.getAuthorizationGrant().getType().getValue(), "authorization_code");
+        Assert.assertEquals(message.getResources().size(), 2);
+        Assert.assertTrue(message.getResources().contains(
+                new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "resource.example.org")));
+        Assert.assertTrue(message.getResources().contains(
+                new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://resource.example.org")));
     }
 
     @Test
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoderTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoderTest.java
index b406690d..a5c02604 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoderTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoderTest.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl;
 
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Map;
 
@@ -80,8 +81,8 @@ public class OAuth2AuthorizationRequestDecoderTest {
         assert messageContext != null;
         if (messageContext.getMessage() instanceof AuthorizationRequest authzRequest) {
             Assert.assertEquals(authzRequest.getResponseType().toString(), ResponseType.Value.CODE.toString());
-            Assert.assertTrue(authzRequest.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("https://resource.example.org/"));
+            Assert.assertTrue(authzRequest.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://resource.example.org/")));
         } else {
             Assert.fail();
         }
@@ -98,15 +99,15 @@ public class OAuth2AuthorizationRequestDecoderTest {
         assert messageContext != null;
         if (messageContext.getMessage() instanceof AuthorizationRequest authzRequest) {
             Assert.assertEquals(authzRequest.getResponseType().toString(), ResponseType.Value.CODE.toString());
-            Assert.assertTrue(authzRequest.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("https://resource.example.org/"));
+            Assert.assertTrue(authzRequest.getResources()
+                    .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://resource.example.org/")));
         } else {
             Assert.fail();
         }
     }
 
     @Test
-    public void testRequestDecodingWithNonURIResource() throws MessageDecodingException {
+    public void testRequestDecodingWithNonURIResource() throws MessageDecodingException, URISyntaxException {
         httpRequest
                 .setQueryString("response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=profile&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj&resource=mockResourceId");
         decoder.decode();
@@ -116,15 +117,15 @@ public class OAuth2AuthorizationRequestDecoderTest {
         assert messageContext != null;
         if (messageContext.getMessage() instanceof AuthorizationRequest authzRequest) {
             Assert.assertEquals(authzRequest.getResponseType().toString(), ResponseType.Value.CODE.toString());
-            Assert.assertTrue(authzRequest.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                    .contains("mockResourceId"));
+            Assert.assertTrue(authzRequest.getResources()
+                    .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
         } else {
             Assert.fail();
         }
     }
 
     @Test
-    public void testRequestDecodingWithNonURIResourceFirst() throws MessageDecodingException {
+    public void testRequestDecodingWithNonURIResourceFirst() throws MessageDecodingException, URISyntaxException {
         httpRequest
                 .setQueryString("resource=mockResourceId&response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=profile&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj");
         decoder.decode();
@@ -134,8 +135,29 @@ public class OAuth2AuthorizationRequestDecoderTest {
         assert messageContext != null;
         if (messageContext.getMessage() instanceof AuthorizationRequest authzRequest) {
             Assert.assertEquals(authzRequest.getResponseType().toString(), ResponseType.Value.CODE.toString());
-            Assert.assertTrue(authzRequest.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                    .contains("mockResourceId"));
+            Assert.assertTrue(authzRequest.getResources()
+                    .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
+        } else {
+            Assert.fail();
+        }
+    }
+
+    @Test
+    public void testRequestDecodingWithTwoResources() throws MessageDecodingException, URISyntaxException {
+        httpRequest
+                .setQueryString("resource=mockResourceId&response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=profile&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj&resource=https%3A%2F%2Fresource.example.org%2F");
+        decoder.decode();
+        final MessageContext messageContext = decoder.getMessageContext();
+        // We are not testing nimbus itself here, i.e. we are happy to decode
+        // one parameter successfully
+        assert messageContext != null;
+        if (messageContext.getMessage() instanceof AuthorizationRequest authzRequest) {
+            Assert.assertEquals(authzRequest.getResponseType().toString(), ResponseType.Value.CODE.toString());
+            Assert.assertEquals(authzRequest.getResources().size(), 2);
+            Assert.assertTrue(authzRequest.getResources()
+                    .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
+            Assert.assertTrue(authzRequest.getResources()
+                    .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://resource.example.org/")));
         } else {
             Assert.fail();
         }
@@ -143,7 +165,7 @@ public class OAuth2AuthorizationRequestDecoderTest {
 
     @SuppressWarnings("null")
     @Test
-    public void testPostRequestDecodingWithNonURIResource() throws MessageDecodingException {
+    public void testPostRequestDecodingWithNonURIResource() throws MessageDecodingException, URISyntaxException {
         httpRequest.setMethod("POST");
         httpRequest.setContentType(ContentType.APPLICATION_URLENCODED.toString());
         httpRequest.setParameters(CollectionSupport.copyToMap(Map.of("response_type", "code", "client_id", "s6BhdRkqt3",
@@ -158,8 +180,8 @@ public class OAuth2AuthorizationRequestDecoderTest {
         final ResponseType responseType = request.getResponseType();
         assert responseType != null;
         Assert.assertEquals(responseType.toString(), ResponseType.Value.CODE.toString());
-        Assert.assertTrue(request.getCustomParameter(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME)
-                .contains("mockResourceId"));
+        Assert.assertTrue(request.getResources()
+                .contains(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "mockResourceId")));
     }
 
     @Test(expectedExceptions = MessageDecodingException.class)
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java
index 97f278a8..cf077ca6 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java
@@ -18,7 +18,6 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.time.Instant;
 import java.util.List;
-import java.util.Map;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.DefaultRequestAudienceLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
@@ -82,9 +81,8 @@ public class ValidateAudienceTest extends BaseOIDCResponseActionTest {
                 new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
                 new ClientCredentialsGrant(),
                 null,
-                null,
-                Map.of(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME,
-                        List.of("https://sp3.example.org"))
+                List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://sp3.example.org")),
+                null
                 );
         setTokenRequest(req);
         
@@ -106,9 +104,10 @@ public class ValidateAudienceTest extends BaseOIDCResponseActionTest {
                 new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
                 new ClientCredentialsGrant(),
                 null,
-                null,
-                Map.of(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME,
-                        List.of("https://sp.example.org", "https://sp3.example.org", "https://sp2.example.org"))
+                List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX +"https://sp.example.org"),
+                        new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX +"https://sp3.example.org"),
+                        new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX +"https://sp2.example.org")),
+                null
                 );
         setTokenRequest(req);
         
@@ -156,9 +155,8 @@ public class ValidateAudienceTest extends BaseOIDCResponseActionTest {
                new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
                new AuthorizationCodeGrant(new AuthorizationCode("foo"), new URI("http://localhost")),
                null,
-               null,
-               Map.of(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_NAME,
-                       List.of("https://sp3.example.org")));
+               List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://sp3.example.org")),
+               null);
        setTokenRequest(req);
        
        final AuthorizeCodeClaimsSet claims = new AuthorizeCodeClaimsSet.Builder()
@@ -194,7 +192,7 @@ public class ValidateAudienceTest extends BaseOIDCResponseActionTest {
                new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
                new AuthorizationCodeGrant(new AuthorizationCode("foo"), new URI("http://localhost")),
                null,
-               List.of(new URI("https://sp3.example.org")),
+               List.of(new URI(DefaultRequestAudienceLookupFunction.CUSTOM_RESOURCE_PARAM_PREFIX + "https://sp3.example.org")),
                null);
        setTokenRequest(req);
        
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 3a54eff1..8dda7b4b 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -586,6 +586,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
                 new Pair<>("response_type", "code"),
                 new Pair<>("scope", "openid profile"),
                 new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("resource", resource),
                 new Pair<>("resource", resourceNonUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
@@ -602,8 +603,9 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNull(successResponse.getIssuer());
         final List<String> audience = getAudienceFromAuthorizeCodeClaimsSet(successResponse);
         Assert.assertNotNull(audience);
-        Assert.assertEquals(audience.size(), 1);
+        Assert.assertEquals(audience.size(), 2);
         Assert.assertTrue(audience.contains(resourceNonUri));
+        Assert.assertTrue(audience.contains(resource));
     }
 
     @Test

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


More information about the commits mailing list