[java-oidc-common] 08/20: JCOMOIDC-145 - Move authentication request handlers from the RP-Proxy into oidc-common
Codeberg
noreply at shibboleth.net
Tue Feb 17 20:14:43 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch dev/JCOMOIDC-139
in repository java-oidc-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidc-common/commit/b3b1555fe5780ca026353d0c2ad7978514fdb1a7
commit b3b1555fe5780ca026353d0c2ad7978514fdb1a7
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Oct 15 15:12:21 2025 +0100
JCOMOIDC-145 - Move authentication request handlers from the RP-Proxy
into oidc-common
- Move the authn/authnz handlers across
https://shibboleth.atlassian.net/browse/JCOMOIDC-145
---
...icationRequestParameterValueMessageHandler.java | 199 +++++++++++++++++++++
...uthenticationContextClassReferencesHandler.java | 57 ++++++
.../messaging/handler/impl/AddDisplayHandler.java | 56 ++++++
.../handler/impl/AddEndpointURIHandler.java | 54 ++++++
.../impl/AddForceAuthenticationHandler.java | 68 +++++++
.../handler/impl/AddLoginHintHandler.java | 48 +++++
.../messaging/handler/impl/AddMaxAgeHandler.java | 48 +++++
.../messaging/handler/impl/AddNonceHandler.java | 54 ++++++
.../impl/AddPKCECodeVerifierAndChallenge.java | 135 ++++++++++++++
.../messaging/handler/impl/AddPromptHandler.java | 53 ++++++
.../handler/impl/AddRedirectURIHandler.java | 52 ++++++
.../handler/impl/AddRequestedClaimsHandler.java | 63 +++++++
.../messaging/handler/impl/AddResourceHandler.java | 53 ++++++
.../handler/impl/AddResponseModeHandler.java | 103 +++++++++++
.../handler/impl/AddResponseTypeHandler.java | 113 ++++++++++++
.../messaging/handler/impl/AddScopesHandler.java | 57 ++++++
.../messaging/handler/impl/AddStateHandler.java | 77 ++++++++
.../handler/impl/AddUiLocalesHandler.java | 66 +++++++
.../handler/impl/BuildPlainRequestObjectJWT.java | 63 +++++++
.../messaging/handler/impl/PKCEOptions.java | 72 ++++++++
.../impl/SetAuthenticationRequestTimeHandler.java | 47 +++++
21 files changed, 1538 insertions(+)
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractAuthenticationRequestParameterValueMessageHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractAuthenticationRequestParameterValueMessageHandler.java
new file mode 100644
index 00000000..5b9aed72
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractAuthenticationRequestParameterValueMessageHandler.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.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.opensaml.profile.context.navigate.ParentProfileRequestContextLookup;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Base class for message handlers that process and apply values of OpenID Connect authentication request parameters.
+ *
+ * <p>
+ * This abstract class provides common functionality for locating:
+ * </p>
+ * <ul>
+ * <li>the {@link OIDCAuthenticationRequest} associated with the current
+ * {@link MessageContext},</li>
+ * <li>the {@link OIDCProviderMetadata} describing the peer OpenID Provider,</li>
+ * <li>and the parameter value to be extracted and validated against
+ * the expected Java type.</li>
+ * </ul>
+ * </p>
+ *
+ * @param <T> the authentication request parameter value type
+ */
+public abstract class AbstractAuthenticationRequestParameterValueMessageHandler<T> extends AbstractMessageHandler {
+
+ /** Lookup function for parent ProfileRequestContext. */
+ @Nonnull protected static final ParentProfileRequestContextLookup<MessageContext> PRC_LOOKUP
+ = new ParentProfileRequestContextLookup<>();
+
+ /** Class logger. */
+ @Nonnull private final Logger log =
+ LoggerFactory.getLogger(AbstractAuthenticationRequestParameterValueMessageHandler.class);
+
+ /** Strategy used to locate the {@link OIDCAuthenticationRequest}. */
+ @Nonnull private Function<MessageContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy;
+
+ /** Lookup strategy to locate the OpenID Provider metadata to use.*/
+ @Nonnull private Function<MessageContext, OIDCProviderMetadataContext> providerMetadataLookupStrategy;
+
+ @Nullable private Function<MessageContext, T> parameterValueLookupStrategy;
+
+ /** The authentication request parameter value type.*/
+ @Nonnull private Class<T> type;
+
+ /** The stashed {@link OIDCAuthenticationRequest}.*/
+ @NonnullBeforeExec private OIDCAuthenticationRequest authnRequest;
+
+ /** The stashed OpenID Provider metadata .*/
+ @NonnullBeforeExec private OIDCProviderMetadata providerMetadata;
+
+
+ /** Constructor.*/
+ protected AbstractAuthenticationRequestParameterValueMessageHandler(@Nonnull final Class<T> valueType) {
+ type = Constraint.isNotNull(valueType, "Authentication request parameter value type cannot be null");
+ authenticationRequestLookupStrategy = mc -> {
+ if (mc.getMessage() instanceof final OIDCAuthenticationRequest request) {
+ return request;
+ }
+ return null;
+ };
+ providerMetadataLookupStrategy = new ChildContextLookup<>(OIDCProviderMetadataContext.class).compose(
+ new ChildContextLookup<>(OIDCPeerEntityContext.class));
+ }
+
+ /**
+ * Get the authentication request.
+ *
+ * @return the authentication request
+ */
+ @NonnullBeforeExec protected OIDCAuthenticationRequest getAuthenticationRequest() {
+ return authnRequest;
+ }
+
+ /**
+ * Set the lookup strategy to locate the OpenID providers metadata.
+ *
+ * @param strategy the strategy.
+ */
+ public void setProviderMetadataLookupStrategy(
+ @Nonnull final Function<MessageContext, OIDCProviderMetadataContext> strategy) {
+ checkSetterPreconditions();
+
+ providerMetadataLookupStrategy =
+ Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
+ }
+
+ /**
+ * Returns the OpenID Provider metadata. Should never be {@code null} after
+ * after {@code doPreExecute} has been called.
+ *
+ * @return The provider metadata context.
+ */
+ @NonnullBeforeExec protected OIDCProviderMetadata getProviderMetadata() {
+ return providerMetadata;
+ }
+
+ /**
+ * Set the parameter value lookup strategy used to find the value to set onto the authentication request.
+ *
+ * @param strategy The parameter value lookup strategy to set.
+ */
+ public void setParameterValueLookupStrategy(@Nonnull final Function<MessageContext, T> strategy) {
+ checkSetterPreconditions();
+ parameterValueLookupStrategy = Constraint.isNotNull(strategy,
+ "ParameterValueLookupStrategy can not be null");
+ }
+
+ /**
+ * Retrieves the parameter value or configuration options from the configured lookup strategy,
+ * verifying at runtime that the result matches the type expected by the subclass.
+ *
+ * @param context the message context to pass to the lookup function
+ *
+ * @return the parameter value
+ *
+ * @throws MessageHandlerException if the value is not the expected type
+ */
+ @Nullable protected T getParameterValue(final MessageContext context)
+ throws MessageHandlerException {
+ final var localParameterValueLookupStrategy = parameterValueLookupStrategy;
+ if (localParameterValueLookupStrategy == null) {
+ return null;
+ }
+ final Object value = localParameterValueLookupStrategy.apply(context);
+ if (value == null) {
+ return null;
+ }
+ if (type.isInstance(value)) {
+ return type.cast(value);
+ }
+ throw new MessageHandlerException("Authentication request parameter value lookup returned the "
+ + "wrong value type");
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OIDCAuthenticationRequest} to use.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAuthenticationRequestLookupStrategy(
+ @Nonnull final Function<MessageContext, OIDCAuthenticationRequest> strategy) {
+ checkSetterPreconditions();
+
+ authenticationRequestLookupStrategy =
+ Constraint.isNotNull(strategy, "AuthenticationRequestLookupStrategy lookup strategy cannot be null");
+ }
+
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ authnRequest = authenticationRequestLookupStrategy.apply(messageContext);
+ if (authnRequest == null) {
+ throw new MessageHandlerException("OIDC authentication request is null");
+ }
+ final OIDCProviderMetadataContext providerMetadataContext =
+ providerMetadataLookupStrategy.apply(messageContext);
+ if (providerMetadataContext == null) {
+ throw new MessageHandlerException("No provider metadata context found for peer");
+ }
+ providerMetadata = providerMetadataContext.getProviderInformation();
+ if (providerMetadata == null) {
+ throw new MessageHandlerException("No provider metadata found for peer");
+ }
+
+ return super.doPreInvoke(messageContext);
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddAuthenticationContextClassReferencesHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddAuthenticationContextClassReferencesHandler.java
new file mode 100644
index 00000000..416112c2
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddAuthenticationContextClassReferencesHandler.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/**
+ * A message handler that adds Authentication Context Class References (ACRs) to an authentication request.
+ *
+ * <p>If no ACRs are present in the request, nothing it set onto the request</p>
+ */
+public class AddAuthenticationContextClassReferencesHandler
+ extends AbstractAuthenticationRequestParameterValueMessageHandler<List<ACR>> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddAuthenticationContextClassReferencesHandler.class);
+
+ /** Constructor.*/
+ public AddAuthenticationContextClassReferencesHandler() {
+ super((Class)List.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final List<ACR> acrs = getParameterValue(messageContext);
+ if (acrs != null) {
+ log.trace("{} setting ACRs to '{}' ", getLogPrefix(), acrs);
+ getAuthenticationRequest().setAcrs(acrs);
+ } else {
+ log.trace("{} no ACRs requested", getLogPrefix());
+ }
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddDisplayHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddDisplayHandler.java
new file mode 100644
index 00000000..feb7aa3f
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddDisplayHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.Display;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+
+/**
+ * Message handler that adds the optional 'display' request parameter.
+ */
+public class AddDisplayHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<String> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddDisplayHandler.class);
+
+ /** Constructor. */
+ protected AddDisplayHandler() {
+ super(String.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final String display = getParameterValue(messageContext);
+ if (StringSupport.trimOrNull(display) != null) {
+ try {
+ getAuthenticationRequest().setDisplay(Display.parse(display));
+ log.trace("Adding 'display' request parameter value '{}'", display);
+ } catch (final ParseException e) {
+ throw new MessageHandlerException("Unable to add a 'display' value of: " + display);
+ }
+ }
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddEndpointURIHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddEndpointURIHandler.java
new file mode 100644
index 00000000..ebc91ad8
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddEndpointURIHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/**
+ * A message handler that adds the authorization endpoint URI. If an authorization endpoint does
+ * not exist, an exception is thrown.
+ */
+public class AddEndpointURIHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<URI> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddEndpointURIHandler.class);
+
+ /** Constructor.*/
+ public AddEndpointURIHandler() {
+ super(URI.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final URI authzEndpoint = getParameterValue(messageContext);
+ if (authzEndpoint == null) {
+ throw new MessageHandlerException("OAuth 2.0 authorization endpoint URI not found in provider metadata");
+ }
+
+ getAuthenticationRequest().setEndpointURI(authzEndpoint);
+ log.trace("{} Added authorization endpoint '{}' to authentication request",getLogPrefix(),
+ getAuthenticationRequest().getEndpointURI());
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddForceAuthenticationHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddForceAuthenticationHandler.java
new file mode 100644
index 00000000..f2731170
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddForceAuthenticationHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.time.Duration;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.Prompt;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * An action that sets the 'prompt' parameter to 'login' and max_age to 0
+ * seconds, iff force authn was requested by the upstream SP (or is overridden
+ * in the profile config).
+ */
+public class AddForceAuthenticationHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Boolean> {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(AddForceAuthenticationHandler.class);
+
+ /**
+ * Constructor.
+ */
+ protected AddForceAuthenticationHandler() {
+ super(Boolean.class);
+ }
+
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final Boolean isForceAuthn = getParameterValue(messageContext);
+ if (isForceAuthn != null && isForceAuthn) {
+ log.trace("{} Setting prompt=login and max_age=0 (ForceAuthn) for OIDC AuthnRequest", getLogPrefix());
+ try {
+ getAuthenticationRequest().setPrompt(Prompt.parse(Prompt.Type.LOGIN.toString()));
+ getAuthenticationRequest().setMaxAge(Duration.ofSeconds(0));
+ } catch (final ParseException e) {
+ // This should never happen
+ throw new MessageHandlerException(
+ "Unable to honour force-authn, " + "setting prompt to force-login as failed", e);
+ }
+ } else {
+ log.trace("{} No ForceAuthn requirement, so no prompt or max_age set", getLogPrefix());
+ }
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLoginHintHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLoginHintHandler.java
new file mode 100644
index 00000000..54371548
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLoginHintHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/** Message handler that adds the login_hint parameter.*/
+public class AddLoginHintHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<String> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddLoginHintHandler.class);
+
+ public AddLoginHintHandler() {
+ super(String.class);
+ }
+
+ @Override
+ protected void doInvoke(final MessageContext messageContext) throws MessageHandlerException {
+
+ final String loginHint = getParameterValue(messageContext);
+
+ if (loginHint != null) {
+ log.trace("{} Added login_hint parameter '{}'", getLogPrefix(), loginHint);
+ getAuthenticationRequest().setLoginHint(loginHint);
+ }
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddMaxAgeHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddMaxAgeHandler.java
new file mode 100644
index 00000000..59b6d0c4
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddMaxAgeHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.time.Duration;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/** Message handler that adds the max_age parameter.*/
+public class AddMaxAgeHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Duration> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddMaxAgeHandler.class);
+
+ public AddMaxAgeHandler() {
+ super(Duration.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final Duration maxAge = getParameterValue(messageContext);
+
+ if (maxAge != null) {
+ log.trace("{} Added max_age parameter '{}'", getLogPrefix(), maxAge);
+ getAuthenticationRequest().setMaxAge(maxAge);
+ }
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddNonceHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddNonceHandler.java
new file mode 100644
index 00000000..eae74371
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddNonceHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.Nonce;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that adds a nonce from a lookup strategy to the authentication request.
+ * The nonce can be {@literal null}.
+ */
+public class AddNonceHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Nonce> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddNonceHandler.class);
+
+ /** Constructor.*/
+ public AddNonceHandler() {
+ super(Nonce.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final Nonce nonce = getParameterValue(messageContext);
+ if (nonce != null) {
+ getAuthenticationRequest().setNonce(nonce);
+ log.trace("{} Added nonce '{}' to authentication request",getLogPrefix(),
+ getAuthenticationRequest().getNonce());
+ }
+ }
+
+
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPKCECodeVerifierAndChallenge.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPKCECodeVerifierAndChallenge.java
new file mode 100644
index 00000000..f6acab08
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPKCECodeVerifierAndChallenge.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.oidc.profile.core.OAuthAuthorizationRequest.CodeChallengeMethod;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Create an OAuth 2.0 PCKE code_verifier to use in the token request, and derives a code_challenge for immediate use in
+ * the authorization request.
+ */
+public class AddPKCECodeVerifierAndChallenge
+ extends AbstractAuthenticationRequestParameterValueMessageHandler<PKCEOptions> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddPKCECodeVerifierAndChallenge.class);
+
+ /** Constructor.*/
+ public AddPKCECodeVerifierAndChallenge() {
+ super(PKCEOptions.class);
+ }
+
+ @Override
+ protected void doInvoke(final MessageContext messageContext) throws MessageHandlerException {
+
+ final PKCEOptions pkceOptions = getParameterValue(messageContext);
+
+ if (pkceOptions != null && pkceOptions.isEnabled()) {
+
+ log.trace("{} PKCE enabled, adding code_challenge to request", getLogPrefix());
+
+ final CodeChallengeMethod method =
+ pkceOptions.isAllowPlain() ? CodeChallengeMethod.PLAIN : CodeChallengeMethod.S256;
+
+ final String codeVerifier = generateCodeVerifier(32);
+ final String challenge = computeCodeChallenge(codeVerifier, method);
+
+ if (log.isTraceEnabled()) {
+ log.trace("{} Created code verifier '...{}'", getLogPrefix(),
+ codeVerifier.substring(challenge.length()-3));
+ log.trace("{} Derived code challenge '...{}'", getLogPrefix(),
+ challenge.substring(challenge.length()-3));
+ }
+ getAuthenticationRequest().setCodeVerifier(codeVerifier);
+ getAuthenticationRequest().setCodeChallenge(challenge);
+ getAuthenticationRequest().setCodeChallengeMethod(method);
+
+ } else {
+ log.trace("{} PKCE not enabled", getLogPrefix());
+ }
+ }
+
+ /**
+ * Generates a code_verifier for use during Proof Key for Code Exchange. The generated bytes are base64 URL
+ * encoded before they are returned.
+ *
+ * @param length the byte length of the code_verifier. Must be at least 32 bytes long (RFC7636 section 7.1).
+ *
+ * @return the base64 URL encoded coder_verifier value.
+ *
+ * @throws MessageHandlerException if there is an error generating the verifier.
+ */
+ @Nonnull private static String generateCodeVerifier(@Nonnull final Integer length) throws MessageHandlerException {
+ if (length < 32) {
+ throw new MessageHandlerException("PKCE coder_verifier must be at least 32 bytes long");
+ }
+ try {
+ final SecureRandom secureRandom = new SecureRandom();
+ final byte[] verifierInBytes = new byte[length];
+ secureRandom.nextBytes(verifierInBytes);
+ return Base64Support.encodeURLSafe(verifierInBytes);
+ } catch (final Exception e) {
+ throw new MessageHandlerException(e);
+ }
+ }
+
+ /**
+ * Compute the code_challenge from the code_verifier. If the {@link CodeChallengeMethod#PLAIN} method is used, the
+ * codeVerifier is returned directly. If the {@link CodeChallengeMethod#S256} method is used, the bytes of the
+ * codeVerifier are SHA-256 hashed and base 64 URL encoded before being returned.
+ *
+ * @param codeVerifier the code_verifier to compute the code_challenge from
+ * @param method the code_challenge_method
+ *
+ * @return the computed code_challenge
+ *
+ * @throws MessageHandlerException on error computing the code_challenge
+ */
+ @Nonnull @NotEmpty private String computeCodeChallenge(@Nonnull @NotEmpty final String codeVerifier,
+ @Nonnull final CodeChallengeMethod method) throws MessageHandlerException {
+
+ if (method == CodeChallengeMethod.PLAIN) {
+ return codeVerifier;
+ }
+ try {
+ final MessageDigest md = MessageDigest.getInstance("SHA-256");
+ final byte[] hash = md.digest(codeVerifier.getBytes());
+ assert hash != null;
+ return Base64Support.encodeURLSafe(hash);
+
+ } catch (final NoSuchAlgorithmException | EncodingException e) {
+ throw new MessageHandlerException("Unable to compute code_challenge", e);
+ }
+
+
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPromptHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPromptHandler.java
new file mode 100644
index 00000000..e281f135
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPromptHandler.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.Prompt;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that sets the 'prompt' parameter to 'none' if passive
+ * authentication has been requested by the SP.
+ */
+public class AddPromptHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Prompt> {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(AddPromptHandler.class);
+
+ /** Constructor.*/
+ public AddPromptHandler() {
+ super(Prompt.class);
+ }
+
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final Prompt prompt = getParameterValue(messageContext);
+ if (prompt != null) {
+ if (log.isTraceEnabled()) {
+ log.trace("{} Setting 'prompt={}'", getLogPrefix(), prompt.toString());
+ }
+ getAuthenticationRequest().setPrompt(prompt);
+ }
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRedirectURIHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRedirectURIHandler.java
new file mode 100644
index 00000000..3b5fdbd1
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRedirectURIHandler.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.net.URI;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that adds a redirect_uri to the authentication request.
+ */
+public class AddRedirectURIHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<URI> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddRedirectURIHandler.class);
+
+ /** Constructor.*/
+ public AddRedirectURIHandler() {
+ super(URI.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final URI redirectUri = getParameterValue(messageContext);
+ if (redirectUri == null) {
+ throw new MessageHandlerException("Redirect URI could not be located or created using the strategy");
+ }
+ log.trace("{} Created redirect_uri '{}'", getLogPrefix(), redirectUri);
+ getAuthenticationRequest().setRedirectURI(redirectUri);
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRequestedClaimsHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRequestedClaimsHandler.java
new file mode 100644
index 00000000..590e82a2
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddRequestedClaimsHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that adds requested claims to the authentication request.
+ *
+ * <p>Also records in the request whether the OP supports the claims parameter, for later inspection by
+ * downstream components that only has access to the request e.g. an encoder.</p>
+ */
+public class AddRequestedClaimsHandler
+ extends AbstractAuthenticationRequestParameterValueMessageHandler<OIDCClaimsRequest> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddRequestedClaimsHandler.class);
+
+ /** Constructor.*/
+ public AddRequestedClaimsHandler() {
+ super(OIDCClaimsRequest.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ // Stash whether the OP supports the claims parameter for later introspection
+ getAuthenticationRequest().setProviderSupportsClaimsParameter(getProviderMetadata().supportsClaimsParam());
+
+ if (!getProviderMetadata().supportsClaimsParam()) {
+ log.trace("{} OpenID Provider does not support the 'claims' parameter", getLogPrefix());
+ return;
+ }
+
+ final OIDCClaimsRequest requestedClaims = getParameterValue(messageContext);
+ if (requestedClaims != null) {
+ getAuthenticationRequest().setRequestedClaims(requestedClaims);
+ log.trace("{} Added requested claims '{}' to the authentication request",getLogPrefix(), requestedClaims);
+ } else {
+ log.trace("{} No individual claims requested", getLogPrefix());
+ }
+ }
+}
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResourceHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResourceHandler.java
new file mode 100644
index 00000000..4ca2c91e
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResourceHandler.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/**
+ * A message handler that adds OAuth 2.0 resource indicators to the authentication request.
+ */
+public class AddResourceHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<List<URI>> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddResourceHandler.class);
+
+ /**
+ * Constructor.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected AddResourceHandler() {
+ super((Class)List.class);
+ }
+
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+ final List<URI> resources = getParameterValue(messageContext);
+ log.debug("{} Adding resource indicators '{}'", getLogPrefix(), resources);
+ getAuthenticationRequest().setResources(resources);
+
+ }
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseModeHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseModeHandler.java
new file mode 100644
index 00000000..ece6812a
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseModeHandler.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ResponseMode;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that populates the authentication request response_type.
+ */
+public class AddResponseModeHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<ResponseMode> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddResponseModeHandler.class);
+
+ /** Constructor.*/
+ public AddResponseModeHandler() {
+ super(ResponseMode.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final ResponseMode responseModeFromLookup = getParameterValue(messageContext);
+
+ log.trace("{} response mode configured as '{}'",getLogPrefix(), responseModeFromLookup);
+
+ final ResponseMode compatibleMode = ResponseMode.resolve(null, getAuthenticationRequest().getResponseType());
+ if (compatibleMode == null) {
+ throw new MessageHandlerException("A compatible response_mode for response_type "
+ + "'"+getAuthenticationRequest().getResponseType()+"' could not be found");
+ }
+
+ checkProviderSupportsResponseMode(compatibleMode);
+
+ log.trace("{} Compatible response_mode '{}' resolved from response_type '{}'", getLogPrefix(), compatibleMode,
+ getAuthenticationRequest().getResponseType());
+
+ if (responseModeFromLookup != null && !responseModeFromLookup.equals(compatibleMode)) {
+ log.debug("{} response_mode override '{}' exists from configuration and is different than the"
+ + " default mode '{}' for response_type '{}'",
+ getLogPrefix(), responseModeFromLookup, compatibleMode,
+ getAuthenticationRequest().getResponseType());
+
+ checkProviderSupportsResponseMode(responseModeFromLookup);
+ getAuthenticationRequest().setResponseMode(responseModeFromLookup);
+
+ } else {
+ getAuthenticationRequest().setResponseMode(compatibleMode);
+ }
+
+ log.trace("{} response_mode '{}' selected", getLogPrefix(), getAuthenticationRequest().getResponseMode());
+ }
+
+ /**
+ * Check the OpenID Provider supports the response_mode from its metadata value response_modes_supported. Throws
+ * an exception if not.
+ *
+ * @param responseMode the response_mode to check is supported
+ *
+ * @throws MessageHandlerException if the OpenID Provider does not support the response_mode
+ */
+ private void checkProviderSupportsResponseMode(@Nonnull final ResponseMode responseMode)
+ throws MessageHandlerException {
+ final List<ResponseMode> responseModesSupportedOP = getProviderMetadata().getResponseModes();
+ final List<ResponseMode> responseModesSupported = new ArrayList<>(2);
+ if (responseModesSupportedOP == null || responseModesSupportedOP.isEmpty()) {
+ // Add the defaults from the specification
+ responseModesSupported.add(ResponseMode.QUERY);
+ responseModesSupported.add(ResponseMode.FRAGMENT);
+ } else {
+ responseModesSupportedOP.forEach(responseModesSupported::add);
+ }
+ if (!responseModesSupported.contains(responseMode)) {
+ throw new MessageHandlerException("OpenID Provider does not support chosen response_mode: "
+ + responseMode.toString());
+ }
+
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseTypeHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseTypeHandler.java
new file mode 100644
index 00000000..77bfb62a
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddResponseTypeHandler.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.util.List;
+
+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 com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.ResponseType;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that populates the authentication request response_type.
+ */
+public class AddResponseTypeHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<ResponseType> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddResponseTypeHandler.class);
+
+ /** Constructor.*/
+ public AddResponseTypeHandler() {
+ super(ResponseType.class);
+ }
+
+ @Override protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+
+ final ResponseType responseType = getParameterValue(messageContext);
+ if (responseType == null){
+ throw new MessageHandlerException("response_type '"+responseType+"' is not supported");
+ }
+ checkProviderSupportsResponseType(responseType);
+
+ getAuthenticationRequest().setResponseType(responseType);
+ log.trace("{} response_type '{}' selected", getLogPrefix(), getAuthenticationRequest().getResponseType());
+ }
+
+ /**
+ * Check the OpenID Provider supports the response_type from its metadata value response_types_supported. Throws
+ * an exception if not.
+ *
+ * @param responseType the response_type to check is supported
+ *
+ * @throws MessageHandlerException if the OpenID Provider does not support the response_type
+ */
+ private void checkProviderSupportsResponseType(@Nonnull final ResponseType responseType)
+ throws MessageHandlerException {
+ final List<ResponseType> responseTypesSupported = getProviderMetadata().getResponseTypes();
+ if (responseTypesSupported == null) {
+ throw new MessageHandlerException("OpenID Provider has not specified supported response types "
+ + "(response_types_supported). It MUST.");
+ }
+ if (!responseTypesSupported.contains(responseType)) {
+ throw new MessageHandlerException("OpenID Provider does not support chosen response_type: "
+ + responseType.toString());
+ }
+ }
+
+
+ // Checkstyle: ReturnCount OFF
+ /**
+ * Parse the response_mode into a known {@link ResponseMode}.
+ *
+ * @param responseModeFromProfile the response_mode as a string
+ *
+ * @return the parsed {@link ResponseMode}, or {@literal null} if the input type is unknown
+ */
+ @Nullable private ResponseMode parseResponseMode(@Nullable final String responseModeFromProfile) {
+
+ if (responseModeFromProfile == null) {
+ return null;
+ }
+
+ if (responseModeFromProfile.equals(ResponseMode.FORM_POST.getValue())) {
+ return ResponseMode.FORM_POST;
+ } else if (responseModeFromProfile.equals(ResponseMode.FORM_POST_JWT.getValue())) {
+ return ResponseMode.FORM_POST_JWT;
+ } else if (responseModeFromProfile.equals(ResponseMode.QUERY.getValue())) {
+ return ResponseMode.QUERY;
+ } else if (responseModeFromProfile.equals(ResponseMode.FRAGMENT.getValue())) {
+ return ResponseMode.FRAGMENT;
+ } else if (responseModeFromProfile.equals(ResponseMode.FRAGMENT_JWT.getValue())) {
+ return ResponseMode.FRAGMENT_JWT;
+ } else if (responseModeFromProfile.equals(ResponseMode.JWT.getValue())) {
+ return ResponseMode.JWT;
+ } else if (responseModeFromProfile.equals(ResponseMode.QUERY_JWT.getValue())) {
+ return ResponseMode.QUERY_JWT;
+ } else {
+ return null;
+ }
+ }
+ // Checkstyle: ReturnCount ON
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddScopesHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddScopesHandler.java
new file mode 100644
index 00000000..5d05d6e6
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddScopesHandler.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/**
+ * A message handler that adds OAuth 2.0 scopes to the authentication request.
+ */
+public class AddScopesHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Set<String>> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddScopesHandler.class);
+
+ /**
+ * Constructor.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected AddScopesHandler() {
+ super((Class)Set.class);
+ }
+
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext)
+ throws MessageHandlerException {
+ final Set<String> scopes = getParameterValue(messageContext);
+ if (scopes != null && !scopes.isEmpty()) {
+ scopes.forEach(s -> getAuthenticationRequest().getScope().add(s));
+ }
+ log.trace("{} Added scopes '{}' to authentication request",getLogPrefix(),
+ getAuthenticationRequest().getScope());
+ }
+
+
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddStateHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddStateHandler.java
new file mode 100644
index 00000000..193666ce
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddStateHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Add an OAuth 2.0 / OpenID Connect {@code state} value to the authentication request URL and the request object
+ * claims (if present). By default this is generated by concatenating the Hex value of the spring webflow execution
+ * key with a secure random 32 character nonce.
+ *
+ * * <p>
+ * The {@code state} parameter helps prevent cross-site request forgery
+ * (CSRF) attacks and can be used by clients to maintain request
+ * integrity.
+ * </p>
+ *
+ */
+//TODO This need thinking about in the case of the RP-Full.
+public class AddStateHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<String> {
+
+ /** The 'state' claim name.*/
+ @Nonnull private static final String STATE_CLAIM = "state";
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddStateHandler.class);
+
+
+ /** Constructor.*/
+ public AddStateHandler() {
+ super(String.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final String stateString = getParameterValue(messageContext);
+ if (stateString == null) {
+ throw new MessageHandlerException("Generated state was null");
+ }
+ log.trace("{} Generated state '{}'", getLogPrefix(), stateString);
+ final State state = new State(stateString);
+
+ // Add to outer request
+ getAuthenticationRequest().setState(state);
+
+ // Add to Request Object if exists
+ final ClaimsSet claims = getAuthenticationRequest().getRequestObjectClaimsSet();
+ if (claims != null) {
+ log.trace("{} Adding state to JWT RequestObject", getLogPrefix());
+ claims.setClaim(STATE_CLAIM, state);
+ }
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddUiLocalesHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddUiLocalesHandler.java
new file mode 100644
index 00000000..6d071784
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddUiLocalesHandler.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.langtag.LangTag;
+import com.nimbusds.langtag.LangTagException;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that sets the 'ui_locales' parameter on the authentication request.
+ */
+public class AddUiLocalesHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<List<String>> {
+
+ /** Class logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(AddUiLocalesHandler.class);
+
+ /** Constructor.*/
+ public AddUiLocalesHandler() {
+ super((Class)List.class);
+ }
+
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final List<String> locales = getParameterValue(messageContext);
+ if (locales != null && !locales.isEmpty()) {
+ if (log.isTraceEnabled()) {
+ log.trace("{} Setting 'ui_locales={}'", getLogPrefix(), locales);
+ }
+ final List<LangTag> uiLocals = locales.stream().map(tag -> {
+ try {
+ return LangTag.parse(tag);
+ } catch (final LangTagException e) {
+ log.warn("Can not parse language tag '{}'", tag);
+ }
+ return null;
+ }).filter(Objects::nonNull).toList();
+
+ getAuthenticationRequest().setUiLocales(uiLocals);
+ }
+ }
+
+}
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/BuildPlainRequestObjectJWT.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/BuildPlainRequestObjectJWT.java
new file mode 100644
index 00000000..796cc5bd
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/BuildPlainRequestObjectJWT.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * If the Request Object claims are present in the authentication request, convert them
+ * into a JWTClaimsSet inside a PlainJWT.
+ */
+public class BuildPlainRequestObjectJWT extends AbstractAuthenticationRequestParameterValueMessageHandler<ClaimsSet> {
+
+ /** Logger. */
+ @Nonnull
+ private final Logger log = LoggerFactory.getLogger(BuildPlainRequestObjectJWT.class);
+
+ /**
+ * Constructor.
+ */
+ protected BuildPlainRequestObjectJWT() {
+ super(ClaimsSet.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/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/PKCEOptions.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/PKCEOptions.java
new file mode 100644
index 00000000..53ca68e4
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/PKCEOptions.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+
+/**
+ * Configuration options for Proof Key for Code Exchange (PKCE).
+ */
+public class PKCEOptions {
+
+ /** Indicates whether PKCE is enabled for the client. */
+ private final boolean isEnabled;
+
+ /** Indicates whether the "plain" code challenge method is allowed. */
+ private final boolean allowPlain;
+
+ /**
+ * Constructor.
+ *
+ * @param enabled whether PKCE is enabled
+ * @param plainAllowed whether the "plain" code challenge method is allowed
+ */
+ public PKCEOptions(final boolean enabled, final boolean plainAllowed) {
+ isEnabled = enabled;
+ allowPlain = plainAllowed;
+ }
+
+ /**
+ * Returns whether PKCE is enabled.
+ *
+ * @return {@code true} if PKCE is enabled; {@code false} otherwise
+ */
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ /**
+ * Returns whether the "plain" code challenge method is allowed.
+ *
+ * @return {@code true} if "plain" is allowed; {@code false} otherwise
+ */
+ public boolean isAllowPlain() {
+ return allowPlain;
+ }
+
+ /**
+ * Returns a string representation of this PKCE configuration.
+ *
+ * @return a string describing the PKCE options
+ */
+ @Override
+ public String toString() {
+ return "PKCEOptions{" +
+ "isEnabled=" + isEnabled +
+ ", allowPlain=" + allowPlain +
+ '}';
+ }
+
+}
+
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/SetAuthenticationRequestTimeHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/SetAuthenticationRequestTimeHandler.java
new file mode 100644
index 00000000..c6ea598d
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/SetAuthenticationRequestTimeHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.profile.messaging.handler.impl;
+
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+
+/** Handler that adds the authentication request time to the authentication request.*/
+public class SetAuthenticationRequestTimeHandler extends AbstractAuthenticationRequestParameterValueMessageHandler<Instant> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(SetAuthenticationRequestTimeHandler.class);
+
+ /**
+ * Constructor.
+ */
+ protected SetAuthenticationRequestTimeHandler() {
+ super(Instant.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+ getAuthenticationRequest().setAuthnRequestTime(Instant.now());
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list