[java-oidc-common] branch main updated: JCOMOIDC-158 - Add protocol message logging support to AuthnResponseDecoders

Codeberg noreply at shibboleth.net
Wed Mar 11 16:47:17 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/f9e4a03daf60e8e2e6234d86d95a04dbdf10c773

The following commit(s) were added to refs/heads/main by this push:
     new f9e4a03d JCOMOIDC-158 - Add protocol message logging support to AuthnResponseDecoders
f9e4a03d is described below

commit f9e4a03daf60e8e2e6234d86d95a04dbdf10c773
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Mar 11 16:47:09 2026 +0000

    JCOMOIDC-158 - Add protocol message logging support to
    AuthnResponseDecoders
    
     - Added logging support to the base decoder class
     - Is only logging errors and Authorization Code response params e.g.
    code and state.
    
    https://shibboleth.atlassian.net/browse/JCOMOIDC-158
---
 .../claims/impl/ChainingJWTClaimsValidator.java    |  2 +-
 .../decoding/impl/BaseHttpOIDCRequestDecoder.java  | 30 +++++++-
 .../impl/HTTPPostAuthnResponseDecoder.java         |  9 ---
 .../impl/HTTPRedirectAuthnResponseDecoder.java     |  8 ---
 .../impl/HTTPPostAuthnResponseDecoderTest.java     | 83 ++++++++++++++++++++++
 .../impl/HTTPRedirectAuthnResponseDecoderTest.java | 27 ++++++-
 6 files changed, 138 insertions(+), 21 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
index a2c4caab..46bf8424 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
@@ -42,7 +42,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
  * <p>Normally, validation terminates when one of the validators throws a {@link JWTValidationException}. 
  * If no {@link JWTValidationException} is thrown, the claims set is 'valid'.</p>
  * 
- * <p>Optionally this can be toggled such that any of the validators succeeeding implies overall success.</p>
+ * <p>Optionally this can be toggled such that any of the validators succeeding implies overall success.</p>
  * 
  * <p>Note, does not represent a chain of responsibility pattern despite the name.</p>
  */
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/BaseHttpOIDCRequestDecoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/BaseHttpOIDCRequestDecoder.java
index fb37f88e..b2493865 100644
--- a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/BaseHttpOIDCRequestDecoder.java
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/BaseHttpOIDCRequestDecoder.java
@@ -17,11 +17,16 @@ package net.shibboleth.oidc.profile.decoding.impl;
 import java.util.function.Function;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.decoder.MessageDecodingException;
 import org.opensaml.messaging.decoder.servlet.AbstractHttpServletRequestMessageDecoder;
 
+import com.google.common.base.MoreObjects;
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+
 import net.shibboleth.oidc.profile.decoding.OIDCMessageDecoder;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
@@ -29,6 +34,7 @@ import net.shibboleth.shared.logic.FunctionSupport;
 /**
  * A base class for OpenID Connect request decoders. 
  */
+//TODO in v4 rename this class AbstractOIDCMessageDecoder
 public abstract class BaseHttpOIDCRequestDecoder extends AbstractHttpServletRequestMessageDecoder 
                     implements OIDCMessageDecoder {
     
@@ -42,6 +48,7 @@ public abstract class BaseHttpOIDCRequestDecoder extends AbstractHttpServletRequ
     /** Constructor.*/
     protected BaseHttpOIDCRequestDecoder() {
         postDecodeStrategy = FunctionSupport.constant(true);
+        setProtocolMessageLoggerSubCategory("OAUTH2");        
     }
     
     /**
@@ -81,6 +88,27 @@ public abstract class BaseHttpOIDCRequestDecoder extends AbstractHttpServletRequ
      */
     protected abstract void performDecode() throws MessageDecodingException;
     
-    
+    /** {@inheritDoc} */
+    @Override
+    @Nullable
+    protected String serializeMessageForLogging(@Nullable final Object message) {
+        if (message instanceof final AuthenticationSuccessResponse response) {
+            return MoreObjects.toStringHelper(this).omitNullValues()
+                    .add("state", response.getState())
+                    .add("code", response.getAuthorizationCode())
+                    .toString();
+        }
+        if (message instanceof final AuthenticationErrorResponse response && response.getErrorObject() != null) {
+            return MoreObjects.toStringHelper(this).omitNullValues()
+                    .add("status", response.getErrorObject().getHTTPStatusCode())
+                    .add("error_description", response.getErrorObject().getDescription())
+                    .add("error_uri", response.getErrorObject().getURI() == null ? null : 
+                        response.getErrorObject().getURI().toString())
+                    .add("error", response.getErrorObject().getCode())
+                    .add("state", response.getState())
+                    .toString();
+        }
+        return null;
+    }
 
 }
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoder.java
index c4f75a95..4a7957ad 100644
--- a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoder.java
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoder.java
@@ -17,7 +17,6 @@ package net.shibboleth.oidc.profile.decoding.impl;
 import java.io.IOException;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.decoder.MessageDecoder;
