[java-oidc-common] branch main updated: JCOMOIDC-187 - Add Custom Request Encoder Param Hook
Codeberg
noreply at shibboleth.net
Wed Sep 30 14:28:58 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-oidc-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidc-common/commit/d9089e5aafea331b7ed5281387e6dcd15cdf4f68
The following commit(s) were added to refs/heads/main by this push:
new d9089e5a JCOMOIDC-187 - Add Custom Request Encoder Param Hook
d9089e5a is described below
commit d9089e5aafea331b7ed5281387e6dcd15cdf4f68
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Sep 30 15:03:41 2026 +0100
JCOMOIDC-187 - Add Custom Request Encoder Param Hook
- Added a hook to allow 'new' parameters to be added to the query
string, not the request object.
https://shibboleth.atlassian.net/browse/JCOMOIDC-187
---
.../encoding/impl/AbstractOIDCMessageEncoder.java | 68 +++++++++++++++++++++-
.../impl/HTTPRedirectRequestEncoderTest.java | 64 ++++++++++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoding/impl/AbstractOIDCMessageEncoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoding/impl/AbstractOIDCMessageEncoder.java
index fc46b040..6afe4959 100644
--- a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoding/impl/AbstractOIDCMessageEncoder.java
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoding/impl/AbstractOIDCMessageEncoder.java
@@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -47,6 +48,8 @@ import net.shibboleth.oidc.profile.core.OIDCLogoutRequest;
import net.shibboleth.oidc.profile.encoding.AuthenticationContextClassReferenceSupport;
import net.shibboleth.oidc.profile.encoding.OIDCMessageEncoder;
import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
import net.shibboleth.shared.logic.PredicateSupport;
import net.shibboleth.shared.net.URLBuilder;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -66,10 +69,17 @@ public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResp
/** A hook to allow additional checking of the authorization parameters after they are built.*/
@Nonnull private Predicate<List<Pair<String, String>>> authorizationParamsAreValidPredicate;
+ /**
+ * A hook to allow additional parameters to be added to the authorisation request, but not the request object
+ * (if one exists).
+ */
+ @Nonnull private Function<OIDCAuthenticationRequest, List<Pair<String, String>>> customParamsLookupStrategy;
+
/** Constructor. */
protected AbstractOIDCMessageEncoder() {
authorizationParamsAreValidPredicate = PredicateSupport.alwaysTrue();
setProtocolMessageLoggerSubCategory("OAUTH2");
+ customParamsLookupStrategy = FunctionSupport.constant(null);
}
/**
@@ -89,6 +99,25 @@ public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResp
}
}
+ /**
+ * Set a hook that generates a list of parameters to add to the authorization request.
+ *
+ * <p>Will not add parameters to the Request Object if used.</p>
+ *
+ * <p>Will not override system set parameters, e.g., client_id can not be duplicated and appended to the request.
+ * </p>
+ *
+ * @param strategy the strategy to set.
+ *
+ * @since 3.4.0
+ */
+ public void setCustomParamsLookupStrategy(
+ @Nonnull final Function<OIDCAuthenticationRequest, List<Pair<String, String>>> strategy) {
+ checkSetterPreconditions();
+
+ customParamsLookupStrategy = Constraint.isNotNull(strategy, "Custom parameters lookup strategy cannot be null");
+ }
+
/**
* Serialize OAuth 2.0 authorization parameters from the authentication request to the query string
* of the URL.
@@ -272,7 +301,10 @@ public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResp
} catch (final IllegalStateException e) {
throw new MessageEncodingException("Couldn't serialize request object to JWT: " + e.getMessage(), e);
}
- }
+ }
+
+ // Add custom params
+ addCustomParameters(req, params);
if (!validateParams(params)) {
throw new MessageEncodingException("Authorization parameters are not valid");
@@ -352,6 +384,9 @@ public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResp
}
//TODO: requestURI, includedGrantedScopes?, resource_uris?
+ // Add custom params
+ addCustomParameters(req, params);
+
if (!validateParams(params)) {
throw new MessageEncodingException("Authorization parameters are not valid");
}
@@ -359,6 +394,37 @@ public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResp
}
// Checkstyle: CyclomaticComplexity ON
+
+ /**
+ * Adds any custom request parameters to the list of parameters providing the new parameter does not conflict
+ * with an existing, system set, parameter.
+ *
+ * @param request the request that contains the system constructed parameters
+ * @param params the current list of parameters to add the custom parameters
+ */
+ private void addCustomParameters(@Nonnull final OIDCAuthenticationRequest request,
+ @Nonnull final List<Pair<String, String>> params) {
+ final List<Pair<String,String>> customParams = customParamsLookupStrategy.apply(request);
+
+ // Snapshot the existing params to check for duplicates so that duplicates can be set from the lookup strategy
+ // but are not allowed in the original params set.
+ final List<String> existingParams = params.stream()
+ .map(Pair::getFirst)
+ .toList();
+
+ if (customParams != null) {
+ for (final Pair<String, String> customParam : customParams) {
+ if (customParam != null && customParam.getFirst() != null) {
+ if (existingParams.contains(customParam.getFirst())) {
+ log.warn("Ignoring custom authorization parameter '{}' that duplicates a system set parameter ",
+ customParam.getFirst());
+ } else {
+ params.add(customParam);
+ }
+ }
+ }
+ }
+ }
/**
* Ensure the authorization parameters are valid.
diff --git a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoding/impl/HTTPRedirectRequestEncoderTest.java b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoding/impl/HTTPRedirectRequestEncoderTest.java
index 604a8f3b..1e389da4 100644
--- a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoding/impl/HTTPRedirectRequestEncoderTest.java
+++ b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoding/impl/HTTPRedirectRequestEncoderTest.java
@@ -14,6 +14,7 @@
package net.shibboleth.oidc.profile.encoding.impl;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.net.URI;
@@ -50,6 +51,7 @@ import com.nimbusds.openid.connect.sdk.assurance.claims.VerifiedClaimsSetRequest
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
import net.shibboleth.oidc.profile.core.OIDCLogoutRequest;
import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
import net.shibboleth.shared.component.UninitializedComponentException;
import net.shibboleth.shared.servlet.impl.HttpServletRequestResponseContext;
import net.shibboleth.shared.servlet.impl.ThreadLocalHttpServletResponseSupplier;
@@ -148,6 +150,68 @@ public class HTTPRedirectRequestEncoderTest {
}
+ @Test
+ public void testSuccesfullEncoding_WithCustomParams() throws Exception {
+ context.setMessage(request);
+ encoder.setCustomParamsLookupStrategy(req ->
+ CollectionSupport.listOf(new Pair<String,String>("custom", "custom-value")));
+ encoder.initialize();
+ encoder.encode();
+ final String response = mockResponse.getRedirectedUrl();
+ assert response != null;
+ // These are all required
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("response_type"));
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("scope"));
+ assertTrue(response.contains("custom"));
+ assertTrue(response.contains("custom-value"));
+
+ }
+
+ @Test
+ public void testSuccesfullEncoding_WithDuplicateCustomParams() throws Exception {
+ context.setMessage(request);
+ // Should be ignored, can not override a system set parameter
+ encoder.setCustomParamsLookupStrategy(req ->
+ CollectionSupport.listOf(new Pair<String,String>("client_id", "custom-client_id")));
+ encoder.initialize();
+ encoder.encode();
+ final String response = mockResponse.getRedirectedUrl();
+ assert response != null;
+ // These are all required
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("response_type"));
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("scope"));
+ assertFalse(response.contains("custom-client_id"));
+
+ }
+
+ @Test
+ public void testSuccesfullEncoding_WithCustomParams_WithDuplicateCustomParams() throws Exception {
+ context.setMessage(request);
+ // client_id should be ignored, can not override a system set parameter
+ // allowed to set more than one param with the same value
+ encoder.setCustomParamsLookupStrategy(req ->
+ CollectionSupport.listOf(new Pair<String,String>("client_id", "custom-client_id"),
+ new Pair<String,String>("resource", "resource-a"),
+ new Pair<String,String>("resource", "resource-b")));
+ encoder.initialize();
+ encoder.encode();
+ final String response = mockResponse.getRedirectedUrl();
+ assert response != null;
+ // These are all required
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("response_type"));
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("scope"));
+ assertTrue(response.contains("resource-a"));
+ assertTrue(response.contains("resource-b"));
+ assertFalse(response.contains("custom-client_id"));
+
+ }
+
@Test
public void testSuccesfullLogoutRequestEncoding_WithPostRedirect() throws Exception {
logoutRequest.setPostLogoutRedirectURI(new URI("https://localhost:8080/end-session-callback"));
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list