[java-oidc-common] branch main updated: JCOMOIDC-176 - Add logout request handlers
Codeberg
noreply at shibboleth.net
Fri Jul 3 13:48:23 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-oidc-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidc-common/commit/b03a000471defa02ee3165bd0a40804740c8861c
The following commit(s) were added to refs/heads/main by this push:
new b03a0004 JCOMOIDC-176 - Add logout request handlers
b03a0004 is described below
commit b03a000471defa02ee3165bd0a40804740c8861c
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jul 3 14:48:12 2026 +0100
JCOMOIDC-176 - Add logout request handlers
https://shibboleth.atlassian.net/browse/JCOMOIDC-176
---
...tLogoutRequestParameterValueMessageHandler.java | 204 +++++++++++++++++++++
.../messaging/handler/impl/AddClientIDHandler.java | 52 ++++++
.../handler/impl/AddIDTokenHintHandler.java | 91 +++++++++
.../handler/impl/AddLogoutHintHandler.java | 54 ++++++
.../handler/impl/AddLogoutUILocalesHandler.java | 68 +++++++
.../impl/AddPostLogoutRedirectURIHandler.java | 55 ++++++
6 files changed, 524 insertions(+)
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractLogoutRequestParameterValueMessageHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractLogoutRequestParameterValueMessageHandler.java
new file mode 100644
index 00000000..a3094b44
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AbstractLogoutRequestParameterValueMessageHandler.java
@@ -0,0 +1,204 @@
+/*
+ * 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.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.profile.context.navigate.ParentProfileRequestContextLookup;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.oidc.profile.core.OIDCLogoutRequest;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Base class for message handlers that process and apply values of OpenID Connect logout requests.
+ *
+ * <p>
+ * This abstract class provides common functionality for locating:
+ * </p>
+ * <ul>
+ * <li>the {@link OIDCLogoutRequest} 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>
+ *
+ * @param <T> the logout request parameter value type
+ *
+ * TODO: move to commons
+ */
+public abstract class AbstractLogoutRequestParameterValueMessageHandler<T> extends AbstractMessageHandler {
+
+ /** Lookup function for parent ProfileRequestContext. */
+ @Nonnull protected static final ParentProfileRequestContextLookup<MessageContext> PRC_LOOKUP
+ = new ParentProfileRequestContextLookup<>();
+
+ /** Strategy used to locate the {@link OIDCLogoutRequest}. */
+ @NonnullAfterInit private Function<MessageContext, OIDCLogoutRequest> logoutRequestLookupStrategy;
+
+ /** Lookup strategy to locate the OpenID Provider metadata to use.*/
+ @NonnullAfterInit private Function<MessageContext, OIDCProviderMetadataContext> providerMetadataLookupStrategy;
+
+ /** Lookup strategy for parameter value. */
+ @Nullable private Function<MessageContext,T> parameterValueLookupStrategy;
+
+ /** The logout request parameter value type.*/
+ @Nonnull private final Class<T> type;
+
+ /** The stashed {@link OIDCLogoutRequest}.*/
+ @NonnullBeforeExec private OIDCLogoutRequest logoutRequest;
+
+ /** The stashed OpenID Provider metadata .*/
+ @NonnullBeforeExec private OIDCProviderMetadata providerMetadata;
+
+
+ /**
+ * Constructor.
+ *
+ * @param valueType type of value returned by handler
+ */
+ protected AbstractLogoutRequestParameterValueMessageHandler(@Nonnull final Class<T> valueType) {
+ type = Constraint.isNotNull(valueType, "Logout request parameter value type cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (logoutRequestLookupStrategy == null) {
+ throw new ComponentInitializationException("LogoutRequestLookupStrategy cannot be null");
+ }
+ if (providerMetadataLookupStrategy == null) {
+ throw new ComponentInitializationException("ProviderMetadataLookupStrategy cannot be null");
+ }
+ }
+
+ /**
+ * Get the logout request.
+ *
+ * @return the logout request
+ */
+ @NonnullBeforeExec protected OIDCLogoutRequest getLogoutRequest() {
+ return logoutRequest;
+ }
+
+ /**
+ * 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
+ * {@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 logout 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(@Nonnull 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("Logout request parameter value lookup returned the "
+ + "wrong value type");
+ }
+
+ /**
+ * Set the strategy used to locate the {@link OIDCLogoutRequest} to use.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLogoutRequestLookupStrategy(
+ @Nonnull final Function<MessageContext, OIDCLogoutRequest> strategy) {
+ checkSetterPreconditions();
+
+ logoutRequestLookupStrategy =
+ Constraint.isNotNull(strategy, "LogoutContext lookup strategy cannot be null");
+ }
+
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ logoutRequest = logoutRequestLookupStrategy.apply(messageContext);
+ if (logoutRequest == null) {
+ throw new MessageHandlerException("OIDC logout 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/AddClientIDHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddClientIDHandler.java
new file mode 100644
index 00000000..dd9707bf
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddClientIDHandler.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 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.ClientID;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * A message handler that populates the client_id parameter into the logout request.
+ */
+public class AddClientIDHandler extends AbstractLogoutRequestParameterValueMessageHandler<String> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddClientIDHandler.class);
+
+ /** Constructor.*/
+ public AddClientIDHandler() {
+ super(String.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final String clientId = getParameterValue(messageContext);
+ if (StringSupport.trimOrNull(clientId) == null) {
+ return;
+ }
+ getLogoutRequest().setClientID(new ClientID(clientId));
+ log.trace("{}: Set client_id to '{}'", getLogPrefix(), clientId);
+ }
+}
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddIDTokenHintHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddIDTokenHintHandler.java
new file mode 100644
index 00000000..dae52338
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddIDTokenHintHandler.java
@@ -0,0 +1,91 @@
+/*
+ * 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.text.ParseException;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A message handler that populates the id_token_hint parameter into the logout request.
+ */
+public class AddIDTokenHintHandler extends AbstractLogoutRequestParameterValueMessageHandler<JWT> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddIDTokenHintHandler.class);
+
+ /** Constructor.*/
+ public AddIDTokenHintHandler() {
+ super(JWT.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final JWT idTokenHint = getParameterValue(messageContext);
+ if (idTokenHint == null) {
+ return;
+ }
+ if (idTokenHint instanceof EncryptedJWT) {
+ log.trace("{}: ID Token is still encrypted, possibly for the wrong target, this maybe ignored by "
+ + "the OP", getLogPrefix());
+ }
+ getLogoutRequest().setIdTokenHint(idTokenHint);
+ if (log.isTraceEnabled()) {
+ log.trace("{}: Set id_token_hint for subject '{}'", getLogPrefix(),
+ getSubjectOrDefault(idTokenHint,"not-available"));
+ }
+
+ }
+
+ /**
+ * Get the subject from the id_token, else return the default value.
+ *
+ * @param idToken the id_token to extract the subject from
+ * @param defaultSubject the default subject to return if the subject can not be extracted
+ *
+ * @return the subject, either from the id_token or the default value
+ */
+ private String getSubjectOrDefault(final JWT idToken, final String defaultSubject) {
+
+ if (idToken instanceof EncryptedJWT) {
+ return defaultSubject;
+ }
+
+ try {
+ final JWTClaimsSet claims = idToken.getJWTClaimsSet();
+ if (claims != null) {
+ return claims.getSubject() != null ? claims.getSubject() : defaultSubject;
+ }
+ } catch (final ParseException e) {
+ // Ignore
+ }
+ return defaultSubject;
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutHintHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutHintHandler.java
new file mode 100644
index 00000000..125b3f77
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutHintHandler.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 net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * A message handler that populates the logout_hint parameter into the logout request.
+ */
+public class AddLogoutHintHandler extends AbstractLogoutRequestParameterValueMessageHandler<String> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddLogoutHintHandler.class);
+
+ /** Constructor.*/
+ public AddLogoutHintHandler() {
+ super(String.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final String logoutHint = getParameterValue(messageContext);
+ if (StringSupport.trimOrNull(logoutHint) == null) {
+ return;
+ }
+ getLogoutRequest().setLogoutHint(logoutHint);
+ log.trace("{}: Set logout_hint to '{}'", getLogPrefix(), logoutHint);
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutUILocalesHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutUILocalesHandler.java
new file mode 100644
index 00000000..578c5f8a
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddLogoutUILocalesHandler.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.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 populates the client_id parameter into the logout request.
+ */
+public class AddLogoutUILocalesHandler extends AbstractLogoutRequestParameterValueMessageHandler<List<String>> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddLogoutUILocalesHandler.class);
+
+ /** Constructor.*/
+ public AddLogoutUILocalesHandler() {
+ super((Class)List.class);
+ }
+
+ /** {@inheritDoc} */
+ @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();
+
+ getLogoutRequest().setUiLocales(uiLocals);
+ }
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPostLogoutRedirectURIHandler.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPostLogoutRedirectURIHandler.java
new file mode 100644
index 00000000..4c91b553
--- /dev/null
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/messaging/handler/impl/AddPostLogoutRedirectURIHandler.java
@@ -0,0 +1,55 @@
+/*
+ * 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 populates the client_id parameter into the logout request.
+ */
+public class AddPostLogoutRedirectURIHandler extends AbstractLogoutRequestParameterValueMessageHandler<URI> {
+
+ /** Logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddPostLogoutRedirectURIHandler.class);
+
+ /** Constructor.*/
+ public AddPostLogoutRedirectURIHandler() {
+ super(URI.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final URI postRedirectUri = getParameterValue(messageContext);
+ if (postRedirectUri == null) {
+ return;
+ }
+ getLogoutRequest().setPostLogoutRedirectURI(postRedirectUri);
+ log.trace("{}: Set post_logout_redirect_uri to '{}'", getLogPrefix(), postRedirectUri);
+
+ }
+
+
+
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list