@@ -65,12 +64,4 @@ public class HTTPPostAuthnResponseDecoder extends BaseHttpOIDCRequestDecoder {
         setMessageContext(messageContext);        
     }
     
-    /** {@inheritDoc} */
-    @Override
-    @Nullable
-    protected String serializeMessageForLogging(@Nullable final Object message) {
-        // Returning null disables log output. If want protocol message logging, need to implement this.
-        return null;
-    }
-
 }
\ No newline at end of file
diff --git a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoder.java b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoder.java
index 63864fbe..f5efdb38 100644
--- a/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoder.java
+++ b/oidc-common-profile-impl/src/main/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoder.java
@@ -17,7 +17,6 @@ package net.shibboleth.oidc.profile.decoding.impl;
 import java.io.IOException;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.decoder.MessageDecoder;
@@ -67,11 +66,4 @@ public class HTTPRedirectAuthnResponseDecoder extends BaseHttpOIDCRequestDecoder
         setMessageContext(messageContext);        
     }
 
-    /** {@inheritDoc} */
-    @Override
-    @Nullable
-    protected String serializeMessageForLogging(@Nullable final Object message) {
-        // Returning null disables log output. If want protocol message logging, need to implement this.
-        return null;
-    }
 }
diff --git a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoderTest.java b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoderTest.java
index 1348e3fc..a0e422c1 100644
--- a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoderTest.java
+++ b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPPostAuthnResponseDecoderTest.java
@@ -14,6 +14,8 @@
 
 package net.shibboleth.oidc.profile.decoding.impl;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import org.opensaml.messaging.context.MessageContext;
@@ -23,6 +25,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 
@@ -65,6 +68,86 @@ public class HTTPPostAuthnResponseDecoderTest {
         assertTrue(((AuthenticationSuccessResponse) msg).getAuthorizationCode().getValue().equals("XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR"));
     }
     
