[java-idp-plugin-oidc-rp] branch main updated: Add request object signing support
Phil Smart
philip.smart at jisc.ac.uk
Mon Jul 11 10:53:50 UTC 2022
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=6eb003b09590f51719496771dea8640a09bda44e
The following commit(s) were added to refs/heads/main by this push:
new 6eb003b Add request object signing support
6eb003b is described below
commit 6eb003b09590f51719496771dea8640a09bda44e
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Jul 11 11:53:44 2022 +0100
Add request object signing support
Cleanup flow predicates
fix tests
---
.../oidc/rp/config/logic/IsCodeFlowPredicate.java | 56 ++++++++
.../rp/config/logic/IsHybridFlowPredicate.java | 56 ++++++++
.../rp/config/logic/IsImplicitFlowPredicate.java | 56 ++++++++
.../config/logic/SignRequestObjectPredicate.java | 45 +++++++
.../OutboundMessageContextFromProxyPRC.java | 55 ++++++++
.../rp/context/OutboundMessageHandlerContext.java | 119 +++++++++++++++++
.../logic/RequestObjectRequiredAndSupported.java | 12 +-
.../oidc/rp/impl/AuthorizationController.java | 55 ++------
.../authn/oidc/rp/impl/BuildRequestObject.java | 99 ++++++++++++--
.../impl/DefaultRedirectUriCreationFunction.java | 4 +-
.../authn/oidc/rp/impl/OIDCProxySupport.java | 8 +-
...actOIDCAuthenticationRequestMessageHandler.java | 133 +++++++++++++++++++
.../oidc/rp/messaging/impl/AddRedirectURI.java | 111 ++++++++++++++++
.../authn/oidc/rp/messaging/impl/AddState.java | 108 ++++++++++++++++
.../messaging/impl/BuildPlainRequestObjectJWT.java | 58 +++++++++
.../rp/{ => messaging}/impl/SignRequestObject.java | 124 ++++++++++++------
.../META-INF/net.shibboleth.idp/postconfig.xml | 39 ++++--
.../oidc-relying-party-authn-beans.xml | 121 +++++++++--------
.../oidc-relying-party-authn-flow.xml | 80 +++++-------
.../oidc/rp/impl/AuthorizationControllerTest.java | 143 +++++++++++++++++++--
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 92 +++----------
.../authn/oidc/rp/impl/TestCredentialHelper.java | 86 +++++++++++++
.../resources/conf/test-relying-party-system.xml | 2 +-
23 files changed, 1364 insertions(+), 298 deletions(-)
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java
new file mode 100644
index 0000000..72d2295
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsCodeFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/**
+ * A predicate that determines if the flow is a 'code-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsCodeFlowPredicate implements Predicate<MessageContext> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(IsCodeFlowPredicate.class);
+
+ @Override
+ public boolean test(@Nullable final MessageContext input) {
+ if (input == null) {
+ log.trace("Message context was null, can not determine flow type");
+ return false;
+ }
+ if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+ log.trace("Message context did not contain an authentication request, can not determine flow type");
+ return false;
+ }
+ if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+ log.trace("Authentication request did not contain a response_type, can not determine flow type");
+ return false;
+ }
+ return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesCodeFlow();
+ }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java
new file mode 100644
index 0000000..d43b0ec
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsHybridFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/**
+ * A predicate that determines if the flow is a 'hybrid-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsHybridFlowPredicate implements Predicate<MessageContext> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(IsHybridFlowPredicate.class);
+
+ @Override
+ public boolean test(@Nullable final MessageContext input) {
+ if (input == null) {
+ log.trace("Message context was null, can not determine flow type");
+ return false;
+ }
+ if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+ log.trace("Message context did not contain an authentication request, can not determine flow type");
+ return false;
+ }
+ if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+ log.trace("Authentication request did not contain a response_type, can not determine flow type");
+ return false;
+ }
+ return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesHybridFlow();
+ }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java
new file mode 100644
index 0000000..ed40d87
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/IsImplicitFlowPredicate.java
@@ -0,0 +1,56 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.config.logic;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+
+/**
+ * A predicate that determines if the flow is a 'implicit-flow' using the AuthenticationRequest that is inside
+ * the message context.
+ */
+public class IsImplicitFlowPredicate implements Predicate<MessageContext> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(IsImplicitFlowPredicate.class);
+
+ @Override
+ public boolean test(@Nullable final MessageContext input) {
+ if (input == null) {
+ log.trace("Message context was null, can not determine flow type");
+ return false;
+ }
+ if (!(input.getMessage() instanceof OIDCAuthenticationRequest)) {
+ log.trace("Message context did not contain an authentication request, can not determine flow type");
+ return false;
+ }
+ if (((OIDCAuthenticationRequest)input.getMessage()).getResponseType() == null) {
+ log.trace("Authentication request did not contain a response_type, can not determine flow type");
+ return false;
+ }
+ return ((OIDCAuthenticationRequest)input.getMessage()).getResponseType().impliesImplicitFlow();
+ }
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java
new file mode 100644
index 0000000..cb48477
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/logic/SignRequestObjectPredicate.java
@@ -0,0 +1,45 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.config.logic;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.logic.AbstractRelyingPartyPredicate;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+
+/** A predicate that determines if the RequestObject JWT should be signed based on the profile configuration.*/
+public class SignRequestObjectPredicate extends AbstractRelyingPartyPredicate {
+
+ @Override
+ public boolean test(@Nullable final ProfileRequestContext input) {
+
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof OIDCAuthorizationConfiguration) {
+ return ((OIDCAuthorizationConfiguration) pc).isSignRequestObject(input);
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java
new file mode 100644
index 0000000..6c942bc
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/OutboundMessageContextFromProxyPRC.java
@@ -0,0 +1,55 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.config.navigate;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+
+/**
+ * A {@link ContextDataLookupFunction} that returns the outbound {@link MessageContext} for a
+ * {@link ProfileRequestContext} located inside an {@link AuthenticationContext} i.e. for use in the proxy case.
+ */
+public class OutboundMessageContextFromProxyPRC
+ implements ContextDataLookupFunction<ProfileRequestContext, MessageContext> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public MessageContext apply(@Nullable final ProfileRequestContext input) {
+
+ if (input == null) {
+ return null;
+ }
+ final AuthenticationContext authContext = input.getSubcontext(AuthenticationContext.class);
+ if (authContext == null) {
+ return null;
+ }
+ final ProfileRequestContext nestedProfileRequestContext =
+ authContext.getSubcontext(ProfileRequestContext.class);
+ if (nestedProfileRequestContext != null) {
+ return nestedProfileRequestContext.getOutboundMessageContext();
+ }
+ return null;
+ }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java
new file mode 100644
index 0000000..562c001
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OutboundMessageHandlerContext.java
@@ -0,0 +1,119 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.context;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.opensaml.messaging.context.BaseContext;
+
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A context to stash HTTP request/response controller parameters for use by message handlers.
+ * For example, the OIDC RP preEncodeMessageHandlers.
+ */
+public class OutboundMessageHandlerContext extends BaseContext {
+
+ /** The Http servlet request.*/
+ @Nullable private HttpServletRequest servletRequest;
+
+ /** The Http servlet response.*/
+ @Nullable private HttpServletResponse servletResponse;
+
+ /** The spring webflow key.*/
+ @Nullable private String webflowKey;
+
+ /**
+ * Constructor.
+ *
+ * @param request the servlet request
+ * @param response the servlet response
+ * @param key the swf key
+ */
+ public OutboundMessageHandlerContext(@Nonnull final HttpServletRequest request,
+ @Nonnull final HttpServletResponse response, @Nonnull final String key) {
+ super();
+ servletRequest = Constraint.isNotNull(request, "Http Servlet Request can not be null");
+ servletResponse = Constraint.isNotNull(response, "Http Servlet Response can not be null");
+ webflowKey = Constraint.isNotNull(key, "Spring Webflow Key can not be null");
+ }
+
+ /** Constructor to allow no-arg construction.*/
+ public OutboundMessageHandlerContext() {
+ // Do nothing
+ }
+
+ /**
+ * Set the Http servlet request.
+ * @param request
+ */
+ public void setServletRequest(@Nonnull final HttpServletRequest request) {
+ servletRequest = Constraint.isNotNull(request, "Http Servlet Request can not be null");
+ }
+
+ /**
+ * Get the Http servlet request.
+ *
+ * @return Returns the servletRequest.
+ */
+ @Nullable public HttpServletRequest getServletRequest() {
+ return servletRequest;
+ }
+
+ /**
+ * Set the Http servlet response.
+ *
+ * @param response the servlet response
+ */
+ public void setServletResponse(@Nonnull final HttpServletResponse response) {
+ servletResponse = Constraint.isNotNull(response, "Http Servlet Response can not be null");
+ }
+
+ /**
+ * Get the Http servlet response.
+ *
+ * @return Returns the servletResponse.
+ */
+ @Nullable public HttpServletResponse getServletResponse() {
+ return servletResponse;
+ }
+
+ /**
+ * Set the Spring Webflow execution key.
+ *
+ * @param key the swf execution key
+ */
+ public void setWebflowKey(@Nonnull final String key) {
+ webflowKey = Constraint.isNotNull(key, "Spring Webflow Key can not be null");
+ }
+
+ /**
+ * Get the Spring Webflow execution key.
+ *
+ * @return Returns the webflowKey.
+ */
+ @Nullable public String getWebflowKey() {
+ return webflowKey;
+ }
+
+
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
index 3246ed0..49e2f59 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/context/logic/RequestObjectRequiredAndSupported.java
@@ -83,14 +83,14 @@ public class RequestObjectRequiredAndSupported extends AbstractRelyingPartyPredi
return false;
}
+ final boolean isSupportedByOP = metadata.getProviderInformation().supportsRequestParam();
+
final boolean requestedAndSupport =
- requestObjectRequestedFromConfig && metadata.getProviderInformation().supportsRequestParam();
- if (requestedAndSupport) {
- log.debug("Authentication request RequestObject was requested and is supported");
- } else {
- log.debug("Authentication request RequestObject not requested or not supported");
- }
+ requestObjectRequestedFromConfig && isSupportedByOP;
+ log.debug("Authentication RequestObject was requested '{}', is supported by the OP '{}', "
+ + "will be used '{}'", requestObjectRequestedFromConfig, isSupportedByOP, requestedAndSupport);
+
return requestedAndSupport;
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
index b57c363..37ac136 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
@@ -53,6 +53,7 @@ import net.shibboleth.idp.plugin.authn.oidc.rp.OIDCProxyException;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OutboundMessageHandlerContext;
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -100,11 +101,8 @@ public class AuthorizationController extends AbstractInitializableComponent {
@Nonnull private final Logger log = LoggerFactory.getLogger(AuthorizationController.class);
/** Lookup strategy to locate the nested ProfileRequestContext. */
- @Nonnull private Function<ProfileRequestContext,ProfileRequestContext> profileRequestContextLookupStrategy;
-
- /** Function to create a suitable redirect URI from the given servlet request and profile request context.*/
- @NonnullAfterInit private BiFunction<HttpServletRequest, ProfileRequestContext, URI> redirectUriCreationStrategy;
-
+ @Nonnull private Function<ProfileRequestContext,ProfileRequestContext> profileRequestContextLookupStrategy;
+
/** Lookup strategy to locate the SAML context. */
@Nonnull private Function<ProfileRequestContext,OIDCAuthnContext> oidcContextLookupStrategy;
@@ -168,31 +166,9 @@ public class AuthorizationController extends AbstractInitializableComponent {
ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
oidcContextLookupStrategy = Constraint.isNotNull(strategy, "OIDCAuthnContext lookup strategy cannot be null");
- }
-
- /**
- * Set the creation strategy used to compute or lookup a redirect URI.
- *
- * @param strategy the creation strategy
- */
- public void setRedirectUriCreationStrategy(
- @Nonnull final BiFunction<HttpServletRequest, ProfileRequestContext, URI> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+ }
+
- redirectUriCreationStrategy =
- Constraint.isNotNull(strategy, "RedirectURI creation lookup strategy cannot be null");
- }
-
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (redirectUriCreationStrategy == null) {
- throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
- }
- }
-
/**
* Begin an authorization request to the configured downstream OP.
*
@@ -229,20 +205,13 @@ public class AuthorizationController extends AbstractInitializableComponent {
if (nestedPRC.getOutboundMessageContext() != null &&
nestedPRC.getOutboundMessageContext().getMessage() instanceof OIDCAuthenticationRequest) {
- // Add key and nonce to state
- final String state = OIDCProxySupport.generateState(OIDCProxySupport.generateNonce(32), key);
- ((OIDCAuthenticationRequest)nestedPRC.getOutboundMessageContext().getMessage()).setState(new State(state));
- //add redirect URI based on the request URL and any found in the OIDCAuthnContext
- final URI redirectUri = redirectUriCreationStrategy.apply(httpRequest, nestedPRC);
- if (redirectUri == null) {
- log.error("Redirect URI could not be located or created from strategy");
- httpRequest.setAttribute(ExternalAuthentication.AUTHENTICATION_ERROR_KEY, EventIds.INVALID_PROFILE_CTX);
- ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
- return;
- }
- log.trace("Created redirect_uri '{}'", redirectUri);
- ((OIDCAuthenticationRequest)nestedPRC.getOutboundMessageContext().getMessage()).setRedirectURI(redirectUri);
+ // Build a handler context to allow certain parameters to be set e.g. state, redirect_uri,
+ // by the pre-message-encoders.
+ final OutboundMessageHandlerContext handlerContext = new OutboundMessageHandlerContext(httpRequest,
+ httpResponse, key);
+
+ nestedPRC.getOutboundMessageContext().addSubcontext(handlerContext);
} else {
log.error("Outbound Authorization message not found");
@@ -258,7 +227,7 @@ public class AuthorizationController extends AbstractInitializableComponent {
oidcContext.getEncodeMessageAction().execute(nestedPRC);
// Handle error added by the EncodeMessage action.
- final EventContext eventCtx = prc.getSubcontext(EventContext.class);
+ final EventContext eventCtx = nestedPRC.getSubcontext(EventContext.class);
if (eventCtx != null && eventCtx.getEvent() != null
&& !EventIds.PROCEED_EVENT_ID.equals(eventCtx.getEvent())) {
log.error("Message encoding action signaled non-proceed event {}", eventCtx.getEvent());
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
index 8fb8afd..7402645 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/BuildRequestObject.java
@@ -1,5 +1,24 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.impl;
+import java.util.function.Predicate;
+
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -8,9 +27,8 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Predicates;
import com.nimbusds.jwt.JWT;
-import com.nimbusds.jwt.PlainJWT;
-import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.id.Audience;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
@@ -19,31 +37,60 @@ import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
/**
* Action that creates a Request Object {@link JWT} object, and sets it to work context
* {@link OIDCAuthenticationRequest} located under {@link ProfileRequestContext#getOutboundMessageContext()}.
+ *
+ * <p>Note, some parameters are set downstream before the request object is signed and or encrypted. These
+ * parameters are only available to the Http Controller e.g. state and hostname, and must be set during the external
+ * authentication redirect.</p>
*/
public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(BuildRequestObject.class);
+ /** A hook to allow additional checking of the request object claims after it is build.*/
+ @Nonnull private Predicate<ClaimsSet> claimsSetIsValidPredicate;
+
/** The RelyingPartyContext to operate on. */
@Nullable private RelyingPartyContext rpCtx;
+
+ /** Constructor.*/
+ public BuildRequestObject() {
+ claimsSetIsValidPredicate = Predicates.alwaysTrue();
+ }
+
+ /**
+ * Set a hook that allows the built request object to be validated before it is used.
+ * This is run in addition too, but before, the built in validation taken from the specification.
+ * If this returns false, the built in validation is not run, and validation fails.
+ *
+ * @param predicate the hook to run
+ */
+ public void setClaimsSetIsValidPredicate(@Nullable final Predicate<ClaimsSet> predicate) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ if (predicate != null) {
+ claimsSetIsValidPredicate = predicate;
+ }
+ }
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@Nonnull final AuthenticationContext authenticationContext) {
- log.debug("{} Building a Request Object JWT for the authentication request", getLogPrefix());
+ log.debug("{} Building a plain RequestObject JWT", getLogPrefix());
final ClaimsSet requestObjectClaims = new ClaimsSet();
//TODO check we are signing if we have selected a signing params, or if we just say must sign?
- requestObjectClaims.setClaim("client_id", getAuthenticationRequest().getClientID().getValue());
+ requestObjectClaims.setClaim("client_id", getAuthenticationRequest().getClientID());
requestObjectClaims.setAudience(
new Audience(getProviderMetadataContext().getProviderInformation().getIssuer().getValue()));
requestObjectClaims.setIssuer(new Issuer(getAuthenticationRequest().getClientID().getValue()));
@@ -59,18 +106,48 @@ public class BuildRequestObject extends AbstractOIDCAuthenticationRequestAction
requestObjectClaims.setClaim("scope", getAuthenticationRequest().getScope());
+ // ACRs?
+
+ // Validate the request object
+ if (!validateRequestObject(requestObjectClaims)) {
+ log.error("{} RequestObject claims are not valid", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+ return;
+ }
+
if (log.isDebugEnabled()) {
- log.debug("{} Setting request object response claims to authentication context {}", getLogPrefix(),
+ log.debug("{} Setting the RequestObject claims: {}", getLogPrefix(),
requestObjectClaims.toJSONString());
}
- // Create a plain JWT at first, can be signed and encrypted later
- try {
- getAuthenticationRequest().setRequestObject(new PlainJWT(requestObjectClaims.toJWTClaimsSet()));
- } catch (final ParseException e) {
- log.error("{} Unable to set request object claims as JWT claims", getLogPrefix(), e);
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+ // Create a plain JWT at first, can be signed and encrypted later
+ getAuthenticationRequest().setRequestObjectClaimsSet(requestObjectClaims);
+
+ }
+
+ /**
+ * Ensure the request object is valid by assessing the claims are correct.
+ *
+ * @param requestObjectClaims the claims of the request object
+ *
+ * @return true if the request object claims are valid, false otherwise
+ */
+ private boolean validateRequestObject(final ClaimsSet requestObjectClaims) {
+
+ if (!claimsSetIsValidPredicate.test(requestObjectClaims)) {
+ return false;
+ }
+ // TODO only if signing is enabled
+ if (true) {
+ if (requestObjectClaims.getClaim("iss") == null) {
+ return false;
+ }
+ if (requestObjectClaims.getClaim("aud") == null) {
+ return false;
+ }
+
}
+ return true;
}
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
index b67d742..36244bf 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/DefaultRedirectUriCreationFunction.java
@@ -68,7 +68,7 @@ public class DefaultRedirectUriCreationFunction
/** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
@Nonnull private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
- /** The path, excluding the context and servlet paths, to the Duo callback handler.*/
+ /** The path, excluding the context and servlet paths, to the RP callback handler.*/
@Nonnull @NotEmpty private final String callbackServletPath;
/**
@@ -87,7 +87,7 @@ public class DefaultRedirectUriCreationFunction
@Nonnull @NotEmpty @ParameterName(name="callbackPath") final String callbackPath,
@Nonnull @NotEmpty @ParameterName(name="allowedOrigins") @Nullable final Set<String> origins) {
- callbackServletPath = Constraint.isNotNull(callbackPath,"Duo Call back path can not be null");
+ callbackServletPath = Constraint.isNotNull(callbackPath,"RP Proxy Call back path can not be null");
// Default under OIDCPeerEntityContext in the outbound context (create true) under the nested PRC.
oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
index c888feb..8028995 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCProxySupport.java
@@ -45,7 +45,7 @@ public final class OIDCProxySupport {
*
* @return the randomly generated nonce value.
*/
- @Nonnull static String generateNonce(@Nonnull final Integer length) {
+ @Nonnull public static String generateNonce(@Nonnull final Integer length) {
final SecureRandom secureRandom = new SecureRandom();
final StringBuilder sb = new StringBuilder();
while(sb.length() < length){
@@ -69,7 +69,7 @@ public final class OIDCProxySupport {
*
* @return the combined state component.
*/
- @Nonnull static String generateState(@Nonnull final String nonce, @Nonnull final String key) {
+ @Nonnull public static String generateState(@Nonnull final String nonce, @Nonnull final String key) {
Constraint.isNotNull(nonce, "NonceHex key can not be null");
Constraint.isNotNull(key, "Webflow execution key can not be null");
@@ -87,7 +87,7 @@ public final class OIDCProxySupport {
*
* @throws OIDCProxyException if the key component can not be found, or hex decoding fails.
*/
- @Nonnull static String extractKeyFromState(@Nonnull final String state) throws OIDCProxyException {
+ @Nonnull public static String extractKeyFromState(@Nonnull final String state) throws OIDCProxyException {
Constraint.isNotNull(state, "State can not be null");
final String[] stateSplit = state.split("\\.");
@@ -114,7 +114,7 @@ public final class OIDCProxySupport {
*
* @throws DuoException if the nonce component can not be found.
*/
- @Nonnull static String extractNonceFromState(@Nonnull final String state) throws OIDCProxyException {
+ @Nonnull public static String extractNonceFromState(@Nonnull final String state) throws OIDCProxyException {
Constraint.isNotNull(state, "State can not be null");
final String[] stateSplit = state.split("\\.");
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java
new file mode 100644
index 0000000..d56dab5
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AbstractOIDCAuthenticationRequestMessageHandler.java
@@ -0,0 +1,133 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.messaging.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OutboundMessageHandlerContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** An abstract message handler that pulls out the {@link OutboundMessageHandlerContext} from the message context.*/
+public abstract class AbstractOIDCAuthenticationRequestMessageHandler extends AbstractMessageHandler {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOIDCAuthenticationRequestMessageHandler.class);
+
+ /** Strategy to locate the {@link OutboundMessageHandlerContext}.*/
+ @Nonnull
+ private Function<MessageContext, OutboundMessageHandlerContext> outboundMessageHandlerContextLookupStrategy;
+
+ /** Strategy used to locate the {@link OIDCAuthenticationRequest} to sign. */
+ @Nonnull private Function<MessageContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
+
+ /** The stashed {@link OutboundMessageHandlerContext}.*/
+ @Nullable private OutboundMessageHandlerContext outboundMessageContext;
+
+ /** The stashed {@link OIDCAuthenticationRequest}.*/
+ @Nullable private OIDCAuthenticationRequest authnRequest;
+
+ /** Constructor.*/
+ protected AbstractOIDCAuthenticationRequestMessageHandler() {
+ outboundMessageHandlerContextLookupStrategy = new ChildContextLookup<>(OutboundMessageHandlerContext.class);
+
+ authenticationRequestLookupStrategy = mc -> {
+ if (mc.getMessage() instanceof OIDCAuthenticationRequest) {
+ return (OIDCAuthenticationRequest)mc.getMessage();
+ }
+ return null;
+ };
+ }
+
+
+ /**
+ * Set the strategy used to locate the {@link OIDCAuthenticationRequest} to use.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAuthenticationRequestLookupStrategy(
+ @Nonnull final Function<MessageContext, OIDCAuthenticationRequest> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ authenticationRequestLookupStrategy =
+ Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OutboundMessageHandlerContext}.
+ *
+ * @param strategy the strategy
+ */
+ public void setOutboundMessageHandlerContextLookupStrategy(
+ @Nonnull final Function<MessageContext, OutboundMessageHandlerContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ outboundMessageHandlerContextLookupStrategy =
+ Constraint.isNotNull(strategy, "outboundMessageHandlerContextLookupStrategy can not be null");
+ }
+
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ outboundMessageContext = outboundMessageHandlerContextLookupStrategy.apply(messageContext);
+ if (outboundMessageContext == null) {
+ log.debug("{} Outbound message context is null", getLogPrefix());
+ throw new MessageHandlerException("Outbound message context is null");
+ }
+
+ authnRequest = authenticationRequestLookupStrategy.apply(messageContext);
+ if (authnRequest == null) {
+ log.debug("{} OIDC authentication request is null", getLogPrefix());
+ throw new MessageHandlerException("OIDC authentication request is null");
+ }
+
+ return super.doPreInvoke(messageContext);
+ }
+
+ /**
+ * Get the outbound message context.
+ *
+ * @return the outbound message context.
+ */
+ @Nullable protected OutboundMessageHandlerContext getOutboundMessageContext() {
+ return outboundMessageContext;
+ }
+
+ /**
+ * Get the authentication request.
+ *
+ * @return the authentication request
+ */
+ @Nullable protected OIDCAuthenticationRequest getAuthenticationRequest() {
+ return authnRequest;
+ }
+
+
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java
new file mode 100644
index 0000000..d8c7cca
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddRedirectURI.java
@@ -0,0 +1,111 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl;
+
+import java.net.URI;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** Adds a redirect_uri to the authentication request URL and the request object claims (if present).*/
+public class AddRedirectURI extends AbstractOIDCAuthenticationRequestMessageHandler {
+
+ /** The name of the redirect uri claim.*/
+ private static final String REDIRECT_URI_CLAIM = "redirect_uri";
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddRedirectURI.class);
+
+ /** Function to create a suitable redirect URI from the given servlet request and profile request context.*/
+ @NonnullAfterInit private BiFunction<HttpServletRequest, ProfileRequestContext, URI> redirectUriCreationStrategy;
+
+ /** Locat the profile request context from the given message context. */
+ @Nonnull private Function<MessageContext, ProfileRequestContext> locateProfileRequestContextStrategy;
+
+ /** Constructor.*/
+ public AddRedirectURI() {
+ locateProfileRequestContextStrategy = new RecursiveTypedParentContextLookup<>(ProfileRequestContext.class);
+ }
+
+ /**
+ * Set the strategy used to locate the profile request context from the message context.
+ *
+ * @param strategy the strategy.
+ */
+ public void setLocateProfileRequestContextStrategy(
+ final Function<MessageContext, ProfileRequestContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ locateProfileRequestContextStrategy =
+ Constraint.isNotNull(strategy, "LocateProfileRequestStrategy cannot be null");
+ }
+
+ /**
+ * Set the creation strategy used to compute or lookup a redirect URI.
+ *
+ * @param strategy the creation strategy
+ */
+ public void setRedirectUriCreationStrategy(
+ @Nullable final BiFunction<HttpServletRequest, ProfileRequestContext, URI> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ if (strategy != null) {
+ redirectUriCreationStrategy =
+ Constraint.isNotNull(strategy, "RedirectURI creation lookup strategy cannot be null");
+ }
+ }
+
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (redirectUriCreationStrategy == null) {
+ throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final ProfileRequestContext prc = locateProfileRequestContextStrategy.apply(messageContext);
+ if (prc == null) {
+ throw new MessageHandlerException(
+ "Unable to locate the profile request context, redirect_uri can not be determined");
+ }
+
+ final URI redirectUri =
+ redirectUriCreationStrategy.apply(getOutboundMessageContext().getServletRequest(), prc);
+ if (redirectUri == null) {
+ throw new MessageHandlerException("Redirect URI could not be located or created from strategy");
+ }
+ log.trace("Created redirect_uri '{}'", redirectUri);
+ getAuthenticationRequest().setRedirectURI(redirectUri);
+
+ // Also add to request object if exists
+ if (getAuthenticationRequest().getRequestObjectClaimsSet() != null) {
+ final ClaimsSet claims = getAuthenticationRequest().getRequestObjectClaimsSet();
+ log.trace("{} Adding redirect_uri to JWT RequestObject", getLogPrefix());
+ claims.setClaim(REDIRECT_URI_CLAIM, redirectUri);
+ }
+
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java
new file mode 100644
index 0000000..579c441
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/AddState.java
@@ -0,0 +1,108 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.messaging.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCProxySupport;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+
+/** Add state to the authentication request URL and the request object claims (if present) .*/
+public class AddState extends AbstractOIDCAuthenticationRequestMessageHandler {
+
+ /** The 'state' claim name.*/
+ @Nonnull private static final String STATE_CLAIM = "state";
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddState.class);
+
+ /** Function to create a suitable 'state' to add to the authentication request.*/
+ @Nonnull private Function<MessageContext, String> stateGenerationStrategy;
+
+ /** Constructor.*/
+ public AddState() {
+ // By default, generate state from the SWF key and a 32 character nonce.
+ stateGenerationStrategy = msg -> {
+ if (getOutboundMessageContext() != null) {
+ return OIDCProxySupport.generateState(OIDCProxySupport.generateNonce(32),
+ getOutboundMessageContext().getWebflowKey());
+ }
+ return null;
+ };
+ }
+
+ /**
+ * Set the creation strategy used to compute the 'state'.
+ *
+ * @param strategy the creation strategy
+ */
+ public void setStateGenerationStrategy(
+ @Nullable final Function<MessageContext, String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ if (strategy != null) {
+ stateGenerationStrategy = strategy;
+ }
+
+ }
+
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (stateGenerationStrategy == null) {
+ throw new ComponentInitializationException("redirectUriCreationStrategy cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final String stateString = stateGenerationStrategy.apply(messageContext);
+ log.trace("{} Generated state '{}'", getLogPrefix(), stateString);
+ if (stateString == null) {
+ throw new MessageHandlerException("Generated state was null");
+ }
+ final State state = new State(stateString);
+
+ // Add to outer request
+ getAuthenticationRequest().setState(state);
+
+ // Add to Request Object if exists
+ if (getAuthenticationRequest().getRequestObjectClaimsSet() != null) {
+ final ClaimsSet claims = getAuthenticationRequest().getRequestObjectClaimsSet();
+ log.trace("{} Adding state to JWT RequestObject", getLogPrefix());
+ claims.setClaim(STATE_CLAIM, state);
+ }
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java
new file mode 100644
index 0000000..df2de95
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/BuildPlainRequestObjectJWT.java
@@ -0,0 +1,58 @@
+/*
+ * 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.idp.plugin.authn.oidc.rp.messaging.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+/**
+ * If the Request Object claims are present in the authentication request, convert them
+ * into a JWTClaimsSet inside a PlainJWT.
+ */
+public class BuildPlainRequestObjectJWT extends AbstractOIDCAuthenticationRequestMessageHandler {
+
+ /** Logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(BuildPlainRequestObjectJWT.class);
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final ClaimsSet requestObjectClaims = getAuthenticationRequest().getRequestObjectClaimsSet();
+ if (requestObjectClaims == null) {
+ log.trace("{} RequestObject claims are not present, request object JWT skipped", getLogPrefix());
+ return;
+ }
+ try {
+ getAuthenticationRequest().setRequestObject(new PlainJWT(requestObjectClaims.toJWTClaimsSet()));
+ log.trace("{} Built Plain JWT RequestObject from claims", getLogPrefix());
+ } catch (final ParseException e) {
+ throw new MessageHandlerException("Unable to generate request object JWT", e);
+ }
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
similarity index 62%
rename from idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java
rename to idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
index 797821f..0e6c98e 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SignRequestObject.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/messaging/impl/SignRequestObject.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+package net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl;
import java.security.interfaces.ECPrivateKey;
import java.text.ParseException;
@@ -24,11 +24,10 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
import org.opensaml.security.credential.Credential;
import org.opensaml.xmlsec.SignatureSigningParameters;
import org.opensaml.xmlsec.context.SecurityParametersContext;
@@ -40,6 +39,7 @@ import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.JWSObject.State;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.MACSigner;
@@ -48,7 +48,7 @@ import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
import net.shibboleth.oidc.security.credential.JWKCredential;
import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
@@ -58,17 +58,18 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
- * Action that signs a request object and sets it to ???.
+ * Action that signs a request object and sets it as the request object to the authentication request.
*/
-public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
+public class SignRequestObject extends AbstractMessageHandler {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(SignRequestObject.class);
- /**
- * Strategy used to locate the {@link SecurityParametersContext} to use for signing.
- */
- @Nonnull private Function<ProfileRequestContext, JWTSecurityParametersContext> securityParametersLookupStrategy;
+ /** Strategy used to locate the {@link SecurityParametersContext} to use for signing. */
+ @Nonnull private Function<MessageContext, JWTSecurityParametersContext> securityParametersLookupStrategy;
+
+ /** Strategy used to locate the {@link OIDCAuthenticationRequest} to sign. */
+ @Nonnull private Function<MessageContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
/** The signature signing parameters. */
@Nullable private SignatureSigningParameters signatureSigningParameters;
@@ -82,10 +83,18 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
/** "typ" header to insert while signing. */
@Nullable @NotEmpty private String typeHeader;
+ /** The stashed authentication request.*/
+ @Nullable private OIDCAuthenticationRequest authnRequest;
+
/** Constructor.*/
public SignRequestObject() {
- securityParametersLookupStrategy = new ChildContextLookup<>(JWTSecurityParametersContext.class)
- .compose(new OutboundMessageContextLookup());
+ securityParametersLookupStrategy = new ChildContextLookup<>(JWTSecurityParametersContext.class);
+ authenticationRequestLookupStrategy = mc -> {
+ if (mc.getMessage() instanceof OIDCAuthenticationRequest) {
+ return (OIDCAuthenticationRequest)mc.getMessage();
+ }
+ return null;
+ };
}
/**
@@ -105,58 +114,79 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
* @param strategy lookup strategy
*/
public void setSecurityParametersLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, JWTSecurityParametersContext> strategy) {
+ @Nonnull final Function<MessageContext, JWTSecurityParametersContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
securityParametersLookupStrategy =
Constraint.isNotNull(strategy, "SecurityParameterContext lookup strategy cannot be null");
}
+ /**
+ * Set the strategy used to locate the {@link OIDCAuthenticationRequest} to use.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAuthenticationRequestLookupStrategy(
+ @Nonnull final Function<MessageContext, OIDCAuthenticationRequest> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ authenticationRequestLookupStrategy =
+ Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+ }
+
+
/** {@inheritDoc} */
@Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ if (!super.doPreInvoke(messageContext)) {
+ return false;
+ }
+
+ authnRequest = authenticationRequestLookupStrategy.apply(messageContext);
+ if (authnRequest == null) {
+ log.debug("{} No authentication request available", getLogPrefix());
return false;
}
- final JWTSecurityParametersContext secParamCtx = securityParametersLookupStrategy.apply(profileRequestContext);
+ final JWT requestObject = authnRequest.getRequestObject();
+ if (requestObject == null) {
+ log.debug("{} No JWT RequestObject found, nothing to sign", getLogPrefix());
+ return false;
+ }
+
+ final JWTSecurityParametersContext secParamCtx =
+ securityParametersLookupStrategy.apply(messageContext);
if (secParamCtx == null) {
- log.debug("{} no security parameters context are available", getLogPrefix());
+ log.debug("{} Message context did not contain signing parameters, "
+ + "request object will not be signed", getLogPrefix());
return false;
}
-
+
signatureSigningParameters = secParamCtx.getSignatureSigningParameters();
if (signatureSigningParameters == null || signatureSigningParameters.getSigningCredential() == null) {
- log.debug("{} no signature signing credentials available", getLogPrefix());
- return false;
- }
- final JWT requestObject = getAuthenticationRequest().getRequestObject();
- if (requestObject == null) {
- log.debug("{} no JWT request object found, nothing to sign", getLogPrefix());
+ log.debug("{} No signature signing credentials available", getLogPrefix());
return false;
}
try {
jwtClaimSetToSign = requestObject.getJWTClaimsSet();
} catch (final ParseException e) {
- log.debug("{} no JWT request object found, nothing to sign", getLogPrefix(), e);
+ log.debug("{} No JWT RequestObject found, nothing to sign", getLogPrefix(), e);
return false;
}
credential = signatureSigningParameters.getSigningCredential();
-
+
return true;
+
}
- /** {@inheritDoc} */
@Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
-
- SignedJWT jwt = null;
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
try {
+ SignedJWT jwt = null;
final Algorithm jwsAlgorithm = resolveAlgorithm();
final JWSSigner signer = getSigner(jwsAlgorithm);
final JWSHeader.Builder headerBuilder = new JWSHeader.Builder(new JWSAlgorithm(jwsAlgorithm.getName()))
@@ -166,13 +196,27 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
}
jwt = new SignedJWT(headerBuilder.build(), jwtClaimSetToSign);
jwt.sign(signer);
+ if (log.isDebugEnabled() && !log.isTraceEnabled()) {
+ log.debug("{} Signed RequestObject", getLogPrefix());
+ } else if (log.isTraceEnabled()) {
+ log.debug("{} Signed RequestObject: {}", getLogPrefix(), jwt.serialize());
+ }
+
+ if (jwt.getState() != State.SIGNED) {
+ // Should not really happen, as JOSEException should be thrown
+ log.error("{} RequestObject was not signed", getLogPrefix());
+ throw new MessageHandlerException("RequestObject was not signed, unknown cause");
+ }
+
+ // Add the signed JWT over the unsigned JWT
+ authnRequest.setRequestObject(jwt);
+
} catch (final JOSEException e) {
log.error("{} Error signing claim set: {}", getLogPrefix(), e.getMessage());
- ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_SIGN);
- return;
+ throw new MessageHandlerException("Error signing claims set",e);
}
- // Add the signed JWT over the unsigned JWT
- getAuthenticationRequest().setRequestObject(jwt);
+
+
}
/**
@@ -213,4 +257,6 @@ public class SignRequestObject extends AbstractOIDCAuthenticationRequestAction {
return algorithm;
}
+
+
}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index fd0fae4..9583830 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -67,6 +67,25 @@
</constructor-arg>
</bean>
+ <bean id="shibboleth.ChildLookup.Proxy.RelyingPartyContext"
+ parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <ref bean="shibboleth.ChildLookup.RelyingParty" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <ref bean="shibboleth.ChildLookup.ProfileRequestContext" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <ref bean="shibboleth.ChildLookup.AuthenticationContext" />
+ </constructor-arg>
+ </bean>
+
<bean id="shibboleth.ChildLookup.OIDCProviderMetadataFromPeerEntityContext"
parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
@@ -94,6 +113,16 @@
<bean id="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound"
class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.InboundMessageContextFromProxyPRC"/>
+
+ <bean id="shibboleth.ChildLookup.Proxy.MessageContextLookup.Outbound"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.OutboundMessageContextFromProxyPRC"/>
+
+ <!-- Alias to use in the flow config -->
+ <alias name="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound" alias="InboundMessageContextFromRootPRC"/>
+ <alias name="shibboleth.ChildLookup.Proxy.MessageContextLookup.Outbound" alias="OutboundMessageContextFromRootPRC"/>
+ <alias name="shibboleth.ChildLookup.Proxy.ProxyProfileRequestContext" alias="ProxyProfileRequestContextLookup"/>
+
+
<!-- The authentication flow descriptor -->
@@ -135,16 +164,8 @@
<bean id="shibboleth.authn.OIDC.externalAuthnPath" class="java.lang.String"
c:_0="servletRelative:#{getObject('shibboleth.authn.OIDC.externalServletPath')}#{T(net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController).AUTHORIZE_PATH_SEGMENT}" />
-
-
<bean id="shibboleth.oidc.rp.AuthorizationController"
- p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
-
- <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
- c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
- c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
<!-- OIDC OP information resolver service beans. -->
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index e79adc0..a768faa 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -99,74 +99,71 @@
<bean id="SelectProfileConfiguration" class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration"
scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
-
-
- <bean id="InitializeAuthorizationRequest"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeAuthorizationRequest"
- scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
+
+
+ <bean id="InitializeAuthorizationRequest"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeAuthorizationRequest" scope="prototype"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
<bean id="PopulateResponseTypeAndMode" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateResponseTypeAndMode"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
- <bean id="PopulateScopes" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateScopes"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
- <bean id="PopulateNonce" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateNonce"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <bean id="PopulateScopes" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateScopes"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <bean id="PopulateNonce" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateNonce"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
<bean id="PopulateEndpointURI" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateEndpointURI"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
- <bean id="PopulateACRs" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateACRs"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
+ <bean id="PopulateACRs" scope="prototype" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateACRs"
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
+
<bean id="PopulateForceAuthenticationPrompt" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.PopulateForceAuthenticationPrompt"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
+ p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup" />
<!-- Build RequestObject if required -->
<bean id="RequestObjectRequiredAndSupportedPredicate"
class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.context.logic.RequestObjectRequiredAndSupported" />
-
- <bean id="PopulateRequestObjectSignatureSigningParameters"
+
+ <bean id="PopulateRequestObjectSignatureSigningParameters"
class="net.shibboleth.oidc.security.impl.PopulateJWTSignatureSigningParameters"
c:strategy-ref="shibboleth.ChildLookup.Proxy.MessageContextLookup.Inbound"
p:configurationLookupStrategy-ref="RequestObjectSignatureSigningConfigurationLookup"
p:providerMetadataContextLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
- p:signatureSigningParametersResolver-ref="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"/>
-
- <bean id="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"
+ p:signatureSigningParametersResolver-ref="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver">
+ <property name="activationCondition">
+ <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.SignRequestObjectPredicate"
+ p:relyingPartyContextLookupStrategy-ref="shibboleth.ChildLookup.Proxy.RelyingPartyContext"/>
+ </property>
+ </bean>
+
+ <bean id="shibboleth.authn.oidc.rp.RequestObjectSignatureSigningParametersResolver"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.RelyingPartyProxySigningParametersResolver"
- p:providerMetadataAlgorithmLookupStrategy-ref="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"/>
-
- <bean id="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.config.RequestObjectSupportedSignatureSigningAlgorithms"/>
-
+ p:providerMetadataAlgorithmLookupStrategy-ref="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms" />
+
+ <bean id="shibboleth.authn.oidc.rp.RequestObjectSupportedSignatureSigningAlgorithms"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.RequestObjectSupportedSignatureSigningAlgorithms" />
+
<bean id="RequestObjectSignatureSigningConfigurationLookup" lazy-init="true"
class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.RequestObjectSignatureSigningConfigurationLookupFunction"
p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
- <bean id="BuildRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.BuildRequestObject"
- scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
- <bean id="SignRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.SignRequestObject" scope="prototype"
- p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
- p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"/>
-
+ <bean id="BuildRequestObject" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.BuildRequestObject"
+ scope="prototype" p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+ p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
+ p:claimsSetIsValidPredicate="#{getObject('shibboleth.authn.oidc.rp.RequestObjectClaimsSetIsValidPredicate')}" />
<!-- Message Encoder factory is a prototype to allow reuse of the encoders -->
@@ -182,26 +179,45 @@
<bean id="HTTPRedirectAuthnRequestEncoder"
class="net.shibboleth.oidc.profile.encoder.impl.HTTPRedirectAuthnRequestEncoder" init-method=""
- scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+ scope="prototype" p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+ p:authorizationParamsAreValidPredicate="#{getObject('shibboleth.authn.oidc.rp.AuthzParamsValidPredicate')}" />
<bean id="HTTPPostAuthnRequestEncoder"
class="net.shibboleth.oidc.profile.encoder.impl.HTTPPostAuthnRequestEncoder" init-method="" scope="prototype"
- p:velocityEngine-ref="shibboleth.VelocityEngine" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
+ p:velocityEngine-ref="shibboleth.VelocityEngine" p:httpServletResponse-ref="shibboleth.HttpServletResponse"
+ p:authorizationParamsAreValidPredicate="#{getObject('shibboleth.authn.oidc.rp.AuthzParamsValidPredicate')}" />
<bean id="EncodeMessage" class="org.opensaml.profile.action.impl.EncodeMessage" scope="prototype"
p:messageEncoderFactory-ref="messageEncoderFactory" p:httpServletResponse-ref="shibboleth.HttpServletResponse" />
- <!-- TODO: Place holder for message handlers -->
<bean id="PreEncodeMessageHandler" class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain"
scope="prototype">
<property name="handlers">
<list>
+ <bean id="AddState" class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddState"
+ scope="prototype"
+ p:stateGenerationStrategy="#{getObject('shibboleth.authn.oidc.rp.StateGenerationStrategy')}" />
+
+ <bean id="AddRedirectURI"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddRedirectURI" scope="prototype"
+ p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}" />
+ <bean id="BuildPlainRequestObjectJWT"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.BuildPlainRequestObjectJWT"
+ scope="prototype" />
+
+ <bean id="SignRequestObject"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.SignRequestObject" scope="prototype" />
</list>
</property>
</bean>
+ <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
+ c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
+ c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+
<!-- Message Decoding -->
<bean id="messageDecoderFactory" class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageDecoderFactory">
<property name="beanMappings">
@@ -251,7 +267,10 @@
</bean>
</property>
</bean>
-
+
+ <bean id="IsCodeFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsCodeFlowPredicate"/>
+ <bean id="IsHybridFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsHybridFlowPredicate"/>
+ <bean id="IsImplicitFlow" class="net.shibboleth.idp.plugin.authn.oidc.rp.config.logic.IsImplicitFlowPredicate"/>
<!-- CODE flow beans -->
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index b2c98a9..59ccafb 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -23,7 +23,6 @@
<!-- Nest new PRC inside AC -->
<evaluate expression="InitializeProxyProfileRequestContext" />
<evaluate expression="FlowStartPopulateAuditContext" />
- <!-- Init inbound msg context and create OP Peer -->
<evaluate expression="PrepareOIDCInboundMessageContext" />
<evaluate expression="OIDCProviderMetadataLookup" />
@@ -39,32 +38,32 @@
<evaluate expression="PopulateScopes" />
<evaluate expression="PopulateNonce" />
<evaluate expression="PopulateForceAuthenticationPrompt" />
- <evaluate expression="PopulateEndpointURI"/>
- <evaluate expression="PopulateACRs"/>
- <!-- <evaluate expression="PostRequestPopulateAuditContext" />
- <evaluate expression="WriteAuditLog" /> -->
+ <evaluate expression="PopulateEndpointURI" />
+ <evaluate expression="PopulateACRs" />
+ <!-- <evaluate expression="PostRequestPopulateAuditContext" /> <evaluate expression="WriteAuditLog" /> -->
<!-- <evaluate expression="InitializeMessageChannelSecurityContext" /> -->
<evaluate expression="'proceed'" />
<transition on="proceed" to="RequestObjectRequiredAndSupported" />
</action-state>
-
- <!-- Is a request object requested by the config, and does the OP support it? -->
+
+ <!-- Is a request object requested by the config, and does the OP support it? -->
<decision-state id="RequestObjectRequiredAndSupported">
- <if test="RequestObjectRequiredAndSupportedPredicate.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
- then="BuildRequestObject" else="AuthRequest"/>
+ <if
+ test="RequestObjectRequiredAndSupportedPredicate.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
+ then="BuildRequestObject" else="AuthnRequest" />
</decision-state>
-
+
<action-state id="BuildRequestObject">
- <evaluate expression="PopulateRequestObjectSignatureSigningParameters"/>
+ <evaluate expression="PopulateRequestObjectSignatureSigningParameters" />
<evaluate expression="BuildRequestObject" />
- <evaluate expression="SignRequestObject" />
- <!-- <evaluate expression="'proceed'" />
- <transition on="proceed" to="AuthRequest" /> -->
+ <!-- We can not sign and encrypt the RO here, it has to be done as part of the controller so we can add state etc. -->
+ <evaluate expression="'proceed'" />
+ <transition on="proceed" to="AuthRequest" />
</action-state>
-
- <view-state id="AuthRequest"
+
+ <view-state id="AuthnRequest"
view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OIDC.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
<attribute name="csrf_excluded" value="true" type="boolean" />
<on-render>
@@ -93,27 +92,10 @@
<!-- Switch flow path based on OIDC grant_type used. TODO possible places for an NPE, use strategy? -->
<decision-state id="SwitchOnGrantType">
- <!-- check a null response_type first, should never get here -->
- <if
- test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
- .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType() == null"
- then="UnsupportedFlow" />
- <!-- Check for CODE flow -->
- <if
- test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
- .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesCodeFlow()"
- then="CodeFlow" />
- <!-- Check for Hybrid flow -->
- <if
- test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
- .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesImplicitFlow()"
- then="HybridFlow" />
- <!-- Check for IMPLICIT flow -->
+ <if test="IsCodeFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="CodeFlow" />
+ <if test="IsHybridFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="HybridFlow" />
<!-- final IF has an else if an unsupported flow is used (should not happen) -->
- <if
- test="opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext')
- .getSubcontext('org.opensaml.profile.context.ProfileRequestContext').getOutboundMessageContext().getMessage().getResponseType().impliesHybridFlow()"
- then="ImplicitFlow" else="UnsupportedFlow" />
+ <if test="IsImplicitFlow.test(OutboundMessageContextFromRootPRC.apply(opensamlProfileRequestContext))" then="ImplicitFlow" else="UnsupportedFlow" />
</decision-state>
<action-state id="CodeFlow">
@@ -129,17 +111,17 @@
<!-- TODO claim validation will differ per grant_type -->
<action-state id="ValidateToken">
<evaluate expression="PopulateIDTokenDecryptionParameters" />
- <evaluate expression="DecryptJWT"/>
+ <evaluate expression="DecryptJWT" />
<evaluate expression="PopulateIDTokenSignatureValidationParameters" />
- <!-- TODO Not sure if HandleIDTokenValidation needs to be a message chain? -->
+ <!-- TODO Not sure if HandleIDTokenValidation needs to be a message chain? -->
<evaluate expression="HandleIDTokenValidation" />
<evaluate expression="ValidateIDTokenClaims" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="CheckUserInfoClaimsRequired" />
</action-state>
- <decision-state id="CheckUserInfoClaimsRequired">
- <if test="CheckUserInfoRequiredCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
+ <decision-state id="CheckUserInfoClaimsRequired">
+ <if test="CheckUserInfoRequiredCondition.test(ProxyProfileRequestContextLookup.apply(opensamlProfileRequestContext))"
then="UserInfoRequest" else="FinalizeResponse" />
<!-- if else here, we need to set id_token claims into EndUserClaimsContext -->
</decision-state>
@@ -152,22 +134,22 @@
</action-state>
<!-- A plain JWT will skip token validation and go straight to claims validation -->
- <decision-state id="CheckUserInfoResponseType">
- <if test="CheckUserInfoPlainResponseTypeCondition.test(opensamlProfileRequestContext.getSubcontext('net.shibboleth.idp.authn.context.AuthenticationContext').getSubcontext('org.opensaml.profile.context.ProfileRequestContext'))"
- then="ValidateUserInfoPlaimClaimsSet" else="ValidateUserInfoJWT"/>
+ <decision-state id="CheckUserInfoResponseType">
+ <if test="CheckUserInfoPlainResponseTypeCondition.test(ProxyProfileRequestContextLookup.apply(opensamlProfileRequestContext))"
+ then="ValidateUserInfoPlaimClaimsSet" else="ValidateUserInfoJWT" />
</decision-state>
-
+
<!-- Actions to perform if the UserInfo response is a JWT type -->
<action-state id="ValidateUserInfoJWT">
<evaluate expression="PopulateUserInfoDecryptionParameters" />
- <evaluate expression="DecryptUserInfoJWT"/>
+ <evaluate expression="DecryptUserInfoJWT" />
<evaluate expression="PopulateUserInfoTokenSignatureValidationParameters" />
- <evaluate expression="HandleUserInfoTokenValidation" />
- <evaluate expression="ValidateUserInfoTokenClaims" />
+ <evaluate expression="HandleUserInfoTokenValidation" />
+ <evaluate expression="ValidateUserInfoTokenClaims" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="FinalizeResponse" />
+ <transition on="proceed" to="FinalizeResponse" />
</action-state>
-
+
<!-- Plain UserInfo response types will skip straight to this stage -->
<action-state id="ValidateUserInfoPlaimClaimsSet">
<evaluate expression="ValidateUserInfoPlainResponseClaims" />
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
index 1550a2a..98b636e 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationControllerTest.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@@ -27,9 +28,11 @@ import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
+import java.util.ArrayList;
import java.util.Set;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -40,10 +43,13 @@ import org.opensaml.messaging.decoder.MessageDecodingException;
import org.opensaml.messaging.decoder.servlet.AbstractHttpServletRequestMessageDecoder;
import org.opensaml.messaging.encoder.MessageEncoder;
import org.opensaml.messaging.encoder.MessageEncodingException;
+import org.opensaml.messaging.handler.MessageHandler;
+import org.opensaml.messaging.handler.impl.BasicMessageHandlerChain;
import org.opensaml.profile.action.AbstractProfileAction;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.xmlsec.SignatureSigningParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
@@ -64,7 +70,11 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.id.Audience;
import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
import net.shibboleth.idp.authn.ExternalAuthentication;
@@ -75,6 +85,10 @@ import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCAuthnContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddRedirectURI;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.AddState;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.BuildPlainRequestObjectJWT;
+import net.shibboleth.idp.plugin.authn.oidc.rp.messaging.impl.SignRequestObject;
import net.shibboleth.idp.plugin.authn.test.flow.mock.IdPPropertyConfigurer;
import net.shibboleth.idp.session.IdPSession;
import net.shibboleth.idp.session.context.SessionContext;
@@ -83,11 +97,12 @@ import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration.OIDCHtt
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
import net.shibboleth.oidc.profile.decoding.OIDCMessageDecoder;
import net.shibboleth.oidc.profile.encoder.impl.AbstractOIDCMessageEncoder;
+import net.shibboleth.oidc.security.context.JWTSecurityParametersContext;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.net.HttpServletSupport;
import net.shibboleth.utilities.java.support.net.URLBuilder;
-
+/** Integration tests, using other 'live' actions, for the AuthorizationController.*/
@ContextConfiguration(classes = {AuthorizationController.class, IdPPropertyConfigurer.class})
@WebAppConfiguration
@TestPropertySource(properties = {"shibboleth.authn.OIDC.externalAuthnPath=/Authn/OIDC/RP",})
@@ -99,6 +114,9 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
/** The endpoint to redirect the user-agent to.*/
@Nonnull private final String ENDPOINT_URI = "https://op.example.com/";
+ /** A client_secret to use.*/
+ @Nonnull private final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+
/** The issuer or OP identifier.*/
@Nonnull private final String ISSUER = "https://op.example.com";
@@ -117,6 +135,9 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
/** The mock http servlet request.*/
@Nonnull @Autowired private HttpServletRequest request;
+ /** The PRC that has been setup.*/
+ @Nullable private ProfileRequestContext context;
+
/**
* Setup.
*
@@ -131,13 +152,11 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
assertNotNull(controller);
assertNotNull(response);
assertNotNull(request);
- controller.setRedirectUriCreationStrategy(
- new DefaultRedirectUriCreationFunction("/Authn/OIDC/RP/callback", Set.of("http://localhost")));
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
//add and mock attributes of the servlet context.
- exportServletContextAttributes();
+ context = exportServletContextAttributes();
}
@Test
@@ -157,24 +176,72 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
assertTrue(result.getResponse().getRedirectedUrl().contains("client_id"));
assertTrue(result.getResponse().getRedirectedUrl().contains("response_type"));
assertTrue(result.getResponse().getRedirectedUrl().contains("scope"));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("state"));
+ // Should not contain the request object
+ assertFalse(result.getResponse().getRedirectedUrl().contains("request"));
}
- //TODO the callback method
+ @Test
+ public void testSuccessfulAuthorizeRequest_WithRequestObject() throws Exception {
+
+ assertTrue(context.getSubcontext(AuthenticationContext.class)
+ .getSubcontext(ProfileRequestContext.class)
+ .getOutboundMessageContext().getMessage() instanceof OIDCAuthenticationRequest);
+ // Add a request object into the request
+ final OIDCAuthenticationRequest request =
+ (OIDCAuthenticationRequest) context.getSubcontext(AuthenticationContext.class)
+ .getSubcontext(ProfileRequestContext.class)
+ .getOutboundMessageContext().getMessage();
+ addRequestObject(request);
+
+ final MvcResult result = mockMvc.perform(get("/Authn/OIDC/RP/authz")
+ .param("conversation", "e1s1").characterEncoding("UTF-8"))
+ .andDo(print())
+ .andExpect(status().is3xxRedirection()).andReturn();
+ assertNotNull(result.getResponse().getHeader("Location"));
+ final ExternalAuthenticationContext extContext = extractExternalAuthContext();
+ // assert no error in the context
+ assertNull(extContext.getAuthnError());
+ //basic check of the redirection URL.
+ assertTrue(result.getResponse().getRedirectedUrl().contains(ENDPOINT_URI));
+ assertTrue(result.getResponse().getRedirectedUrl().contains(URLEncoder.encode(REDIRECT_URI, "UTF-8")));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("client_id"));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("response_type"));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("scope"));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("request"));
+ assertTrue(result.getResponse().getRedirectedUrl().contains("state"));
+ }
+ /** Add a simple RequestObject JWT to the authentication request.*/
+ private void addRequestObject(@Nonnull final OIDCAuthenticationRequest request) {
+
+ final ClaimsSet requestObjectClaims = new ClaimsSet();
+ requestObjectClaims.setClaim("client_id", "mock_client");
+ requestObjectClaims.setAudience(new Audience("mock-op"));
+ requestObjectClaims.setIssuer(new Issuer("mock_client"));
+ requestObjectClaims.setClaim("scope", new Scope("openid"));
+
+ request.setRequestObjectClaimsSet(requestObjectClaims);
+
+ }
+
/**
* Export the FlowExecutor to the servlet context with the correct set of configured contexts. Mimicking the
* IdP's configuration of the {@link ServletContextAttributeExporter}.
*
+ * @return the profile request context.
+ *
* @throws Exception on error.
*/
- private void exportServletContextAttributes() throws Exception {
+ private ProfileRequestContext exportServletContextAttributes() throws Exception {
final FlowExecutorImpl mockFlowExecutor = Mockito.mock(FlowExecutorImpl.class);
final FlowExecutionRepository mockFlowExecutionRepo = Mockito.mock(FlowExecutionRepository.class);
final FlowExecution mockFlowExecution = Mockito.mock(FlowExecution.class);
final MutableAttributeMap<Object> map = new LocalAttributeMap<Object>();
- map.put(ProfileRequestContext.BINDING_KEY, buildProfileRequestContext());
+ final var prc = buildProfileRequestContext();
+ map.put(ProfileRequestContext.BINDING_KEY, prc);
Mockito.when(mockFlowExecutor.getExecutionRepository()).thenReturn(mockFlowExecutionRepo);
final CompositeFlowExecutionKey key = new CompositeFlowExecutionKey("1", "1");
@@ -184,6 +251,64 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
// overwrites previous if set from previous method executions.
servletContext.setAttribute(ExternalAuthentication.SWF_KEY, mockFlowExecutor);
+
+ // add a security params context to nested prc
+ addSecurityParametersContext(
+ prc.getSubcontext(AuthenticationContext.class).getSubcontext(ProfileRequestContext.class));
+
+ // add standard set of preencode message handlers to authn context under ac
+ addPreEncodeMessageHandler(
+ prc.getSubcontext(AuthenticationContext.class).getSubcontext(OIDCAuthnContext.class));
+
+ return prc;
+ }
+
+ /**
+ * Add the standard set of pre encode message handlers e.g. those to sign a request object if present.
+ *
+ * @param authnContext the authentication context
+ * @throws ComponentInitializationException on error
+ */
+ private void addPreEncodeMessageHandler(@Nonnull final OIDCAuthnContext authnContext)
+ throws ComponentInitializationException {
+
+ final var chainingMsgHandler = new BasicMessageHandlerChain();
+ final var handlers = new ArrayList<MessageHandler>();
+
+ final var addState = new AddState();
+ addState.initialize();
+ final var addRedirectUri = new AddRedirectURI();
+ addRedirectUri.setRedirectUriCreationStrategy(
+ new DefaultRedirectUriCreationFunction("/Authn/OIDC/RP/callback", Set.of("http://localhost")));
+ addRedirectUri.initialize();
+ final var signer = new SignRequestObject();
+ signer.initialize();
+ final var buildRequestObjectJwt = new BuildPlainRequestObjectJWT();
+ buildRequestObjectJwt.initialize();
+
+
+ handlers.add(addState);
+ handlers.add(addRedirectUri);
+ handlers.add(buildRequestObjectJwt);
+ handlers.add(signer);
+ chainingMsgHandler.setHandlers(handlers);
+ authnContext.setOutboundMessageHandler(chainingMsgHandler);
+
+ }
+
+ /**
+ * A a security parameters context, almost always for request object signing/encryption by this point.
+ *
+ * @param prc the profile request context
+ */
+ private void addSecurityParametersContext(@Nonnull final ProfileRequestContext prc) {
+ // Create a sec context under the nested prc outbound msg context
+ final var secContext = prc.getOutboundMessageContext().getSubcontext(JWTSecurityParametersContext.class, true);
+
+ final var sigParams = new SignatureSigningParameters();
+ sigParams.setSignatureAlgorithm("HS256");
+ sigParams.setSigningCredential(TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
+ secContext.setSignatureSigningParameters(sigParams);
}
/**
@@ -247,9 +372,7 @@ public class AuthorizationControllerTest extends AbstractTestNGSpringContextTest
final OIDCPeerEntityContext peerEntity = new OIDCPeerEntityContext();
peerEntity.setIdentifier(ISSUER);
- final OAuth2ClientContext clientContext = peerEntity.getSubcontext(OAuth2ClientContext.class, true);
-
-
+ final OAuth2ClientContext clientContext = peerEntity.getSubcontext(OAuth2ClientContext.class, true);
prc.getOutboundMessageContext().addSubcontext(peerEntity);
return rootPrc;
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index 607412e..2d3f7bc 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -475,67 +475,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
new URI("/idp/profile/Authn/OIDC/RP/callback"
+ "?state=8df98fd63a53fa5b5433d6f8754bca5d.65317332&code=z8C2DCp6sn0D9aGbEqlrFesdPVRXPtDX"));
}
-
-
- /**
- * Create a direct encryption {@link JWKCredential} from the given shared secret.
- *
- * @param secret the secret to convert to a {@link JWKCredential}.
- *
- * @return the credential
- */
- private JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) {
- final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
- jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
- jwkCredential.setCredentialExpiresAt(Duration.ZERO);
- jwkCredential.setUsageType(UsageType.UNSPECIFIED);
- jwkCredential.getCredentialContextSet().add(
- new JWKEncryptionCredentialContext(EncryptionMethod.A256GCM));
- jwkCredential.setKid("mockKey");
- jwkCredential.getKeyNames().add("mockKey");
- jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
- return jwkCredential;
- }
-
- /**
- * Create a simple client credential from from the given shared secret.
- *
- * @param secret the secret to convert to a {@link JWKCredential}.
- *
- * @return the credential
- */
- //TODO used for both signing and encryption, so alg needs to reflect this
- private JWKCredential createClientSecretCredential(final String secret) {
- final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
- jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
- jwkCredential.setCredentialExpiresAt(Duration.ZERO);
- jwkCredential.setUsageType(UsageType.UNSPECIFIED);
- jwkCredential.setKid("mockKey");
- jwkCredential.getKeyNames().add("mockKey");
- return jwkCredential;
- }
-
- /**
- * Create a direct encryption {@link JWKCredential} from the given shared secret.
- *
- * @param secret the secret to convert to a {@link JWKCredential}.
- *
- * @return the credential
- * @throws JOSEException
- */
- private JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
- final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
- jwkCredential.setPrivateKey(secret.toPrivateKey());
- jwkCredential.setPublicKey(secret.toPublicKey());
- jwkCredential.setCredentialExpiresAt(Duration.ZERO);
- jwkCredential.setUsageType(UsageType.ENCRYPTION);
-
- jwkCredential.setKid(secret.getKeyID());
- jwkCredential.getKeyNames().add("mockKey");
- jwkCredential.setAlgorithm(secret.getAlgorithm());
- return jwkCredential;
- }
-
+
@Override
@Nonnull protected ProfileRequestContext buildProfileRequestContext(@Nonnull final String flowId,
@@ -575,9 +515,14 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
buildProfileRequestContext("authn/OIDCRelyingParty",false,true));
updateFlowExecution(flowExecution);
flowExecution.start(inputMap, externalContext);
- assertCurrentStateEquals("AuthRequest");
+ assertCurrentStateEquals("AuthnRequest");
}
+ /**
+ * Test to the authorization redirect using an OP who's RP config in XML is set to use a request object.
+ *
+ * @throws Exception on error.
+ */
@Test
public void testFlowToAuthorizationRedirect_UsingRequestObject() throws Exception {
setFlowPath(FLOW);
@@ -609,7 +554,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
flowExecution.getConversationScope().put("opensamlProfileRequestContext",prc);
updateFlowExecution(flowExecution);
flowExecution.start(inputMap, externalContext);
- assertCurrentStateEquals("AuthRequest");
+ assertCurrentStateEquals("AuthnRequest");
}
@Test
@@ -650,7 +595,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
flowExecution.getConversationScope().put("opensamlProfileRequestContext", prc);
updateFlowExecution(flowExecution);
flowExecution.start(inputMap, externalContext);
- assertCurrentStateEquals("AuthRequest");
+ assertCurrentStateEquals("AuthnRequest");
}
/**
@@ -745,7 +690,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -797,7 +742,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -849,7 +794,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -901,7 +846,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
.getSubcontext(RelyingPartyContext.class)
.getProfileConfig();
- partyConfig.setClientCredential(createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
+ partyConfig.setClientCredential(
+ TestCredentialHelper.createDirectEncryptionCredentialFromSharedSecret(CLIENT_SECRET));
// Set a default security config for the profile config
final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
@@ -974,7 +920,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -1069,7 +1015,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -1122,7 +1068,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
mockOPServer.shutdown();
@@ -1149,7 +1095,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
final RelyingPartyContext partyContext = new RelyingPartyContext();
final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
partyContext.setProfileConfig(partyConfig);
- partyConfig.setClientCredential(createClientSecretCredential(CLIENT_SECRET));
+ partyConfig.setClientCredential(TestCredentialHelper.createClientSecretCredential(CLIENT_SECRET));
partyConfig.setTokenEndpointAuthMethods(Set.of("client_secret_basic"));
partyConfig.setClientId(CLIENT_ID);
final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
@@ -1232,7 +1178,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
//set start view and ending event to transition on.
externalContext.setEventId("proceed");
- setCurrentState("AuthRequest");
+ setCurrentState("AuthnRequest");
resumeFlow(externalContext);
//assert success conditions
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
new file mode 100644
index 0000000..0569db9
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/TestCredentialHelper.java
@@ -0,0 +1,86 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.time.Duration;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import org.opensaml.security.credential.UsageType;
+
+import com.nimbusds.jose.EncryptionMethod;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.RSAKey;
+
+import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.credential.impl.JWKEncryptionCredentialContext;
+import net.shibboleth.oidc.security.impl.JWSAssemblyUtils;
+
+/** Helper that creates different credentials.*/
+public final class TestCredentialHelper {
+
+ private TestCredentialHelper() {
+
+ }
+
+ /**
+ * Create a simple client credential from from the given shared secret.
+ *
+ * @param secret the secret to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ */
+ //TODO used for both signing and encryption, so alg needs to reflect this
+ public static JWKCredential createClientSecretCredential(final String secret) {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+ jwkCredential.setKid("mockKey");
+ jwkCredential.getKeyNames().add("mockKey");
+ return jwkCredential;
+ }
+
+ /**
+ * Create a direct encryption {@link JWKCredential} from the given shared secret.
+ *
+ * @param secret the secret to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ */
+ public static JWKCredential createDirectEncryptionCredentialFromSharedSecret(final String secret) {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+ jwkCredential.getCredentialContextSet().add(
+ new JWKEncryptionCredentialContext(EncryptionMethod.A256GCM));
+ jwkCredential.setKid("mockKey");
+ jwkCredential.getKeyNames().add("mockKey");
+ jwkCredential.setAlgorithm(JWEAlgorithm.DIR);
+ return jwkCredential;
+ }
+
+
+ /**
+ * Create a direct encryption {@link JWKCredential} from the given shared secret.
+ *
+ * @param secret the secret to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ * @throws JOSEException
+ */
+ public static JWKCredential createKeyEncryptionCredential(final RSAKey secret) throws JOSEException {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setPrivateKey(secret.toPrivateKey());
+ jwkCredential.setPublicKey(secret.toPublicKey());
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.ENCRYPTION);
+
+ jwkCredential.setKid(secret.getKeyID());
+ jwkCredential.getKeyNames().add("mockKey");
+ jwkCredential.setAlgorithm(secret.getAlgorithm());
+ return jwkCredential;
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index 15518de..4cb7807 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -53,7 +53,7 @@
<bean id="TestRequestObject" parent="RelyingPartyByName" c:relyingPartyIds="https://localhost:9919">
<property name="profileConfigurations">
<list>
- <bean parent="OIDC.SSO" p:useRequestObject="true"/>
+ <bean parent="OIDC.SSO" p:useRequestObject="true" p:signRequestObject="true"/>
</list>
</property>
</bean>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list