[java-plugin-shibd-oidc] branch main updated: Improve token session data handling

Codeberg noreply at shibboleth.net
Thu Dec 4 16:19:11 UTC 2025


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd-oidc.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-oidc/commit/073b916556529598254f888e648c98244eb7d534

The following commit(s) were added to refs/heads/main by this push:
     new 073b916  Improve token session data handling
073b916 is described below

commit 073b916556529598254f888e648c98244eb7d534
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Dec 4 16:18:56 2025 +0000

    Improve token session data handling
    
     - Only set access_token attributes if the access_token can be set
     - Add a test class, but is not complete until the API is updated.
---
 sp-oidc-impl/pom.xml                               |   6 +
 .../sp/oidc/profile/impl/PrepareAgentResponse.java |  26 ++-
 .../profile/impl/PrepareAgentResponseTest.java     | 184 +++++++++++++++++++++
 3 files changed, 207 insertions(+), 9 deletions(-)

diff --git a/sp-oidc-impl/pom.xml b/sp-oidc-impl/pom.xml
index c50781f..99a0102 100644
--- a/sp-oidc-impl/pom.xml
+++ b/sp-oidc-impl/pom.xml
@@ -198,6 +198,12 @@
             <artifactId>shib-testing</artifactId>
             <scope>test</scope>
         </dependency>
