[java-idp-oidc] branch main updated: JOIDC-254 - New method for manipulating token claims sets
Henri Mikkonen
henri.mikkonen at iki.fi
Tue Sep 9 10:21:13 UTC 2025
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=c1f2ca17b95ad968014d4bd6ffc57eb6ae2b132e
The following commit(s) were added to refs/heads/main by this push:
new c1f2ca17 JOIDC-254 - New method for manipulating token claims sets
c1f2ca17 is described below
commit c1f2ca17b95ad968014d4bd6ffc57eb6ae2b132e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Sep 9 13:20:54 2025 +0300
JOIDC-254 - New method for manipulating token claims sets
https://shibboleth.atlassian.net/browse/JOIDC-254
The following new interfaces can be used for manipulating the claims sets:
- AuthorizationCodeClaimsSetDecorator for authorization code claims sets (SetAuthorizationCodeToResponseContext, authorize flow)
- AccessTokenClaimsSetDecorator for access token claims sets (BuildAccessToken, authorize and token flows)
- IdTokenClaimsSetDecorator for ID token claims sets (ManipulateClaimsForIDToken, authorize and token flows)
- RefreshTokenClaimsSetDecorator for refresh token claims sets (SetRefreshTokenToResponseContext, token flow)
- RequestUriClaimsSetDecorator for pushed authorization requests (FormOutbounPushedAuthorizationResponseMessage, par flow)
The interfaces extend BiConsumer<Map<String, Object>, ProfileRequestContext> and IdentifiedComponent. The map is a modifiable HashMap.
---
.../op/profile/AccessTokenClaimsSetDecorator.java | 35 ++++++++++
.../AuthorizationCodeClaimsSetDecorator.java | 35 ++++++++++
.../oidc/op/profile/IdTokenClaimsSetDecorator.java | 35 ++++++++++
.../op/profile/RefreshTokenClaimsSetDecorator.java | 35 ++++++++++
.../op/profile/RequestUriClaimsSetDecorator.java | 35 ++++++++++
.../op/oauth2/profile/impl/BuildAccessToken.java | 35 +++++++++-
...mOutbounPushedAuthorizationResponseMessage.java | 25 +++++++-
.../SetAuthorizationCodeToResponseContext.java | 42 ++++++++++--
.../profile/impl/ManipulateClaimsForIDToken.java | 63 ++++++++++++++----
.../impl/SetRefreshTokenToResponseContext.java | 31 ++++++++-
.../oauth2/profile/impl/BuildAccessTokenTest.java | 2 +-
.../SetAuthorizationCodeToResponseContextTest.java | 2 +-
.../oidc/op/profile/flow/AuthorizeFlowTest.java | 51 ++++++++++++++-
.../op/profile/flow/PushedAuthorizeFlowTest.java | 2 +
.../flow/TestAccessTokenClaimsSetDecorator.java | 48 ++++++++++++++
.../TestAuthorizationCodeClaimsSetDecorator.java | 48 ++++++++++++++
.../flow/TestIdTokenClaimsSetDecorator.java | 48 ++++++++++++++
.../flow/TestRefreshTokenClaimsSetDecorator.java | 48 ++++++++++++++
.../flow/TestRequestUriClaimsSetDecorator.java | 48 ++++++++++++++
.../plugin/oidc/op/profile/flow/TokenFlowTest.java | 74 ++++++++++++++++++++++
.../impl/ManipulateClaimsForIDTokenTest.java | 2 +-
.../impl/SetRefreshTokenToResponseContextTest.java | 2 +-
.../net/shibboleth/idp/module/conf/global.xml | 5 ++
23 files changed, 721 insertions(+), 30 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AccessTokenClaimsSetDecorator.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AccessTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..898d91c7
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AccessTokenClaimsSetDecorator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.profile;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * An interface for decorating claims sets related to access tokens.
+ *
+ * The interface extends the {@code BiConsumer} by specifying the input types explicitly and is expected to operate via
+ * side-effects.
+ *
+ * @since 4.4.0
+ */
+public interface AccessTokenClaimsSetDecorator
+ extends BiConsumer<Map<String, Object>, ProfileRequestContext>, IdentifiedComponent {
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AuthorizationCodeClaimsSetDecorator.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AuthorizationCodeClaimsSetDecorator.java
new file mode 100644
index 00000000..f94f53a8
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/AuthorizationCodeClaimsSetDecorator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.profile;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * An interface for decorating claims sets related to authorization codes.
+ *
+ * The interface extends the {@code BiConsumer} by specifying the input types explicitly and is expected to operate via
+ * side-effects.
+ *
+ * @since 4.4.0
+ */
+public interface AuthorizationCodeClaimsSetDecorator
+ extends BiConsumer<Map<String, Object>, ProfileRequestContext>, IdentifiedComponent {
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/IdTokenClaimsSetDecorator.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/IdTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..933a04f1
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/IdTokenClaimsSetDecorator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.profile;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * An interface for decorating claims sets related to ID tokens.
+ *
+ * The interface extends the {@code BiConsumer} by specifying the input types explicitly and is expected to operate via
+ * side-effects.
+ *
+ * @since 4.4.0
+ */
+public interface IdTokenClaimsSetDecorator
+ extends BiConsumer<Map<String, Object>, ProfileRequestContext>, IdentifiedComponent {
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RefreshTokenClaimsSetDecorator.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RefreshTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..cb452001
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RefreshTokenClaimsSetDecorator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.profile;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * An interface for decorating claims sets related to refresh tokens.
+ *
+ * The interface extends the {@code BiConsumer} by specifying the input types explicitly and is expected to operate via
+ * side-effects.
+ *
+ * @since 4.4.0
+ */
+public interface RefreshTokenClaimsSetDecorator
+ extends BiConsumer<Map<String, Object>, ProfileRequestContext>, IdentifiedComponent {
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RequestUriClaimsSetDecorator.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RequestUriClaimsSetDecorator.java
new file mode 100644
index 00000000..23b9f893
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/RequestUriClaimsSetDecorator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.profile;
+
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * An interface for decorating claims sets related to pushed authorization requests.
+ *
+ * The interface extends the {@code BiConsumer} by specifying the input types explicitly and is expected to operate via
+ * side-effects.
+ *
+ * @since 4.4.0
+ */
+public interface RequestUriClaimsSetDecorator
+ extends BiConsumer<Map<String, Object>, ProfileRequestContext>, IdentifiedComponent {
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
index cf981cf3..e55c9a54 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
@@ -17,6 +17,7 @@ package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
@@ -31,6 +32,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jwt.JWT;
@@ -50,6 +52,7 @@ import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationRes
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.TokenRequestClientIDLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.AccessTokenClaimsSetDecorator;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.OIDCAuthenticationResponseContextLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction;
import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
@@ -65,6 +68,7 @@ import net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunc
import net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction;
import net.shibboleth.profile.context.navigate.IssuerLookupFunction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.FunctionSupport;
@@ -152,6 +156,9 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
/** The strategy used for manipulating the token claims set. */
@Nullable private BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> manipulationStrategy;
+ /** Autowired claims set decorators. */
+ @Nonnull private Collection<AccessTokenClaimsSetDecorator> tokenClaimsSetDecorators;
+
/** Object mapper used for pretty-printing JWT contents. */
@NonnullAfterInit private ObjectMapper objectMapper;
@@ -176,8 +183,13 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
/** The xmlSafe-flag passed to the identifier generator. */
private boolean xmlSafeIdentifier;
- /** Constructor. */
- public BuildAccessToken() {
+ /**
+ * Constructor.
+ *
+ * @param freeDecorators free-standing decorators to add
+ */
+ @Autowired
+ public BuildAccessToken(@Nullable final Collection<AccessTokenClaimsSetDecorator> freeDecorators) {
accessTokenTypeLookupStrategy = new AccessTokenTypeLookupFunction();
accessTokenLifetimeLookupStrategy = new AccessTokenLifetimeLookupFunction();
issuerLookupStrategy = new IssuerLookupFunction();
@@ -213,6 +225,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
new AccessTokenClaimsSetManipulationStrategyLookupFunction();
xmlSafeIdentifier = true;
alwaysIssueBearerAccessTokenCondition = new AlwaysIssueBearerAccessTokenPredicate();
+ tokenClaimsSetDecorators = freeDecorators == null ? CollectionSupport.emptyList() : freeDecorators;
}
/**
@@ -595,6 +608,24 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
}
final AccessTokenClaimsSet claimsSet = builder.build();
+ final JWTClaimsSet jwtClaimsSet = claimsSet.getClaimsSet();
+ assert jwtClaimsSet != null;
+ final Map<String, Object> claimsMap = new HashMap<>(jwtClaimsSet.toJSONObject());
+
+ for (final AccessTokenClaimsSetDecorator decorator : tokenClaimsSetDecorators) {
+ log.debug("{} Applying decorator {}", getLogPrefix(), decorator);
+ decorator.accept(claimsMap, profileRequestContext);
+ try {
+ final JWTClaimsSet parsedClaimsSet = JWTClaimsSet.parse(claimsMap);
+ assert parsedClaimsSet != null;
+ claimsSet.setClaimsSet(parsedClaimsSet);
+ } catch (final ParseException e) {
+ log.error("{} The resulted claims set after decorator {} could not be parsed",
+ getLogPrefix(), decorator.getId(), e);
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+ }
if (manipulationStrategy != null) {
final JWTClaimsSet acClaimsSet = claimsSet.getClaimsSet();
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/FormOutbounPushedAuthorizationResponseMessage.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/FormOutbounPushedAuthorizationResponseMessage.java
index 141cbe11..c66614a9 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/FormOutbounPushedAuthorizationResponseMessage.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/FormOutbounPushedAuthorizationResponseMessage.java
@@ -17,6 +17,8 @@ package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
import java.net.URI;
import java.text.ParseException;
import java.time.Duration;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -28,12 +30,14 @@ import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import com.nimbusds.jwt.JWT;
import com.nimbusds.oauth2.sdk.PushedAuthorizationRequest;
import com.nimbusds.oauth2.sdk.PushedAuthorizationSuccessResponse;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.RequestUriClaimsSetDecorator;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.oidc.profile.config.navigate.PushedAuthorizationRequestUriClaimsSetManipulationStrategyLookupFunction;
import net.shibboleth.oidc.profile.config.navigate.PushedAuthorizationRequestUriLifetimeLookupFunction;
@@ -67,6 +71,9 @@ public class FormOutbounPushedAuthorizationResponseMessage extends AbstractOAuth
@Nonnull private Map<String, BiFunction<ProfileRequestContext,Map<String,Object>,URI>>
requestUriClaimsSetSerializationStrategies;
+ /** The auto-wired decorators for request URI claims set. */
+ @Nonnull private Collection<RequestUriClaimsSetDecorator> requestUriClaimsSetDecorators;
+
/** The request URI type to use. */
@Nullable private String requestUriType;
@@ -79,15 +86,23 @@ public class FormOutbounPushedAuthorizationResponseMessage extends AbstractOAuth
/** The request message to operate on. */
@Nullable private PushedAuthorizationRequest requestMessage;
+
/**
+ *
* Constructor.
+ *
+ * @param decorators free-standing decorators to add
*/
- public FormOutbounPushedAuthorizationResponseMessage() {
+ @Autowired
+ public FormOutbounPushedAuthorizationResponseMessage(
+ @Nullable final Collection<RequestUriClaimsSetDecorator> decorators) {
requestUriTypeLookupStrategy = new PushedAuthorizationRequestUriTypeLookupFunction();
requestUriLifetimeLookupStrategy = new PushedAuthorizationRequestUriLifetimeLookupFunction();
requestUriClaimsSetManipulationStrategyLookupStrategy =
new PushedAuthorizationRequestUriClaimsSetManipulationStrategyLookupFunction();
requestUriClaimsSetSerializationStrategies = CollectionSupport.emptyMap();
+ requestUriClaimsSetDecorators =
+ decorators != null ? CollectionSupport.copyToList(decorators) : CollectionSupport.emptyList();
}
/**
@@ -189,9 +204,13 @@ public class FormOutbounPushedAuthorizationResponseMessage extends AbstractOAuth
final JWT requestObject = oidcContext.getRequestObject();
if (requestObject == null) {
assert requestMessage != null;
- claimsSet = requestMessage.getAuthorizationRequest().toJWTClaimsSet().getClaims();
+ claimsSet = new HashMap<>(requestMessage.getAuthorizationRequest().toJWTClaimsSet().getClaims());
} else {
- claimsSet = requestObject.getJWTClaimsSet().getClaims();
+ claimsSet = new HashMap<>(requestObject.getJWTClaimsSet().getClaims());
+ }
+
+ for (final RequestUriClaimsSetDecorator decorator : requestUriClaimsSetDecorators) {
+ decorator.accept(claimsSet, profileRequestContext);
}
if (manipulationStrategy != null) {
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
index d166c6c6..55b41353 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
@@ -18,6 +18,8 @@ import java.net.URI;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -28,6 +30,7 @@ import javax.annotation.Nullable;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.oauth2.sdk.AuthorizationRequest;
@@ -40,6 +43,7 @@ import net.shibboleth.idp.authn.context.SubjectContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.AuthorizationCodeClaimsSetDecorator;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.OIDCAuthenticationResponseContextLookupFunction;
import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
import net.shibboleth.idp.profile.IdPEventIds;
@@ -48,6 +52,7 @@ import net.shibboleth.oidc.profile.config.navigate.AuthorizationCodeClaimsSetMan
import net.shibboleth.oidc.profile.config.navigate.AuthzCodeLifetimeLookupFunction;
import net.shibboleth.profile.context.navigate.IssuerLookupFunction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.FunctionSupport;
@@ -116,6 +121,9 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthoriz
/** The strategy used for manipulating the token claims set. */
@Nullable private BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> manipulationStrategy;
+ /** Autowired claims set decorators. */
+ @Nonnull private Collection<AuthorizationCodeClaimsSetDecorator> tokenClaimsSetDecorators;
+
/** Subject context. */
@Nullable private SubjectContext subjectCtx;
@@ -130,8 +138,12 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthoriz
/**
* Constructor.
+ *
+ * @param freeDecorators free-standing decorators to add
*/
- public SetAuthorizationCodeToResponseContext() {
+ @Autowired
+ public SetAuthorizationCodeToResponseContext(
+ @Nullable final Collection<AuthorizationCodeClaimsSetDecorator> freeDecorators) {
final Function<ProfileRequestContext, OIDCAuthenticationResponseTokenClaimsContext> tccls =
new ChildContextLookup<>(OIDCAuthenticationResponseTokenClaimsContext.class).compose(
new OIDCAuthenticationResponseContextLookupFunction());
@@ -151,6 +163,7 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthoriz
tokenClaimsSetManipulationStrategyLookupStrategy =
new AuthorizationCodeClaimsSetManipulationStrategyLookupFunction();
xmlSafeIdentifier = true;
+ tokenClaimsSetDecorators = freeDecorators == null ? CollectionSupport.emptyList() : freeDecorators;
}
/**
@@ -429,14 +442,29 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthoriz
.setDpopProofJwkThumbprint(responseCtx.getDpopProofJwkThumbprint())
.build();
+ final JWTClaimsSet jwtClaimsSet = claimsSet.getClaimsSet();
+ assert jwtClaimsSet != null;
+ final Map<String, Object> claimsMap = new HashMap<>(jwtClaimsSet.toJSONObject());
+
+ for (final AuthorizationCodeClaimsSetDecorator decorator : tokenClaimsSetDecorators) {
+ log.debug("{} Applying decorator {}", getLogPrefix(), decorator);
+ decorator.accept(claimsMap, profileRequestContext);
+ try {
+ final JWTClaimsSet parsedClaimsSet = JWTClaimsSet.parse(claimsMap);
+ assert parsedClaimsSet != null;
+ claimsSet.setClaimsSet(parsedClaimsSet);
+ } catch (final ParseException e) {
+ log.error("{} The resulted claims set after decorator {} could not be parsed",
+ getLogPrefix(), decorator.getId(), e);
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+ }
+
if (manipulationStrategy != null) {
- final JWTClaimsSet jwtClaimsSet = claimsSet.getClaimsSet();
- assert jwtClaimsSet != null;
log.debug("{} Manipulation strategy has been set, applying it to the claims set {}", getLogPrefix(),
claimsSet.serialize());
- assert manipulationStrategy != null;
- final Map<String, Object> result = manipulationStrategy.apply(profileRequestContext,
- jwtClaimsSet.toJSONObject());
+ final Map<String, Object> result = manipulationStrategy.apply(profileRequestContext, claimsMap);
if (result == null) {
log.debug("{} Manipulation strategy returned null, leaving token claims set untouched.",
getLogPrefix());
@@ -447,7 +475,7 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthoriz
assert parsedClaimsSet != null;
claimsSet.setClaimsSet(parsedClaimsSet);
} catch (final ParseException e) {
- log.error("{} The resulted claims set could not be transformed into ", getLogPrefix(), e);
+ log.error("{} The resulted claims set after manipulation could not be parsed ", getLogPrefix(), e);
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
return;
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDToken.java
index 29dd5cd1..e71d4422 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDToken.java
@@ -15,17 +15,21 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.text.ParseException;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.opensaml.messaging.encoder.AbstractMessageEncoder;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jwt.JWTClaimsSet;
@@ -33,9 +37,12 @@ import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
import net.shibboleth.idp.plugin.oidc.op.encoding.impl.ResponseUtil;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.IdTokenClaimsSetDecorator;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.oidc.profile.config.navigate.IDTokenManipulationStrategyLookupFunction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -70,14 +77,21 @@ public class ManipulateClaimsForIDToken extends AbstractOIDCAuthenticationRespon
/** Object mapper used for pretty-printing JWT contents. */
@NonnullAfterInit private ObjectMapper objectMapper;
+ /** Autowired claims set decorators. */
+ @Nonnull private Collection<IdTokenClaimsSetDecorator> tokenClaimsSetDecorators;
+
/** The id_token to operate on. */
- private IDTokenClaimsSet idToken;
+ @NonnullBeforeExec private IDTokenClaimsSet idToken;
/**
* Constructor.
+ *
+ * @param freeDecorators free-standing decorators to add
*/
- public ManipulateClaimsForIDToken() {
+ @Autowired
+ public ManipulateClaimsForIDToken(@Nullable final Collection<IdTokenClaimsSetDecorator> freeDecorators) {
idTokenManipulationStrategyLookupStrategy = new IDTokenManipulationStrategyLookupFunction();
+ tokenClaimsSetDecorators = freeDecorators == null ? CollectionSupport.emptyList() : freeDecorators;
}
/**
@@ -85,6 +99,12 @@ public class ManipulateClaimsForIDToken extends AbstractOIDCAuthenticationRespon
*
* @param strategy What to set
*/
+
+ /**
+ * Set the lookup function to supply strategy bi-function for manipulating id_token claims set.
+ *
+ * @param strategy What to set
+ */
public void setIDTokenManipulationStrategyLookupStrategy(@Nonnull final
Function<ProfileRequestContext,BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>>
strategy) {
@@ -132,12 +152,6 @@ public class ManipulateClaimsForIDToken extends AbstractOIDCAuthenticationRespon
return false;
}
manipulationStrategy = idTokenManipulationStrategyLookupStrategy.apply(profileRequestContext);
- if (manipulationStrategy == null) {
- log.debug("{} No manipulation strategy resolved, nothing to do.", getLogPrefix());
- assert idToken != null;
- doProtocolLog(profileRequestContext, idToken);
- return false;
- }
return true;
}
@@ -145,10 +159,37 @@ public class ManipulateClaimsForIDToken extends AbstractOIDCAuthenticationRespon
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final Map<String, Object> result = manipulationStrategy.apply(profileRequestContext, idToken.toJSONObject());
+ final Map<String, Object> claimsMap = new HashMap<>(idToken.toJSONObject());
+
+ if (!tokenClaimsSetDecorators.isEmpty()) {
+ for (final IdTokenClaimsSetDecorator decorator : tokenClaimsSetDecorators) {
+ log.debug("{} Applying decorator {}", getLogPrefix(), decorator);
+ decorator.accept(claimsMap, profileRequestContext);
+ try {
+ final JWTClaimsSet parsedClaimsSet = JWTClaimsSet.parse(claimsMap);
+ assert parsedClaimsSet != null;
+ idToken = new IDTokenClaimsSet(JWTClaimsSet.parse(claimsMap));
+ } catch (final ParseException | com.nimbusds.oauth2.sdk.ParseException e) {
+ log.error("{} The resulted claims set after decorator {} could not be parsed",
+ getLogPrefix(), decorator.getId(), e);
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+ }
+
+ final OIDCAuthenticationResponseContext oidcResponseContext = getOidcResponseContext();
+ assert oidcResponseContext != null;
+ oidcResponseContext.setIDToken(idToken);
+ }
+ if (manipulationStrategy == null) {
+ log.debug("{} No manipulation strategy resolved, nothing to do.", getLogPrefix());
+ doProtocolLog(profileRequestContext, idToken);
+ return;
+ }
+
+ final Map<String, Object> result = manipulationStrategy.apply(profileRequestContext, claimsMap);
if (result == null) {
- log.debug("{} Manipulation strategy retruned null, leaving id_token claims untouched.", getLogPrefix());
- assert idToken != null;
+ log.debug("{} Manipulation strategy returned null, leaving id_token claims untouched.", getLogPrefix());
doProtocolLog(profileRequestContext, idToken);
return;
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContext.java
index 1e4729d6..796fff20 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContext.java
@@ -17,6 +17,8 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -29,10 +31,12 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.storage.RevocationCache;
import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import com.nimbusds.jwt.JWTClaimsSet;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.RefreshTokenClaimsSetDecorator;
import net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultTokenRevocationLifetimeLookupStrategy;
import net.shibboleth.idp.plugin.oidc.op.storage.RevocationCacheContexts;
import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
@@ -90,6 +94,9 @@ public class SetRefreshTokenToResponseContext extends AbstractOIDCResponseAction
/** The strategy used for manipulating the token claims set. */
@Nullable private BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> manipulationStrategy;
+ /** Autowired claims set decorators. */
+ @Nonnull private Collection<RefreshTokenClaimsSetDecorator> tokenClaimsSetDecorators;
+
/** Strategy used to locate the {@link IdentifierGenerationStrategy} to use. */
@Nonnull private Function<ProfileRequestContext,IdentifierGenerationStrategy> idGeneratorLookupStrategy;
@@ -128,8 +135,11 @@ public class SetRefreshTokenToResponseContext extends AbstractOIDCResponseAction
* Constructor.
*
* @param sealer sealer to encrypt/hmac refresh token.
+ * @param freeDecorators free-standing decorators to add
*/
- public SetRefreshTokenToResponseContext(@Nonnull @ParameterName(name = "sealer") final DataSealer sealer) {
+ @Autowired
+ public SetRefreshTokenToResponseContext(@Nonnull @ParameterName(name = "sealer") final DataSealer sealer,
+ @Nullable final Collection<RefreshTokenClaimsSetDecorator> freeDecorators) {
refreshTokenChainLifetimeLookupStrategy = new RefreshTokenChainLifetimeLookupFunction();
refreshTokenTimeoutLookupStrategy = new RefreshTokenTimeoutLookupFunction();
dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
@@ -141,6 +151,7 @@ public class SetRefreshTokenToResponseContext extends AbstractOIDCResponseAction
refreshTokenTypeLookupStrategy = new RefreshTokenTypeLookupFunction();
refreshTokenSerializationStrategies = CollectionSupport.emptyMap();
xmlSafeIdentifier = true;
+ tokenClaimsSetDecorators = freeDecorators == null ? CollectionSupport.emptyList() : freeDecorators;
}
/**
@@ -347,6 +358,24 @@ public class SetRefreshTokenToResponseContext extends AbstractOIDCResponseAction
builder.setRootTokenIdentifier(rootTokenId);
builder.setDpopProofJwkThumbprint(oidcResponseContext.getDpopProofJwkThumbprint());
final RefreshTokenClaimsSet claimsSet = builder.build();
+ final JWTClaimsSet jwtClaimsSet = claimsSet.getClaimsSet();
+ assert jwtClaimsSet != null;
+ final Map<String, Object> claimsMap = new HashMap<>(jwtClaimsSet.toJSONObject());
+
+ for (final RefreshTokenClaimsSetDecorator decorator : tokenClaimsSetDecorators) {
+ log.debug("{} Applying decorator {}", getLogPrefix(), decorator);
+ decorator.accept(claimsMap, profileRequestContext);
+ try {
+ final JWTClaimsSet parsedClaimsSet = JWTClaimsSet.parse(claimsMap);
+ assert parsedClaimsSet != null;
+ claimsSet.setClaimsSet(parsedClaimsSet);
+ } catch (final ParseException e) {
+ log.error("{} The resulted claims set after decorator {} could not be parsed",
+ getLogPrefix(), decorator.getId(), e);
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+ }
if (manipulationStrategy != null) {
log.debug("{} Manipulation strategy has been set, applying it to the claims set {}", getLogPrefix(),
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
index 277a57f7..4a2d0313 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
@@ -77,7 +77,7 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
respCtx.setScope(new Scope());
respCtx.getAudience().add("https://rp.example.org");
- action = new BuildAccessToken();
+ action = new BuildAccessToken(null);
action.setObjectMapper(new ObjectMapper());
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
index 2bc9f523..8cdeb2db 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
@@ -73,7 +73,7 @@ public class SetAuthorizationCodeToResponseContextTest extends BaseOIDCResponseA
respCtx.setAuthTime(Instant.now());
respCtx.setAcr("0");
respCtx.setRedirectURI(new URI("http://example.com"));
- action = new SetAuthorizationCodeToResponseContext();
+ action = new SetAuthorizationCodeToResponseContext(null);
final DataSealer dataSealer = getDataSealer();
assert dataSealer != null;
action.setDataSealer(dataSealer);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 72fdc256..45fcdd87 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -130,6 +130,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNotNull(getSidFromAuthorizeCodeClaimsSet(successResponse));
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -149,6 +150,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -169,6 +171,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -189,6 +192,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -209,6 +213,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -333,6 +338,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNotNull(successResponse.getAuthorizationCode());
Assert.assertNotNull(getSidFromAuthorizeCodeClaimsSet(successResponse));
Assert.assertNull(successResponse.getIssuer());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
}
@Test
@@ -734,6 +740,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(getSidFromIDToken(successResponse));
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
@@ -793,6 +800,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(getSidFromIDToken(successResponse));
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
@@ -834,6 +842,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken().getJWTClaimsSet().getClaim("c_hash"));
Assert.assertNull(successResponse.getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertNotNull(getSidFromIDToken(successResponse));
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
Assert.assertNotNull(successResponse.getIssuer());
@@ -859,6 +868,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(getSidFromIDToken(successResponse));
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
@@ -883,6 +893,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
Assert.assertNotNull(getSidFromIDToken(successResponse));
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNull(successResponse.getAuthorizationCode());
Assert.assertNull(successResponse.getIssuer());
@@ -910,6 +921,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNotNull(successResponse.getAccessToken());
+ assertDecoratedIDToken(successResponse);
final String sid2 = getSidFromOpaqueAccessTokenClaimsSet(successResponse);
Assert.assertEquals(sid1, sid2);
Assert.assertNull(successResponse.getAuthorizationCode());
@@ -920,6 +932,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final JWTClaimsSet claimsSet = token.getClaimsSet();
assert claimsSet != null;
Assert.assertNull(claimsSet.getStringClaim("eduPersonScopedAffiliation"));
+ Assert.assertEquals(claimsSet.getClaim(TestAccessTokenClaimsSetDecorator.KEY),
+ TestAccessTokenClaimsSetDecorator.VALUE);
Assert.assertNull(successResponse.getIssuer());
}
@@ -956,6 +970,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
+ assertDecoratedIDToken(successResponse);
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNotNull(successResponse.getAccessToken());
@@ -991,6 +1006,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
+ assertDecoratedIDToken(successResponse);
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNotNull(successResponse.getAccessToken());
@@ -1023,6 +1039,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
+ assertDecoratedIDToken(successResponse);
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNotNull(successResponse.getAccessToken());
@@ -1100,7 +1117,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
}
@Test
- public void testWithHybridIdTokenFlow() throws IOException, SessionException, ParseException {
+ public void testWithHybridIdTokenFlow() throws IOException, SessionException, ParseException, DataSealerException {
request.setMethod("GET");
setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
new Pair<>("response_type", "code id_token"),
@@ -1120,8 +1137,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
Assert.assertNull(successResponse.getIDToken().getJWTClaimsSet().getClaim("at_hash"));
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
+ assertDecoratedIDToken(successResponse);
Assert.assertNull(successResponse.getAccessToken());
Assert.assertNotNull(successResponse.getAuthorizationCode());
+ assertDecoratedAuthorizeCodeClaimsSet(successResponse);
final String sid2 = getSidFromAuthorizeCodeClaimsSet(successResponse);
Assert.assertEquals(sid1, sid2);
Assert.assertNull(successResponse.getIssuer());
@@ -1160,6 +1179,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
Assert.assertNotNull(successResponse.getIDToken());
+ assertDecoratedIDToken(successResponse);
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNull(successResponse.getAccessToken());
@@ -1217,6 +1237,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+ assertDecoratedIDToken(successResponse);
final String sid1 = getSidFromIDToken(successResponse);
Assert.assertNotNull(sid1);
Assert.assertNull(successResponse.getAccessToken());
@@ -1502,6 +1523,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final String sid3 = getSidFromIDToken(successResponse);
Assert.assertEquals(sid1, sid2);
Assert.assertEquals(sid2, sid3);
+ assertDecoratedIDToken(successResponse);
final IDTokenClaimsSet idToken = new IDTokenClaimsSet(successResponse.getIDToken().getJWTClaimsSet());
Assert.assertNotNull(idToken);
@@ -1546,6 +1568,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final String sid3 = getSidFromIDToken(successResponse);
Assert.assertEquals(sid1, sid2);
Assert.assertEquals(sid2, sid3);
+ assertDecoratedIDToken(successResponse);
final JWTClaimsSet token = SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
@@ -1580,6 +1603,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
final String sid3 = getSidFromIDToken(successResponse);
Assert.assertEquals(sid1, sid2);
Assert.assertEquals(sid2, sid3);
+ assertDecoratedIDToken(successResponse);
final JWTClaimsSet token = SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
Assert.assertEquals(token.getAudience(), List.of(resourceNonUri, issuer));
@@ -2728,6 +2752,18 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
}
}
+ protected void assertDecoratedAuthorizeCodeClaimsSet(final AuthorizationSuccessResponse successResponse) {
+ Assert.assertNotNull(successResponse.getAuthorizationCode());
+ final AuthorizeCodeClaimsSet claims;
+ try {
+ claims = AuthorizeCodeClaimsSet.parse(successResponse.getAuthorizationCode().getValue(), getDataSealer());
+ Assert.assertEquals(claims.getClaimsSet().getClaim(TestAuthorizationCodeClaimsSetDecorator.KEY),
+ TestAuthorizationCodeClaimsSetDecorator.VALUE);
+ } catch (ParseException | DataSealerException e) {
+ Assert.fail();
+ }
+ }
+
protected String getSidFromOpaqueAccessTokenClaimsSet(final AuthenticationSuccessResponse successResponse) {
Assert.assertNotNull(successResponse.getAccessToken());
final AccessTokenClaimsSet claims;
@@ -2761,7 +2797,18 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
return null;
}
}
-
+
+ protected void assertDecoratedIDToken(final AuthenticationSuccessResponse successResponse) {
+ final JWT idToken = successResponse.getIDToken();
+ Assert.assertNotNull(idToken);
+ try {
+ Assert.assertEquals(idToken.getJWTClaimsSet().getStringClaim(TestIdTokenClaimsSetDecorator.KEY),
+ TestIdTokenClaimsSetDecorator.VALUE);
+ } catch (ParseException e) {
+ Assert.fail();
+ }
+ }
+
protected void setRequestParameters(final List<Pair<String, String>> pairs) {
setRequestParameters(request, pairs);
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
index 94d7d189..f95b62af 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/PushedAuthorizeFlowTest.java
@@ -580,6 +580,7 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
Assert.assertNull(object.get("scope"));
Assert.assertNull(object.get("custom1"));
Assert.assertEquals(object.get("custom2"), "custom2Value");
+ Assert.assertEquals(object.get(TestRequestUriClaimsSetDecorator.KEY), TestRequestUriClaimsSetDecorator.VALUE);
}
@SuppressWarnings("null")
@@ -613,6 +614,7 @@ public class PushedAuthorizeFlowTest extends AbstractOidcClientAuthenticationFlo
Assert.assertEquals(object.get("scope"), "openid profile");
Assert.assertNull(object.get("custom1"));
Assert.assertEquals(object.get("custom2"), "custom2Value");
+ Assert.assertEquals(object.get(TestRequestUriClaimsSetDecorator.KEY), TestRequestUriClaimsSetDecorator.VALUE);
}
@Test
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAccessTokenClaimsSetDecorator.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAccessTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..95744af7
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAccessTokenClaimsSetDecorator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.profile.flow;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.AccessTokenClaimsSetDecorator;
+
+/**
+ * Test implementation for {@link AccessTokenClaimsSetDecorator}.
+ */
+public class TestAccessTokenClaimsSetDecorator implements AccessTokenClaimsSetDecorator {
+
+ public static final String KEY = "customAccessTokenKey";
+ public static final String VALUE = "customAccessTokenValue";
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final @Nullable Map<String, Object> claimsSet,
+ final @Nullable ProfileRequestContext profileRequestContext) {
+ if (claimsSet != null && !claimsSet.containsKey(KEY)) {
+ claimsSet.put(KEY, VALUE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getId() {
+ return "testAccessTokenClaimsSetDecorator";
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAuthorizationCodeClaimsSetDecorator.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAuthorizationCodeClaimsSetDecorator.java
new file mode 100644
index 00000000..a7ba2850
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestAuthorizationCodeClaimsSetDecorator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.profile.flow;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.AuthorizationCodeClaimsSetDecorator;
+
+/**
+ * Test implementation for {@link AuthorizationCodeClaimsSetDecorator}.
+ */
+public class TestAuthorizationCodeClaimsSetDecorator implements AuthorizationCodeClaimsSetDecorator {
+
+ public static final String KEY = "customAuthorizationCodeKey";
+ public static final String VALUE = "customAuthorizationCodeValue";
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final @Nullable Map<String, Object> claimsSet,
+ final @Nullable ProfileRequestContext profileRequestContext) {
+ if (claimsSet != null && !claimsSet.containsKey(KEY)) {
+ claimsSet.put(KEY, VALUE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getId() {
+ return "testAuthorizationCodeClaimsSetDecorator";
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestIdTokenClaimsSetDecorator.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestIdTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..095a549c
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestIdTokenClaimsSetDecorator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.profile.flow;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.IdTokenClaimsSetDecorator;
+
+/**
+ * Test implementation for {@link IdTokenClaimsSetDecorator}.
+ */
+public class TestIdTokenClaimsSetDecorator implements IdTokenClaimsSetDecorator {
+
+ public static final String KEY = "customIdTokenKey";
+ public static final String VALUE = "customIdTokenValue";
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final @Nullable Map<String, Object> claimsSet,
+ final @Nullable ProfileRequestContext profileRequestContext) {
+ if (claimsSet != null && !claimsSet.containsKey(KEY)) {
+ claimsSet.put(KEY, VALUE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getId() {
+ return "testIdTokenClaimsSetDecorator";
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRefreshTokenClaimsSetDecorator.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRefreshTokenClaimsSetDecorator.java
new file mode 100644
index 00000000..d0fe7f31
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRefreshTokenClaimsSetDecorator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.profile.flow;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.RefreshTokenClaimsSetDecorator;
+
+/**
+ * Test implementation for {@link RefreshTokenClaimsSetDecorator}.
+ */
+public class TestRefreshTokenClaimsSetDecorator implements RefreshTokenClaimsSetDecorator {
+
+ public static final String KEY = "customRefreshTokenKey";
+ public static final String VALUE = "customRefreshTokenValue";
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final @Nullable Map<String, Object> claimsSet,
+ final @Nullable ProfileRequestContext profileRequestContext) {
+ if (claimsSet != null && !claimsSet.containsKey(KEY)) {
+ claimsSet.put(KEY, VALUE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getId() {
+ return "testRefreshTokenClaimsSetDecorator";
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRequestUriClaimsSetDecorator.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRequestUriClaimsSetDecorator.java
new file mode 100644
index 00000000..c2824f1e
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TestRequestUriClaimsSetDecorator.java
@@ -0,0 +1,48 @@
+/*
+ * 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.profile.flow;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.RequestUriClaimsSetDecorator;
+
+/**
+ * Test implementation for {@link RequestUriClaimsSetDecorator}.
+ */
+public class TestRequestUriClaimsSetDecorator implements RequestUriClaimsSetDecorator {
+
+ public static final String KEY = "customRequestUriKey";
+ public static final String VALUE = "customRequestUriValue";
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(final @Nullable Map<String, Object> claimsSet,
+ final @Nullable ProfileRequestContext profileRequestContext) {
+ if (claimsSet != null && !claimsSet.containsKey(KEY)) {
+ claimsSet.put(KEY, VALUE);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getId() {
+ return "testRequestUriClaimsSetDecorator";
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index 1f970226..a3cbd3cd 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -209,6 +209,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
final AccessTokenResponse response = parseSuccessResponse(result, AccessTokenResponse.class);
Assert.assertNotNull(response.getTokens().getAccessToken());
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
}
@Test
@@ -270,7 +271,10 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromJWT(response.getOIDCTokens().getIDToken()));
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
}
}
@@ -291,11 +295,13 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
final SignedJWT jwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
final List<String> audience = jwt.getJWTClaimsSet().getAudience();
Assert.assertEquals(audience.size(), 2);
Assert.assertTrue(audience.contains(resourceUri));
Assert.assertTrue(audience.contains(issuer));
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
}
@Test
@@ -316,6 +322,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
final List<String> audience = getAudienceFromAccessToken(response.getTokens().getAccessToken());
Assert.assertEquals(audience.size(), 1);
Assert.assertTrue(audience.contains(issuer));
@@ -339,7 +346,9 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
final SignedJWT jwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
final List<String> audience = jwt.getJWTClaimsSet().getAudience();
Assert.assertEquals(audience.size(), 1);
@@ -363,7 +372,9 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
final SignedJWT jwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
final List<String> audience = jwt.getJWTClaimsSet().getAudience();
Assert.assertEquals(audience.size(), 2);
@@ -382,11 +393,13 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
// test that the other requested scopes (profile email offline_access) are stripped out
Assert.assertEquals(response.getTokens().getAccessToken().getScope().toString(), "openid");
+ assertDecoratedAccessToken(response.getTokens().getAccessToken());
Assert.assertNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromJWT(response.getOIDCTokens().getIDToken()));
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
}
@Test
@@ -424,7 +437,9 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromJWT(response.getOIDCTokens().getIDToken()));
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
}
}
@@ -440,6 +455,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
}
@Test
@@ -459,6 +475,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
final SignedJWT jwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
final List<String> audience = jwt.getJWTClaimsSet().getAudience();
Assert.assertEquals(audience.size(), 1);
@@ -482,6 +499,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNull(getSidFromAccessToken(response.getTokens().getAccessToken()));
Assert.assertNull(getSidFromRefreshToken(response.getTokens().getRefreshToken()));
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
final SignedJWT jwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
final List<String> audience = jwt.getJWTClaimsSet().getAudience();
Assert.assertEquals(audience.size(), 1);
@@ -528,7 +546,9 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertEquals(getSidFromAccessToken(response.getTokens().getAccessToken()), sid);
Assert.assertEquals(getSidFromRefreshToken(response.getTokens().getRefreshToken()), sid);
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
Assert.assertEquals(getSidFromJWT(response.getOIDCTokens().getIDToken()), sid);
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
}
@Test
@@ -543,9 +563,11 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
Assert.assertEquals(getSidFromAccessToken(response.getTokens().getAccessToken()), sid);
Assert.assertEquals(getSidFromRefreshTokenJWT(response.getTokens().getRefreshToken()), sid);
+ assertDecoratedRefreshToken(response.getTokens().getRefreshToken());
}
@Test
@@ -567,6 +589,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
}
@@ -602,6 +625,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNotNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
final IDTokenClaimsSet idToken = new IDTokenClaimsSet(response.getOIDCTokens().getIDToken().getJWTClaimsSet());
@@ -662,6 +686,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
}
@Test
@@ -676,6 +701,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
Assert.assertNotNull(response.getTokens().getAccessToken());
Assert.assertNull(response.getTokens().getRefreshToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+ assertDecoratedIdToken(response.getOIDCTokens().getIDToken());
Assert.assertNotNull(response.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim("at_hash"));
}
@@ -2202,6 +2228,24 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
}
}
+ protected void assertDecoratedAccessToken(final AccessToken accessToken) {
+ Assert.assertNotNull(accessToken.getValue());
+ final AccessTokenClaimsSet claims;
+ try {
+ claims = AccessTokenClaimsSet.parse(accessToken.getValue(), getDataSealer());
+ Assert.assertEquals(claims.getClaimsSet().getClaim(TestAccessTokenClaimsSetDecorator.KEY),
+ TestAccessTokenClaimsSetDecorator.VALUE);
+ } catch (final ParseException | DataSealerException e) {
+ try {
+ final SignedJWT jwt = SignedJWT.parse(accessToken.getValue());
+ Assert.assertEquals(jwt.getJWTClaimsSet().getClaim(TestAccessTokenClaimsSetDecorator.KEY),
+ TestAccessTokenClaimsSetDecorator.VALUE);
+ } catch (final ParseException e1) {
+ Assert.fail();
+ }
+ }
+ }
+
protected String getSidFromRefreshToken(final RefreshToken refreshToken) {
Assert.assertNotNull(refreshToken.getValue());
final RefreshTokenClaimsSet claims;
@@ -2213,6 +2257,26 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
}
}
+ protected void assertDecoratedRefreshToken(final RefreshToken refreshToken) {
+ Assert.assertNotNull(refreshToken.getValue());
+ final RefreshTokenClaimsSet claims;
+ try {
+ claims = RefreshTokenClaimsSet.parse(refreshToken.getValue(), getDataSealer());
+ Assert.assertEquals(claims.getClaimsSet().getClaim(TestRefreshTokenClaimsSetDecorator.KEY),
+ TestRefreshTokenClaimsSetDecorator.VALUE);
+ } catch (ParseException | DataSealerException e) {
+ try {
+ final SignedJWT signedJwt = SignedJWT.parse(refreshToken.getValue());
+ final RefreshTokenClaimsSet jwtClaims = RefreshTokenClaimsSet.parse(signedJwt.getJWTClaimsSet()
+ .getStringClaim(TokenClaimsSet.KEY_SEALED_FOR_OP), getDataSealer());
+ Assert.assertEquals(jwtClaims.getClaimsSet().getClaim(TestRefreshTokenClaimsSetDecorator.KEY),
+ TestRefreshTokenClaimsSetDecorator.VALUE);
+ } catch (ParseException | DataSealerException e1) {
+ Assert.fail();
+ }
+ }
+ }
+
protected String getSidFromJWT(final JWT jwt) {
Assert.assertNotNull(jwt);
try {
@@ -2222,6 +2286,16 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
}
}
+ protected void assertDecoratedIdToken(final JWT jwt) {
+ Assert.assertNotNull(jwt);
+ try {
+ Assert.assertEquals(jwt.getJWTClaimsSet().getStringClaim(TestIdTokenClaimsSetDecorator.KEY),
+ TestIdTokenClaimsSetDecorator.VALUE);
+ } catch (ParseException e) {
+ Assert.fail();
+ }
+ }
+
protected String getSidFromRefreshTokenJWT(final RefreshToken refreshToken) {
assertRefreshTokenJWT(refreshToken);
final RefreshTokenClaimsSet claims;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
index 0b825ffc..660581f0 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ManipulateClaimsForIDTokenTest.java
@@ -48,7 +48,7 @@ public class ManipulateClaimsForIDTokenTest extends BaseOIDCResponseActionTest {
private void init(final BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> strategy)
throws ComponentInitializationException {
- action = new ManipulateClaimsForIDToken();
+ action = new ManipulateClaimsForIDToken(null);
action.setObjectMapper(new ObjectMapper());
action.initialize();
final DefaultOIDCAuthorizationConfiguration config = new DefaultOIDCAuthorizationConfiguration();
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContextTest.java
index df52fb4e..fd426da4 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRefreshTokenToResponseContextTest.java
@@ -109,7 +109,7 @@ public class SetRefreshTokenToResponseContextTest extends BaseOIDCResponseAction
protected SetRefreshTokenToResponseContext initAction(final Function<ProfileRequestContext,
BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>> manipulationStrategy)
throws ComponentInitializationException, NoSuchAlgorithmException {
- final SetRefreshTokenToResponseContext action = new SetRefreshTokenToResponseContext(getDataSealer());
+ final SetRefreshTokenToResponseContext action = new SetRefreshTokenToResponseContext(getDataSealer(), null);
action.setRevocationCache(revocationCache);
if (manipulationStrategy != null) {
action.setTokenClaimsSetManipulationStrategyLookupStrategy(manipulationStrategy);
diff --git a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
index 0cdc7aeb..4b7b0f9e 100644
--- a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
+++ b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
@@ -167,4 +167,9 @@
</property>
</bean>
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.TestIdTokenClaimsSetDecorator" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.TestRequestUriClaimsSetDecorator" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.TestAccessTokenClaimsSetDecorator" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.TestRefreshTokenClaimsSetDecorator" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.TestAuthorizationCodeClaimsSetDecorator" />
</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list