[java-idp-oidc] branch main updated: JOIDC-11 - Support for client_credentials grant
Scott Cantor
cantor.2 at osu.edu
Wed Feb 9 16:45:33 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor 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=4568cc735ef46fba6eb4eb2cfc22fbf497889eac
The following commit(s) were added to refs/heads/main by this push:
new 4568cc73 JOIDC-11 - Support for client_credentials grant
4568cc73 is described below
commit 4568cc735ef46fba6eb4eb2cfc22fbf497889eac
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Feb 9 11:45:30 2022 -0500
JOIDC-11 - Support for client_credentials grant
https://shibboleth.atlassian.net/browse/JOIDC-11
Redesign that begins to handle unregistered clients/resource servers.
Encryption support done but untested.
Reworked optional encryption into a proper config option.
Moved scope/audience validation more fully to metadata for now.
---
.../navigate/AudienceClientIDLookupFunction.java | 55 +++++
.../context/OAuthAttributeResolutionContext.java | 67 ------
.../navigate/ClientInfoAudienceLookupFunction.java | 66 ++++++
.../DefaultOIDCMetadataContextLookupFunction.java | 30 ++-
...ava => TokenRequestAudienceLookupFunction.java} | 33 ++-
.../oidc/op/profile/context/package-info.java | 21 --
.../impl/PopulateOIDCMetadataContext.java | 57 ++---
.../impl/SetEntityIdToSAMLPeerEntityContext.java | 28 +--
.../profile/impl/AbstractEncryptTokenAction.java} | 69 +++---
.../op/oauth2/profile/impl/BuildAccessToken.java | 173 +--------------
...esponseContext.java => EncryptAccessToken.java} | 69 +++---
.../profile/impl/EnforceAudienceConfiguration.java | 171 +++++++++++++++
.../OAuthAttributeResolutionContextDecorator.java | 70 ------
.../impl/SetAccessTokenToResponseContext.java | 12 +-
.../op/oauth2/profile/impl/ValidateAudience.java | 182 +++++++++++++++
.../AbstractOIDCAuthenticationResponseAction.java | 2 +-
.../op/profile/impl/EncryptProcessedToken.java | 129 ++---------
.../impl/InitializeRelyingPartyContext.java | 35 ++-
.../op/profile/impl/OIDCMetadataLookupHandler.java | 11 +-
.../impl/PopulateOIDCEncryptionParameters.java | 107 ++++++---
.../PopulateOIDCSignatureSigningParameters.java | 3 +-
.../plugin/oidc/op/profile/impl/ValidateScope.java | 22 +-
...entInformationEncryptionParametersResolver.java | 22 +-
.../META-INF/net.shibboleth.idp/postconfig.xml | 13 +-
.../flows/oidc/abstract/oidc-abstract-beans.xml | 1 -
.../idp/flows/oidc/authorize/authorize-beans.xml | 2 +-
.../oidc/metadata-lookup/metadata-lookup-beans.xml | 79 ++++---
.../idp/flows/oidc/token/token-beans.xml | 242 +++++++++++++++++---
.../shibboleth/idp/flows/oidc/token/token-flow.xml | 103 +++++++--
.../idp/flows/oidc/userinfo/userinfo-beans.xml | 4 +-
.../idp/service/relying-party/postconfig.xml | 30 ++-
.../SetEntityIdToSAMLPeerEntityContextTest.java | 69 +++---
.../oauth2/profile/impl/BuildAccessTokenTest.java | 243 +--------------------
.../oauth2/profile/impl/ValidateAudienceTest.java | 219 +++++++++++++++++++
.../oidc/op/profile/flow/AbstractOidcFlowTest.java | 1 +
.../flow/ClientCredentialsTokenFlowTest.java | 2 -
.../profile/impl/BaseOIDCResponseActionTest.java | 3 +-
.../op/profile/impl/EncryptProcessedTokenTest.java | 7 +-
.../impl/PopulateOIDCEncryptionParametersTest.java | 22 +-
.../oidc/op/profile/impl/ValidateScopeTest.java | 8 +-
.../src/test/resources/conf/attribute-resolver.xml | 25 ---
.../src/test/resources/conf/oidc.properties | 3 -
.../src/test/resources/conf/relying-party.xml | 4 +-
43 files changed, 1464 insertions(+), 1050 deletions(-)
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/AudienceClientIDLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/AudienceClientIDLookupFunction.java
new file mode 100644
index 00000000..394e4c31
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/navigate/AudienceClientIDLookupFunction.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.messaging.context.navigate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+
+/**
+ * Returns a {@link ClientID} from the first value found in {@link OIDCAuthenticationResponseContext#getAudience()}.
+ *
+ * @since 3.1.0
+ */
+public class AudienceClientIDLookupFunction implements ContextDataLookupFunction<MessageContext,ClientID> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(AudienceClientIDLookupFunction.class);
+
+ /** {@inheritDoc} */
+ @Nullable public ClientID apply(@Nullable final MessageContext input) {
+ if (input != null) {
+ final OIDCAuthenticationResponseContext respCtx =
+ input.getSubcontext(OIDCAuthenticationResponseContext.class);
+ if (respCtx != null && !respCtx.getAudience().isEmpty()) {
+ return new ClientID(respCtx.getAudience().get(0));
+ }
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/OAuthAttributeResolutionContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/OAuthAttributeResolutionContext.java
deleted file mode 100644
index 0bcf6d2f..00000000
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/OAuthAttributeResolutionContext.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.context;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.BaseContext;
-
-import net.shibboleth.utilities.java.support.annotation.constraint.Live;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-
-/**
- * A supplemental context adding OAuth- and OIDC-related input to the attribute resolution process.
- *
- * @since 3.1.0
- */
-public final class OAuthAttributeResolutionContext extends BaseContext {
-
- /** Requested and validated scopes. */
- @Nonnull @NonnullElements private final Collection<String> scopes;
-
- /** Requested resources. */
- @Nonnull @NonnullElements private final Collection<String> resources;
-
- /** Constructor. */
- public OAuthAttributeResolutionContext() {
- scopes = new ArrayList<>();
- resources = new ArrayList<>();
- }
-
- /**
- * Get the requested and metadata-validated scope collection for this request.
- *
- * @return modifiable collection of scope values
- */
- @Nonnull @NonnullElements @Live public Collection<String> getScope() {
- return scopes;
- }
-
- /**
- * Get the requested audience collection for this request.
- *
- * @return modifiable collection of audience values
- */
- @Nonnull @NonnullElements @Live public Collection<String> getResources() {
- return resources;
- }
-
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java
new file mode 100644
index 00000000..0e24f991
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/ClientInfoAudienceLookupFunction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.context.navigate;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+
+/** A function that returns {@link OIDCMetadataContext}. */
+public class ClientInfoAudienceLookupFunction implements ContextDataLookupFunction<OIDCMetadataContext,List<String>> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(ClientInfoAudienceLookupFunction.class);
+
+ /** {@inheritDoc} */
+ @Nullable @NonnullElements
+ public List<String> apply(@Nullable final OIDCMetadataContext input) {
+ if (input == null || input.getClientInformation() == null) {
+ return null;
+ }
+
+ final Object obj = input.getClientInformation().getMetadata().getCustomField("audience");
+ if (obj instanceof Collection) {
+ final List<String> audience = new ArrayList<>();
+
+ for (final Object val: (Collection) obj) {
+ if (val instanceof String) {
+ audience.add((String) val);
+ } else if (val instanceof URI) {
+ audience.add(val.toString());
+ }
+ }
+
+ return audience;
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java
index bf16f50c..cc4ac730 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
import javax.annotation.Nullable;
+import org.opensaml.messaging.context.MessageContext;
import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
import org.opensaml.profile.context.ProfileRequestContext;
@@ -28,13 +29,38 @@ import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
public class DefaultOIDCMetadataContextLookupFunction implements
ContextDataLookupFunction<ProfileRequestContext, OIDCMetadataContext> {
+ /** Use the inbound message context. */
+ private boolean inbound;
+
+ /** Constructor. */
+ public DefaultOIDCMetadataContextLookupFunction() {
+ inbound = true;
+ }
+
+ /**
+ * Sets whether to pull the subcontext from the inbound message context.
+ *
+ * <p>Default is true. False means to use the outbound context.</p>
+ *
+ * @param flag
+ */
+ public void setInbound(final boolean flag) {
+ inbound = flag;
+ }
+
/** {@inheritDoc} */
@Nullable
public OIDCMetadataContext apply(@Nullable final ProfileRequestContext input) {
- if (input == null || input.getInboundMessageContext() == null) {
+ if (input == null) {
+ return null;
+ }
+
+ final MessageContext mc = inbound ? input.getInboundMessageContext() : input.getOutboundMessageContext();
+ if (mc == null) {
return null;
}
- return input.getInboundMessageContext().getSubcontext(OIDCMetadataContext.class, false);
+
+ return mc.getSubcontext(OIDCMetadataContext.class);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestAudienceLookupFunction.java
similarity index 53%
copy from idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java
copy to idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestAudienceLookupFunction.java
index bf16f50c..fb4add50 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultOIDCMetadataContextLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestAudienceLookupFunction.java
@@ -17,24 +17,35 @@
package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
+import java.net.URI;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
-import org.opensaml.profile.context.ProfileRequestContext;
+import com.nimbusds.oauth2.sdk.TokenRequest;
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-/** A function that returns {@link OIDCMetadataContext}. */
-public class DefaultOIDCMetadataContextLookupFunction implements
- ContextDataLookupFunction<ProfileRequestContext, OIDCMetadataContext> {
+/**
+ * A function that returns a copy of requested resource list from a {@link TokenRequest}.
+ */
+public class TokenRequestAudienceLookupFunction extends AbstractTokenRequestLookupFunction<List<String>> {
/** {@inheritDoc} */
- @Nullable
- public OIDCMetadataContext apply(@Nullable final ProfileRequestContext input) {
- if (input == null || input.getInboundMessageContext() == null) {
- return null;
+ @Override
+ @Nullable @NonnullElements List<String> doLookup(@Nonnull final TokenRequest req) {
+
+ if (req != null) {
+ final List<URI> uris = req.getResources();
+ if (uris != null) {
+ return uris.stream()
+ .map(URI::toString)
+ .collect(Collectors.toUnmodifiableList());
+ }
}
- return input.getInboundMessageContext().getSubcontext(OIDCMetadataContext.class, false);
+ return null;
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/package-info.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/package-info.java
deleted file mode 100644
index 7fa5371a..00000000
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.
- */
-
-/**
- * Contexts related to OP profile actions.
- */
-package net.shibboleth.idp.plugin.oidc.op.profile.context;
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCMetadataContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/PopulateOIDCMetadataContext.java
similarity index 65%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCMetadataContext.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/PopulateOIDCMetadataContext.java
index 3bea04f6..bd3c7878 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCMetadataContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/PopulateOIDCMetadataContext.java
@@ -15,19 +15,18 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.saml2.metadata.RoleDescriptor;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
import org.slf4j.Logger;
@@ -36,34 +35,29 @@ import org.slf4j.LoggerFactory;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.idp.saml.profile.context.navigate.SAMLMetadataContextLookupFunction;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * An action that attempts to locate OIDC client information from the SAML entity descriptor containing role
+ * A message handler that attempts to locate OIDC client information from the SAML entity descriptor containing role
* descriptor of type {@link SPSSODescriptor}. If it contains {@link OIDCClientInformation} in the
- * object metadata, it is attached inside {@link OIDCMetadataContext} as a child of a pre-existing instance of
- * inbound {@link MessageContext}.
+ * object metadata, it is attached inside {@link OIDCMetadataContext} as a child of the input {@link MessageContext}.
*/
-public class PopulateOIDCMetadataContext extends AbstractProfileAction {
+public class PopulateOIDCMetadataContext extends AbstractMessageHandler {
/** Class logger. */
@Nonnull
private final Logger log = LoggerFactory.getLogger(PopulateOIDCMetadataContext.class);
/** Set the strategy used to look up the {@link SAMLMetadataContext} to draw from. */
- @Nonnull private Function<ProfileRequestContext, SAMLMetadataContext> samlMetadataContextLookupStrategy;
-
- /** SAML metadata context to populate from. */
- @Nullable private SAMLMetadataContext samlMetadataCtx;
+ @Nonnull private Function<MessageContext,SAMLMetadataContext> samlMetadataContextLookupStrategy;
/**
* Constructor.
*/
public PopulateOIDCMetadataContext() {
- samlMetadataContextLookupStrategy = new SAMLMetadataContextLookupFunction();
+ samlMetadataContextLookupStrategy = new ChildContextLookup<>(SAMLMetadataContext.class).compose(
+ new ChildContextLookup<>(SAMLPeerEntityContext.class));
}
/**
@@ -71,31 +65,24 @@ public class PopulateOIDCMetadataContext extends AbstractProfileAction {
*
* @param strategy strategy used to look up the {@link SAMLMetadataContext}
*/
- public void setSamlMetadataContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,SAMLMetadataContext> strategy) {
+ public void setSAMLMetadataContextLookupStrategy(
+ @Nonnull final Function<MessageContext,SAMLMetadataContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
samlMetadataContextLookupStrategy =
Constraint.isNotNull(strategy, "SAMLMetadataContext lookup strategy cannot be null");
}
-
+
/** {@inheritDoc} */
@Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ protected void doInvoke(@Nonnull final MessageContext messageContext) {
- samlMetadataCtx = samlMetadataContextLookupStrategy.apply(profileRequestContext);
+ final SAMLMetadataContext samlMetadataCtx = samlMetadataContextLookupStrategy.apply(messageContext);
if (samlMetadataCtx == null) {
- log.debug("{} Unable to locate SAMLMetadataContext", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
+ log.error("Unable to locate SAMLMetadataContext");
+ return;
}
-
- return super.doPreExecute(profileRequestContext);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
final List<RoleDescriptor> roleDescriptors
= samlMetadataCtx.getEntityDescriptor().getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
for (final RoleDescriptor roleDescriptor : roleDescriptors) {
@@ -105,12 +92,14 @@ public class PopulateOIDCMetadataContext extends AbstractProfileAction {
if (clientInformations != null && clientInformations.size() > 0) {
final OIDCMetadataContext oidcCtx = new OIDCMetadataContext();
oidcCtx.setClientInformation(clientInformations.get(0));
- profileRequestContext.getInboundMessageContext().addSubcontext(oidcCtx);
- log.debug("{} Client information found and attached.", getLogPrefix());
+ messageContext.addSubcontext(oidcCtx);
+ log.debug("{} Client information found and attached", getLogPrefix());
return;
}
}
}
+
log.debug("{} No client information found to be attached into OIDC metadata context.", getLogPrefix());
}
-}
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContext.java
similarity index 80%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContext.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContext.java
index 1faef605..055be821 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContext.java
@@ -15,16 +15,16 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl;
import java.util.function.Function;
import javax.annotation.Nonnull;
import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
import org.opensaml.saml.common.messaging.context.AbstractSAMLEntityContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.slf4j.Logger;
@@ -33,15 +33,14 @@ import org.slf4j.LoggerFactory;
import com.nimbusds.oauth2.sdk.id.ClientID;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultClientIDLookupFunction;
-import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * Action that sets the entityID to the given {@link SAMLPeerEntityContext} class, found as the subcontext of the
- * inbound message context. The value to be set is resolved via the given lookup strategy for client ID.
+ * {@link MessageHandler} that sets the entityID to the given {@link SAMLPeerEntityContext} class.
+ * The value to be set is resolved via the given lookup strategy for client ID.
*/
-public class SetEntityIdToSAMLPeerEntityContext extends AbstractProfileAction {
+public class SetEntityIdToSAMLPeerEntityContext extends AbstractMessageHandler {
/** Class logger. */
@Nonnull
@@ -87,13 +86,13 @@ public class SetEntityIdToSAMLPeerEntityContext extends AbstractProfileAction {
/** {@inheritDoc} */
@Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final MessageContext messageContext = profileRequestContext.getInboundMessageContext();
- final AbstractSAMLEntityContext entityCtx = messageContext.getSubcontext(entityContextClass);
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ final AbstractSAMLEntityContext entityCtx = messageContext.getSubcontext(entityContextClass);
if (entityCtx == null) {
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return;
+ throw new MessageHandlerException("Unable to locate subcontext of type " + entityContextClass);
}
+
final ClientID clientID = clientIDLookupStrategy.apply(messageContext);
if (clientID != null) {
log.debug("{} Set clientID '{}' to the peer entity context", getLogPrefix(), clientID.getValue());
@@ -102,4 +101,5 @@ public class SetEntityIdToSAMLPeerEntityContext extends AbstractProfileAction {
log.debug("{} No clientID could be resolved, nothing to do", getLogPrefix());
}
}
-}
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractEncryptTokenAction.java
similarity index 74%
copy from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java
copy to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractEncryptTokenAction.java
index 83cd30a3..1b077f13 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractEncryptTokenAction.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
@@ -45,40 +45,35 @@ import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.crypto.ECDHEncrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jwt.EncryptedJWT;
-import com.nimbusds.jwt.SignedJWT;
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * Action that serves both id token and user info response encryption. Existence of encryption parameters is taken as
- * indication whether the encryption should take place. Action assumes the content to be encrypted is located primarily
- * by {@link OIDCAuthenticationResponseContext#getProcessedToken()} returning either signed id token or signed user info
- * response. If such information is not available action assumes the data to be encrypted is
- * {@link OIDCAuthenticationResponseContext#getUserInfo()} containing bare user info response. If neither of the sources
- * for encryption exists the actions fails.
+ * Action that encrypts a source object into an {@link EncryptedJWT}.
+ *
+ * <p> The existence of encryption parameters is used to decide whether the encryption should take place.
+ *
+ * @since 3.1.0
*/
-public class EncryptProcessedToken extends AbstractOIDCResponseAction {
+public abstract class AbstractEncryptTokenAction extends AbstractOIDCResponseAction {
/** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(EncryptProcessedToken.class);
+ @Nonnull private Logger log = LoggerFactory.getLogger(AbstractEncryptTokenAction.class);
/** Strategy used to look up the {@link EncryptionContext} to store parameters in. */
- @Nonnull private Function<ProfileRequestContext, EncryptionContext> encryptionContextLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,EncryptionContext> encryptionContextLookupStrategy;
/** Encryption parameters for encrypting payload. */
@Nullable private EncryptionParameters params;
- /** Payload to encrypt. */
- @Nullable private Payload payload;
-
/**
* Constructor.
*/
- public EncryptProcessedToken() {
+ public AbstractEncryptTokenAction() {
encryptionContextLookupStrategy = new ChildContextLookup<>(EncryptionContext.class).compose(
new ChildContextLookup<>(RelyingPartyContext.class));
}
@@ -89,7 +84,7 @@ public class EncryptProcessedToken extends AbstractOIDCResponseAction {
* @param strategy lookup strategy
*/
public void setEncryptionContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, EncryptionContext> strategy) {
+ @Nonnull final Function<ProfileRequestContext,EncryptionContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
encryptionContextLookupStrategy =
@@ -102,7 +97,7 @@ public class EncryptProcessedToken extends AbstractOIDCResponseAction {
if (!super.doPreExecute(profileRequestContext)) {
return false;
}
-
+
final EncryptionContext encryptCtx = encryptionContextLookupStrategy.apply(profileRequestContext);
if (encryptCtx == null) {
log.error("{} No EncryptionContext returned by lookup strategy", getLogPrefix());
@@ -114,16 +109,6 @@ public class EncryptProcessedToken extends AbstractOIDCResponseAction {
log.debug("{} No Encryption parameters, nothing to do", getLogPrefix());
return false;
}
- if (getOidcResponseContext().getProcessedToken() != null) {
- payload = new Payload((SignedJWT) getOidcResponseContext().getProcessedToken());
- } else if (getOidcResponseContext().getUserInfo() != null) {
- payload = new Payload(getOidcResponseContext().getUserInfo().toJSONObject());
- }
- if (payload == null) {
- log.error("{} Instructed to encrypt but no plain text source available", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
- return false;
- }
return true;
}
@@ -132,6 +117,12 @@ public class EncryptProcessedToken extends AbstractOIDCResponseAction {
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final Payload payload = getPayload(profileRequestContext);
+ if (payload == null) {
+ log.debug("{} No plain text source provided to encrypt", getLogPrefix());
+ return;
+ }
+
final JWEAlgorithm encAlg = JWEAlgorithm.parse(params.getKeyTransportEncryptionAlgorithm());
final Credential credential = params.getKeyTransportEncryptionCredential();
final EncryptionMethod encEnc = EncryptionMethod.parse(params.getDataEncryptionAlgorithm());
@@ -153,11 +144,29 @@ public class EncryptProcessedToken extends AbstractOIDCResponseAction {
log.error("{} Unsupported algorithm {}", getLogPrefix(), encAlg.getName());
ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
}
- getOidcResponseContext().setProcessedToken(EncryptedJWT.parse(jweObject.serialize()));
+ setProcessedToken(profileRequestContext, EncryptedJWT.parse(jweObject.serialize()));
} catch (final JOSEException | ParseException e) {
- log.error("{} Encryption failed {}", getLogPrefix(), e.getMessage());
+ log.error("{} Encryption failed {}", getLogPrefix(), e);
ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
}
}
+ /**
+ * Get the payload to encrypt.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return payload to encrypt
+ */
+ @Nonnull protected abstract Payload getPayload(@Nonnull final ProfileRequestContext profileRequestContext);
+
+ /**
+ * Store the resulting token.
+ *
+ * @param profileRequestContext profile request context
+ * @param token encrypted token
+ */
+ protected abstract void setProcessedToken(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final EncryptedJWT token);
+
}
\ No newline at end of file
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 27b4c694..ba01d36c 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,12 +17,9 @@
package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
-import java.net.URI;
import java.time.Duration;
import java.time.Instant;
-import java.util.Collection;
import java.util.function.Function;
-import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -36,11 +33,8 @@ import org.slf4j.LoggerFactory;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.oauth2.sdk.TokenRequest;
import com.nimbusds.oauth2.sdk.id.ClientID;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.StringAttributeValue;
import net.shibboleth.idp.attribute.context.AttributeContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
@@ -49,7 +43,6 @@ import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction
import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet.Builder;
import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
import net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunction;
import net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction;
@@ -57,12 +50,9 @@ import net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.logic.FunctionSupport;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
import net.shibboleth.utilities.java.support.security.DataSealer;
import net.shibboleth.utilities.java.support.security.DataSealerException;
import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
@@ -113,18 +103,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
/** Strategy used to create the subcontext to hold the token. */
@Nonnull private Function<ProfileRequestContext,AccessTokenContext> accessTokenContextCreationStrategy;
-
- /** Strategy used to locate the attribute source for scope/audience. */
- @Nonnull private Function<ProfileRequestContext,AttributeContext> attributeContextLookupStrategy;
-
- /** Source scope/audience from unfiltered attributes. */
- private boolean useUnfilteredAttributes;
-
- /** ID of attribute to populate scope from. */
- @Nullable @NotEmpty private String scopeAttribute;
-
- /** ID of attribute to populate audience from. */
- @Nullable @NotEmpty private String audienceAttribute;
/** Use a JWT for the token. */
private boolean jwtTokenType;
@@ -138,9 +116,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
/** The generator to use. */
@Nullable private IdentifierGenerationStrategy idGenerator;
- /** Token request. */
- @Nullable private TokenRequest tokenRequest;
-
/** Constructor. */
public BuildAccessToken() {
accessTokenTypeLookupStrategy = new AccessTokenTypeLookupFunction();
@@ -156,14 +131,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
accessTokenContextCreationStrategy = new ChildContextLookup<>(AccessTokenContext.class, true).compose(
new ChildContextLookup<>(OIDCAuthenticationResponseContext.class).compose(
new OutboundMessageContextLookup()));
-
- // PRC -> RPC -> AC
- attributeContextLookupStrategy = new ChildContextLookup<>(AttributeContext.class).compose(
- new ChildContextLookup<>(RelyingPartyContext.class));
-
- useUnfilteredAttributes = true;
- scopeAttribute = "scope";
- audienceAttribute = "audience";
}
/**
@@ -226,20 +193,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
accessTokenContextCreationStrategy =
Constraint.isNotNull(strategy, "AccessTokenContext creation strategy cannot be null");
}
-
- /**
- * Set the strategy used to locate the {@link AttributeContext} associated with a given
- * {@link ProfileRequestContext}.
- *
- * @param strategy lookup strategy
- */
- public void setAttributeContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,AttributeContext> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- attributeContextLookupStrategy =
- Constraint.isNotNull(strategy, "AttributeContext lookup strategy cannot be null");
- }
/**
* Set the strategy used to locate the issuer value to use.
@@ -262,48 +215,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
clientIDLookupStrategy = Constraint.isNotNull(strategy, "ClientID lookup strategy cannot be null");
}
-
- /**
- * Set whether to source the scope and audience claims from unfiltered attributes.
- *
- * <p>Default is true.</p>
- *
- * @param flag flag to set
- */
- public void setUseUnfilteredAttributes(final boolean flag) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- useUnfilteredAttributes = flag;
- }
-
- /**
- * Set the ID of an {@link IdPAttribute} to source the scope claim.
- *
- * <p>If unset or no values can be extracted, the validated scopes from the request are used.</p>
- *
- * @param id attribute ID
- */
- public void setScopeAttribute(@Nullable @NotEmpty final String id) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- scopeAttribute = StringSupport.trimOrNull(id);
- }
- /**
- * Set the ID of an {@link IdPAttribute} to source the audience claim.
- *
- * <p>If set, an attribute must be available to pull the values from. If unset, the requested resource
- * values are used.</p>
- *
- * @param id attribute ID
- */
- public void setAudienceAttribute(@Nullable @NotEmpty final String id) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- audienceAttribute = StringSupport.trimOrNull(id);
- }
-
-// Checkstyle: CyclomaticComplexity OFF
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -311,14 +223,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
return false;
}
- if (profileRequestContext.getInboundMessageContext().getMessage() instanceof TokenRequest) {
- tokenRequest = (TokenRequest) profileRequestContext.getInboundMessageContext().getMessage();
- } else {
- log.error("{} No inbound TokenRequest message", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
final String tokenType = accessTokenTypeLookupStrategy.apply(profileRequestContext);
jwtTokenType = tokenType != null && "JWT".equals(tokenType);
@@ -342,13 +246,6 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
return false;
}
- attributeCtx = attributeContextLookupStrategy.apply(profileRequestContext);
- if (attributeCtx == null && (scopeAttribute != null || audienceAttribute != null)) {
- log.error("{} Unable to locate AttributeContext", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_ATTRIBUTE_CTX);
- return false;
- }
-
final Duration lifetime = accessTokenLifetimeLookupStrategy.apply(profileRequestContext);
if (lifetime == null) {
log.error("{} No lifetime supplied for access token", getLogPrefix());
@@ -356,10 +253,8 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
return false;
}
accessTokenCtx.setLifetime(lifetime);
-
return true;
}
-// Checkstyle: CyclomaticComplexity ON
/** {@inheritDoc} */
@Override
@@ -375,16 +270,9 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
final OIDCAuthenticationResponseContext responseCtx = getOidcResponseContext();
- final Collection<String> audience = getAudience();
- if (audience == null || audience.isEmpty()) {
- log.warn("{} Unable to determine audience value, failing request", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
- return;
- }
- responseCtx.getAudience().addAll(audience);
- log.debug("{} Building access token with audience: {}", getLogPrefix(), audience);
-
- final Scope scope = getScope(responseCtx.getScope());
+ final Scope scope = responseCtx.getScope() != null ? responseCtx.getScope() : new Scope();
+
+ log.debug("{} Building access token with audience: {}", getLogPrefix(), responseCtx.getAudience());
log.debug("{} Building access token with scope: {}", getLogPrefix(), scope);
final Instant now = Instant.now();
@@ -400,7 +288,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
.setACR(responseCtx.getAcr())
.setAuthenticationTime(responseCtx.getAuthTime())
.setScope(scope)
- .setAudience(audience);
+ .setAudience(responseCtx.getAudience());
if (jwtTokenType && responseCtx.getAccessTokenClaimSet() != null) {
builder.setCustomClaims(responseCtx.getAccessTokenClaimSet().toJSONObject());
@@ -423,56 +311,5 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
ActionSupport.buildEvent(profileRequestContext, EventIds.MESSAGE_PROC_ERROR);
}
}
-
- /**
- * Produce the desired {@link Scope} claim for the access token.
- *
- * @param validatedScope requested scope(s) that are valid according to client's metadata
- *
- * @return derived {@link Scope} to use or null
- */
- @Nonnull private Scope getScope(@Nullable final Scope validatedScope) {
-
- if (scopeAttribute != null) {
- final IdPAttribute source = (useUnfilteredAttributes ? attributeCtx.getUnfilteredIdPAttributes()
- : attributeCtx.getIdPAttributes()).get(scopeAttribute);
- if (source != null) {
- return Scope.parse(
- source.getValues().stream()
- .filter(StringAttributeValue.class::isInstance)
- .map(StringAttributeValue.class::cast)
- .map(StringAttributeValue::getValue)
- .collect(Collectors.toUnmodifiableList()));
- }
- }
-
- log.debug("{} Using originally requested/validated scope", getLogPrefix());
- return validatedScope != null ? validatedScope : new Scope();
- }
-
- @Nullable @NonnullElements private Collection<String> getAudience() {
- if (audienceAttribute != null) {
- final IdPAttribute source = (useUnfilteredAttributes ? attributeCtx.getUnfilteredIdPAttributes()
- : attributeCtx.getIdPAttributes()).get(audienceAttribute);
- if (source != null) {
- return source.getValues().stream()
- .filter(StringAttributeValue.class::isInstance)
- .map(StringAttributeValue.class::cast)
- .map(StringAttributeValue::getValue)
- .collect(Collectors.toUnmodifiableList());
- }
-
- log.warn("{} No source attribute {} available to produce audience claim", getLogPrefix(),
- audienceAttribute);
- return null;
- }
-
- if (tokenRequest.getResources() != null && !tokenRequest.getResources().isEmpty()) {
- log.debug("{} Using originally requested resource(s) as audience", getLogPrefix());
- return tokenRequest.getResources().stream().map(URI::toString).collect(Collectors.toUnmodifiableList());
- }
-
- return null;
- }
-}
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EncryptAccessToken.java
similarity index 71%
copy from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
copy to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EncryptAccessToken.java
index a1ba2a4a..72949c67 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EncryptAccessToken.java
@@ -17,47 +17,48 @@
package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
+import java.text.ParseException;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.nimbusds.jose.Payload;
+import com.nimbusds.jwt.EncryptedJWT;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.jwt.SignedJWT;
+
import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * Action that creates a Access Token, and sets it to work context
- * {@link OIDCAuthenticationResponseContext#getAccessToken()} located under
- * {@link ProfileRequestContext#getOutboundMessageContext()}.
+ * Action that handles JWT access token encryption.
*/
-public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction {
+public class EncryptAccessToken extends AbstractEncryptTokenAction {
/** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(SetAccessTokenToResponseContext.class);
+ @Nonnull private Logger log = LoggerFactory.getLogger(EncryptAccessToken.class);
/** Strategy used to locate the subcontext with the token. */
@Nonnull private Function<ProfileRequestContext,AccessTokenContext> accessTokenContextLookupStrategy;
/** Token context. */
@Nullable private AccessTokenContext tokenCtx;
-
+
/** Constructor. */
- public SetAccessTokenToResponseContext() {
- // PRC -> inbound message context -> OIDC response context -> ATC
- accessTokenContextLookupStrategy = new ChildContextLookup<>(AccessTokenContext.class, true).compose(
+ public EncryptAccessToken() {
+ // PRC -> outbound message context -> OIDC response context -> ATC
+ accessTokenContextLookupStrategy = new ChildContextLookup<>(AccessTokenContext.class).compose(
new ChildContextLookup<>(OIDCAuthenticationResponseContext.class).compose(
new OutboundMessageContextLookup()));
}
@@ -72,9 +73,9 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
accessTokenContextLookupStrategy =
- Constraint.isNotNull(strategy, "AccessTokenContext creation strategy cannot be null");
+ Constraint.isNotNull(strategy, "AccessTokenContext lookup strategy cannot be null");
}
-
+
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -90,27 +91,31 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
return false;
}
- if (tokenCtx.getJWT() == null && tokenCtx.getOpaque() == null) {
- log.debug("{} Access token is missing", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
return true;
}
-
+
/** {@inheritDoc} */
@Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- final String token;
- if (tokenCtx.getJWT() != null) {
- token = tokenCtx.getJWT().serialize();
- } else {
- token = tokenCtx.getOpaque();
+ protected Payload getPayload(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (tokenCtx.getJWT() instanceof SignedJWT) {
+ return new Payload((SignedJWT) tokenCtx.getJWT());
+ } else if (tokenCtx.getJWT() instanceof PlainJWT) {
+ try {
+ return new Payload(tokenCtx.getJWT().getJWTClaimsSet().getClaims());
+ } catch (final ParseException e) {
+ log.error("{} Unable to convert plaintext JWT to claims set", getLogPrefix(), e);
+ }
}
-
- getOidcResponseContext().setAccessToken(token, tokenCtx.getLifetime(), getOidcResponseContext().getScope());
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void setProcessedToken(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final EncryptedJWT token) {
+ tokenCtx.setJWT(token);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EnforceAudienceConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EnforceAudienceConfiguration.java
new file mode 100644
index 00000000..70ab9f43
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/EnforceAudienceConfiguration.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.oauth2.profile.impl;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.profile.config.logic.AllowUntrustedAudiencePredicate;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Profile action that evaluates the resolved configuration settings for the audience of a
+ * token request.
+ *
+ * <p>This action recognizes the duality of a token request whereby the original client and
+ * the primary recipient are both relying parties of the system at different stages of the
+ * flow. Either or both of them may be unverified, but enforcement of this for the audience
+ * depends on a profile setting applied to the client's original profile configuration.</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link IdPEventIds#INVALID_RELYING_PARTY_CTX}
+ * @event {@link IdPEventIds#INVALID_PROFILE_CONFIG}
+ *
+ * @since 3.1.0
+ */
+public class EnforceAudienceConfiguration extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(EnforceAudienceConfiguration.class);
+
+ /** Strategy that will return the requester's {@link RelyingPartyContext}. */
+ @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> requesterContextLookupStrategy;
+
+ /** Strategy that will return the audience's {@link RelyingPartyContext}. */
+ @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> audienceContextLookupStrategy;
+
+ /** Predicate that determines whether to allow an unverified audience. */
+ @Nonnull private Predicate<ProfileRequestContext> allowUntrustedAudienceCondition;
+
+ /** Original context. */
+ @Nullable private RelyingPartyContext reqContext;
+
+ /** New context. */
+ @Nullable private RelyingPartyContext audContext;
+
+ /** Constructor. */
+ public EnforceAudienceConfiguration() {
+ requesterContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+ audienceContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class).compose(
+ new OutboundMessageContextLookup());
+ allowUntrustedAudienceCondition = new AllowUntrustedAudiencePredicate();
+ }
+
+ /**
+ * Set the strategy used to return the original requester's {@link RelyingPartyContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRequesterContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ requesterContextLookupStrategy =
+ Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to return the audience's {@link RelyingPartyContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAudienceContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ audienceContextLookupStrategy =
+ Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the condition used to establish whether to allow an unverified audience.
+ *
+ * @param condition condition to set
+ */
+ public void setUntrustedAudienceCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ allowUntrustedAudienceCondition = Constraint.isNotNull(condition,
+ "Untrusted audience condition cannot be null");
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ reqContext = requesterContextLookupStrategy.apply(profileRequestContext);
+ audContext = audienceContextLookupStrategy.apply(profileRequestContext);
+ if (reqContext == null ||audContext == null) {
+ log.error("{} Requester or audience RelyingPartyContext missing", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc}. */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (audContext.isVerified() && audContext.getProfileConfig() == null) {
+ log.warn("{} Token profile not enabled for verified audience: {}", getLogPrefix(),
+ audContext.getRelyingPartyId());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+
+ if (!audContext.isVerified()) {
+ final boolean allow = allowUntrustedAudienceCondition.test(profileRequestContext);
+ if (!allow) {
+ log.warn("{} Client {} not permitted to request access token for untrusted audience {}", getLogPrefix(),
+ reqContext.getRelyingPartyId(), audContext.getRelyingPartyId());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+
+ if (audContext.getProfileConfig() == null) {
+ log.debug("{} Applying previously resolved Token profile configuration to untrusted audience",
+ getLogPrefix());
+ audContext.setProfileConfig(reqContext.getProfileConfig());
+ }
+ }
+
+ log.debug("{} Authorized token issuance from {} client ({}) to {} audience ({})", getLogPrefix(),
+ reqContext.isVerified() ? "verified" : "unverified", reqContext.getRelyingPartyId(),
+ audContext.isVerified() ? "verified" : "unverified", audContext.getRelyingPartyId());
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/OAuthAttributeResolutionContextDecorator.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/OAuthAttributeResolutionContextDecorator.java
deleted file mode 100644
index b031350f..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/OAuthAttributeResolutionContextDecorator.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You 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.oauth2.profile.impl;
-
-import java.net.URI;
-import java.util.Collection;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import com.nimbusds.oauth2.sdk.TokenRequest;
-
-import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.context.OAuthAttributeResolutionContext;
-
-/**
- * Adds OAuth-specific details about the resolution request to the tree.
- *
- * @since 3.1.0
- */
-public class OAuthAttributeResolutionContextDecorator implements Consumer<AttributeResolutionContext> {
-
- /** {@inheritDoc} */
- @Override
- public void accept(@Nullable final AttributeResolutionContext input) {
-
- final OAuthAttributeResolutionContext oauthCtx =
- input.getSubcontext(OAuthAttributeResolutionContext.class, true);
-
- if (input != null && input.getParent() instanceof ProfileRequestContext) {
- final MessageContext inbound = ((ProfileRequestContext) input.getParent()).getInboundMessageContext();
- if (inbound != null && inbound.getMessage() instanceof TokenRequest) {
- final Collection<URI> resources = ((TokenRequest) inbound.getMessage()).getResources();
- if (resources != null) {
- oauthCtx.getResources().addAll(
- resources.stream().map(URI::toString).collect(Collectors.toUnmodifiableList()));
- }
- }
-
- final MessageContext outbound = ((ProfileRequestContext) input.getParent()).getOutboundMessageContext();
- if (outbound != null) {
- final OIDCAuthenticationResponseContext responseCtx =
- outbound.getSubcontext(OIDCAuthenticationResponseContext.class);
- if (responseCtx != null && responseCtx.getScope() != null) {
- oauthCtx.getScope().addAll(responseCtx.getScope().toStringList());
- }
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
index a1ba2a4a..16c590cf 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
@@ -56,23 +56,23 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
/** Constructor. */
public SetAccessTokenToResponseContext() {
- // PRC -> inbound message context -> OIDC response context -> ATC
- accessTokenContextLookupStrategy = new ChildContextLookup<>(AccessTokenContext.class, true).compose(
+ // PRC -> outbound message context -> OIDC response context -> ATC
+ accessTokenContextLookupStrategy = new ChildContextLookup<>(AccessTokenContext.class).compose(
new ChildContextLookup<>(OIDCAuthenticationResponseContext.class).compose(
new OutboundMessageContextLookup()));
}
/**
- * Set the strategy used to create the {@link AccessTokenContext} to use.
+ * Set the strategy used to lookup the {@link AccessTokenContext} to use.
*
- * @param strategy creation strategy
+ * @param strategy lookup strategy
*/
- public void setAccessTokenContextCreationStrategy(
+ public void setAccessTokenContextLookupStrategy(
@Nonnull final Function<ProfileRequestContext,AccessTokenContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
accessTokenContextLookupStrategy =
- Constraint.isNotNull(strategy, "AccessTokenContext creation strategy cannot be null");
+ Constraint.isNotNull(strategy, "AccessTokenContext lookup strategy cannot be null");
}
/** {@inheritDoc} */
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
new file mode 100644
index 00000000..c73bb8f5
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.oauth2.profile.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.ClientInfoAudienceLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestAudienceLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCAuthenticationResponseAction;
+import net.shibboleth.idp.profile.context.navigate.RelyingPartyIdLookupFunction;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Action that validates requested resource/audience values against a computed set of "allowed"
+ * values.
+ *
+ * <p>This is an ordered list, so the first allowed value determines the "primary" audience of
+ * the eventual token.</p>
+ *
+ * <p>Requesting values is optional, in the absence of which at least one allowed value must exist
+ * and be returned, or a failure event will be signaled.</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#ACCESS_DENIED}
+ */
+public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(ValidateAudience.class);
+
+ /** Strategy used to obtain the relying party ID. */
+ @Nonnull private Function<ProfileRequestContext,String> relyingPartyIdLookupStrategy;
+
+ /** Strategy used to obtain the requested audience. */
+ @Nullable private Function<ProfileRequestContext,List<String>> requestedAudienceLookupStrategy;
+
+ /** Strategy used to obtain the audience allowed for the client. */
+ @Nonnull private Function<ProfileRequestContext,List<String>> allowedAudienceLookupStrategy;
+
+ /** Strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext}. */
+ @Nonnull
+ private Function<ProfileRequestContext,OIDCAuthenticationResponseTokenClaimsContext>
+ tokenClaimsContextLookupStrategy;
+
+ /** Constructor. */
+ public ValidateAudience() {
+ requestedAudienceLookupStrategy = new TokenRequestAudienceLookupFunction();
+ relyingPartyIdLookupStrategy = new RelyingPartyIdLookupFunction();
+ allowedAudienceLookupStrategy = new ClientInfoAudienceLookupFunction().compose(
+ new DefaultOIDCMetadataContextLookupFunction());
+ }
+
+ /**
+ * Set the strategy used to obtain the relying party ID.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRelyingPartyIdLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ relyingPartyIdLookupStrategy = Constraint.isNotNull(strategy,
+ "Relying party ID lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to locate the requested audience to validate.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRequestedAudienceLookupStrategy(
+ @Nullable final Function<ProfileRequestContext,List<String>> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ requestedAudienceLookupStrategy = strategy;
+ }
+
+
+ /**
+ * Set the strategy used to locate the allowed audience for the client.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAllowedAudienceLookupStrategy(@Nonnull final Function<ProfileRequestContext,List<String>> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ allowedAudienceLookupStrategy = Constraint.isNotNull(strategy,
+ "Allowed scope lookyp strategy cannot be null");
+ }
+
+// Checkstyle: CyclomaticComplexity OFF
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ final String clientId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
+
+ // These may come from metadata or be supplemented or substituted from elsewhere.
+ final List<String> allowedAudience = allowedAudienceLookupStrategy.apply(profileRequestContext);
+ if (allowedAudience == null || allowedAudience.isEmpty()) {
+ log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ return;
+ }
+
+ // These come from a previous authorization grant (authz code or refresh token).
+ List<String> previouslyGrantedAudience = null;
+ if (getOidcResponseContext().getAuthorizationGrantClaimsSet() != null) {
+ previouslyGrantedAudience = getOidcResponseContext().getAuthorizationGrantClaimsSet().getAudience();
+ }
+
+ // These come from a request object or parameter. Absent by definition on the UserInfo endpoint.
+ List<String> requestedAudience = requestedAudienceLookupStrategy != null ?
+ requestedAudienceLookupStrategy.apply(profileRequestContext) : null;
+
+ if (requestedAudience == null) {
+ // With none requested, simply swap requested for previously granted, if any.
+ // Set previous set to null since there's no need to filter against it.
+ requestedAudience = previouslyGrantedAudience;
+ previouslyGrantedAudience = null;
+ }
+
+ if (requestedAudience == null) {
+ // Nothing requested or previously granted, so take the first allowed value.
+ log.debug("{} No audience in request for client {}, using first allowed", getLogPrefix(), clientId);
+ requestedAudience = Collections.singletonList(allowedAudience.get(0));
+ }
+
+ final List<String> effectiveAudience = new ArrayList<>();
+
+ for (final String aud : requestedAudience) {
+ if (!allowedAudience.contains(aud)) {
+ log.warn("{} Omitting requested but unregistered audience {} for RP {}", getLogPrefix(), aud,
+ clientId);
+ } else if (previouslyGrantedAudience != null && !previouslyGrantedAudience.contains(aud)) {
+ log.warn("{} Omitting requested but previously ungranted audience {} for RP {}", getLogPrefix(),
+ aud, clientId);
+ } else {
+ effectiveAudience.add(aud);
+ }
+ }
+
+ if (effectiveAudience.isEmpty()) {
+ log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
+ ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+ } else {
+ log.debug("{} Computed audience for client {}: {}", getLogPrefix(), clientId, effectiveAudience);
+ getOidcResponseContext().getAudience().addAll(effectiveAudience);
+ }
+ }
+// Checkstyle: CyclomaticComplexity ON
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCAuthenticationResponseAction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCAuthenticationResponseAction.java
index 69fbf388..33703ce6 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCAuthenticationResponseAction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AbstractOIDCAuthenticationResponseAction.java
@@ -37,7 +37,7 @@ import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
* {@link ProfileRequestContext#getOutboundMessageContext()}. Extends baseclass that offers actions on
* {@link AuthenticationRequest} found via {@link MessageContext#getMessage()}.
*/
-abstract class AbstractOIDCAuthenticationResponseAction extends AbstractOIDCAuthenticationRequestAction {
+public abstract class AbstractOIDCAuthenticationResponseAction extends AbstractOIDCAuthenticationRequestAction {
/** Class logger. */
@Nonnull
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java
index 83cd30a3..5a82b9b9 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedToken.java
@@ -17,147 +17,46 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.text.ParseException;
-import java.util.function.Function;
-
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.saml.saml2.profile.context.EncryptionContext;
-import org.opensaml.security.credential.Credential;
-import org.opensaml.xmlsec.EncryptionParameters;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import com.nimbusds.jose.EncryptionMethod;
-import com.nimbusds.jose.JOSEException;
-import com.nimbusds.jose.JWEAlgorithm;
-import com.nimbusds.jose.JWEHeader;
-import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.Payload;
-import com.nimbusds.jose.crypto.AESEncrypter;
-import com.nimbusds.jose.crypto.ECDHEncrypter;
-import com.nimbusds.jose.crypto.RSAEncrypter;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.SignedJWT;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
-import net.shibboleth.idp.profile.context.RelyingPartyContext;
-import net.shibboleth.oidc.security.impl.CredentialConversionUtil;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.AbstractEncryptTokenAction;
/**
- * Action that serves both id token and user info response encryption. Existence of encryption parameters is taken as
- * indication whether the encryption should take place. Action assumes the content to be encrypted is located primarily
+ * Action that serves both id token and user info response encryption.
+ *
+ * <p>Action assumes the content to be encrypted is located primarily
* by {@link OIDCAuthenticationResponseContext#getProcessedToken()} returning either signed id token or signed user info
* response. If such information is not available action assumes the data to be encrypted is
* {@link OIDCAuthenticationResponseContext#getUserInfo()} containing bare user info response. If neither of the sources
- * for encryption exists the actions fails.
+ * for encryption exists the actions fails.</p>
*/
-public class EncryptProcessedToken extends AbstractOIDCResponseAction {
-
- /** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(EncryptProcessedToken.class);
-
- /** Strategy used to look up the {@link EncryptionContext} to store parameters in. */
- @Nonnull private Function<ProfileRequestContext, EncryptionContext> encryptionContextLookupStrategy;
-
- /** Encryption parameters for encrypting payload. */
- @Nullable private EncryptionParameters params;
-
- /** Payload to encrypt. */
- @Nullable private Payload payload;
-
- /**
- * Constructor.
- */
- public EncryptProcessedToken() {
- encryptionContextLookupStrategy = new ChildContextLookup<>(EncryptionContext.class).compose(
- new ChildContextLookup<>(RelyingPartyContext.class));
- }
-
- /**
- * Set the strategy used to look up the {@link EncryptionContext} to set the flags for.
- *
- * @param strategy lookup strategy
- */
- public void setEncryptionContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, EncryptionContext> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
- encryptionContextLookupStrategy =
- Constraint.isNotNull(strategy, "EncryptionContext lookup strategy cannot be null");
- }
+public class EncryptProcessedToken extends AbstractEncryptTokenAction {
/** {@inheritDoc} */
@Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
+ protected Payload getPayload(@Nonnull final ProfileRequestContext profileRequestContext) {
- final EncryptionContext encryptCtx = encryptionContextLookupStrategy.apply(profileRequestContext);
- if (encryptCtx == null) {
- log.error("{} No EncryptionContext returned by lookup strategy", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
- params = encryptCtx.getAssertionEncryptionParameters();
- if (params == null) {
- log.debug("{} No Encryption parameters, nothing to do", getLogPrefix());
- return false;
- }
if (getOidcResponseContext().getProcessedToken() != null) {
- payload = new Payload((SignedJWT) getOidcResponseContext().getProcessedToken());
+ return new Payload((SignedJWT) getOidcResponseContext().getProcessedToken());
} else if (getOidcResponseContext().getUserInfo() != null) {
- payload = new Payload(getOidcResponseContext().getUserInfo().toJSONObject());
+ return new Payload(getOidcResponseContext().getUserInfo().toJSONObject());
+ } else {
+ return null;
}
- if (payload == null) {
- log.error("{} Instructed to encrypt but no plain text source available", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
- return false;
- }
-
- return true;
}
/** {@inheritDoc} */
@Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- final JWEAlgorithm encAlg = JWEAlgorithm.parse(params.getKeyTransportEncryptionAlgorithm());
- final Credential credential = params.getKeyTransportEncryptionCredential();
- final EncryptionMethod encEnc = EncryptionMethod.parse(params.getDataEncryptionAlgorithm());
- final String kid = CredentialConversionUtil.resolveKid(credential);
-
- log.debug("{} encrypting with key {} and params alg: {} enc: {}", getLogPrefix(), kid, encAlg.getName(),
- encEnc.getName());
-
- final JWEObject jweObject =
- new JWEObject(new JWEHeader.Builder(encAlg, encEnc).contentType("JWT").keyID(kid).build(), payload);
- try {
- if (JWEAlgorithm.Family.RSA.contains(encAlg)) {
- jweObject.encrypt(new RSAEncrypter((RSAPublicKey) credential.getPublicKey()));
- } else if (JWEAlgorithm.Family.ECDH_ES.contains(encAlg)) {
- jweObject.encrypt(new ECDHEncrypter((ECPublicKey) credential.getPublicKey()));
- } else if (JWEAlgorithm.Family.SYMMETRIC.contains(encAlg)) {
- jweObject.encrypt(new AESEncrypter(credential.getSecretKey()));
- } else {
- log.error("{} Unsupported algorithm {}", getLogPrefix(), encAlg.getName());
- ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
- }
- getOidcResponseContext().setProcessedToken(EncryptedJWT.parse(jweObject.serialize()));
- } catch (final JOSEException | ParseException e) {
- log.error("{} Encryption failed {}", getLogPrefix(), e.getMessage());
- ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
- }
+ protected void setProcessedToken(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final EncryptedJWT token) {
+ getOidcResponseContext().setProcessedToken(token);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRelyingPartyContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRelyingPartyContext.java
index a2e21376..b92d5722 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRelyingPartyContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/InitializeRelyingPartyContext.java
@@ -25,10 +25,10 @@ import javax.annotation.Nullable;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
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.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
@@ -67,13 +67,16 @@ public class InitializeRelyingPartyContext extends AbstractProfileAction {
/** OIDC client id. */
@Nullable private ClientID clientId;
+
+ /** Use the inbound message tree? */
+ private boolean inbound;
/** Constructor. */
public InitializeRelyingPartyContext() {
relyingPartyContextCreationStrategy = new ChildContextLookup<>(RelyingPartyContext.class, true);
- oidcMetadataContextLookupStrategy = new ChildContextLookup<>(OIDCMetadataContext.class).compose(
- new InboundMessageContextLookup());
+ oidcMetadataContextLookupStrategy = new DefaultOIDCMetadataContextLookupFunction();
clientIDLookupStrategy = new DefaultClientIDLookupFunction();
+ inbound = true;
}
/**
@@ -113,16 +116,32 @@ public class InitializeRelyingPartyContext extends AbstractProfileAction {
Constraint.isNotNull(strategy, "OIDCMetadataContext lookup strategy cannot be null");
}
+ /**
+ * Sets which message tree to use in deriving the ClientID and OIDC metadata.
+ *
+ * <p>Defaults to true (the inbound tree).</p>
+ *
+ * @param flag true iff the inbound tree should be used
+ *
+ * @since 3.1.0
+ */
+ public void setInbound(final boolean flag) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ inbound = flag;
+ }
+
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
if (!super.doPreExecute(profileRequestContext)) {
- log.error("{} pre-execute failed", getLogPrefix());
return false;
}
- clientId = clientIDLookupStrategy.apply(profileRequestContext.getInboundMessageContext());
+
+ clientId = clientIDLookupStrategy.apply(inbound ? profileRequestContext.getInboundMessageContext() :
+ profileRequestContext.getOutboundMessageContext());
if (clientId == null) {
- log.error("{} Unable to locate client id from the request", getLogPrefix());
+ log.error("{} Unable to obtain client ID", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
return false;
}
@@ -139,8 +158,10 @@ public class InitializeRelyingPartyContext extends AbstractProfileAction {
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
return;
}
- log.debug("Attaching RelyingPartyContext for rp {}", clientId.getValue());
+
+ log.debug("Attaching RelyingPartyContext for {}", clientId.getValue());
rpContext.setRelyingPartyId(clientId.getValue());
+
final OIDCMetadataContext oidcContext = oidcMetadataContextLookupStrategy.apply(profileRequestContext);
if (oidcContext != null && oidcContext.getClientInformation() != null
&& clientId.equals(oidcContext.getClientInformation().getID())) {
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/OIDCMetadataLookupHandler.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/OIDCMetadataLookupHandler.java
index c75e4ee0..0fb31333 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/OIDCMetadataLookupHandler.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/OIDCMetadataLookupHandler.java
@@ -99,11 +99,14 @@ public class OIDCMetadataLookupHandler extends AbstractMessageHandler {
@Override
protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- // Resolve client id from inbound message
+
final ClientID clientId = clientIDLookupStrategy.apply(messageContext);
- // Resolve metadata for client id
- final ClientIDCriterion clientCriterion = new ClientIDCriterion(clientId);
- final CriteriaSet criteria = new CriteriaSet(clientCriterion);
+ if (clientId == null) {
+ log.debug("{} No client ID available", getLogPrefix());
+ return;
+ }
+
+ final CriteriaSet criteria = new CriteriaSet(new ClientIDCriterion(clientId));
try {
final OIDCClientInformation clientInformation = clientResolver.resolveSingle(criteria);
if (clientInformation == null) {
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParameters.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParameters.java
index 7166ae90..7aa18f6a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParameters.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParameters.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
+import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -40,6 +41,7 @@ import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.profile.config.logic.EncryptionOptionalPredicate;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -76,9 +78,12 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
/** Whether we resolve encryption or decryption parameters. */
private boolean forDecryption;
+
+ /** Predicate to determine how to proceed if no encryption parameters are resolved. */
+ @Nonnull private Predicate<ProfileRequestContext> encryptionOptionalPredicate;
/** Strategy used to look up the {@link EncryptionContext} to store parameters in. */
- @Nonnull private Function<ProfileRequestContext, EncryptionContext> encryptionContextLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,EncryptionContext> encryptionContextLookupStrategy;
/** Strategy used to look up a per-request {@link EncryptionConfiguration} list. */
@NonnullAfterInit private Function<ProfileRequestContext,List<EncryptionConfiguration>> configurationLookupStrategy;
@@ -90,7 +95,13 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
@Nullable @NonnullElements private List<EncryptionConfiguration> encryptionConfigurations;
/** Strategy used to look up a OIDC metadata context. */
- @Nullable private Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataContextLookupStrategy;
+ @Nullable private Function<ProfileRequestContext,OIDCMetadataContext> oidcMetadataContextLookupStrategy;
+
+ /** Whether encryption is optional. */
+ private boolean encryptionOptional;
+
+ /** Context to populate. */
+ private EncryptionContext encryptionContext;
/** Constructor. */
public PopulateOIDCEncryptionParameters() {
@@ -98,8 +109,9 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
oidcMetadataContextLookupStrategy = new DefaultOIDCMetadataContextLookupFunction();
encryptionContextLookupStrategy = new ChildContextLookup<>(EncryptionContext.class, true).compose(
new ChildContextLookup<>(RelyingPartyContext.class));
+ encryptionOptionalPredicate = new EncryptionOptionalPredicate();
}
-
+
/**
* Whether we resolve encryption or decryption parameters.
*
@@ -107,8 +119,22 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
*/
public void setForDecryption(final boolean flag) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
forDecryption = flag;
}
+
+ /**
+ * Sets the condition to apply to determine how to proceed if encryption parameter resolution fails.
+ *
+ * @param condition condition to set
+ *
+ * @since 3.1.0
+ */
+ public void setEncryptionOptionalPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ encryptionOptionalPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
+ }
/**
* Set the strategy used to look up the {@link EncryptionContext} to set the flags for.
@@ -129,7 +155,7 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
*
* @param strategy lookup strategy
*/
- public void setOIDCMetadataContextContextLookupStrategy(
+ public void setOidcMetadataContextLookupStrategy(
@Nonnull final Function<ProfileRequestContext, OIDCMetadataContext> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
@@ -177,20 +203,35 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
}
}
- // Checkstyle: CyclomaticComplexity OFF
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ encryptionContext = encryptionContextLookupStrategy.apply(profileRequestContext);
+ if (encryptionContext == null) {
+ log.debug("{} No EncryptionContext returned by lookup strategy", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+
+ if (!forDecryption) {
+ encryptionOptional = encryptionOptionalPredicate.test(profileRequestContext);
+ }
+
+ return true;
+ }
+// Checkstyle: CyclomaticComplexity OFF
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- log.debug("{} Resolving EncryptionParameters for request, purpose {}", getLogPrefix(),
+ log.debug("{} Resolving EncryptionParameters for {}", getLogPrefix(),
forDecryption ? "request object decryption" : "response encryption");
- final EncryptionContext encryptCtx = encryptionContextLookupStrategy.apply(profileRequestContext);
- if (encryptCtx == null) {
- log.debug("{} No EncryptionContext returned by lookup strategy", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return;
- }
+
try {
encryptionConfigurations = configurationLookupStrategy.apply(profileRequestContext);
if (encryptionConfigurations == null || encryptionConfigurations.isEmpty()) {
@@ -198,34 +239,37 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
}
final CriteriaSet criteria = buildCriteriaSet(profileRequestContext);
final EncryptionParameters params = encParamsresolver.resolveSingle(criteria);
- log.debug("{} {} EncryptionParameters for {}", getLogPrefix(),
- params != null ? "Resolved" : "Failed to resolve",
- forDecryption ? "request object decryption" : "response encryption");
+
if (params != null) {
+ log.debug("{} Resolved EncryptionParameters for {}", getLogPrefix(),
+ forDecryption ? "request object decryption" : "response encryption");
if (forDecryption) {
// Decryption parameters for request object decryption
- encryptCtx.setAttributeEncryptionParameters(params);
+ encryptionContext.setAttributeEncryptionParameters(params);
} else {
// Indicates that id token or userinfo response should be encrypted
- encryptCtx.setAssertionEncryptionParameters(params);
+ encryptionContext.setAssertionEncryptionParameters(params);
}
- return;
- }
- final EncryptionOptionalCriterion encryptionOptionalCrit = criteria.get(EncryptionOptionalCriterion.class);
- if (encryptionOptionalCrit != null) {
- if (encryptionOptionalCrit.isEncryptionOptional()) {
- log.debug("{} Encryption optional", getLogPrefix());
- return;
+ } else {
+ if (encryptionOptional) {
+ log.debug("{} Resolver returned no EncryptionParameters", getLogPrefix());
+ log.debug("{} Encryption is optional, ignoring inability to encrypt", getLogPrefix());
+ } else {
+ log.warn("{} Resolver returned no EncryptionParameters", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
}
}
} catch (final ResolverException e) {
log.error("{} Error resolving EncryptionParameters", getLogPrefix(), e);
+ if (encryptionOptional) {
+ log.debug("{} Encryption is optional, ignoring inability to encrypt", getLogPrefix());
+ } else {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
+ }
}
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_SEC_CFG);
}
+// Checkstyle: CyclomaticComplexity ON
- // Checkstyle: CyclomaticComplexity ON
-
/**
* Build the criteria used as input to the {@link EncryptionParametersResolver}.
*
@@ -237,14 +281,19 @@ public class PopulateOIDCEncryptionParameters extends AbstractProfileAction {
private CriteriaSet buildCriteriaSet(@Nonnull final ProfileRequestContext profileRequestContext) {
final CriteriaSet criteria = new CriteriaSet(new EncryptionConfigurationCriterion(encryptionConfigurations));
+
+ if (!forDecryption) {
+ criteria.add(new EncryptionOptionalCriterion(encryptionOptional));
+ }
+
final OIDCMetadataContext oidcMetadataCtx = oidcMetadataContextLookupStrategy.apply(profileRequestContext);
if (oidcMetadataCtx != null && oidcMetadataCtx.getClientInformation() != null) {
log.debug(
- "{} Adding oidc client information to resolution criteria for key transport/encryption algorithms",
+ "{} Adding OIDC client information to resolution criteria for encryption algorithms",
getLogPrefix());
criteria.add(new ClientInformationCriterion(oidcMetadataCtx.getClientInformation()));
} else {
- log.debug("{} oidcMetadataCtx is null", getLogPrefix());
+ log.debug("{} No OIDC client information available", getLogPrefix());
}
return criteria;
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCSignatureSigningParameters.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCSignatureSigningParameters.java
index 284ced12..4f84a09b 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCSignatureSigningParameters.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCSignatureSigningParameters.java
@@ -28,6 +28,7 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.AbstractHandlerDelegatingProfileAction;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
@@ -94,7 +95,7 @@ public class PopulateOIDCSignatureSigningParameters
// Default: outbound msg context -> SAMLPeerEntityContext -> SAMLMetadataContext
metadataContextLookupStrategy = new ChildContextLookup<>(SAMLMetadataContext.class).compose(
new ChildContextLookup<>(SAMLPeerEntityContext.class).compose(
- new OutboundMessageContextLookup()));
+ new InboundMessageContextLookup()));
setErrorEvent(EventIds.INVALID_SEC_CFG);
}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
index a048ca54..ae30bf7a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
@@ -69,10 +69,10 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
@Nonnull private Function<ProfileRequestContext,String> relyingPartyIdLookupStrategy;
/** Strategy used to obtain the requested scope value. */
- @Nullable private Function<ProfileRequestContext,Scope> requestedScopesLookupStrategy;
+ @Nullable private Function<ProfileRequestContext,Scope> requestedScopeLookupStrategy;
/** Strategy used to obtain the scope allowed for the client. */
- @Nonnull private Function<ProfileRequestContext,Scope> allowedScopesLookupStrategy;
+ @Nonnull private Function<ProfileRequestContext,Scope> allowedScopeLookupStrategy;
/** Strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext}. */
@Nonnull
@@ -81,9 +81,9 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
/** Constructor. */
public ValidateScope() {
- requestedScopesLookupStrategy = new DefaultRequestedScopeLookupFunction();
+ requestedScopeLookupStrategy = new DefaultRequestedScopeLookupFunction();
relyingPartyIdLookupStrategy = new RelyingPartyIdLookupFunction();
- allowedScopesLookupStrategy = new ClientInfoScopeLookupFunction().compose(
+ allowedScopeLookupStrategy = new ClientInfoScopeLookupFunction().compose(
new DefaultOIDCMetadataContextLookupFunction());
tokenClaimsContextLookupStrategy =
new ChildContextLookup<>(OIDCAuthenticationResponseTokenClaimsContext.class).compose(
@@ -107,10 +107,10 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
*
* @param strategy lookup strategy
*/
- public void setRequestedScopesLookupStrategy(@Nullable final Function<ProfileRequestContext,Scope> strategy) {
+ public void setRequestedScopeLookupStrategy(@Nullable final Function<ProfileRequestContext,Scope> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- requestedScopesLookupStrategy = strategy;
+ requestedScopeLookupStrategy = strategy;
}
@@ -119,10 +119,10 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
*
* @param strategy lookup strategy
*/
- public void setAllowedScopesLookupStrategy(@Nonnull final Function<ProfileRequestContext,Scope> strategy) {
+ public void setAllowedScopeLookupStrategy(@Nonnull final Function<ProfileRequestContext,Scope> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- allowedScopesLookupStrategy = Constraint.isNotNull(strategy,
+ allowedScopeLookupStrategy = Constraint.isNotNull(strategy,
"Allowed scope lookyp strategy cannot be null");
}
@@ -149,7 +149,7 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
final String clientId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
// These typically come from metadata but may be supplemented or substituted from elsewhere.
- final Scope allowedScopes = allowedScopesLookupStrategy.apply(profileRequestContext);
+ final Scope allowedScopes = allowedScopeLookupStrategy.apply(profileRequestContext);
if (allowedScopes == null || allowedScopes.isEmpty()) {
log.debug("{} No allowed scope for client {}, nothing to do", getLogPrefix(), clientId);
return;
@@ -162,8 +162,8 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
}
// These come from a request object or parameter. Absent by definition on the UserInfo endpoint.
- Scope requestedScopes = requestedScopesLookupStrategy != null ?
- requestedScopesLookupStrategy.apply(profileRequestContext) : null;
+ Scope requestedScopes = requestedScopeLookupStrategy != null ?
+ requestedScopeLookupStrategy.apply(profileRequestContext) : null;
if (requestedScopes == null) {
// With none requested, simply swap requested for previously granted, if any.
// Set previous set to null since there's no need to filter against it.
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/impl/OIDCClientInformationEncryptionParametersResolver.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/impl/OIDCClientInformationEncryptionParametersResolver.java
index 47d30b3b..daa183f4 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/impl/OIDCClientInformationEncryptionParametersResolver.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/impl/OIDCClientInformationEncryptionParametersResolver.java
@@ -182,29 +182,28 @@ public class OIDCClientInformationEncryptionParametersResolver extends BasicEncr
@Nonnull final CriteriaSet criteria, @Nonnull final Predicate<String> whitelistBlacklistPredicate) {
if (!criteria.contains(ClientInformationCriterion.class)) {
- log.debug("No client criterion, nothing to do");
- super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
- return;
- }
- final OIDCClientInformation clientInformation =
- criteria.get(ClientInformationCriterion.class).getOidcClientInformation();
- if (clientInformation == null) {
- log.debug("No client information, nothing to do");
+ log.debug("No client criterion, falling back to local configuration");
super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
return;
}
+
if (!criteria.contains(EncryptionConfigurationCriterion.class)) {
- log.debug("No encryption configuration criterion, nothing to do");
+ log.debug("No encryption configuration criterion, falling back to default configuration");
super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
return;
}
+
final List<EncryptionConfiguration> encryptionConfigurations =
criteria.get(EncryptionConfigurationCriterion.class).getConfigurations();
if (encryptionConfigurations == null || encryptionConfigurations.isEmpty()) {
- log.debug("No encrypt configuration nothing to do");
+ log.debug("No encryption configuration, falling back to default configuration");
super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
return;
}
+
+ final OIDCClientInformation clientInformation =
+ criteria.get(ClientInformationCriterion.class).getOidcClientInformation();
+
// We populate the parameters only for the algorithm the client has registered
JWEAlgorithm keyTransportAlgorithm = null;
EncryptionMethod encryptionMethod = null;
@@ -224,8 +223,7 @@ public class OIDCClientInformationEncryptionParametersResolver extends BasicEncr
encryptionMethod = clientInformation.getOIDCMetadata().getIDTokenJWEEnc();
}
if (keyTransportAlgorithm == null) {
- log.debug("No algorithm information in client information, nothing to do");
- criteria.add(new EncryptionOptionalCriterion(true));
+ log.debug("No algorithm information in client information, falling back to default configuration");
super.resolveAndPopulateCredentialsAndAlgorithms(params, criteria, whitelistBlacklistPredicate);
return;
}
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 7990b687..d36c2058 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
@@ -206,8 +206,8 @@
p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}" />
<!--
- This is a default souurce of "allowed" scope for access tokens. It's public to allow an overridden
- source function to pull in the original metadata-registered scope easily.
+ This is a default souurce of "allowed" scope/audience for access tokens. It's public to allow an overridden
+ source function to pull in the original metadata-registered values easily.
-->
<bean id="shibboleth.oidc.DefaultAllowedScopeStrategy" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
@@ -217,5 +217,14 @@
<bean class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction" />
</constructor-arg>
</bean>
+
+ <bean id="shibboleth.oidc.DefaultAllowedAudienceStrategy" parent="shibboleth.Functions.Compose">
+ <constructor-arg name="g">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.ClientInfoAudienceLookupFunction" />
+ </constructor-arg>
+ <constructor-arg name="f">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction" />
+ </constructor-arg>
+ </bean>
</beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
index 0097db7c..0219c006 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
@@ -33,7 +33,6 @@
<bean id="ResolveAttributes" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
c:resolverService-ref="shibboleth.AttributeResolverService"
p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
- p:resolutionContextDecorator="#{getObject('OAuthResolutionContextDecorator')}"
p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}" />
<bean id="FilterAttributes" class="net.shibboleth.idp.profile.impl.FilterAttributes" scope="prototype"
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 c9f7de9d..6fea194a 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
@@ -172,7 +172,7 @@
scope="prototype" />
<bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
- p:allowedScopesLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
+ p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
<bean id="PopulateIDTokenSignatureSigningParameters"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup/metadata-lookup-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup/metadata-lookup-beans.xml
index 73f440d0..b898c7b0 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup/metadata-lookup-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup/metadata-lookup-beans.xml
@@ -7,35 +7,60 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize" default-destroy-method="destroy">
+
+ <bean id="OIDCMetadataLookup" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
+ scope="prototype" c:executionDirection="INBOUND">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.impl.OIDCMetadataLookupHandler" scope="prototype">
+ <property name="clientInformationResolver">
+ <ref bean="shibboleth.ClientInformationResolver" />
+ </property>
+ <property name="clientIDLookupStrategy">
+ <ref bean="shibboleth.ClientIDLookupStrategy" />
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="InitializeRelyingPartyContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
+ p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy" />
+
<util:constant id="shibboleth.MetadataLookup.Role"
static-field="org.opensaml.saml.saml2.metadata.SPSSODescriptor.DEFAULT_ELEMENT_NAME" />
<bean id="SAMLProtocolAndRole"
class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
- c:executionDirection="INBOUND" p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ c:executionDirection="INBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
<constructor-arg name="messageHandler">
<bean class="org.opensaml.saml.common.binding.impl.SAMLProtocolAndRoleHandler" scope="prototype"
p:protocol="http://openid.net/specs/openid-connect-core-1_0.html"
- p:role-ref="shibboleth.MetadataLookup.Role" p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext"/>
+ p:role-ref="shibboleth.MetadataLookup.Role"
+ p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext"/>
</constructor-arg>
</bean>
-
- <bean id="SetEntityIdToSAMLPeerEntityContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetEntityIdToSAMLPeerEntityContext"
- p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy"
- p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext"
- p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}" />
+ <bean id="SetEntityIdToSAMLPeerEntityContext"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="INBOUND"
+ p:errorEvent="#{T(org.opensaml.profile.action.EventIds).INVALID_MSG_CTX}"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.SetEntityIdToSAMLPeerEntityContext"
+ scope="prototype"
+ p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy"
+ p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext" />
+ </constructor-arg>
+ </bean>
+
<bean id="InitializeRelyingPartyContextFromSAMLPeer"
class="net.shibboleth.idp.saml.profile.impl.InitializeRelyingPartyContextFromSAMLPeer" scope="prototype" />
- <bean id="InitializeRelyingPartyContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
- p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy" />
-
<bean id="SAMLMetadataLookup"
- class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
- c:executionDirection="INBOUND" p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="INBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
<constructor-arg name="messageHandler">
<bean class="org.opensaml.saml.common.binding.impl.SAMLMetadataLookupHandler" scope="prototype"
p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext">
@@ -47,29 +72,13 @@
</constructor-arg>
</bean>
- <bean id="LookupSAMLPeerEntityContext" parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookup.SAMLPeerEntityContext"
- c:f-ref="shibboleth.MessageContextLookup.Inbound"/>
-
- <bean id="LookupSAMLMetadataContext" parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookup.SAMLMetadataContext"
- c:f-ref="LookupSAMLPeerEntityContext"/>
-
<bean id="PopulateOIDCMetadataContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCMetadataContext" scope="prototype"
- p:samlMetadataContextLookupStrategy-ref="LookupSAMLMetadataContext" />
-
- <bean id="OIDCMetadataLookup" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
- scope="prototype" c:executionDirection="INBOUND">
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="INBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
<constructor-arg name="messageHandler">
- <bean class="net.shibboleth.idp.plugin.oidc.op.profile.impl.OIDCMetadataLookupHandler" scope="prototype">
- <property name="clientInformationResolver">
- <ref bean="shibboleth.ClientInformationResolver" />
- </property>
- <property name="clientIDLookupStrategy">
- <ref bean="shibboleth.ClientIDLookupStrategy" />
- </property>
- </bean>
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.PopulateOIDCMetadataContext"
+ scope="prototype" />
</constructor-arg>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index a5be7e28..d434469a 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -25,10 +25,12 @@
<bean id="shibboleth.ClientIDLookupStrategy"
class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestClientIDLookupFunction" />
- <bean id="ResolveAttributesPredicate" class="net.shibboleth.oidc.profile.config.logic.ResolveAttributesPredicate" />
-
- <bean id="OAuthResolutionContextDecorator"
- class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.OAuthAttributeResolutionContextDecorator" />
+ <bean id="ResolveAttributesForClientPredicate"
+ class="net.shibboleth.oidc.profile.config.logic.ResolveAttributesPredicate" />
+
+ <bean id="ResolveAttributesForAudiencePredicate"
+ class="net.shibboleth.oidc.profile.config.logic.ResolveAttributesPredicate"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
<bean id="InitializeOutboundMessageContext"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundTokenResponseMessageContext"
@@ -156,22 +158,164 @@
<!-- Common grant handling. -->
<bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
- p:requestedScopesLookupStrategy-ref="shibboleth.TokenRequestScopeLookupStrategy" />
+ p:requestedScopeLookupStrategy-ref="TokenRequestScopeLookupStrategy"
+ p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
- <bean id="shibboleth.TokenRequestScopeLookupStrategy"
+ <bean id="TokenRequestScopeLookupStrategy"
class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestScopeLookupFunction" />
+ <bean id="ValidateAudience"
+ class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateAudience" scope="prototype"
+ p:requestedAudienceLookupStrategy-ref="TokenRequestAudienceLookupStrategy"
+ p:allowedAudienceLookupStrategy="#{getObject('shibboleth.oidc.AllowedAudienceStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedAudienceStrategy')}" />
+
+ <bean id="TokenRequestAudienceLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestAudienceLookupFunction" />
+
<!--
- TODO: May need to eventually conditionalize this and the encryption action following.
- With opaque access tokens, this wouldn't be used for the client_credentials grant, but
- with JWT access tokens it will be used for ID and access tokens. For now leaving enabled.
- Encryption has to be redone anyway since the recipient isn't the client but the resource server.
+ Do a metadata lookup for the primary audience of the token for encryption purposes.
+ Contexts are stored under the outbound MessageContext, including the new RelyingPartyContext.
-->
+
+ <bean id="AudienceOIDCMetadataLookup" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
+ scope="prototype" c:executionDirection="OUTBOUND">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.impl.OIDCMetadataLookupHandler" scope="prototype">
+ <property name="clientInformationResolver">
+ <ref bean="shibboleth.ClientInformationResolver" />
+ </property>
+ <property name="clientIDLookupStrategy">
+ <ref bean="AudienceClientIDLookupStrategy" />
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="AudienceClientIDLookupStrategy"
+ class="net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.AudienceClientIDLookupFunction" />
+
+ <bean id="InitializeAudienceRelyingPartyContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
+ p:relyingPartyContextCreationStrategy-ref="AudienceRelyingPartyCreationStrategy"
+ p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
+ p:clientIDLookupStrategy-ref="AudienceClientIDLookupStrategy"
+ p:inbound="false" />
+
+ <bean id="LookupOutboundOIDCMetadataContext"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction"
+ p:inbound="false" />
+
+ <bean id="AudienceRelyingPartyCreationStrategy" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookupOrCreate.RelyingPartyContext"
+ c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+
+ <bean id="AudienceSAMLProtocolAndRole"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="OUTBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ <constructor-arg name="messageHandler">
+ <bean class="org.opensaml.saml.common.binding.impl.SAMLProtocolAndRoleHandler" scope="prototype"
+ p:protocol="http://openid.net/specs/openid-connect-core-1_0.html"
+ p:role-ref="shibboleth.MetadataLookup.Role"
+ p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext"/>
+ </constructor-arg>
+ </bean>
- <bean id="PopulateTokenSignatureSigningParameters"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
- p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
- p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
+ <bean id="SetAudienceEntityIdToSAMLPeerEntityContext"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="OUTBOUND"
+ p:errorEvent="#{T(org.opensaml.profile.action.EventIds).INVALID_MSG_CTX}"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.SetEntityIdToSAMLPeerEntityContext"
+ scope="prototype"
+ p:clientIDLookupStrategy-ref="AudienceClientIDLookupStrategy"
+ p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="AudienceSAMLMetadataLookup"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="OUTBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ <constructor-arg name="messageHandler">
+ <bean class="org.opensaml.saml.common.binding.impl.SAMLMetadataLookupHandler" scope="prototype"
+ p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext">
+ <property name="roleDescriptorResolver">
+ <bean class="org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver"
+ c:mdResolver-ref="shibboleth.MetadataResolver" />
+ </property>
+ </bean>
+ </constructor-arg>
+ </bean>
+
+ <bean id="PopulateAudienceOIDCMetadataContext"
+ class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+ c:executionDirection="OUTBOUND"
+ p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+ <constructor-arg name="messageHandler">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.PopulateOIDCMetadataContext"
+ scope="prototype" />
+ </constructor-arg>
+ </bean>
+
+ <bean id="LookupPeerEntityContext" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookup.SAMLPeerEntityContext"
+ c:f-ref="shibboleth.MessageContextLookup.Outbound"/>
+
+ <bean id="LookupSAMLMetadataContext" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookup.SAMLMetadataContext"
+ c:f-ref="LookupPeerEntityContext"/>
+
+ <bean id="InitializeAudienceRelyingPartyContextFromSAMLPeer"
+ class="net.shibboleth.idp.saml.profile.impl.InitializeRelyingPartyContextFromSAMLPeer" scope="prototype"
+ p:relyingPartyContextCreationStrategy-ref="AudienceRelyingPartyCreationStrategy"
+ p:peerEntityContextLookupStrategy-ref="LookupPeerEntityContext" />
+
+ <bean id="SelectAudienceRelyingPartyConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+
+ <bean id="SelectAudienceProfileConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy"
+ p:failIfMissing="false" />
+
+ <bean id="EnforceAudienceConfiguration"
+ class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.EnforceAudienceConfiguration"
+ scope="prototype" />
+
+ <bean id="ResolveAttributesForAudience" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
+ c:resolverService-ref="shibboleth.AttributeResolverService"
+ p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+ p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}"
+ p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction"
+ p:recipientLookupStrategy-ref="AudienceIDLookupFunction" />
+
+ <bean id="FilterAttributesForAudience" class="net.shibboleth.idp.profile.impl.FilterAttributes" scope="prototype"
+ c:filterService-ref="shibboleth.AttributeFilterService"
+ p:maskFailures="%{idp.service.attribute.filter.maskFailures:true}"
+ p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction"
+ p:recipientLookupStrategy-ref="AudienceIDLookupFunction"
+ p:metadataContextLookupStrategy-ref="LookupSAMLMetadataContext" />
+
+ <bean id="AudienceIssuerLookupFunction"
+ class="net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
+ <bean id="AudienceIDLookupFunction"
+ class="net.shibboleth.idp.profile.context.navigate.RelyingPartyIdLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
+ <!-- Back to token prep. -->
+
+ <!-- Traditional third-party grant response handling. -->
+
+ <bean id="PopulateIDTokenSignatureSigningParameters"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters" scope="prototype"
+ p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
+ p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
<property name="securityParametersContextLookupStrategy">
<bean parent="shibboleth.Functions.Compose"
c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
@@ -184,7 +328,7 @@
</property>
</bean>
- <bean id="PopulateTokenEncryptionParameters"
+ <bean id="PopulateIDTokenEncryptionParameters"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver" />
@@ -194,8 +338,6 @@
p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache"
p:keyFetchInterval="%{idp.oidc.jwksuri.fetchInterval:PT30M}" />
- <!-- Traditional third-party grant response handling. -->
-
<bean id="SetOIDCAccessTokenToResponseContext"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAccessTokenToResponseContext" scope="prototype"
p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}" />
@@ -263,32 +405,72 @@
<!-- client_credentials grant response actions. -->
+ <bean id="PopulateAccessTokenSignatureSigningParameters"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters" scope="prototype"
+ p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
+ p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver"
+ p:metadataContextLookupStrategy-ref="LookupSAMLMetadataContext"
+ p:securityParametersContextLookupStrategy-ref="AudienceSecurityParametersCreationStrategy">
+ <property name="existingParametersContextLookupStrategy">
+ <bean parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookup.SecurityParameters"
+ c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+ </property>
+ </bean>
+
+ <bean id="AudienceSecurityParametersCreationStrategy" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
+ c:f-ref="AudienceRelyingPartyCreationStrategy" />
+
+ <bean id="PopulateAccessTokenEncryptionParameters"
+ class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
+ p:encryptionOptionalPredicate-ref="AudienceEncryptionOptionalPredicate"
+ p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
+ p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
+ p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver"
+ p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy" />
+
+ <bean id="AudienceEncryptionOptionalPredicate"
+ class="net.shibboleth.oidc.profile.config.logic.EncryptionOptionalPredicate"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
+ <bean id="AudienceEncryptionContextCreationStrategy" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookupOrCreate.EncryptionParameters"
+ c:f-ref="AudienceRelyingPartyCreationStrategy" />
+
<bean id="AddAttributeClaimsToAccessToken"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAttributesToClaimsSet" scope="prototype"
p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
- p:responseClaimsSetLookupStrategy-ref="shibboleth.AccessTokenClaimsSetLookupStrategy"
+ p:responseClaimsSetLookupStrategy-ref="AccessTokenClaimsSetLookupFunction"
p:reservedClaimNames="#{getObject('shibboleth.oidc.AccessTokenReservedClaimNames') ?: getObject('shibboleth.oidc.DefaultAccessTokenReservedClaimNames')}" />
- <bean id="shibboleth.AccessTokenClaimsSetLookupStrategy"
+ <bean id="AccessTokenClaimsSetLookupFunction"
class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.AccessTokenClaimsSetLookupFunction"
p:autoCreate="true" />
<bean id="BuildAccessToken"
class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildAccessToken" scope="prototype"
p:dataSealer="#{getObject('%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim())}"
- p:useUnfilteredAttributes="%{idp.oauth.accessToken.useUnfilteredAttributes:true}"
- p:scopeAttribute="#{'%{idp.oauth.accessToken.scopeAttribute:scope}'.trim()}"
- p:audienceAttribute="#{'%{idp.oauth.accessToken.audienceAttribute:audience}'.trim()}" />
+ p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction"
+ p:accessTokenTypeLookupStrategy-ref="AccessTokenTypeLookupFunction"
+ p:accessTokenLifetimeLookupStrategy-ref="AccessTokenLifetimeLookupFunction" />
+
+ <bean id="AccessTokenTypeLookupFunction"
+ class="net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+ <bean id="AccessTokenLifetimeLookupFunction"
+ class="net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
<bean id="SignAccessToken"
- class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
- p:typeHeader="at+jwt">
- <property name="securityParametersLookupStrategy">
- <bean parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookup.SecurityParameters"
- c:f-ref="shibboleth.ChildLookup.RelyingParty" />
- </property>
- </bean>
+ class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
+ p:securityParametersLookupStrategy-ref="AudienceSecurityParametersCreationStrategy"
+ p:typeHeader="at+jwt" />
+
+ <bean id="EncryptAccessToken"
+ class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.EncryptAccessToken" scope="prototype"
+ p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy" />
<bean id="SetOAuthAccessTokenToResponseContext"
class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SetAccessTokenToResponseContext"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
index de879956..b8222360 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
@@ -8,6 +8,7 @@
<evaluate expression="FlowStartPopulateAuditContext" />
<evaluate expression="InitializeOutboundMessageContext" />
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="DecodeMessage" />
</action-state>
@@ -15,16 +16,18 @@
<evaluate expression="DecodeMessage" />
<evaluate expression="PostDecodePopulateAuditContext" />
<evaluate expression="'proceed'" />
+
<!-- DoMetadataLookup is expected to proceed to SelectConfiguration -->
<transition on="proceed" to="DoMetadataLookup" />
</action-state>
<action-state id="SelectConfiguration">
- <evaluate expression="SelectRelyingPartyConfiguration" />
+ <evaluate expression="SelectRelyingPartyConfiguration" />
<evaluate expression="SelectProfileConfiguration" />
<evaluate expression="PostLookupPopulateAuditContext" />
<evaluate expression="PopulateTokenEndpointJwtSignatureValidationParameters"/>
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="AuthenticationSetup" />
</action-state>
@@ -33,6 +36,7 @@
<action-state id="ResumeAfterAuthentication">
<evaluate expression="ValidateGrantType" />
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="BranchOnGrantType" />
</action-state>
@@ -54,8 +58,10 @@
<evaluate expression="SetConsentToResponseContext" />
<evaluate expression="InitializeSubjectContext" />
<evaluate expression="SetSubjectFromAuthzCodeToResponseContext" />
+ <evaluate expression="ValidateScope" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="CommonGrantProcessing" />
+
+ <transition on="proceed" to="CheckAttributeResolutionForClient" />
</action-state>
<!-- These steps apply to grants that are self-contained on this endpoint. -->
@@ -63,34 +69,80 @@
<evaluate expression="SetAuthenticationContextClassReferenceToResponseContext" />
<evaluate expression="SetAuthenticationTimeToResponseContext" />
<evaluate expression="SetSubjectFromSubjectContextToResponseContext" />
+ <evaluate expression="ValidateScope" />
+ <evaluate expression="ValidateAudience" />
+
<evaluate expression="'proceed'" />
- <transition on="proceed" to="CommonGrantProcessing" />
+ <transition on="proceed" to="LookupAudienceMetadata" />
</action-state>
- <!-- These steps are common to all grant types. -->
- <action-state id="CommonGrantProcessing">
- <evaluate expression="ValidateScope" />
- <evaluate expression="PopulateTokenSignatureSigningParameters" />
- <evaluate expression="PopulateTokenEncryptionParameters" />
+ <!-- For client credentials grant, need to flip the Relying Party here to the primary resource/audience. -->
+
+ <action-state id="LookupAudienceMetadata">
+ <evaluate expression="AudienceOIDCMetadataLookup" />
+ <evaluate expression="InitializeAudienceRelyingPartyContext" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="CheckIfAudienceFoundFromClientInformationService" />
+ </action-state>
+
+ <decision-state id="CheckIfAudienceFoundFromClientInformationService">
+ <if test="opensamlProfileRequestContext.getOutboundMessageContext().containsSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext))"
+ then="SelectAudienceProfileConfiguration" else="LookupAudienceSAMLMetadata" />
+ </decision-state>
+
+ <action-state id="LookupAudienceSAMLMetadata">
+ <evaluate expression="AudienceSAMLProtocolAndRole" />
+ <evaluate expression="SetAudienceEntityIdToSAMLPeerEntityContext" />
+ <evaluate expression="AudienceSAMLMetadataLookup" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="CheckIfAudienceFoundFromSAMLMetadata" />
+ </action-state>
+
+ <decision-state id="CheckIfAudienceFoundFromSAMLMetadata">
+ <if test="opensamlProfileRequestContext.getOutboundMessageContext().containsSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext)) and opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext)).containsSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLMetadataContext))"
+ then="PopulateAudienceOIDCMetadataContextFromSAML"
+ else="SelectAudienceProfileConfiguration" />
+ </decision-state>
+
+ <action-state id="PopulateAudienceOIDCMetadataContextFromSAML">
+ <evaluate expression="PopulateAudienceOIDCMetadataContext" />
+ <evaluate expression="InitializeAudienceRelyingPartyContextFromSAMLPeer" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="SelectAudienceProfileConfiguration" />
+ </action-state>
+
+ <action-state id="SelectAudienceProfileConfiguration">
+ <evaluate expression="SelectAudienceRelyingPartyConfiguration" />
+ <evaluate expression="SelectAudienceProfileConfiguration" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="EnforceAudienceConfiguration" />
+ </action-state>
+
+ <action-state id="EnforceAudienceConfiguration">
+ <evaluate expression="EnforceAudienceConfiguration" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="CheckAttributeResolution" />
+
+ <transition on="proceed" to="CheckAttributeResolutionForAudience" />
</action-state>
- <decision-state id="CheckAttributeResolution">
- <if test="ResolveAttributesPredicate.test(opensamlProfileRequestContext)"
- then="AttributeResolution"
+ <decision-state id="CheckAttributeResolutionForClient">
+ <if test="ResolveAttributesForClientPredicate.test(opensamlProfileRequestContext)"
+ then="AttributeResolutionForClient"
else="DoConsentLookup" />
</decision-state>
- <action-state id="AttributeResolution">
+ <action-state id="AttributeResolutionForClient">
<evaluate expression="ResolveAttributes" />
<evaluate expression="FilterAttributes" />
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="DoConsentLookup" />
</action-state>
- <!-- Consent check is not applicable to some grant types but does no harm. -->
-
<decision-state id="BuildResponse">
<if test="NotClientCredentialsGrantCondition.test(opensamlProfileRequestContext)"
then="TraditionalGrantResponse"
@@ -98,6 +150,8 @@
</decision-state>
<action-state id="TraditionalGrantResponse">
+ <evaluate expression="PopulateIDTokenSignatureSigningParameters" />
+ <evaluate expression="PopulateIDTokenEncryptionParameters" />
<evaluate expression="SetOIDCAccessTokenToResponseContext" />
<evaluate expression="SetRefreshTokenToResponseContext" />
<evaluate expression="AddIDTokenShell" />
@@ -110,15 +164,34 @@
<evaluate expression="SignIDToken" />
<evaluate expression="EncryptIDToken" />
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="BuildResponseMessage" />
</action-state>
+ <decision-state id="CheckAttributeResolutionForAudience">
+ <if test="ResolveAttributesForAudiencePredicate.test(opensamlProfileRequestContext)"
+ then="AttributeResolutionForAudience"
+ else="ClientCredentialsGrantResponse" />
+ </decision-state>
+
+ <action-state id="AttributeResolutionForAudience">
+ <evaluate expression="ResolveAttributesForAudience" />
+ <evaluate expression="FilterAttributesForAudience" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="ClientCredentialsGrantResponse" />
+ </action-state>
+
<action-state id="ClientCredentialsGrantResponse">
+ <evaluate expression="PopulateAccessTokenSignatureSigningParameters" />
+ <evaluate expression="PopulateAccessTokenEncryptionParameters" />
<evaluate expression="AddAttributeClaimsToAccessToken" />
<evaluate expression="BuildAccessToken" />
<evaluate expression="SignAccessToken" />
+ <evaluate expression="EncryptAccessToken" />
<evaluate expression="SetOAuthAccessTokenToResponseContext" />
<evaluate expression="'proceed'" />
+
<transition on="proceed" to="BuildResponseMessage" />
</action-state>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
index fd8dd794..e902612a 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
@@ -35,8 +35,8 @@
class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.UserInfoRequestClientIDLookupFunction" />
<bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
- p:allowedScopesLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}">
- <property name="requestedScopesLookupStrategy">
+ p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}">
+ <property name="requestedScopeLookupStrategy">
<null/>
</property>
</bean>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 46762697..960407b3 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -22,8 +22,10 @@
p:issuer-ref="issuer"
p:tokenEndpointAuthMethods="%{idp.oidc.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
p:claimsValidator-ref="DefaultJWTClaimsValidator"
+ p:encryptionOptional="%{idp.oidc.encryptionOptional:true}"
p:forcePKCE="%{idp.oidc.forcePKCE:false}"
- p:allowPKCEPlain="%{idp.oidc.allowPKCEPlain:false}"
+ p:allowPKCEPlain="%{idp.oidc.allowPKCEPlain:false}"
+ p:allowUntrustedAudience="%{idp.oauth2.allowUntrustedAudience:false}"
p:iDTokenLifetime="%{idp.oidc.idToken.defaultLifetime:PT1H}"
p:accessTokenLifetime="%{idp.oidc.accessToken.defaultLifetime:PT10M}"
p:refreshTokenLifetime="%{idp.oidc.refreshToken.defaultLifetime:PT2H}"
@@ -43,6 +45,7 @@
<bean id="OIDC.UserInfo" parent="AbstractOIDCProfile" lazy-init="true"
class="net.shibboleth.oidc.profile.config.OIDCUserInfoConfiguration"
p:issuer-ref="issuer"
+ p:encryptionOptional="%{idp.oidc.encryptionOptional:true}"
p:deniedUserInfoAttributes="%{idp.oidc.deniedUserInfoAttributes:}" />
<bean id="OIDC.Registration" parent="AbstractOIDCProfile" lazy-init="true"
@@ -176,6 +179,22 @@
<constructor-arg value="true" />
</bean>
</property>
+ <property name="encryptionOptionalPredicate">
+ <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+ <constructor-arg>
+ <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptionOptional" />
+ </constructor-arg>
+ <constructor-arg value="%{idp.oidc.encryptionOptional:true}" />
+ </bean>
+ </property>
+ <property name="allowUntrustedAudiencePredicate">
+ <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+ <constructor-arg>
+ <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="allowUntrustedAudience" />
+ </constructor-arg>
+ <constructor-arg value="%{idp.oauth2.allowUntrustedAudience:false}" />
+ </bean>
+ </property>
<property name="iDTokenLifetimeLookupStrategy">
<bean parent="shibboleth.MDDrivenDurationProperty" p:propertyName="iDTokenLifetime"
p:defaultValue="%{idp.oidc.idToken.defaultLifetime:PT1H}" />
@@ -238,7 +257,6 @@
</property>
</bean>
</property>
- <!-- Last 3 are marked for removal. -->
<property name="forcePKCEPredicate">
<bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
<constructor-arg>
@@ -290,6 +308,14 @@
<property name="issuerLookupStrategy">
<bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="issuer" p:defaultValue-ref="issuer" />
</property>
+ <property name="encryptionOptionalPredicate">
+ <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+ <constructor-arg>
+ <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encryptionOptional" />
+ </constructor-arg>
+ <constructor-arg value="%{idp.oidc.encryptionOptional:true}" />
+ </bean>
+ </property>
<property name="deniedUserInfoAttributesLookupStrategy">
<bean parent="shibboleth.MDDrivenSetProperty" p:propertyName="deniedUserInfoAttributes">
<property name="defaultValue">
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContextTest.java
similarity index 61%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContextTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContextTest.java
index ea6aca21..6fdd01a4 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetEntityIdToSAMLPeerEntityContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/messaging/impl/SetEntityIdToSAMLPeerEntityContextTest.java
@@ -15,27 +15,20 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl;
import java.util.function.Function;
import org.mockito.Mockito;
import org.opensaml.messaging.context.MessageContext;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
-import org.springframework.webflow.execution.Event;
-import org.springframework.webflow.execution.RequestContext;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.nimbusds.oauth2.sdk.id.ClientID;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
-import net.shibboleth.idp.profile.testing.RequestContextBuilder;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.SetEntityIdToSAMLPeerEntityContext;
-import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.UnmodifiableComponentException;
import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
@@ -45,78 +38,72 @@ import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
*/
public class SetEntityIdToSAMLPeerEntityContextTest {
- private SetEntityIdToSAMLPeerEntityContext action;
+ private SetEntityIdToSAMLPeerEntityContext handler;
- private RequestContext requestCtx;
-
- private ProfileRequestContext prc;
+ private MessageContext messageCtx;
private String clientId = "mockClientId";
@BeforeMethod
public void setup() {
- action = new SetEntityIdToSAMLPeerEntityContext();
+ handler = new SetEntityIdToSAMLPeerEntityContext();
}
public void initialize(final SAMLPeerEntityContext entityContext,
final Function<MessageContext, ClientID> clientIdLookupStrategy) throws ComponentInitializationException {
- action.setClientIDLookupStrategy(clientIdLookupStrategy);
- action.initialize();
- requestCtx = new RequestContextBuilder().buildRequestContext();
- prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
+ handler.setClientIDLookupStrategy(clientIdLookupStrategy);
+ handler.initialize();
+ messageCtx = new MessageContext();
if (entityContext != null) {
- prc.getInboundMessageContext().addSubcontext(entityContext);
+ messageCtx.addSubcontext(entityContext);
}
}
@Test(expectedExceptions = { ConstraintViolationException.class })
public void setClientIDLookupStrategy_shouldThrowIfGivenNull() {
- action.setClientIDLookupStrategy(null);
+ handler.setClientIDLookupStrategy(null);
}
@Test(expectedExceptions = { UnmodifiableComponentException.class })
public void setClientIDLookupStrategy_shouldThrowIfCalledAfterInitialization()
throws ComponentInitializationException {
- action.initialize();
- action.setClientIDLookupStrategy(null);
+ handler.initialize();
+ handler.setClientIDLookupStrategy(null);
}
@Test(expectedExceptions = { ConstraintViolationException.class })
public void setEntityContextClass_shouldThrowIfGivenNull() {
- action.setEntityContextClass(null);
+ handler.setEntityContextClass(null);
}
@Test(expectedExceptions = { UnmodifiableComponentException.class })
- public void setEntityContextClass_shouldThrowIfCalledAfterInitialization()
- throws ComponentInitializationException {
- action.initialize();
- action.setEntityContextClass(null);
+ public void setEntityContextClass_shouldThrowIfCalledAfterInitialization() throws ComponentInitializationException {
+ handler.initialize();
+ handler.setEntityContextClass(null);
}
- @Test
- public void execute_shouldSetInvalidMsgEventIfNoEntityContext() throws ComponentInitializationException {
+ @Test(expectedExceptions=MessageHandlerException.class)
+ public void execute_shouldSetInvalidMsgEventIfNoEntityContext() throws ComponentInitializationException,
+ MessageHandlerException {
initialize(null, mockClientIdLookupStrategy(clientId));
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
+ handler.invoke(messageCtx);
}
@Test
- public void execute_shouldSetNullClientIdWhenLookupReturnsNull() throws ComponentInitializationException {
+ public void execute_shouldSetNullClientIdWhenLookupReturnsNull() throws ComponentInitializationException,
+ MessageHandlerException {
initialize(new SAMLPeerEntityContext(), mockClientIdLookupStrategy(null));
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
- final SAMLPeerEntityContext entityCtx =
- prc.getInboundMessageContext().getSubcontext(SAMLPeerEntityContext.class);
+ handler.invoke(messageCtx);
+ final SAMLPeerEntityContext entityCtx = messageCtx.getSubcontext(SAMLPeerEntityContext.class);
Assert.assertNull(entityCtx.getEntityId());
}
@Test
- public void execute_shouldSetClientIdWhenPrequisitesMet() throws ComponentInitializationException {
+ public void execute_shouldSetClientIdWhenPrequisitesMet() throws ComponentInitializationException,
+ MessageHandlerException {
initialize(new SAMLPeerEntityContext(), mockClientIdLookupStrategy(clientId));
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
- final SAMLPeerEntityContext entityCtx =
- prc.getInboundMessageContext().getSubcontext(SAMLPeerEntityContext.class);
+ handler.invoke(messageCtx);
+ final SAMLPeerEntityContext entityCtx = messageCtx.getSubcontext(SAMLPeerEntityContext.class);
Assert.assertEquals(entityCtx.getEntityId(), clientId);
}
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 b345a871..d883801b 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
@@ -21,11 +21,6 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
-import net.shibboleth.idp.attribute.EmptyAttributeValue;
-import net.shibboleth.idp.attribute.EmptyAttributeValue.EmptyType;
-import net.shibboleth.idp.attribute.IdPAttribute;
-import net.shibboleth.idp.attribute.StringAttributeValue;
-import net.shibboleth.idp.attribute.context.AttributeContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
@@ -37,15 +32,12 @@ import net.shibboleth.utilities.java.support.component.ComponentInitializationEx
import net.shibboleth.utilities.java.support.logic.FunctionSupport;
import net.shibboleth.utilities.java.support.security.DataSealerException;
-import java.net.URI;
-import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -56,11 +48,7 @@ import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.nimbusds.jwt.JWTClaimsSet;
-import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.oauth2.sdk.TokenRequest;
-import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
-import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.id.ClientID;
// Checkstyle: ThrowsCount OFF
@@ -75,41 +63,15 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
protected void setUp() throws Exception {
super.setUp();
- final TokenRequest req = new TokenRequest(new URI("http://localhost"),
- new ClientSecretBasic(new ClientID(clientId), new Secret("foo")),
- new ClientCredentialsGrant(),
- Scope.parse("email profile"),
- Collections.singletonList(new URI("https://rp.example.org")),
- null);
- profileRequestCtx.getInboundMessageContext().setMessage(req);
-
- respCtx.setScope(new Scope());
respCtx.setAuthTime(Instant.now());
respCtx.setSubject(clientId);
respCtx.setAcr("0");
+ respCtx.setScope(new Scope());
+ respCtx.getAudience().add("https://rp.example.org");
action = new BuildAccessToken();
}
- /**
- * Test invalid message cases.
- *
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- */
- @Test
- public void testBadRequest() throws NoSuchAlgorithmException, ComponentInitializationException {
- initAction(null, null, null);
-
- profileRequestCtx.getInboundMessageContext().setMessage(request);
- Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
-
- profileRequestCtx.getInboundMessageContext().setMessage(null);
- event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
- }
-
/**
* Test no client ID.
*
@@ -119,89 +81,15 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
@Test
public void testNoClientID() throws NoSuchAlgorithmException, ComponentInitializationException {
action.setClientIDLookupStrategy(FunctionSupport.constant(null));
- action.setScopeAttribute(null);
- action.setAudienceAttribute(null);
action.setDataSealer(getDataSealer());
action.initialize();
final Event event = action.execute(requestCtx);
ActionTestingSupport.assertEvent(event, EventIds.MESSAGE_PROC_ERROR);
}
-
- /**
- * Test no source of audience claim.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- * @throws URISyntaxException
- */
- @Test
- public void testNoAudience() throws ParseException, DataSealerException, ComponentInitializationException,
- NoSuchAlgorithmException, URISyntaxException {
-
- final TokenRequest req = new TokenRequest(new URI("http://localhost"),
- new ClientSecretBasic(new ClientID(clientId), new Secret("foo")),
- new ClientCredentialsGrant(),
- Scope.parse("email profile"));
- profileRequestCtx.getInboundMessageContext().setMessage(req);
-
- initAction(null, null, null);
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
- }
-
-
- /**
- * Test no source of audience claim from attribute.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- * @throws URISyntaxException
- */
- @Test
- public void testNoAudienceFromAttribute() throws ParseException, DataSealerException,
- ComponentInitializationException, NoSuchAlgorithmException, URISyntaxException {
-
- initAction(null, null, "aud");
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- attributeCtx.setUnfilteredIdPAttributes(Collections.singletonList(new IdPAttribute("aud")));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
- }
-
- /**
- * Test no source of scope claim from attribute.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- * @throws URISyntaxException
- */
- @Test
- public void testNoScopeFromAttribute() throws ParseException, DataSealerException,
- ComponentInitializationException, NoSuchAlgorithmException, URISyntaxException {
-
- initAction(null, "scope", null);
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- attributeCtx.setUnfilteredIdPAttributes(Collections.singletonList(new IdPAttribute("foo")));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
- verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), new Scope(),
- Collections.singletonList("https://rp.example.org"));
- }
/**
- * Basic success case, direct reuse of requested scope/audience.
+ * Basic success case.
*
* @throws ParseException
* @throws DataSealerException
@@ -212,7 +100,7 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
public void testOpaqueSuccess() throws ParseException, DataSealerException, ComponentInitializationException,
NoSuchAlgorithmException {
- initAction(null, null, null);
+ initAction(null);
final Event event = action.execute(requestCtx);
ActionTestingSupport.assertProceedEvent(event);
@@ -220,62 +108,6 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), new Scope(),
Collections.singletonList("https://rp.example.org"));
}
-
- /**
- * Basic success case, resolve scope.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- */
- @Test
- public void testOpaqueSuccessWithScopeResolution() throws ParseException, DataSealerException,
- ComponentInitializationException, NoSuchAlgorithmException {
-
- initAction(null, "scope", null);
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- final IdPAttribute scope = new IdPAttribute("scope");
- scope.setValues(List.of(new StringAttributeValue("foo"),
- new EmptyAttributeValue(EmptyType.NULL_VALUE),
- new StringAttributeValue("bar")));
- attributeCtx.setUnfilteredIdPAttributes(List.of(scope));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
-
- verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), Scope.parse("foo bar"),
- Collections.singletonList("https://rp.example.org"));
- }
-
- /**
- * Basic success case, resolve audience.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- */
- @Test
- public void testOpaqueSuccessWithAudResolution() throws ParseException, DataSealerException,
- ComponentInitializationException, NoSuchAlgorithmException {
-
- initAction(null, null, "aud");
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- final IdPAttribute aud = new IdPAttribute("aud");
- aud.setValues(List.of(new StringAttributeValue("foo"),
- new EmptyAttributeValue(EmptyType.NULL_VALUE),
- new StringAttributeValue("bar")));
- attributeCtx.setUnfilteredIdPAttributes(List.of(aud));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
-
- verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), new Scope(),
- List.of("foo", "bar"));
- }
/**
* Basic success case, direct reuse of requested scope/audience.
@@ -289,7 +121,7 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
public void testJWTSuccess() throws ParseException, ComponentInitializationException, NoSuchAlgorithmException,
DataSealerException {
- initAction(null, null, null);
+ initAction("JWT");
final Event event = action.execute(requestCtx);
ActionTestingSupport.assertProceedEvent(event);
@@ -297,84 +129,23 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), new Scope(),
Collections.singletonList("https://rp.example.org"));
}
-
- /**
- * Basic success case, resolve scope.
- *
- * @throws ParseException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- * @throws DataSealerException
- */
- @Test
- public void testJWTSuccessWithScopeResolution() throws ParseException, ComponentInitializationException,
- NoSuchAlgorithmException, DataSealerException {
-
- initAction(null, "scope", null);
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- final IdPAttribute scope = new IdPAttribute("scope");
- scope.setValues(List.of(new StringAttributeValue("foo"),
- new EmptyAttributeValue(EmptyType.NULL_VALUE),
- new StringAttributeValue("bar")));
- attributeCtx.setUnfilteredIdPAttributes(List.of(scope));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
-
- verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), Scope.parse("foo bar"),
- Collections.singletonList("https://rp.example.org"));
- }
-
- /**
- * Basic success case, resolve audience.
- *
- * @throws ParseException
- * @throws DataSealerException
- * @throws ComponentInitializationException
- * @throws NoSuchAlgorithmException
- */
- @Test
- public void testJWTSuccessWithAudResolution() throws ParseException, DataSealerException,
- ComponentInitializationException, NoSuchAlgorithmException {
-
- initAction("JWT", null, "aud");
-
- final AttributeContext attributeCtx = rpCtx.getSubcontext(AttributeContext.class, true);
- final IdPAttribute aud = new IdPAttribute("aud");
- aud.setValues(List.of(new StringAttributeValue("foo"),
- new EmptyAttributeValue(EmptyType.NULL_VALUE),
- new StringAttributeValue("bar")));
- attributeCtx.setUnfilteredIdPAttributes(List.of(aud));
-
- final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertProceedEvent(event);
-
- verifyClaims(respCtx.getSubcontext(AccessTokenContext.class), new Scope(),
- List.of("foo", "bar"));
- }
/**
* Init action bean based on test.
*
* @param type token type to use
- * @param scope scope attribute ID
- * @param audience audience attribute ID
*
* @throws ComponentInitializationException
* @throws NoSuchAlgorithmException
*/
- private void initAction(@Nullable @NotEmpty final String type, @Nullable @NotEmpty final String scope,
- @Nullable @NotEmpty final String audience)
+ private void initAction(@Nullable @NotEmpty final String type)
throws ComponentInitializationException, NoSuchAlgorithmException {
if ("JWT".equals(type)) {
action.setAccessTokenTypeLookupStrategy(FunctionSupport.constant("JWT"));
} else if (type == null) {
action.setDataSealer(getDataSealer());
}
-
- action.setScopeAttribute(scope);
- action.setAudienceAttribute(audience);
+ action.setClientIDLookupStrategy(FunctionSupport.constant(new ClientID(clientId)));
action.initialize();
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java
new file mode 100644
index 00000000..22996232
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudienceTest.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.oauth2.profile.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.List;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
+import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.profile.action.EventIds;
+import org.springframework.webflow.execution.Event;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.AuthorizationCode;
+import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
+import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+/** {@link ValidateAudience} unit test. */
+public class ValidateAudienceTest extends BaseOIDCResponseActionTest {
+
+ /** Action to test. */
+ private ValidateAudience action;
+
+ /** Client metadata. */
+ private OIDCClientMetadata metaData;
+
+ @BeforeMethod
+ private void init() throws ComponentInitializationException, URISyntaxException {
+ action = new ValidateAudience();
+ action.initialize();
+ final OIDCMetadataContext oidcCtx =
+ profileRequestCtx.getInboundMessageContext().getSubcontext(OIDCMetadataContext.class, true);
+ metaData = new OIDCClientMetadata();
+ metaData.setCustomField("audience", List.of("https://sp.example.org", "https://sp2.example.org"));
+ final OIDCClientInformation information =
+ new OIDCClientInformation(new ClientID("test"), null, metaData, null, null, null);
+ oidcCtx.setClientInformation(information);
+ }
+
+ /**
+ * Test that action filters out unregistered audiences on back-channel with no prior grant.
+ *
+ * <p>This is most common for the client_credentials grant, which has no prior step.</p>
+ *
+ * @throws ComponentInitializationException
+ * @throws URISyntaxException
+ */
+ @Test
+ public void testTokenNoneAllowed() throws ComponentInitializationException, URISyntaxException {
+
+ final TokenRequest req = new TokenRequest(new URI("http://localhost"),
+ new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
+ new ClientCredentialsGrant(),
+ null,
+ List.of(new URI("https://sp3.example.org")),
+ null
+ );
+ setTokenRequest(req);
+
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+ }
+
+ /**
+ * Test that action filters out unregistered audiences on back-channel with no prior grant
+ * and includes the registered values.
+ *
+ * @throws ComponentInitializationException
+ * @throws URISyntaxException
+ */
+ @Test
+ public void testTokenSuccess() throws ComponentInitializationException, URISyntaxException {
+
+ final TokenRequest req = new TokenRequest(new URI("http://localhost"),
+ new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
+ new ClientCredentialsGrant(),
+ null,
+ List.of(new URI("https://sp.example.org"),
+ new URI("https://sp3.example.org"),
+ new URI("https://sp2.example.org")),
+ null
+ );
+ setTokenRequest(req);
+
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(respCtx.getAudience(),
+ List.of("https://sp.example.org", "https://sp2.example.org"));
+ }
+
+ /**
+ * Test that action filters out unregistered audiences on back-channel with no prior grant
+ * and selects the first registered value.
+ *
+ * @throws ComponentInitializationException
+ * @throws URISyntaxException
+ */
+ @Test
+ public void testTokenFirstAudience() throws ComponentInitializationException, URISyntaxException {
+
+ final TokenRequest req = new TokenRequest(new URI("http://localhost"),
+ new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
+ new ClientCredentialsGrant(),
+ null
+ );
+ setTokenRequest(req);
+
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertProceedEvent(event);
+ Assert.assertEquals(respCtx.getAudience(), List.of("https://sp.example.org"));
+ }
+
+ /**
+ * Test that action filters out unregistered audiences on back-channel with prior grant of nothing.
+ *
+ * <p>This is likely impossible in practice, but theoretical.</p>
+ *
+ * @throws ComponentInitializationException
+ * @throws URISyntaxException
+ */
+ @Test
+ public void testTokenFailNonePrevGranted() throws ComponentInitializationException, URISyntaxException {
+
+ final TokenRequest req = new TokenRequest(new URI("http://localhost"),
+ new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
+ new AuthorizationCodeGrant(new AuthorizationCode("foo"), new URI("http://localhost")),
+ null,
+ List.of(new URI("https://sp.example.org")),
+ null);
+ setTokenRequest(req);
+
+ final AuthorizeCodeClaimsSet claims = new AuthorizeCodeClaimsSet.Builder()
+ .setJWTID(idGenerator)
+ .setClientID(new ClientID("s6BhdRkqt3"))
+ .setIssuer("issuer")
+ .setPrincipal("userPrin")
+ .setSubject("subject")
+ .setIssuedAt(Instant.now())
+ .setExpiresAt(Instant.now())
+ .setAuthenticationTime(Instant.now())
+ .setRedirectURI(new URI("http://localhost"))
+ .setACR(new ACR("0"))
+ .setScope(new Scope())
+ .build();
+ respCtx.setAuthorizationGrantClaimsSet(claims);
+
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+ }
+
+ /**
+ * Test that action filters out unregistered audiences on back-channel with a prior grant.
+ *
+ * @throws ComponentInitializationException
+ * @throws URISyntaxException
+ */
+ @Test
+ public void testTokenFailPrevGranted() throws ComponentInitializationException, URISyntaxException {
+
+ final TokenRequest req = new TokenRequest(new URI("http://localhost"),
+ new ClientSecretBasic(new ClientID("s6BhdRkqt3"), new Secret("foo")),
+ new AuthorizationCodeGrant(new AuthorizationCode("foo"), new URI("http://localhost")),
+ null,
+ List.of(new URI("https://sp3.example.org")),
+ null);
+ setTokenRequest(req);
+
+ final AuthorizeCodeClaimsSet claims = new AuthorizeCodeClaimsSet.Builder()
+ .setJWTID(idGenerator)
+ .setClientID(new ClientID("s6BhdRkqt3"))
+ .setIssuer("issuer")
+ .setPrincipal("userPrin")
+ .setSubject("subject")
+ .setIssuedAt(Instant.now())
+ .setExpiresAt(Instant.now())
+ .setAuthenticationTime(Instant.now())
+ .setRedirectURI(new URI("http://localhost"))
+ .setScope(Scope.parse("openid email"))
+ .setAudience(List.of("https://sp3.example.org"))
+ .setACR(new ACR("0"))
+ .build();
+ respCtx.setAuthorizationGrantClaimsSet(claims);
+
+ final Event event = action.execute(requestCtx);
+ ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
index 5f4937fd..2ca7aed5 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
@@ -204,6 +204,7 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
metadata.setTokenEndpointAuthJWSAlg(tokenEndpointSigAlg);
metadata.setTokenEndpointAuthMethod(tokenEndpointMethod);
metadata.setUserInfoJWSAlg(userInfoSigAlg);
+ metadata.setCustomField("audience", List.of("https://rp.example.org", "https://rp2.example.org"));
final OIDCClientInformation information;
if (publicKey == null) {
information = new OIDCClientInformation(new ClientID(clientId), new Date(),
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/ClientCredentialsTokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/ClientCredentialsTokenFlowTest.java
index ecd09153..b109adfc 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/ClientCredentialsTokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/ClientCredentialsTokenFlowTest.java
@@ -42,11 +42,9 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
-import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.GrantType;
import com.nimbusds.oauth2.sdk.OAuth2Error;
import com.nimbusds.oauth2.sdk.Scope;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
index 3c94ef42..ca9944d8 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
@@ -39,6 +39,7 @@ import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrate
import net.shibboleth.utilities.java.support.security.impl.BasicKeystoreKeyStrategy;
import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifierGenerationStrategy;
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.security.credential.Credential;
@@ -68,7 +69,7 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.shibboleth.ext.spring.resource.ResourceHelper;
/** base class for tests expecting to have inbound and outbound msg ctxs etc in place. */
-public abstract class BaseOIDCResponseActionTest {
+public abstract class BaseOIDCResponseActionTest extends OpenSAMLInitBaseTestCase {
protected RequestContext requestCtx;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedTokenTest.java
index 6d7a81d1..c30441b3 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/EncryptProcessedTokenTest.java
@@ -20,7 +20,6 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
import net.minidev.json.JSONObject;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.EncryptProcessedToken;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
@@ -170,13 +169,13 @@ public class EncryptProcessedTokenTest {
}
/**
- * Test fail case of nothing to encrypt.
+ * Test case of nothing to encrypt.
*/
@Test
- public void testFailNoInput() {
+ public void testNoInput() {
oidcRespCtx.setProcessedToken(null);
final Event event = action.execute(requestCtx);
- ActionTestingSupport.assertEvent(event, EventIds.UNABLE_TO_ENCRYPT);
+ ActionTestingSupport.assertProceedEvent(event);
}
/**
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParametersTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParametersTest.java
index 87410b17..967f2329 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParametersTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/PopulateOIDCEncryptionParametersTest.java
@@ -17,7 +17,6 @@
package net.shibboleth.idp.plugin.oidc.op.profile.impl;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -31,11 +30,12 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.saml.saml2.profile.context.EncryptionContext;
import org.opensaml.xmlsec.EncryptionParameters;
import org.opensaml.xmlsec.EncryptionParametersResolver;
-import org.opensaml.xmlsec.criterion.EncryptionOptionalCriterion;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.base.Predicates;
+
/** {@link PopulateOIDCEncryptionParameters} unit test. */
public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseActionTest {
@@ -82,7 +82,11 @@ public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseAction
@Test
public void testSuccessFailsToResolve() throws NoSuchAlgorithmException, ComponentInitializationException {
resolver.resolve = false;
- resolver.optional = true;
+
+ action = new PopulateOIDCEncryptionParameters();
+ action.setEncryptionOptionalPredicate(Predicates.alwaysTrue());
+ action.setEncryptionParametersResolver(resolver);
+ action.initialize();
ActionTestingSupport.assertProceedEvent(action.execute(requestCtx));
Assert.assertNull(profileRequestCtx.getSubcontext(RelyingPartyContext.class)
@@ -95,6 +99,11 @@ public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseAction
@Test
public void testFailureFailsToResolve() throws NoSuchAlgorithmException, ComponentInitializationException {
resolver.resolve = false;
+
+ action = new PopulateOIDCEncryptionParameters();
+ action.setEncryptionOptionalPredicate(Predicates.alwaysFalse());
+ action.setEncryptionParametersResolver(resolver);
+ action.initialize();
ActionTestingSupport.assertEvent(action.execute(requestCtx), EventIds.INVALID_SEC_CFG);
}
@@ -122,7 +131,7 @@ public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseAction
@Test(expectedExceptions = ConstraintViolationException.class)
public void testFailureNullStrategyMetadataContext() {
action = new PopulateOIDCEncryptionParameters();
- action.setOIDCMetadataContextContextLookupStrategy(null);
+ action.setOidcMetadataContextLookupStrategy(null);
}
/**
@@ -138,8 +147,6 @@ public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseAction
public boolean resolve = true;
- public boolean optional;
-
@Override
public Iterable<EncryptionParameters> resolve(CriteriaSet criteria) throws ResolverException {
return null;
@@ -147,9 +154,6 @@ public class PopulateOIDCEncryptionParametersTest extends BaseOIDCResponseAction
@Override
public EncryptionParameters resolveSingle(CriteriaSet criteria) throws ResolverException {
- if (optional) {
- criteria.add(new EncryptionOptionalCriterion(true));
- }
return resolve ? new EncryptionParameters() : null;
}
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java
index 301245ed..d322c947 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java
@@ -132,7 +132,7 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
public void testTokenClientCredentials() throws ComponentInitializationException, URISyntaxException {
action = new ValidateScope();
- action.setRequestedScopesLookupStrategy(new TokenRequestScopeLookupFunction());
+ action.setRequestedScopeLookupStrategy(new TokenRequestScopeLookupFunction());
action.initialize();
final TokenRequest req = new TokenRequest(new URI("http://localhost"),
@@ -166,7 +166,7 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
public void testTokenNoGrantedScopes() throws ComponentInitializationException, URISyntaxException {
action = new ValidateScope();
- action.setRequestedScopesLookupStrategy(new TokenRequestScopeLookupFunction());
+ action.setRequestedScopeLookupStrategy(new TokenRequestScopeLookupFunction());
action.initialize();
final TokenRequest req = new TokenRequest(new URI("http://localhost"),
@@ -209,7 +209,7 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
public void testTokenGrantedScopes() throws ComponentInitializationException, URISyntaxException {
action = new ValidateScope();
- action.setRequestedScopesLookupStrategy(new TokenRequestScopeLookupFunction());
+ action.setRequestedScopeLookupStrategy(new TokenRequestScopeLookupFunction());
action.initialize();
final TokenRequest req = new TokenRequest(new URI("http://localhost"),
@@ -258,7 +258,7 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
public void testUserInfoGrantedScopes() throws ComponentInitializationException, URISyntaxException {
action = new ValidateScope();
- action.setRequestedScopesLookupStrategy(null);
+ action.setRequestedScopeLookupStrategy(null);
action.initialize();
final UserInfoRequest req =
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/attribute-resolver.xml b/idp-oidc-extension-impl/src/test/resources/conf/attribute-resolver.xml
index d93be7c9..a9301b28 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/attribute-resolver.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/attribute-resolver.xml
@@ -76,31 +76,6 @@
<AttributeEncoder xsi:type="oidc:OIDCString" name="sub" />
</AttributeDefinition>
- <!-- Used to drive client_credentials grant handling. -->
-
- <AttributeDefinition id="requestedScope" xsi:type="ScriptedAttribute" resolutionPhases="oidc/token">
- <Script>
- <![CDATA[
- requestedScope.getValues().addAll(
- resolutionContext.getSubcontext(
- "net.shibboleth.idp.plugin.oidc.op.profile.context.OAuthAttributeResolutionContext").getScope()
- );
- ]]>
- </Script>
- </AttributeDefinition>
-
- <AttributeDefinition id="requestedAudience" xsi:type="ScriptedAttribute" resolutionPhases="oidc/token">
- <Script>
- <![CDATA[
- requestedAudience.getValues().addAll(
- resolutionContext.getSubcontext(
- "net.shibboleth.idp.plugin.oidc.op.profile.context.OAuthAttributeResolutionContext").getResources()
- );
- ]]>
- </Script>
- </AttributeDefinition>
-
-
<!-- ========================================== -->
<!-- Data Connectors -->
<!-- ========================================== -->
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/oidc.properties b/idp-oidc-extension-impl/src/test/resources/conf/oidc.properties
index 557b62fd..0163cbbe 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/oidc.properties
+++ b/idp-oidc-extension-impl/src/test/resources/conf/oidc.properties
@@ -7,6 +7,3 @@ idp.oidc.subject.salt = isfd07fsddfs70sdf9d99s8
idp.oidc.discovery.template = src/test/resources/conf/openid-configuration.json
idp.oidc.dynreg.defaultMetadataPolicyFile = src/test/resources/conf/metadata-policy1.json
-
-idp.oauth.accessToken.scopeAttribute = requestedScope
-idp.oauth.accessToken.audienceAttribute = requestedAudience
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
index d9c39df4..13f0b1e0 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/relying-party.xml
@@ -44,7 +44,7 @@
<ref bean="SAML2.AttributeQuery.MDDriven" />
<ref bean="SAML2.ArtifactResolution.MDDriven" />
<ref bean="OIDC.SSO.MDDriven" />
- <ref bean="OIDC.Token.MDDriven" />
+ <bean parent="OIDC.Token.MDDriven" p:allowUntrustedAudience="true" />
<ref bean="OIDC.UserInfo.MDDriven" />
<ref bean="OIDC.Registration.MDDriven" />
<ref bean="OAUTH2.Revocation.MDDriven" />
@@ -81,7 +81,7 @@
<bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdJWT">
<property name="profileConfigurations">
<list>
- <bean parent="OIDC.Token.MDDriven" p:accessTokenType="JWT" />
+ <bean parent="OIDC.Token.MDDriven" p:accessTokenType="JWT" p:allowUntrustedAudience="true" />
</list>
</property>
</bean>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list