+        
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponse.java b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponse.java
index 052ef6d..d7eaa50 100644
--- a/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponse.java
+++ b/sp-oidc-impl/src/main/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponse.java
@@ -125,6 +125,7 @@ public class PrepareAgentResponse extends AbstractTokenConsumerResponseAction {
     /** {@inheritDoc} */
     @Override
     protected String getSessionData(final ProfileRequestContext profileRequestContext) {
+        
         final OIDCTokenResponse tokenResponse = tokenResponseContext.getTokenResponse();
         if (tokenResponse != null) {
             final AccessToken accessToken = tokenResponse.getTokens().getAccessToken();
@@ -133,26 +134,33 @@ public class PrepareAgentResponse extends AbstractTokenConsumerResponseAction {
             log.debug("{} Storing access and refresh tokens in session data", getLogPrefix());
             final DDF tokens = new DDF(null).structure();            
 
-            tokens.addmember("token_type").string(accessToken.getType().getValue());
-            tokens.addmember("expires_in").longinteger(accessToken.getLifetime());
-            if (accessToken.getScope() != null) {
-                tokens.addmember("scope").string(accessToken.getScope().toString());
-            }
-            if (accessToken.getIssuedTokenType() != null) {
-                tokens.addmember("issued_token_type").string(accessToken.getIssuedTokenType().getURI().toString());
-            }
-            
+            boolean accessTokenAdded = false;
             final DataSealer localDataSealer = dataSealer;
             if (localDataSealer != null) {
                 try {
                     tokens.addmember("access_token").string(localDataSealer.wrap(accessToken.getValue()));
+                    accessTokenAdded = true;
                 } catch (final DataSealerException e) {
                     log.debug("{} Error sealing access token for session data", getLogPrefix(), e);
                     // Is not an error, but the access token won't be stored.
                 }
             } else {
                 tokens.addmember("access_token").string(accessToken.getValue());
+                accessTokenAdded = true;
             }
+            
+            // No need to set all the other attributes of an access_token if the access_token itself is not present
+            if (accessTokenAdded) {
+                tokens.addmember("token_type").string(accessToken.getType().getValue());
+                tokens.addmember("expires_in").longinteger(accessToken.getLifetime());
+                if (accessToken.getScope() != null) {
+                    tokens.addmember("scope").string(accessToken.getScope().toString());
+                }
+                if (accessToken.getIssuedTokenType() != null) {
+                    tokens.addmember("issued_token_type").string(accessToken.getIssuedTokenType().getURI().toString());
+                }
+            }
+            
             if (refreshToken != null) {
                 if (localDataSealer != null) {
                     try {
diff --git a/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponseTest.java b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponseTest.java
new file mode 100644
index 0000000..768fc43
--- /dev/null
+++ b/sp-oidc-impl/src/test/java/net/shibboleth/sp/oidc/profile/impl/PrepareAgentResponseTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.sp.oidc.profile.impl;
+
+import org.mockito.Mockito;
+import org.opensaml.messaging.context.MessageContext;
+import org.springframework.webflow.execution.Event;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.token.AccessToken;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.token.OIDCTokens;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.oidc.profile.config.impl.DefaultOIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.profile.context.AccessTokenResponseContext;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.security.DataSealer;
+import net.shibboleth.shared.security.DataSealerException;
+import net.shibboleth.sp.ddf.DDF;
+import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
+import net.shibboleth.sp.profile.ConsumerConstants;
+import net.shibboleth.sp.profile.impl.BaseAgplicationActionTest;
+
+/**
+ *
+ */
+public class PrepareAgentResponseTest extends BaseAgplicationActionTest{
+    
+    private PrepareAgentResponse action;
+    
+    private MessageContext mc;
+    private AuthenticationContext ac;
+    private RelyingPartyContext rpc;
+    private AccessTokenResponseContext atrc;
+    private RelyingPartyContext partyContext;   
+    
+    /** The RP config.*/
+    protected DefaultOIDCAuthorizationConfiguration rpConfig;
+    
+    @Override
+    @BeforeMethod
+    public void beforeMethod() throws ComponentInitializationException {
+        super.beforeMethod();
+        
+        action = new PrepareAgentResponse();
+        
+        ac = prc.ensureSubcontext(AuthenticationContext.class);
+        mc = new MessageContext();        
+        atrc = new AccessTokenResponseContext();
+        prc.ensureInboundMessageContext().addSubcontext(atrc);
+        
+        partyContext = prc.ensureSubcontext(RelyingPartyContext.class);
+        rpConfig = new DefaultOIDCAuthorizationConfiguration();
+        partyContext.setProfileConfig(rpConfig);
+        prc.addSubcontext(partyContext);
+
+    }
+
+
+    @Test
+    public void testSuccess() {
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+    }
+    
+
+    @Test
+    void testGetSessionData_WithTokens_NoSealer() throws ComponentInitializationException {
+        action.initialize();
+        
+        final AccessToken accessToken = new BearerAccessToken("access123", 3600, null);
+        final RefreshToken refreshToken = new RefreshToken("refresh123");
+        final OIDCTokenResponse oidcResponse = new OIDCTokenResponse(
+                new OIDCTokens(new PlainJWT(new JWTClaimsSet.Builder().build()), accessToken, refreshToken));
+        atrc.setTokenResponse(oidcResponse);
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final DDF out = arc.getOutput();
+        assert out != null;
+        Assert.assertTrue(out.isstruct());
+        
+        final DDF http = out.getmember(RemotedHttpServletResponse.STRUCTURE_NAME);
+        Assert.assertTrue(http.isstruct());
+        Assert.assertEquals(http.getmember("redirect").unsafe_string(), new byte[] {'/'});
+        Assert.assertTrue(http.getmember("response").isnull());
+        Assert.assertTrue(http.getmember("headers").isnull());
+        
+        //TODO tests for actual token values when the API is changed.
+        final DDF session = out.getmember(ConsumerConstants.SESSION_OPAQUE);
+        //Assert.assertFalse(session.isnull());
+    }
+
+    @Test
+    void testGetSessionData_WithDataSealer() throws Exception {
+        final DataSealer sealer = Mockito.mock(DataSealer.class);
+        Mockito.when(sealer.wrap("access123")).thenReturn("sealedAccess");
+        Mockito.when(sealer.wrap("refresh123")).thenReturn("sealedRefresh");
+        action.setDataSealer(sealer);
+
+        action.initialize();
+        
+        final AccessToken accessToken = new BearerAccessToken("access123", 3600, null);
+        final RefreshToken refreshToken = new RefreshToken("refresh123");
+        final OIDCTokenResponse oidcResponse = new OIDCTokenResponse(
+                new OIDCTokens(new PlainJWT(new JWTClaimsSet.Builder().build()), accessToken, refreshToken));
+        atrc.setTokenResponse(oidcResponse);
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final DDF out = arc.getOutput();
+        assert out != null;
+        Assert.assertTrue(out.isstruct());
+        
+        final DDF http = out.getmember(RemotedHttpServletResponse.STRUCTURE_NAME);
+        Assert.assertTrue(http.isstruct());
+        Assert.assertEquals(http.getmember("redirect").unsafe_string(), new byte[] {'/'});
+        Assert.assertTrue(http.getmember("response").isnull());
+        Assert.assertTrue(http.getmember("headers").isnull());
+        
+        //TODO tests for actual token values when the API is changed.
+        final DDF session = out.getmember(ConsumerConstants.SESSION_OPAQUE);
+        //Assert.assertFalse(session.isnull());
+    }
+
+    @Test
+    void testGetSessionData_DataSealerException() throws Exception {
+        final DataSealer sealer = Mockito.mock(DataSealer.class);
+        Mockito.when(sealer.wrap(Mockito.anyString())).thenThrow(new DataSealerException("Error"));
+        action.setDataSealer(sealer);
+        
+        action.initialize();
+
+        final AccessToken accessToken = new BearerAccessToken("access123", 3600, null);
+        final RefreshToken refreshToken = new RefreshToken("refresh123");
+        final OIDCTokenResponse oidcResponse = new OIDCTokenResponse(
+                new OIDCTokens(new PlainJWT(new JWTClaimsSet.Builder().build()), accessToken, refreshToken));
+        atrc.setTokenResponse(oidcResponse);
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+        
+        final DDF out = arc.getOutput();
+        assert out != null;
+        Assert.assertTrue(out.isstruct());
+        
+        final DDF http = out.getmember(RemotedHttpServletResponse.STRUCTURE_NAME);
+        Assert.assertTrue(http.isstruct());
+        Assert.assertEquals(http.getmember("redirect").unsafe_string(), new byte[] {'/'});
+        Assert.assertTrue(http.getmember("response").isnull());
+        Assert.assertTrue(http.getmember("headers").isnull());
+        
+        //TODO check the tokens are NOT set on the DDF because the data sealer threw an error
+        final DDF session = out.getmember(ConsumerConstants.SESSION_OPAQUE);
+        // Check access_token and refresh_token not set
+    }
+
+
+}

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


More information about the commits mailing list