[java-oidc-common] branch main updated: JCOMOIDC-31 - Query String serialization message encoder
Phil Smart
philip.smart at jisc.ac.uk
Fri Jan 7 14:24:01 UTC 2022
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-oidc-common.
View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=58cd3eaadb68b12dd457353e832ff2651452ff3d
The following commit(s) were added to refs/heads/main by this push:
new 58cd3ea JCOMOIDC-31 - Query String serialization message encoder
58cd3ea is described below
commit 58cd3eaadb68b12dd457353e832ff2651452ff3d
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jan 7 14:23:54 2022 +0000
JCOMOIDC-31 - Query String serialization message encoder
- Add HTTP redirect query string serlization message encoder
https://shibboleth.atlassian.net/browse/JCOMOIDC-31
---
.../oidc/profile/encoder/OIDCMessageEncoder.java | 15 +++
.../encoder/impl/AbstractOIDCMessageEncoder.java | 107 +++++++++++++++++++
.../encoder/impl/HTTPRedirectAuthnEncoder.java | 113 +++++++++++++++++++++
.../oidc/profile/encoder/impl/package-info.java | 21 ++++
.../encoder/impl/HTTPRedirectAuthnEncoderTest.java | 77 ++++++++++++++
5 files changed, 333 insertions(+)
diff --git a/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/encoder/OIDCMessageEncoder.java b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/encoder/OIDCMessageEncoder.java
new file mode 100644
index 0000000..aa500f6
--- /dev/null
+++ b/oidc-common-profile-api/src/main/java/net/shibboleth/oidc/profile/encoder/OIDCMessageEncoder.java
@@ -0,0 +1,15 @@
+package net.shibboleth.oidc.profile.encoder;
+
+import java.util.function.Predicate;
+
+import org.opensaml.messaging.encoder.MessageEncoder;
+
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration.OIDCHttpRequestMethod;
+
+/**
+ * An OIDC message encoder interface which requires implementations to test if the request
+ * method is supported by the encoder.
+ */
+public interface OIDCMessageEncoder extends Predicate<OIDCHttpRequestMethod>, MessageEncoder {
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/AbstractOIDCMessageEncoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/AbstractOIDCMessageEncoder.java
new file mode 100644
index 0000000..fe68eb7
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/AbstractOIDCMessageEncoder.java
@@ -0,0 +1,107 @@
+/*
+ * 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.oidc.profile.encoder.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.encoder.servlet.AbstractHttpServletResponseMessageEncoder;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.profile.encoder.OIDCMessageEncoder;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.net.URLBuilder;
+
+/**
+ * Base class for OIDC message encoders.
+ */
+//TODO: add a method or strategy for testing the request covers all the required parameters for a given response type
+public abstract class AbstractOIDCMessageEncoder extends AbstractHttpServletResponseMessageEncoder
+ implements OIDCMessageEncoder {
+
+
+ /**
+ * Serialize OAuth 2.0 authorization parameters from the authentication request to the query string
+ * of the URL.
+ *
+ * @param request the authentication request.
+ * @param builder the URL builder to add the query parameters to.
+ */
+ protected void serializeAuthorizationParamsToUrl(@Nonnull final OIDCAuthenticationRequest request,
+ @Nonnull final URLBuilder builder) {
+
+ final List<Pair<String, String>> params = createParametersFromRequest(request);
+ params.forEach(param -> builder.getQueryParams().add(param));
+ }
+
+ /**
+ * Serialize OAuth 2.0 authorization parameters from the authentication request to a query string.
+ *
+ * @param request the authentication request query string.
+ *
+ * @return the query string.
+ */
+ protected String serializeAuthorizationParamsToQueryString(@Nonnull final OIDCAuthenticationRequest request) {
+ //TODO maybe a better way to do this than the full URL builder?
+ final URLBuilder builder = new URLBuilder();
+ final List<Pair<String, String>> params = createParametersFromRequest(request);
+ params.forEach(param -> builder.getQueryParams().add(param));
+ return builder.buildQueryString();
+ }
+
+ /**
+ * Create a list of OAuth 2.0 authorization parameters from the {@link OIDCAuthenticationRequest} object.
+ *
+ * @param req the authentication request
+ *
+ * @return a list of authorization parameters.
+ */
+ protected List<Pair<String, String>> createParametersFromRequest(@Nonnull final OIDCAuthenticationRequest req) {
+
+
+ final List<Pair<String, String>> params = new ArrayList<>();
+
+ params.add(new Pair<>("client_id", req.getClientID().getValue()));
+ params.add(new Pair<>("scope", req.getScope().toString()));
+
+ if (req.getResponseType() != null) {
+ params.add(new Pair<>("response_type", req.getResponseType().toString()));
+ }
+ if (req.getResponseMode() != null) {
+ params.add(new Pair<>("response_mode", req.getResponseMode().getValue()));
+ }
+ if (req.getRedirectURI() != null) {
+ params.add(new Pair<>("redirect_uri", req.getRedirectURI().toString()));
+ }
+ if (req.getState() != null) {
+ params.add(new Pair<>("state", req.getState().getValue()));
+ }
+ if (req.getPrompt() != null) {
+ params.add(new Pair<>("prompt", req.getPrompt().toString()));
+ }
+ if (req.getRequestObject() != null) {
+ params.add(new Pair<>("request", req.getRequestObject().serialize()));
+ }
+ //TODO: requestURI, includedGrantedScopes?, resource_uris?
+ return params;
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoder.java
new file mode 100644
index 0000000..34a53da
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoder.java
@@ -0,0 +1,113 @@
+/*
+ * 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.oidc.profile.encoder.impl;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import javax.annotation.Nonnull;
+import javax.servlet.http.HttpServletResponse;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.encoder.MessageEncoder;
+import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration.OIDCHttpRequestMethod;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.net.HttpServletSupport;
+import net.shibboleth.utilities.java.support.net.URLBuilder;
+
+/**
+ * A {@link MessageEncoder message encoder} that encodes an OpenID authentication request by
+ * Query String Serialization and sends a HTTP redirect response.
+ */
+//TODO maybe this could encode authz responses as well? to replace the NimbusResponseEncoder
+public class HTTPRedirectAuthnEncoder extends AbstractOIDCMessageEncoder {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(HTTPRedirectAuthnEncoder.class);
+
+ @Override
+ public boolean test(@Nonnull final OIDCHttpRequestMethod requestMethod) {
+ return requestMethod == OIDCHttpRequestMethod.GET;
+ }
+
+ @Override
+ protected void doEncode() throws MessageEncodingException {
+
+ log.debug("Encoding OIDC authentication request using Query String Serialization");
+ final MessageContext messageContext = getMessageContext();
+ final Object outboundMessage = messageContext.getMessage();
+ if (!(outboundMessage instanceof OIDCAuthenticationRequest)) {
+ throw new MessageEncodingException("No outbound OIDC authentication request message "
+ + "contained in message context");
+ }
+
+ final String redirectURL = buildRedirectURL(messageContext, (OIDCAuthenticationRequest)outboundMessage);
+
+ final HttpServletResponse response = getHttpServletResponse();
+ HttpServletSupport.addNoCacheHeaders(response);
+ HttpServletSupport.setUTF8Encoding(response);
+ HttpServletSupport.setContentType(response, "application/x-www-form-urlencoded");
+
+ try {
+ log.trace("Redirecting user-agent to '{}'",redirectURL);
+ response.sendRedirect(redirectURL);
+ } catch (final IOException e) {
+ throw new MessageEncodingException("Problem sending HTTP redirect", e);
+ }
+
+ }
+
+ /**
+ * Build the URL to redirect the client to using parameters in the authentication request.
+ *
+ * @param messageContext the current message context
+ * @param request the authentication request
+ *
+ * @return a URL to redirect the client to.
+ *
+ * @throws MessageEncodingException if there is an issue building the URL or the endpoint is null.
+ */
+ protected String buildRedirectURL(final MessageContext messageContext, final OIDCAuthenticationRequest request)
+ throws MessageEncodingException {
+
+ if (request.getEndpointURI() == null) {
+ throw new MessageEncodingException("No endpoint URI specified, URL can not be built");
+ }
+ URLBuilder urlBuilder = null;
+ try {
+ if (request.getEndpointURI() == null) {
+ throw new MessageEncodingException("Endpoint URL is null");
+ }
+ //TODO check the endpoint is always the baseURL, otherwise this may go wrong.
+ urlBuilder = new URLBuilder(request.getEndpointURI().toString());
+ } catch (final MalformedURLException e) {
+ throw new MessageEncodingException("Endpoint URL " + request.getEndpointURI() + " is not a valid URL", e);
+ }
+
+ serializeAuthorizationParamsToUrl(request, urlBuilder);
+ return urlBuilder.buildURL();
+
+ }
+
+
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/package-info.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/package-info.java
new file mode 100644
index 0000000..dfe4580
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * OIDC message encoders.
+ */
+package net.shibboleth.oidc.profile.encoder.impl;
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoderTest.java b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoderTest.java
new file mode 100644
index 0000000..b9a4244
--- /dev/null
+++ b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPRedirectAuthnEncoderTest.java
@@ -0,0 +1,77 @@
+package net.shibboleth.oidc.profile.encoder.impl;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
+
+/** Test for the HTTPRedirectAuthnEncoder.*/
+public class HTTPRedirectAuthnEncoderTest {
+
+ /** The encoder to test.*/
+ private HTTPRedirectAuthnEncoder encoder;
+
+ /** Mock servlet response.*/
+ private MockHttpServletResponse mockResponse;
+
+ /** The request.*/
+ private OIDCAuthenticationRequest request;
+
+ /** The Message context.*/
+ private MessageContext context;
+
+
+ @BeforeMethod public void setUp() throws Exception {
+ encoder = new HTTPRedirectAuthnEncoder();
+ context = new MessageContext();
+ request = new OIDCAuthenticationRequest(new ClientID("clientID"));
+ // This needs to be dynamic
+ request.setResponseType(ResponseType.CODE);
+ request.setEndpointURI(new URI("https://somewhere.com/oauth2/authz"));
+ request.setRedirectURI(new URI("https://localhost:8080/callback"));
+ context.setMessage(request);
+ encoder.setMessageContext(context);
+ mockResponse = new MockHttpServletResponse();
+ encoder.setHttpServletResponse(mockResponse);
+ }
+
+ @Test
+ public void testSuccesfullEncoding() throws Exception {
+ encoder.initialize();
+ encoder.encode();
+ final String response = mockResponse.getRedirectedUrl();
+ assertNotNull(response);
+ // These are all required
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("response_type"));
+ assertTrue(response.contains("client_id"));
+ assertTrue(response.contains("scope"));
+
+ }
+
+ @Test(expectedExceptions = UninitializedComponentException.class)
+ public void testUninitialized() throws MessageEncodingException {
+ encoder.encode();
+ }
+
+ @Test(expectedExceptions = MessageEncodingException.class)
+ public void testNullEndpointURL() throws Exception {
+ request = new OIDCAuthenticationRequest(new ClientID("clientID"));
+ context.setMessage(request);
+ encoder.initialize();
+ encoder.encode();
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list