[java-idp-oidc] branch main updated: JOIDC-206 - Provide method to avoid Nimbus message parsing restrictions
Henri Mikkonen
henri.mikkonen at iki.fi
Mon May 13 15:00:24 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=949455a07cac3a321d76199b73a6ebb3fbadcbb3
The following commit(s) were added to refs/heads/main by this push:
new 949455a0 JOIDC-206 - Provide method to avoid Nimbus message parsing restrictions
949455a0 is described below
commit 949455a07cac3a321d76199b73a6ebb3fbadcbb3
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Mon May 13 18:00:07 2024 +0300
JOIDC-206 - Provide method to avoid Nimbus message parsing restrictions
https://shibboleth.atlassian.net/browse/JOIDC-206
Included setter for customRequestParser to the abstract Nimbus request parser: it's null by
default, but custom parsers may be wired via message-specific beans:
- IntrospectionRequestParser
- PushedAuthorizationRequestParser
- RevocationRequestParser
- AuthenticationRequestParser
- AuthorizationRequestParser
- EndSessionRequestParser
- RegisterRequestParser
- TokenRequestParser
- UserInfoRequestParser
---
.../decoding/impl/BaseOAuth2RequestDecoder.java | 24 +++++++-
.../decoding/impl/CustomNimbusRequestParser.java | 45 +++++++++++++++
.../oauth2/introspection/introspection-beans.xml | 3 +-
.../pushed-authorization-beans.xml | 3 +-
.../flows/oauth2/revocation/revocation-beans.xml | 3 +-
.../idp/flows/oidc/authorize/authorize-beans.xml | 6 +-
.../flows/oidc/end-session/end-session-beans.xml | 3 +-
.../idp/flows/oidc/register/register-beans.xml | 3 +-
.../idp/flows/oidc/token/token-beans.xml | 3 +-
.../idp/flows/oidc/userinfo/userinfo-beans.xml | 3 +-
.../decoding/impl/OIDCTokenRequestDecoderTest.java | 66 ++++++++++++++++++++++
11 files changed, 152 insertions(+), 10 deletions(-)
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 c9f256f3..24b90e87 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
@@ -49,6 +49,9 @@ public abstract class BaseOAuth2RequestDecoder<T extends Request> extends Abstra
/** A flag to remove the IP address from the endpoint URI. */
private boolean removeIpAddressFromEndpointUri;
+ /** A custom extension for parsing Nimbus object out of the servlet request. */
+ @Nullable protected CustomNimbusRequestParser<T> customRequestParser;
+
/** Constructor. */
public BaseOAuth2RequestDecoder() {
super();
@@ -67,11 +70,30 @@ public abstract class BaseOAuth2RequestDecoder<T extends Request> extends Abstra
removeIpAddressFromEndpointUri = flag;
}
+ /**
+ * Set the custom extension for parsing Nimbus object out of the servlet request.
+ *
+ * @param strategy What to set.
+ */
+ public synchronized void setCustomRequestParser(@Nullable final CustomNimbusRequestParser<T> customParser) {
+ ifInitializedThrowUnmodifiabledComponentException();
+ ifDestroyedThrowDestroyedComponentException();
+
+ customRequestParser = customParser;
+ }
+
/** {@inheritDoc} */
@Override
protected void doDecode() throws MessageDecodingException {
final MessageContext messageContext = new MessageContext();
- final T requestMessage = parseMessage();
+ final HttpServletRequest httpServletRequest = getHttpServletRequest();
+ assert httpServletRequest != null;
+ final T requestMessage;
+ if (customRequestParser != null) {
+ requestMessage = customRequestParser.parse(httpServletRequest);
+ } else {
+ requestMessage = parseMessage();
+ }
messageContext.setMessage(requestMessage);
setMessageContext(messageContext);
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomNimbusRequestParser.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomNimbusRequestParser.java
new file mode 100644
index 00000000..6ef0e4f1
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/CustomNimbusRequestParser.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed 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.oidc.op.oauth2.decoding.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.decoder.MessageDecodingException;
+
+import com.nimbusds.oauth2.sdk.Request;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * An interface for custom parsers for transforming {@link HttpServletRequest} into a Nimbus request object.
+ * Implementing classes may be wired to decoders that extends {@link BaseOAuth2RequestDecoder} with the corresponding
+ * Nimbus request object type.
+ *
+ * @param <T> The Nimbus request object type.
+ *
+ * @since 4.2.0
+ */
+public interface CustomNimbusRequestParser<T extends Request> {
+
+ /**
+ * Parse the Nimbus request object from the given servlet request.
+ *
+ * @param httpServletRequest the servlet request used as an input
+ * @return the Nimbus request object
+ * @throws MessageDecodingException if the Nimbus object cannot be parsed
+ */
+ public T parse(@Nonnull final HttpServletRequest httpServletRequest) throws MessageDecodingException;
+
+}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
index ddd835d9..c0628664 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
@@ -19,7 +19,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.OAuth2IntrospectionRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('IntrospectionRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/pushed-authorization/pushed-authorization-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/pushed-authorization/pushed-authorization-beans.xml
index 37311a58..820d4032 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/pushed-authorization/pushed-authorization-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/pushed-authorization/pushed-authorization-beans.xml
@@ -19,7 +19,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.OAuth2PushedAuthorizationRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('PushedAuthorizationRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
index 28236519..61409af9 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
@@ -19,7 +19,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.OAuth2RevocationRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('RevocationRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index a6a14945..8cb6f71c 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -21,7 +21,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCAuthenticationRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('AuthenticationRequestParser')}"/>
</constructor-arg>
</bean>
@@ -29,7 +30,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.OAuth2AuthorizationRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('AuthorizationRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-beans.xml
index 08c7d336..51f0e6d6 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-beans.xml
@@ -21,7 +21,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCLogoutRequestDecoder"
scope="prototype" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('EndSessionRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
index a7969f1c..8108d70e 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/register/register-beans.xml
@@ -31,7 +31,8 @@
scope="prototype"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
- p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"/>
+ p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
+ p:customRequestParser="#{getObject('RegisterRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index 1c48dfd2..157d0308 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -22,7 +22,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCTokenRequestDecoder" scope="prototype"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('TokenRequestParser')}"/>
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
index 4f54c57d..940b6b7c 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
@@ -22,7 +22,8 @@
<constructor-arg>
<bean class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCUserInfoRequestDecoder" scope="prototype"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
+ p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"
+ p:customRequestParser="#{getObject('UserInfoRequestParser')}"/>
</constructor-arg>
</bean>
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 4044d245..3bc9f06c 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
@@ -16,6 +16,9 @@ package net.shibboleth.idp.plugin.oidc.op.decoding.impl;
import java.io.IOException;
import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.decoder.MessageDecodingException;
@@ -24,11 +27,16 @@ import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+import com.nimbusds.oauth2.sdk.http.JakartaServletUtils;
import com.nimbusds.oauth2.sdk.http.HTTPRequest.Method;
import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.CustomNimbusRequestParser;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestAudienceLookupFunction;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.NonnullSupplier;
/**
@@ -109,4 +117,62 @@ public class OIDCTokenRequestDecoderTest {
List.of("resource.example.org"));
}
+ @Test
+ public void testRequestDecodingWithCustomAssertion()
+ throws MessageDecodingException, IOException, ComponentInitializationException {
+ final CustomNimbusRequestParser<TokenRequest> customParser = new CustomNimbusRequestParser<TokenRequest>() {
+
+ @Override
+ public TokenRequest parse(@Nonnull final HttpServletRequest httpServletRequest)
+ throws MessageDecodingException {
+ try {
+ final HTTPRequest httpReq = JakartaServletUtils.createHTTPRequest(httpServletRequest);
+ assert httpReq != null;
+ switchIntoCustomClientAssertion(httpReq);
+ return TokenRequest.parse(httpReq);
+ } catch (final ParseException | IOException e) {
+ throw new MessageDecodingException(e);
+ }
+ }
+ };
+ httpRequest = new MockHttpServletRequest();
+ httpRequest.setMethod(Method.POST.toString());
+ decoder = new OIDCTokenRequestDecoder();
+ decoder.setHttpServletRequestSupplier(new NonnullSupplier<> () {
+ public HttpServletRequest get() { assert httpRequest != null; return httpRequest;}
+ });
+ decoder.setCustomRequestParser(customParser);
+ decoder.initialize();
+ final String customAssertion = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0." +
+ "eyJpc3Mi.cC4hiUPo~eyJzI1NiIsImtphbGciOimtpZCI6IjIyIn0.IjIyIn0.iOiJSUzI1";
+ 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("client_id", "mockClientId");
+ httpRequest.addParameter("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:mock-custom");
+ httpRequest.addParameter("client_assertion", customAssertion);
+ decoder.decode();
+ final MessageContext messageContext = decoder.getMessageContext();
+ assert messageContext != null;
+ final TokenRequest message = (TokenRequest) messageContext.getMessage();
+ assert message != null;
+ Assert.assertEquals(message.getCustomParameter("custom_client_assertion"), List.of(customAssertion));
+ }
+
+ protected void switchIntoCustomClientAssertion(@Nonnull final HTTPRequest httpRequest) {
+ final Map<String,List<String>> params = httpRequest.getQueryParameters();
+ if (params != null && !params.isEmpty() && params.get("client_assertion_type") != null) {
+ final List<String> assertionTypes = params.get("client_assertion_type");
+ String query = httpRequest.getQuery();
+ for (final String assertionType : assertionTypes) {
+ if (assertionType.equals("urn:ietf:params:oauth:client-assertion-type:mock-custom")) {
+ query = query.replace("client_assertion=", "custom_client_assertion=");
+
+ }
+ httpRequest.setQuery(query);
+ }
+ }
+ }
+
}
\ 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