[java-oidc-common] branch main updated: JCOMOIDC-32 - Form serialization message encoder
Phil Smart
philip.smart at jisc.ac.uk
Fri Jan 7 14:27:43 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=5fe24b24e0de1b4926bc58ddd2dab8b4ff00af6d
The following commit(s) were added to refs/heads/main by this push:
new 5fe24b2 JCOMOIDC-32 - Form serialization message encoder
5fe24b2 is described below
commit 5fe24b24e0de1b4926bc58ddd2dab8b4ff00af6d
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jan 7 14:27:37 2022 +0000
JCOMOIDC-32 - Form serialization message encoder
- Add HTTP POST message encoder which uses Form serialization.
https://shibboleth.atlassian.net/browse/JCOMOIDC-32
---
.../profile/encoder/impl/HTTPPostAuthnEncoder.java | 145 +++++++++++++++++++++
.../encoder/impl/HTTPPostAuthnEncoderTest.java | 80 ++++++++++++
.../resources/templates/oidc-request-form-post.vm | 46 +++++++
3 files changed, 271 insertions(+)
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoder.java
new file mode 100644
index 0000000..e7cddfe
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoder.java
@@ -0,0 +1,145 @@
+/*
+ * 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.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+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.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.codec.HTMLEncoder;
+import net.shibboleth.utilities.java.support.collection.Pair;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.net.HttpServletSupport;
+
+/**
+ * A {@link MessageEncoder message encoder} that encodes an OpenID authentication request by
+ * HTTP Form POST Serialization.
+ */
+public class HTTPPostAuthnEncoder extends AbstractOIDCMessageEncoder {
+
+ /** Default template ID for using FORM POST request type. */
+ @Nonnull @NotEmpty public static final String DEFAULT_TEMPLATE_ID = "/templates/oidc-request-form-post.vm";
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(HTTPPostAuthnEncoder.class);
+
+ /** Velocity engine used to evaluate the template when using FORM POST response mode. */
+ @Nullable private VelocityEngine velocityEngine;
+
+ /** ID of the Velocity template used when using FORM POST response mode. */
+ @Nonnull @NotEmpty private String velocityTemplateId = DEFAULT_TEMPLATE_ID;
+
+ /**
+ * Set the Velocity template id.
+ *
+ * <p>
+ * Defaults to {@link #DEFAULT_TEMPLATE_ID}.
+ * </p>
+ *
+ * @param newVelocityTemplateId the new Velocity template id
+ */
+ public void setVelocityTemplateId(@Nonnull @NotEmpty final String newVelocityTemplateId) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ velocityTemplateId =
+ Constraint.isNotEmpty(newVelocityTemplateId, "Velocity template id must not not be null or empty");
+ }
+
+ /**
+ * Set the VelocityEngine instance.
+ *
+ * @param newVelocityEngine the new VelocityEngine instane
+ */
+ public void setVelocityEngine(@Nonnull final VelocityEngine newVelocityEngine) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ velocityEngine = Constraint.isNotNull(newVelocityEngine, "Velocity engine can not be null");
+ }
+
+
+ @Override
+ public boolean test(@Nonnull final OIDCHttpRequestMethod requestMethod) {
+ return requestMethod == OIDCHttpRequestMethod.POST;
+ }
+
+ /**
+ * Construct form POST.
+ *
+ * @param request the authentication request.
+ * @return response message as velocity context.
+ */
+ private VelocityContext doPostEncode(@Nonnull final OIDCAuthenticationRequest request) {
+ final VelocityContext context = new VelocityContext();
+ final List<Pair<String, String>> params = createParametersFromRequest(request);
+ params.forEach(param -> context.put(param.getFirst(), param.getSecond()));
+ context.put("action", HTMLEncoder.encodeForHTMLAttribute(request.getEndpointURI().toString()));
+ log.trace("Velocity context {}", params);
+ return context;
+ }
+
+ @Override
+ protected void doEncode() throws MessageEncodingException {
+
+ if (velocityEngine == null) {
+ throw new MessageEncodingException("VelocityEngine must be supplied for form post request mode");
+ }
+
+ log.debug("Encoding OIDC authentication request using HTTP Form Post 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");
+ }
+ try {
+ final HttpServletResponse response = getHttpServletResponse();
+ HttpServletSupport.addNoCacheHeaders(response);
+ HttpServletSupport.setUTF8Encoding(response);
+ HttpServletSupport.setContentType(response, "text/html");
+ final VelocityContext context = doPostEncode((OIDCAuthenticationRequest) outboundMessage);
+ try (final Writer out = new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8)) {
+ velocityEngine.mergeTemplate(velocityTemplateId, "UTF-8", context, out);
+ out.flush();
+ }
+ } catch (final Exception e) {
+ log.error("Error creating authorization POST request: {}", e.getMessage());
+ throw new MessageEncodingException("Error creating authorization POST request", e);
+ }
+
+
+ }
+
+
+}
diff --git a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoderTest.java b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoderTest.java
new file mode 100644
index 0000000..837c609
--- /dev/null
+++ b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/encoder/impl/HTTPPostAuthnEncoderTest.java
@@ -0,0 +1,80 @@
+package net.shibboleth.oidc.profile.encoder.impl;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.UnsupportedEncodingException;
+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.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
+import net.shibboleth.utilities.java.support.velocity.VelocityEngine;
+
+/** Test for the HTTPPostAuthnEncoder.*/
+public class HTTPPostAuthnEncoderTest {
+
+ /** Mock servlet response.*/
+ private MockHttpServletResponse mockResponse;
+
+ /** The encoder to test.*/
+ private HTTPPostAuthnEncoder encoder;
+
+ /** The authentication request.*/
+ private OIDCAuthenticationRequest request;
+
+ /** The message context.*/
+ private MessageContext context;
+
+
+ @BeforeMethod public void setUp() throws Exception {
+ encoder = new HTTPPostAuthnEncoder();
+ 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);
+ encoder.setVelocityEngine(VelocityEngine.newVelocityEngine());
+ mockResponse = new MockHttpServletResponse();
+ encoder.setHttpServletResponse(mockResponse);
+ }
+
+ @Test
+ public void testSuccesfullEncoding() throws MessageEncodingException, ComponentInitializationException, UnsupportedEncodingException {
+ encoder.initialize();
+ encoder.encode();
+ final String response = mockResponse.getContentAsString();
+ 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();
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/test/resources/templates/oidc-request-form-post.vm b/oidc-common-profile-impl/src/test/resources/templates/oidc-request-form-post.vm
new file mode 100644
index 0000000..1a294c3
--- /dev/null
+++ b/oidc-common-profile-impl/src/test/resources/templates/oidc-request-form-post.vm
@@ -0,0 +1,46 @@
+##
+## Velocity Template for OIDC Form Post response mode.
+##
+##
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta charset="utf-8" />
+</head>
+
+<body onload="document.forms[0].submit()">
+ <noscript>
+ <p>
+ <strong>Note:</strong> Since your browser does not support JavaScript, you must press the Continue button once to proceed.
+ </p>
+ </noscript>
+
+ <form action="${action}" method="post">
+ <div>
+ #if($client_id)
+ <input type="hidden" name="client_id" value="${client_id}" />#end #if($scope)
+
+ <input type="hidden" name="scope" value="${scope}" />#end #if($response_type)
+
+ <input type="hidden" name="response_type" value="${response_type}" />#end #if($response_mode)
+
+ <input type="hidden" name="response_mode" value="${response_mode}" />#end #if($redirect_uri)
+
+ <input type="hidden" name="redirect_uri" value="${redirect_uri}" />#end #if($state)
+
+ <input type="hidden" name="state" value="${state}" />#end #if($prompt)
+
+ <input type="hidden" name="prompt" value="${prompt}" />#end #if($request)
+
+ <input type="hidden" name="request" value="${request}" />#end
+ </div>
+ <noscript>
+ <div>
+ <input type="submit" value="Continue" />
+ </div>
+ </noscript>
+ </form>
+</body>
+
+</html>
\ 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