[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