[java-idp-oidc] 09/14: JOIDC-13 - Support for OIDC Logout
Henri Mikkonen
henri.mikkonen at iki.fi
Wed Jan 3 14:07:16 UTC 2024
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=bfef55f36b6562d39e476fc20b06353b2cbcaf08
commit bfef55f36b6562d39e476fc20b06353b2cbcaf08
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Nov 24 13:53:51 2023 +0200
JOIDC-13 - Support for OIDC Logout
https://shibboleth.atlassian.net/browse/JOIDC-13
Refactored OIDCRPSession to be compatible with implicit flow (i.e. no authorization code involved)
- Root token identifier is now optional
Removed global bean 'shibboleth.oidc.OIDCRPSessionCreationStrategy' and restored using
'DefaultSPSessionCreationStrategy' in authorize flow beans.
---
.../idp/plugin/oidc/op/session/OIDCRPSession.java | 6 +-
.../op/logout/profile/impl/RevokeTokenChain.java | 3 +-
.../impl/OIDCRPSessionCreationStrategy.java | 142 ++++++++--
.../op/session/impl/OIDCRPSessionSerializer.java | 4 +-
.../META-INF/net.shibboleth.idp/postconfig.xml | 4 -
.../idp/flows/oidc/authorize/authorize-beans.xml | 3 +-
.../impl/OIDCRPSessionCreationStrategyTest.java | 293 +++++++++++++++++++++
7 files changed, 425 insertions(+), 30 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/OIDCRPSession.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/OIDCRPSession.java
index 33ab9d96..7021a0f8 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/OIDCRPSession.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/OIDCRPSession.java
@@ -37,7 +37,7 @@ public class OIDCRPSession extends BasicSPSession implements SPSession {
@Nonnull @NotEmpty private final String issuer;
/** The root token identifier. */
- @Nonnull @NotEmpty private final String rootTokenIdentifier;
+ @Nullable private final String rootTokenIdentifier;
/** The session identifier. */
@Nonnull @NotEmpty private final String sessionIdentifier;
@@ -62,11 +62,11 @@ public class OIDCRPSession extends BasicSPSession implements SPSession {
*/
private OIDCRPSession(@Nonnull @NotEmpty final String id, @Nonnull final Instant creation,
@Nonnull final Instant expiration, @Nonnull @NotEmpty final String iss,
- @Nonnull @NotEmpty final String rootJti, @Nonnull @NotEmpty final String sid,
+ @Nullable final String rootJti, @Nonnull @NotEmpty final String sid,
@Nonnull @NotEmpty final String sub, final boolean supportsPropagation) {
super(id, creation, expiration);
issuer = Constraint.isNotEmpty(iss, "The issuer value cannot be empty");
- rootTokenIdentifier = Constraint.isNotEmpty(rootJti, "The root token identifier cannot be empty");
+ rootTokenIdentifier = rootJti;
sessionIdentifier = Constraint.isNotEmpty(sid, "The session identifier cannot be empty");
subject = Constraint.isNotEmpty(sub, "The subject value cannot be empty");
supportsLogoutPropagation = supportsPropagation;
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/logout/profile/impl/RevokeTokenChain.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/logout/profile/impl/RevokeTokenChain.java
index 0933bea3..4e8c88d2 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/logout/profile/impl/RevokeTokenChain.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/logout/profile/impl/RevokeTokenChain.java
@@ -130,8 +130,7 @@ public class RevokeTokenChain extends AbstractOIDCLogoutPropagationAction {
rootTokenIdentifier = ((OIDCRPSession) logoutPropagationContext.getSession()).getRootTokenIdentifier();
if (rootTokenIdentifier == null) {
- log.error("{} OIDCRPSession context did not contain a root json web token identifier.", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ log.debug("{} OIDCRPSession context did not contain a root jti, nothing to revoke.", getLogPrefix());
return false;
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategy.java
index 9dc90880..0ee73b98 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategy.java
@@ -22,26 +22,32 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultClientIDLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.OIDCAuthenticationResponseContextLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.session.OIDCRPSession;
import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
import net.shibboleth.idp.session.SPSession;
import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+import net.shibboleth.shared.component.AbstractInitializableComponent;
import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
import net.shibboleth.shared.primitive.StringSupport;
/**
* A function to create a {@link OIDCRPSession} based on profile execution state.
*/
-public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestContext, SPSession> {
+public class OIDCRPSessionCreationStrategy extends AbstractInitializableComponent
+ implements Function<ProfileRequestContext, SPSession> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(OIDCRPSessionCreationStrategy.class);
@@ -50,11 +56,14 @@ public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestCon
@Nonnull private final Duration sessionLifetime;
/** Lookup strategy for OIDC metadata context. */
- @Nonnull private final Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataContextLookupFunction;
+ @Nonnull private Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataContextLookupStrategy;
- /** Lookup strategy for OIDC authenticatin response context. */
+ /** Lookup strategy for OIDC authentication response context. */
@Nonnull
- private final Function<ProfileRequestContext, OIDCAuthenticationResponseContext> oidcResponseContextLookupStrategy;
+ private Function<ProfileRequestContext, OIDCAuthenticationResponseContext> oidcResponseContextLookupStrategy;
+
+ /** Lookup strategy used to obtain the client id value. */
+ @Nonnull private Function<ProfileRequestContext, ClientID> clientIDLookupStrategy;
/**
* Constructor.
@@ -63,8 +72,39 @@ public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestCon
*/
public OIDCRPSessionCreationStrategy(@Nonnull final Duration lifetime) {
sessionLifetime = Constraint.isNotNull(lifetime, "Lifetime cannot be null");
- oidcMetadataContextLookupFunction = new DefaultOIDCMetadataContextLookupFunction();
+ oidcMetadataContextLookupStrategy = new DefaultOIDCMetadataContextLookupFunction();
oidcResponseContextLookupStrategy = new OIDCAuthenticationResponseContextLookupFunction();
+ clientIDLookupStrategy = FunctionSupport.compose(new DefaultClientIDLookupFunction(),
+ new InboundMessageContextLookup());
+ }
+
+ /**
+ * Set the lookup strategy for OIDC metadata context.
+ * @param strategy lookup strategy
+ */
+ public void setOidcMetadataContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, OIDCMetadataContext> strategy) {
+ checkSetterPreconditions();
+ oidcMetadataContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the lookup strategy for OIDC authentication response context.
+ * @param strategy lookup strategy
+ */
+ public void setOidcResponseContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationResponseContext> strategy) {
+ checkSetterPreconditions();
+ oidcResponseContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the lookup strategy used to obtain the client id value.
+ * @param strategy lookup strategy
+ */
+ public void setClientIDLookupStrategy(@Nonnull final Function<ProfileRequestContext, ClientID> strategy) {
+ checkSetterPreconditions();
+ clientIDLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
/** {@inheritDoc} */
@@ -78,34 +118,41 @@ public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestCon
}
final TokenClaimsSet tokenClaimsSet = oidcAuthenticationResponseContext.getAuthorizationGrantClaimsSet();
- if (tokenClaimsSet == null) {
- log.debug("AuthorizationGrantClaimsSet cannot be null, no OIDCRPSession created.");
- return null;
- }
+ final IDTokenClaimsSet idTokenClaimsSet = oidcAuthenticationResponseContext.getIDToken();
- final ClientID clientIdentifier = tokenClaimsSet.getClientID();
+ final ClientID clientIdentifier = clientIDLookupStrategy.apply(profileRequestContext);
if (clientIdentifier == null || StringSupport.trimOrNull(clientIdentifier.getValue()) == null) {
log.debug("ClientID/RelyingPartyId cannot be null, no OIDCRPSession created.");
return null;
}
final Instant now = Instant.now();
- final String issuer = tokenClaimsSet.getIssuer();
-
- final String rootTokenIdentifier = tokenClaimsSet.getRootTokenIdentifier() != null
- ? tokenClaimsSet.getRootTokenIdentifier() : tokenClaimsSet.getID();
- if (rootTokenIdentifier == null) {
- log.debug("RootTokenIdentifier (root jti) cannot be null, no OIDCRPSession created.");
+ final String issuer = getIssuer(tokenClaimsSet, idTokenClaimsSet);
+ if (issuer == null) {
+ log.debug("Issuer cannot be null, no OIDCRPSession created.");
return null;
}
- final String sessionIdentifier = tokenClaimsSet.getSessionIdentifier();
+ final String rootTokenIdentifier;
+ if (tokenClaimsSet != null) {
+ rootTokenIdentifier = tokenClaimsSet.getRootTokenIdentifier() != null
+ ? tokenClaimsSet.getRootTokenIdentifier() : tokenClaimsSet.getID();
+ if (rootTokenIdentifier == null) {
+ log.debug("RootTokenIdentifier (root jti) cannot be null in the authorization grant context");
+ return null;
+ }
+ } else {
+ log.debug("RootTokenIdentifier won't be set to OIDCRPSession");
+ rootTokenIdentifier = null;
+ }
+
+ final String sessionIdentifier = getSessionIdentifier(tokenClaimsSet, idTokenClaimsSet);
if (sessionIdentifier == null) {
log.debug("SessionIdentifier cannot be null, no OIDCRPSession created.");
return null;
}
- final String subject = tokenClaimsSet.getSubject();
+ final String subject = getSubject(tokenClaimsSet, idTokenClaimsSet);
if (subject == null) {
log.debug("Subject cannot be null, no OIDCRPSession created.");
return null;
@@ -125,6 +172,63 @@ public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestCon
.build();
}
+ /**
+ * Get session identifier from the given token claims set if found, and from the given ID token claims set if not.
+ *
+ * @param tokenClaimsSet token claims set used as a primary source
+ * @param idTokenClaimsSet ID token claims set used as a secondary source
+ * @return session identifier value, or null if not found from neither of the sources
+ */
+ @Nullable
+ protected String getSessionIdentifier(@Nullable final TokenClaimsSet tokenClaimsSet,
+ @Nullable final IDTokenClaimsSet idTokenClaimsSet) {
+ if (tokenClaimsSet != null) {
+ return tokenClaimsSet.getSessionIdentifier();
+ }
+ if (idTokenClaimsSet != null && idTokenClaimsSet.getSessionID() != null) {
+ return idTokenClaimsSet.getSessionID().getValue();
+ }
+ return null;
+ }
+
+ /**
+ * Get issuer from the given token claims set if found, and from the given ID token claims set if not.
+ *
+ * @param tokenClaimsSet token claims set used as a primary source
+ * @param idTokenClaimsSet ID token claims set used as a secondary source
+ * @return issuer value, or null if not found from neither of the sources
+ */
+ @Nullable
+ protected String getIssuer(@Nullable final TokenClaimsSet tokenClaimsSet,
+ @Nullable final IDTokenClaimsSet idTokenClaimsSet) {
+ if (tokenClaimsSet != null) {
+ return tokenClaimsSet.getIssuer();
+ }
+ if (idTokenClaimsSet != null && idTokenClaimsSet.getIssuer() != null) {
+ return idTokenClaimsSet.getIssuer().getValue();
+ }
+ return null;
+ }
+
+ /**
+ * Get subject from the given token claims set if found, and from the given ID token claims set if not.
+ *
+ * @param tokenClaimsSet token claims set used as a primary source
+ * @param idTokenClaimsSet ID token claims set used as a secondary source
+ * @return subject value, or null if not found from neither of the sources
+ */
+ @Nullable
+ protected String getSubject(@Nullable final TokenClaimsSet tokenClaimsSet,
+ @Nullable final IDTokenClaimsSet idTokenClaimsSet) {
+ if (tokenClaimsSet != null) {
+ return tokenClaimsSet.getSubject();
+ }
+ if (idTokenClaimsSet != null && idTokenClaimsSet.getSubject() != null) {
+ return idTokenClaimsSet.getSubject().getValue();
+ }
+ return null;
+ }
+
/**
* Check if the {@link OIDCClientMetadata} attached to the given PRC contains a front- or back-channel logout URI.
*
@@ -132,7 +236,7 @@ public class OIDCRPSessionCreationStrategy implements Function<ProfileRequestCon
* @return true iff the attached metadata contains a front- or back-channel logout URI
*/
protected boolean isSupportsLogoutPropagation(@Nonnull final ProfileRequestContext profileRequestContext) {
- final OIDCMetadataContext oidcMetadataContext = oidcMetadataContextLookupFunction.apply(profileRequestContext);
+ final OIDCMetadataContext oidcMetadataContext = oidcMetadataContextLookupStrategy.apply(profileRequestContext);
if (oidcMetadataContext == null || oidcMetadataContext.getClientInformation() == null) {
log.debug("No metadata found, does not support propagation");
return false;
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionSerializer.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionSerializer.java
index ca62b077..14902ec0 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionSerializer.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionSerializer.java
@@ -54,7 +54,9 @@ public class OIDCRPSessionSerializer extends AbstractSPSessionSerializer {
generator.write(TokenClaimsSet.KEY_SESSION_ID, oidcRpSession.getSessionIdentifier());
generator.write(TokenClaimsSet.KEY_ISSUER, oidcRpSession.getIssuer());
generator.write(TokenClaimsSet.KEY_SUBJECT, oidcRpSession.getSubject());
- generator.write(TokenClaimsSet.KEY_ROOT_JTI, oidcRpSession.getRootTokenIdentifier());
+ if (oidcRpSession.getRootTokenIdentifier() != null) {
+ generator.write(TokenClaimsSet.KEY_ROOT_JTI, oidcRpSession.getRootTokenIdentifier());
+ }
generator.write(LOGOUT_PROP_FIELD, oidcRpSession.supportsLogoutPropagation());
}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index b74e6067..0e158895 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -741,10 +741,6 @@
<bean id="shibboleth.oidc.Conditions.MetadataValueEquals"
class="net.shibboleth.idp.plugin.oidc.op.profile.logic.MetadataValueEqualsCondition" abstract="true" />
- <bean id="shibboleth.oidc.OIDCRPSessionCreationStrategy"
- class="net.shibboleth.idp.plugin.oidc.op.session.impl.OIDCRPSessionCreationStrategy"
- c:lifetime="%{idp.session.defaultSPlifetime:PT2H}"/>
-
<bean id="logoutprop/oidc" class="net.shibboleth.idp.session.LogoutPropagationFlowDescriptor"
c:_0="net.shibboleth.idp.plugin.oidc.op.session.OIDCRPSession"/>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index 4bfabc8f..5e4f05b8 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -877,7 +877,8 @@
p:sessionManager-ref="shibboleth.SessionManager"
p:SPSessionCreationStrategy-ref="%{idp.oidc.SPSessionCreationStrategy:DefaultSPSessionCreationStrategy}"/>
- <bean id="DefaultSPSessionCreationStrategy" class="net.shibboleth.idp.saml.session.impl.SAML2SPSessionCreationStrategy"
+ <bean id="DefaultSPSessionCreationStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.session.impl.OIDCRPSessionCreationStrategy"
c:lifetime="%{idp.session.defaultSPlifetime:PT2H}"/>
<bean id="FormOutboundMessage"
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategyTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategyTest.java
new file mode 100644
index 00000000..f7af3a34
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/session/impl/OIDCRPSessionCreationStrategyTest.java
@@ -0,0 +1,293 @@
+/*
+ * 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.idp.plugin.oidc.op.session.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.List;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.oauth2.sdk.id.Audience;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.oauth2.sdk.id.Subject;
+import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
+import com.nimbusds.openid.connect.sdk.claims.SessionID;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.session.OIDCRPSession;
+import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
+import net.shibboleth.idp.session.SPSession;
+import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+import net.shibboleth.shared.component.ComponentInitializationException;
+
+/**
+ * Unit tests for {@link OIDCRPSessionCreationStrategy}.
+ */
+public class OIDCRPSessionCreationStrategyTest {
+
+ OIDCRPSessionCreationStrategy function;
+
+ private String clientId = "mockClientId";
+ private String sessionIdentifier = "mockSid";
+ private String subject = "mockSubject";
+ private String issuer = "mockIssuer";
+ private String rootId = "mockRootId";
+ private String jwtId = "mockJwtId";
+
+ public void setup() {
+ setup(Duration.ofDays(1), null, null, null);
+ }
+
+ public void setup(final Duration lifetime, final OIDCMetadataContext metadataContext,
+ final OIDCAuthenticationResponseContext responseContext, final String clientId) {
+ function = new OIDCRPSessionCreationStrategy(lifetime);
+ if (metadataContext != null) {
+ function.setOidcMetadataContextLookupStrategy(prc -> metadataContext);
+ }
+ if (responseContext != null) {
+ function.setOidcResponseContextLookupStrategy(prc -> responseContext);
+ }
+ if (clientId != null) {
+ function.setClientIDLookupStrategy(prc -> new ClientID(clientId));
+ }
+ try {
+ function.initialize();
+ } catch (final ComponentInitializationException e) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void nullPRC_shouldReturnNull() {
+ setup();
+ Assert.assertNull(function.apply(null));
+ }
+
+ @Test
+ public void emptyPRC_shouldReturnNull() {
+ setup();
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withTokenClaimsSet_noIssuer_shouldReturnNull() {
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .subject(subject)
+ .claim("sid", sessionIdentifier)
+ .claim("root_jti", rootId)
+ .build();
+ final TokenClaimsSet tokenClaimsSet = new TokenClaimsSet(claimsSet) { };
+ setup(Duration.ofDays(1), null, mockResponseContext(tokenClaimsSet, null), clientId);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withTokenClaimsSet_noRootNorJti_shouldReturnNull() {
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .issuer(issuer)
+ .claim("sid", sessionIdentifier)
+ .subject(subject)
+ .build();
+ final TokenClaimsSet tokenClaimsSet = new TokenClaimsSet(claimsSet) { };
+ setup(Duration.ofDays(1), null, mockResponseContext(tokenClaimsSet, null), clientId);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withTokenClaimsSet_noSubject_shouldReturnNull() {
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .issuer(issuer)
+ .claim("sid", sessionIdentifier)
+ .claim("root_jti", rootId)
+ .build();
+ final TokenClaimsSet tokenClaimsSet = new TokenClaimsSet(claimsSet) { };
+ setup(Duration.ofDays(1), null, mockResponseContext(tokenClaimsSet, null), clientId);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withTokenClaimsSet_noSessionIdentifier_shouldReturnNull() {
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .issuer(issuer)
+ .subject(subject)
+ .jwtID(jwtId)
+ .build();
+ final TokenClaimsSet tokenClaimsSet = new TokenClaimsSet(claimsSet) { };
+ setup(Duration.ofDays(1), null, mockResponseContext(tokenClaimsSet, null), clientId);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withIdTokenClaimsSet_noSessionIdentifier_shouldReturnNull() {
+ final IDTokenClaimsSet claimsSet = new IDTokenClaimsSet(new Issuer(issuer), new Subject(subject),
+ List.of(new Audience(clientId)), Date.from(Instant.now().plusSeconds(60)), Date.from(Instant.now()));
+ setup(Duration.ofDays(1), null, mockResponseContext(null, claimsSet), clientId);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_noClientId_shouldReturnNull() {
+ setup(Duration.ofDays(1), null, mockResponseContext(validTokenClaimsSet(), null), null);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_noClientId_shouldReturnNull() {
+ setup(Duration.ofDays(1), null, mockResponseContext(null, validIdTokenClaimsSet()), null);
+ Assert.assertNull(function.apply(new ProfileRequestContext()));
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_noMetadata_shouldReturnNoPropagationSession() {
+ setup(Duration.ofDays(1), null, mockResponseContext(validTokenClaimsSet(), null), clientId);
+ assertSession(function.apply(new ProfileRequestContext()), false, jwtId);
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_noMetadata_shouldReturnNoPropagationSession() {
+ setup(Duration.ofDays(1), null, mockResponseContext(null, validIdTokenClaimsSet()), clientId);
+ assertSession(function.apply(new ProfileRequestContext()), false, null);
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_noUrisInMetadata_shouldReturnNoPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(false, false), mockResponseContext(validTokenClaimsSet(), null),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), false, jwtId);
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_noUrisInMetadata_shouldReturnNoPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(false, false), mockResponseContext(null, validIdTokenClaimsSet()),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), false, null);
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_frontUriInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(true, false), mockResponseContext(validTokenClaimsSet(), null),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, jwtId);
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_frontUriInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(true, false), mockResponseContext(null, validIdTokenClaimsSet()),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, null);
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_backUriInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(false, true), mockResponseContext(validTokenClaimsSet(), null),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, jwtId);
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_backUriInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(false, true), mockResponseContext(null, validIdTokenClaimsSet()),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, null);
+ }
+
+ @Test
+ public void withValidTokenClaimsSet_bothUrisInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(true, true), mockResponseContext(validTokenClaimsSet(), null),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, jwtId);
+ }
+
+ @Test
+ public void withValidIdTokenClaimsSet_bothUrisInMetadata_shouldReturnPropagationSession() {
+ setup(Duration.ofDays(1), mockMetadataContext(true, true), mockResponseContext(null, validIdTokenClaimsSet()),
+ clientId);
+ assertSession(function.apply(new ProfileRequestContext()), true, null);
+ }
+
+ protected OIDCAuthenticationResponseContext mockResponseContext(final TokenClaimsSet tokenClaimsSet,
+ final IDTokenClaimsSet idTokenClaimsSet) {
+ final OIDCAuthenticationResponseContext responseContext = new OIDCAuthenticationResponseContext();
+ responseContext.setAuthorizationGrantClaimsSet(tokenClaimsSet);
+ responseContext.setIDToken(idTokenClaimsSet);
+ return responseContext;
+ }
+
+ protected OIDCMetadataContext mockMetadataContext(final boolean frontChannel, final boolean backChannel) {
+ final OIDCMetadataContext metadataContext = new OIDCMetadataContext();
+ final OIDCClientMetadata metadata = new OIDCClientMetadata();
+ if (frontChannel) {
+ try {
+ metadata.setFrontChannelLogoutURI(new URI("https://example.org/frontChannel"));
+ } catch (URISyntaxException e) {
+ Assert.fail();
+ }
+ }
+ if (backChannel) {
+ try {
+ metadata.setBackChannelLogoutURI(new URI("https://example.org/backChannel"));
+ } catch (URISyntaxException e) {
+ Assert.fail();
+ }
+ }
+ metadataContext.setClientInformation(new OIDCClientInformation(new ClientID(clientId), metadata));
+ return metadataContext;
+ }
+
+ protected TokenClaimsSet validTokenClaimsSet() {
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .issuer(issuer)
+ .claim("sid", sessionIdentifier)
+ .jwtID(jwtId)
+ .subject(subject)
+ .build();
+ return new TokenClaimsSet(claimsSet) { };
+ }
+
+ protected IDTokenClaimsSet validIdTokenClaimsSet() {
+ IDTokenClaimsSet claimsSet = new IDTokenClaimsSet(new Issuer(issuer), new Subject(subject),
+ List.of(new Audience(clientId)), Date.from(Instant.now().plusSeconds(60)), Date.from(Instant.now()));
+ claimsSet.setSessionID(new SessionID(sessionIdentifier));
+ return claimsSet;
+ }
+
+ protected void assertSession(final SPSession session, final boolean supportsPropagation, final String rootId) {
+ Assert.assertNotNull(session);
+ Assert.assertTrue(session instanceof OIDCRPSession);
+ final OIDCRPSession rpSession = (OIDCRPSession) session;
+ Assert.assertEquals(rpSession.getIssuer(), issuer);
+ Assert.assertEquals(rpSession.getSessionIdentifier(), sessionIdentifier);
+ Assert.assertEquals(rpSession.getSubject(), subject);
+ Assert.assertEquals(rpSession.supportsLogoutPropagation(), supportsPropagation);
+ Assert.assertEquals(rpSession.getRootTokenIdentifier(), rootId);
+ Assert.assertEquals(rpSession.getId(), clientId);
+ Assert.assertTrue(Duration.between(rpSession.getCreationInstant(), Instant.now()).toMillis()
+ < Duration.ofSeconds(2).toMillis());
+
+ Assert.assertTrue(Duration.between(rpSession.getExpirationInstant(), Instant.now().plus(Duration.ofDays(1)))
+ .toMillis() < Duration.ofSeconds(2).toMillis());
+
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list