+    @Test
+    public void testErrorResponseDecoding() throws Exception {
+        mockRequest.setMethod("POST");
+        mockRequest.setRequestURI("/idp/profile/Authn/OIDC/RP/callback");
+        mockRequest.addParameter("state", "91c28622815dd9a92ef7c984c74c9e39.65317332");
+        mockRequest.addParameter("error", "invalid_request");
+        mockRequest.addParameter("error_description", "bad request");
+        mockRequest.setContentType("application/x-www-form-urlencoded");
+        decoder.setHttpServletRequestSupplier(new ThreadLocalHttpServletRequestSupplier());
+        decoder.initialize();
+        decoder.decode();
+        
+        final MessageContext mc = decoder.getMessageContext();
+        assert mc != null;
+        
+        final Object msg = mc.getMessage();
+        assertTrue(msg instanceof AuthenticationErrorResponse);
+        assert msg != null;
+        assertTrue(((AuthenticationResponse) msg).getState().getValue().equals("91c28622815dd9a92ef7c984c74c9e39.65317332"));
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getCode(),"invalid_request");
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getDescription(),"bad request");
+    }
+    
+    /* Test to make sure the protocol logging is creating a string for logging.*/
+    @SuppressWarnings("null")
+    @Test
+    public void testErrorResponseDecoding_ProtocolMessage() throws Exception {
+        mockRequest.setMethod("POST");
+        mockRequest.setRequestURI("/idp/profile/Authn/OIDC/RP/callback");
+        mockRequest.addParameter("state", "91c28622815dd9a92ef7c984c74c9e39.65317332");
+        mockRequest.addParameter("error", "invalid_request");
+        mockRequest.addParameter("error_uri", "http://op.example.org/error");
+        mockRequest.addParameter("error_description", "bad request");
+        mockRequest.setContentType("application/x-www-form-urlencoded");
+        decoder.setHttpServletRequestSupplier(new ThreadLocalHttpServletRequestSupplier());
+        decoder.initialize();
+        decoder.decode();
+        
+        final MessageContext mc = decoder.getMessageContext();
+        assert mc != null;
+        
+        final Object msg = mc.getMessage();
+        assertTrue(msg instanceof AuthenticationErrorResponse);
+        assert msg != null;
+        assertTrue(((AuthenticationResponse) msg).getState().getValue().equals("91c28622815dd9a92ef7c984c74c9e39.65317332"));
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getCode(),"invalid_request");
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getDescription(),"bad request");
+        final String logMessage = decoder.serializeMessageForLogging(msg);
+        assertNotNull(logMessage);
+        assertTrue(logMessage.contains("code"));
+        assertTrue(logMessage.contains("description"));
+    }
+    
+    /* Test to make sure the protocol logging is creating a string for logging.*/
+    @SuppressWarnings("null")
+    @Test
+    public void testSuccessful_ProtocolMessage() throws Exception {
+        mockRequest.setMethod("POST");
+        mockRequest.setRequestURI("/idp/profile/Authn/OIDC/RP/callback");
+        mockRequest.addParameter("state", "91c28622815dd9a92ef7c984c74c9e39.65317332");
+        mockRequest.addParameter("code", "XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR");
+        mockRequest.setContentType("application/x-www-form-urlencoded");
+        decoder.setHttpServletRequestSupplier(new ThreadLocalHttpServletRequestSupplier());
+        decoder.initialize();
+        decoder.decode();
+        
+        final MessageContext mc = decoder.getMessageContext();
+        assert mc != null;
+        
+        final Object msg = mc.getMessage();
+        assertTrue(msg instanceof AuthenticationSuccessResponse);
+        assert msg != null;
+        assertTrue(((AuthenticationResponse) msg).getState().getValue().equals("91c28622815dd9a92ef7c984c74c9e39.65317332"));
+        assertTrue(((AuthenticationSuccessResponse) msg).getAuthorizationCode().getValue().equals("XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR"));
+        final String logMessage = decoder.serializeMessageForLogging(msg);
+        assertNotNull(logMessage);
+        assertTrue(logMessage.contains("state"));
+        assertTrue(logMessage.contains("code"));
+    }
+    
     @Test(expectedExceptions = MessageDecodingException.class)
     public void testWrongMethod() throws Exception {
         mockRequest.setMethod("GET");     
diff --git a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoderTest.java b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoderTest.java
index 379ec400..bed93eee 100644
--- a/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoderTest.java
+++ b/oidc-common-profile-impl/src/test/java/net/shibboleth/oidc/profile/decoding/impl/HTTPRedirectAuthnResponseDecoderTest.java
@@ -14,6 +14,7 @@
 
 package net.shibboleth.oidc.profile.decoding.impl;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
 import org.opensaml.messaging.context.MessageContext;
@@ -23,6 +24,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 
@@ -47,8 +49,7 @@ public class HTTPRedirectAuthnResponseDecoderTest {
     @Test
     public void testSuccessfulDecoding_Query_AuthCode() throws Exception {
         mockRequest.setMethod("GET");
-        mockRequest.setRequestURI("https://localhost/idp/profile/Authn/OIDC/RP/"
-                + "callback?code=XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR&state=91c28622815dd9a92ef7c984c74c9e39.65317332");
+        mockRequest.setRequestURI("https://localhost/idp/profile/Authn/OIDC/RP/callback");
         mockRequest.setQueryString(
                 "code=XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR&state=91c28622815dd9a92ef7c984c74c9e39.65317332");
         mockRequest.setContentType("application/x-www-form-urlencoded");
@@ -68,6 +69,28 @@ public class HTTPRedirectAuthnResponseDecoderTest {
         assertTrue(((AuthenticationSuccessResponse) msg).getAuthorizationCode().getValue().equals("XSpej7DkB2WYcF1Gzth5CCHw6Oxf3gxR"));
     }
     
+    @Test
+    public void testErrorResponseDecoding() throws Exception {        
+        mockRequest.setMethod("GET");
+        mockRequest.setRequestURI("https://localhost/idp/profile/Authn/OIDC/RP/callback");
+        mockRequest.setQueryString(
+                "error=invalid_request&error_description=bad%20request&state=91c28622815dd9a92ef7c984c74c9e39.65317332");
+        mockRequest.setContentType("application/x-www-form-urlencoded");
+        decoder.setHttpServletRequestSupplier(new ThreadLocalHttpServletRequestSupplier());
+        decoder.initialize();
+        decoder.decode();
+        
+        final MessageContext mc = decoder.getMessageContext();
+        assert mc != null;
+        
+        final Object msg = mc.getMessage();
+        assertTrue(msg instanceof AuthenticationErrorResponse);
+        assert msg != null;
+        assertTrue(((AuthenticationResponse) msg).getState().getValue().equals("91c28622815dd9a92ef7c984c74c9e39.65317332"));
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getCode(),"invalid_request");
+        assertEquals(((AuthenticationErrorResponse) msg).getErrorObject().getDescription(),"bad request");
+    }
+    
     @Test(expectedExceptions = MessageDecodingException.class)
     public void testWrongMethod() throws Exception {
         mockRequest.setMethod("POST");     

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list