[java-idp-plugin-oidc-rp] branch main updated: Improve client credential handling and wire into client_auth action
Phil Smart
philip.smart at jisc.ac.uk
Fri May 20 12:59:45 UTC 2022
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=af61cba4154095ab00866314df6324da4eb6fef0
The following commit(s) were added to refs/heads/main by this push:
new af61cba Improve client credential handling and wire into client_auth action
af61cba is described below
commit af61cba4154095ab00866314df6324da4eb6fef0
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri May 20 13:59:36 2022 +0100
Improve client credential handling and wire into client_auth action
- Wire up new CriterionCredentialResolver
---
...atureValidationConfigurationLookupFunction.java | 8 +-
.../rp/storage/ClientAuthenticationDetails.java | 3 +
idp-oidc-rp-impl/pom.xml | 5 +
.../DefaultClientAuthenticationLookupStrategy.java | 3 +
.../config/DefaultClientSecretLookupStrategy.java | 146 +++++++++++++++++++
...nitializeOAuth2ClientAuthenticationContext.java | 114 ++++++++++++++-
.../oidc-relying-party-authn-beans.xml | 21 ++-
.../oidc-relying-party-authn-flow.xml | 7 +-
.../idp/service/relying-party/postconfig.xml | 57 ++++----
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 159 +++++++++++++++++++--
.../resources/conf/test-relying-party-system.xml | 12 +-
11 files changed, 479 insertions(+), 56 deletions(-)
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java
index c4856ff..e29535f 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/IDTokenSignatureValidationConfigurationLookupFunction.java
@@ -24,6 +24,8 @@ import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
+import com.nimbusds.jwt.SignedJWT;
+
import net.shibboleth.idp.profile.config.ProfileConfiguration;
import net.shibboleth.idp.profile.config.SecurityConfiguration;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
@@ -41,7 +43,7 @@ import net.shibboleth.oidc.security.SignatureValidationConfiguration;
* </p>
*/
public class IDTokenSignatureValidationConfigurationLookupFunction
- extends AbstractRelyingPartyLookupFunction<List<SignatureValidationConfiguration>> {
+ extends AbstractRelyingPartyLookupFunction<List<SignatureValidationConfiguration<SignedJWT>>> {
/** A resolver for default security configurations. */
@Nullable
@@ -59,9 +61,9 @@ public class IDTokenSignatureValidationConfigurationLookupFunction
/** {@inheritDoc} */
@Override
@Nullable
- public List<SignatureValidationConfiguration> apply(@Nullable final ProfileRequestContext input) {
+ public List<SignatureValidationConfiguration<SignedJWT>> apply(@Nullable final ProfileRequestContext input) {
- final List<SignatureValidationConfiguration> configs = new ArrayList<>();
+ final List<SignatureValidationConfiguration<SignedJWT>> configs = new ArrayList<>();
final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
if (rpc != null) {
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/storage/ClientAuthenticationDetails.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/storage/ClientAuthenticationDetails.java
index b8cbdd4..fa12f6c 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/storage/ClientAuthenticationDetails.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/storage/ClientAuthenticationDetails.java
@@ -10,6 +10,9 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.logic.Constraint;
/** A bean to hold client_authentication registration details.*/
+//TODO: this is not a storage system bean, the json properties are not needed.
+//TODO we no longer use this, remove!
+ at Deprecated
@Immutable
public class ClientAuthenticationDetails {
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 6ad016c..5e68638 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -153,6 +153,11 @@
<artifactId>idp-profile-impl</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${idp.groupId}</groupId>
+ <artifactId>idp-profile-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>net.shibboleth.idp</groupId>
<artifactId>idp-profile-api</artifactId>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java
index cefdee5..1b0944b 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java
@@ -47,8 +47,11 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
* A strategy that produces a client authentication method either directly from the information supplied,
* or derived from a lookup strategy.
+ *
+ * @deprecated see see InitializeOAuth2ClientAuthenticationContext
*/
@ThreadSafeAfterInit
+ at Deprecated
public class DefaultClientAuthenticationLookupStrategy extends AbstractClientAuthenticationLookupStrategy {
/** A fixed client_id to use.*/
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientSecretLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientSecretLookupStrategy.java
new file mode 100644
index 0000000..f3d3f66
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientSecretLookupStrategy.java
@@ -0,0 +1,146 @@
+/*
+ * 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.authn.oidc.rp.config;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A strategy that produces a client_secret either directly from the one supplied, or derived from the
+ * lookup strategy.
+ */
+//TODO secret could be private key
+ at ThreadSafe
+public class DefaultClientSecretLookupStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<ProfileRequestContext, String> {
+
+ /**
+ * A fixed client_secret to use over any injected strategy to locate one.
+ * Must be UTF-8 encoded.
+ */
+ @Nullable private String clientSecret;
+
+ /** The strategy used to lookup or create the {@link OAuth2ClientContext}.*/
+ @Nonnull private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
+
+ /** Map of client_id to client_secret. Can be {@literal null} if a fixed client_id are secret are used.*/
+ @Nullable @NotLive private Map<String, String> clientIdToClientSecretMap;
+
+ /**
+ * Constructor.
+ */
+ public DefaultClientSecretLookupStrategy() {
+ oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
+ new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
+ new OutboundMessageContextLookup()));
+ }
+
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (clientSecret == null && clientIdToClientSecretMap == null) {
+ throw new ComponentInitializationException("Must supply either a fixed client_secret or a "
+ + "client_id to client_secret map");
+ }
+ }
+
+ /**
+ * Set the client_id to client_secret map.
+ *
+ * @param map the map of client_id to client_secret details
+ */
+ public void setClientIdToClientSecretMap(@Nullable final Map<String, String> map) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ if (map == null) {
+ clientIdToClientSecretMap = Collections.emptyMap();
+ } else {
+ clientIdToClientSecretMap = Collections.unmodifiableMap(map);
+ }
+ }
+
+ /**
+ * Set the strategy to lookup the {@link OAuth2ClientContext}
+ * from the {@link ProfileRequestContext}.
+ *
+ * @param strgy the strategy.
+ */
+ public void setOAuth2ClientContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, OAuth2ClientContext> strgy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ oauth2ClientContextLookupStrategy = Constraint.isNotNull(strgy,
+ "OAuth2 client context lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the client_secret. Must be UTF-8 encoded.
+ *
+ * @param secret the client_secret
+ */
+ public void setClientSecret(@Nullable final String secret) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ clientSecret = secret;
+ }
+
+ @Override
+ @Nullable public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ // Use supplied client_id first
+ if (clientSecret != null) {
+ return clientSecret;
+ }
+ // Else pull it from the map
+ final OAuth2ClientContext clientCtx = oauth2ClientContextLookupStrategy.apply(profileRequestContext);
+ if (clientCtx == null || StringSupport.trimOrNull(clientCtx.getClientId()) == null) {
+ return null;
+ }
+ final String secret =
+ clientIdToClientSecretMap.get(clientCtx.getClientId());
+
+ if (secret!= null && secret.length() > 0) {
+ return secret;
+ }
+ return null;
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
index e9d564a..804b867 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationContext.java
@@ -17,20 +17,31 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import javax.crypto.SecretKey;
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.opensaml.security.credential.Credential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -38,6 +49,7 @@ import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.authn.context.OAuth2ClientAuthenticationContext;
import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
+import net.shibboleth.oidc.security.credential.ExpiringJWKCredential;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -95,6 +107,8 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
*/
public void setRelyingPartyContextLookupStrategy(
@Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
relyingPartyContextLookupStrategy =
Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
}
@@ -149,17 +163,107 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
super.doExecute(profileRequestContext);
- final ClientAuthentication clientAuth = profileConfiguration.getClientAuthentication(profileRequestContext);
-
- if (clientAuth == null) {
+ final String clientAuthMethod =
+ profileConfiguration.getClientAuthenticationMethod(profileRequestContext);
+ if (clientAuthMethod == null) {
log.error("{} No client authentication mode found from profile configuration", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
return;
}
- oauth2ClientAuthenticationContext.setClientAuthentication(clientAuth);
+ final String clientId =
+ profileConfiguration.getClientId(profileRequestContext);
+ if (clientId == null) {
+ log.error("{} No client_id found from profile configuration", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
+ return;
+ }
+
+ final Credential clientCredential =
+ profileConfiguration.getClientCredential(profileRequestContext);
+ if (clientCredential == null) {
+ log.error("{} No client credential found from profile configuration", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
+ return;
+ }
+
+ final ClientAuthentication clientAuthentication =
+ constructClientAuthentication(clientId, clientAuthMethod, clientCredential);
+
+ if (clientAuthentication == null) {
+ log.error("{} No client authentication could be constructed", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
+ return;
+ }
+
+ oauth2ClientAuthenticationContext.setClientAuthentication(clientAuthentication);
log.debug("{} Initialized OAuth2 Client Authentication Context: Found client authentication mode "
- + "'{}' for client '{}'",getLogPrefix(), clientAuth.getMethod(), clientAuth.getClientID());
+ + "'{}' for client '{}'",getLogPrefix(), clientAuthentication.getMethod(),
+ clientAuthentication.getClientID());
+ }
+
+ /**
+ * Construct the client authentication from the given client authentication record.
+ *
+ * @param clientId the client_id
+ * @param tokenEndpointAuthMethod the token endpoint authentication method
+ * @param clientCredential the client credential
+ *
+ * @return the constructed client authentication
+ */
+ @Nullable protected ClientAuthentication constructClientAuthentication(
+ @Nonnull final String clientId, @Nonnull final String tokenEndpointAuthMethod,
+ @Nonnull final Credential clientCredential) {
+
+ // TODO support private key jwt
+ if (clientCredential.getSecretKey() == null) {
+ log.warn("{} Client credential is not a symmetric key, only client_secret currently supported to"
+ + "construct client authentication",getLogPrefix());
+ return null;
+ }
+ Duration secretExpiresAt = Duration.ZERO;
+ if (clientCredential instanceof ExpiringJWKCredential) {
+ secretExpiresAt = ((ExpiringJWKCredential)clientCredential).getCredentialExpiresAt();
+ }
+
+ final String secretKeyConverted = convertSecretKeyToString(clientCredential.getSecretKey());
+ if (secretKeyConverted == null) {
+ log.warn("{} Secret key is null, unable to construct client authentication",getLogPrefix());
+ return null;
+ }
+ final Secret secret = secretExpiresAt.toSeconds() == 0 ?
+ new Secret(secretKeyConverted) :
+ new Secret(secretKeyConverted, Date.from(Instant.ofEpochSecond(secretExpiresAt.toSeconds())));
+
+
+ if (secret.expired()) {
+ log.warn("{} Client secret has expired for client '{}'", getLogPrefix(), clientId);
+ return null;
+ }
+
+ final ClientAuthenticationMethod method = new ClientAuthenticationMethod(tokenEndpointAuthMethod);
+ if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {
+ return new ClientSecretBasic(new ClientID(clientId), secret);
+ } else if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST)) {
+ return new ClientSecretPost(new ClientID(clientId), secret);
+ }
+ log.warn("{}: Client authentication method '{}' not supported for client '{}'", getLogPrefix(),
+ tokenEndpointAuthMethod, clientId);
+ return null;
+
+ }
+
+ /**
+ * Convert the encoded byte array representing the secret into a UTF-8 String.
+ *
+ * @param key the key to convert
+ * @return the UTF-8 encoded string value of the secret.
+ */
+ @Nullable private String convertSecretKeyToString(@Nullable final SecretKey key) {
+ if (key == null || key.getEncoded() == null) {
+ return null;
+ }
+ return new String(key.getEncoded(),StandardCharsets.UTF_8);
}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index b544ef6..62a1059 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -251,7 +251,25 @@
</bean>
- <!-- Process token -->
+ <!-- ID_TOKEN Decryption -->
+
+ <bean id="PopulateIDTokenDecryptionParameters" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
+ <constructor-arg>
+ <bean class="org.opensaml.profile.action.impl.PopulateDecryptionParameters"
+ p:configurationLookupStrategy-ref="shibboleth.authn.oidc.rp.DecryptionConfigurationLookup"
+ p:decryptionParametersResolver-ref="shibboleth.DecryptionParametersResolver" />
+ </constructor-arg>
+ </bean>
+
+ <!-- FIXME: this bean already exists in security-system -->
+ <bean id="shibboleth.authn.oidc.rp.DecryptionConfigurationLookup" lazy-init="true"
+ class="net.shibboleth.idp.profile.config.navigate.DecryptionConfigurationLookupFunction"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
+
+ <bean id="shibboleth.DecryptionParametersResolver"
+ class="org.opensaml.xmlsec.impl.BasicDecryptionParametersResolver" />
+
+ <!-- ID_TOKEN Signature Validation -->
<bean id="PopulateIDTokenSignatureValidationParameters" parent="NestedWebFlowProfileActionAdaptor"
scope="prototype">
@@ -269,7 +287,6 @@
class="net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.IDTokenSignatureValidationConfigurationLookupFunction"
p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyConfigurationResolver" />
-
<!-- TODO decryption as well? -->
<bean id="HandleIDTokenValidation" parent="NestedWebFlowMessageHandlerAdaptor" scope="prototype"
c:executionDirection="INBOUND">
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
index 8a2a9b9..5d45e82 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-flow.xml
@@ -59,6 +59,7 @@
</view-state>
<action-state id="ValidateResponse">
+ <!-- PopulateClientTLSValidationParameters? -->
<evaluate expression="ValidateExternalAuthenticationContext" />
<evaluate expression="ValidateAuthenticationResponseResult" />
<evaluate expression="ValidateResponseStateMatchesRequest" />
@@ -107,7 +108,7 @@
<!-- TODO claim validation will differ per grant_type -->
<action-state id="ValidateToken">
- <!-- <evaluate expression="PopulateTokenEncryptionParameters" /> -->
+ <evaluate expression="PopulateIDTokenDecryptionParameters" />
<evaluate expression="PopulateIDTokenSignatureValidationParameters" />
<evaluate expression="HandleIDTokenValidation" />
@@ -142,7 +143,7 @@
<action-state id="ValidateSignedUserInfoJWT">
<!-- <evaluate expression="PopulateUserInfoTokenSignatureValidationParameters" /> -->
-
+ <!-- SIGNATURE CHECK! -->
<evaluate expression="ValidateUserInfoToken" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="ValidateUserInfoClaimsSet" />
@@ -153,7 +154,7 @@
<!-- <evaluate expression="PopulateTokenEncryptionParameters" /> -->
<evaluate expression="ValidateUserInfoToken" /> <!-- Will die if not decrypted properly first -->
<evaluate expression="'proceed'" />
- <transition on="proceed" to="ValidateUserInfoClaimsSet" />
+ <transition on="proceed" to="ValidateSignedUserInfoJWT" />
</action-state>
<action-state id="ValidateUserInfoClaimsSet">
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index c1fadb1..cc799db 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -12,7 +12,7 @@
<!-- OIDC RP Profile Configurations. -->
<bean id="AbstractOIDCProfile" abstract="true"
- p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.DefaultSecurityConfiguration}" />
+ p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.authn.oidc.rp.DefaultSecurityConfiguration}" />
<bean id="AbstractOIDCSSOProfile" parent="AbstractOIDCProfile" abstract="true" p:issuer-ref="issuer"
p:tokenEndpointAuthMethods="%{idp.oidc.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
@@ -23,6 +23,7 @@
p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}" />
<!-- FIXME This will NEED a new ID and possibly class. If not, the OP plugin and RP plugin can not be installed together -->
+ <!-- Only load the default client_id and client_secret if discovery is disabled -->
<bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"
@@ -30,32 +31,15 @@
p:encodeConsentInTokens="%{idp.authn.oidc.rp.encodeConsentInTokens:false}"
p:encodedAttributes="%{idp.authn.oidc.rp.encodedAttributes:%{idp.oidc.embeddedAttributes:}}"
p:deniedUserInfoAttributes="%{idp.authn.oidc.rp.deniedUserInfoAttributes:}"
- p:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.ClientIdentifierLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.DefaultClientIdentifierLookupStrategy')}"
- p:clientAuthenticationLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.ClientAuthenticationLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.DefaultClientAuthenticationLookStrategy')}" />
+ p:clientId="#{%{idp.authn.oidc.rp.discoveryRequired:false} == true ? null : '%{idp.authn.oidc.rp.client.clientId:}'}"
+ p:clientCredential="#{%{idp.authn.oidc.rp.discoveryRequired:false} == true ? {null} : getObject('shibboleth.authn.oidc.rp.DefaultCredential')}"
+ p:clientAuthenticationMethod="%{idp.authn.oidc.rp.clientAuthenticationMethod:client_secret_basic}"/>
- <bean id="shibboleth.authn.oidc.rp.DefaultClientAuthenticationLookStrategy"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.config.DefaultClientAuthenticationLookupStrategy"
- p:clientId="%{idp.authn.oidc.rp.client.clientId:#{null}}"
- p:clientSecret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"
- p:clientAuthenticationMethod="%{idp.authn.oidc.rp.client.clientAuthenticationMethod:client_secret_basic}"
- p:clientSecretExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}"
- p:clientIdToClientAuthenticationMap="#{getObject('shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap')}"/>
-
- <bean id="shibboleth.authn.oidc.rp.DefaultClientIdentifierLookupStrategy" lazy-init="true"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.config.DefaultClientIdentifierLookupStrategy"
- c:clientId="%{idp.authn.oidc.rp.client.clientId:#{null}}"
- c:issuerToClientIdMap="#{getObject('shibboleth.authn.oidc.rp.IssuerToClientIdMap')}"
- c:providerMetadataLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"/>
-
- <!-- Client Authentication parent bean which defaults secrets to not expire -->
- <bean id="shibboleth.authn.oidc.rp.ClientAuthenticationDetails"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.storage.ClientAuthenticationDetails" abstract="true"
- c:clientSecretExpiresAt="0" />
<!-- Security Configuration Defaults. These settings establish the default security configurations for signatures and
loads the default credentials used. -->
- <bean id="shibboleth.oidc.DefaultSecurityConfiguration"
+ <bean id="shibboleth.authn.oidc.rp.DefaultSecurityConfiguration"
class="net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration">
<!-- Add these back were appropriate -->
<!-- <property name="signatureSigningConfiguration"> <ref bean="#{'%{idp.oidc.signing.config:shibboleth.oidc.SigningConfiguration}'.trim()}"
@@ -69,6 +53,17 @@
</property>
</bean>
+ <!-- For developers to override per RP config -->
+ <bean id="shibboleth.authn.oidc.rp.ExpiringJWKCredential" abstract="true"
+ class="net.shibboleth.oidc.security.impl.BasicExpiringJWTStaticCredentialFactoryBean"
+ p:credentialExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}"/>
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultCredential"
+ parent="shibboleth.authn.oidc.rp.ExpiringJWKCredential"
+ p:secret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"
+ p:keyNames="defaultPropertiesClientSecret"/>
+
+
<!-- Configuration for supported algorithms for token endpoint authentication JWT signature validation. -->
<!-- TODO This was a parent bean, but as that was not compatible with the new trust engine stuff, I moved to it's own class for now -->
@@ -99,13 +94,23 @@
</property> -->
</bean>
+ <bean id="defaultSignedJWTCredentialResolver" class="org.opensaml.security.credential.impl.ChainingCredentialResolver">
+ <constructor-arg>
+ <list>
+ <bean id="OIDCProviderMetadataCredentialResolver"
+ class="net.shibboleth.oidc.security.impl.ProviderMetadataCredentialResolver"
+ p:remoteJwkSetCache-ref="shibboleth.authn.oidc.rp.RemoteJwkSetCache"/>
+ <bean id="CriterionCredentialResolver"
+ class="net.shibboleth.oidc.security.impl.CriterionCredentialResolver"/>
+ </list>
+ </constructor-arg>
+ </bean>
+
<bean id="ExplicitKeySignedJWTTrustEngine"
class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine"
- c:resolver-ref="OIDCProviderMetadataCredentialResolver"/>
+ c:resolver-ref="defaultSignedJWTCredentialResolver"/>
+
- <bean id="OIDCProviderMetadataCredentialResolver"
- class="net.shibboleth.oidc.security.impl.ProviderMetadataCredentialResolver"
- p:remoteJwkSetCache-ref="shibboleth.authn.oidc.rp.RemoteJwkSetCache"/>
</beans>
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index 3640245..e5fe1d2 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -20,6 +20,8 @@ package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
@@ -39,6 +41,8 @@ import org.opensaml.messaging.context.MessageContext;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.xmlsec.DecryptionConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -49,6 +53,7 @@ import org.springframework.webflow.engine.impl.FlowExecutionImpl;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.test.MockFlowBuilderContext;
+import com.google.common.base.Enums;
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JOSEException;
@@ -59,6 +64,7 @@ import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.Payload;
+import com.nimbusds.jose.crypto.DirectEncrypter;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.RSAEncrypter;
@@ -99,12 +105,17 @@ import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
import net.shibboleth.oidc.profile.config.OIDCSecurityConfiguration;
import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
+import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.impl.BasicExpiringJWTStaticCredentialFactoryBean;
import net.shibboleth.oidc.security.impl.BasicSignatureValidationConfiguration;
import net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine;
+import net.shibboleth.oidc.security.impl.JWSAssemblyUtils;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import okhttp3.mockwebserver.MockResponse;
@@ -121,7 +132,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
private static final String CLIENT_ID = "demo_rp";
- private static final String ID_TOKEN_HMAC_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
+ private static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
/**
* Example of good provider metadata. Endpoints are localhost to support the
@@ -262,9 +273,12 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
addBeanDefinition(builderContext, "shibboleth.ReplayCache",BeanDefinitionBuilder.
genericBeanDefinition(org.opensaml.storage.ReplayCache.class).getBeanDefinition());
+ addBeanDefinition(builderContext, "shibboleth.StorageService",BeanDefinitionBuilder.
+ genericBeanDefinition(org.opensaml.storage.impl.MemoryStorageService.class).getBeanDefinition());
+
try {
- // Create a HttpClient which turns off hostname verification and trusts all certificates
+ // Create a HttpClient which turns off hostname verification and trusts all certificates (for TESTS!)
addBeanSingleton(builderContext, "shibboleth.InternalHttpClient",
HttpClients.custom().setSSLContext(new SSLContextBuilder()
.loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
@@ -276,11 +290,13 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
loadBeanDefinitionsFromXmlFile(builderContext,
new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"),
- null);
+ Map.of("idp.authn.oidc.rp.client.clientId", CLIENT_ID,
+ "idp.authn.oidc.rp.client.clientSecret",CLIENT_SECRET));
loadBeanDefinitionsFromXmlFile(builderContext,
new ClassPathResource("conf/test-relyingparty-resolver-service.xml"),
- Map.of("idp.authn.oidc.rp.client.clientId", CLIENT_ID));
+ Map.of("idp.authn.oidc.rp.client.clientId", CLIENT_ID,
+ "idp.authn.oidc.rp.client.clientSecret",CLIENT_SECRET));
loadBeanDefinitionsFromXmlFile(builderContext,
new ClassPathResource("conf/additional-system-beans.xml"), null);
@@ -294,7 +310,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
/**
* Create an OAuth access token with a runtime constructed id_token. This allows the
- * expiry to be current.
+ * expiry to be current. The token is signed with the client_secret using HS256.
*
* TODO the JWT is signed with a local MAC and is not consistent with the OP metadata. Might need to change this
*
@@ -317,7 +333,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
.build();
payload.getClaims().forEach((k,v) -> log.debug("{}:{}",k,v));
final var signedJWT = new SignedJWT(header,payload);
- signedJWT.sign(new MACSigner(ID_TOKEN_HMAC_SECRET));
+ signedJWT.sign(new MACSigner(CLIENT_SECRET));
final String accessTokenSerialized = "{\n"
+ " \"access_token\": \"W0y5aDNAzEPNpSzu1cuMG904BZuQFZJUUwG5F3ct0zydZWy1ji\",\n"
+ " \"token_type\": \"Bearer\",\n"
@@ -328,6 +344,50 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
return accessTokenSerialized;
}
+ /**
+ * Create an OAuth access token with a runtime constructed id_token. This allows the
+ * expiry to be current. The token is signed using HS256 and encrypted using the 'Direct Encryption' (dir)
+ * management mode i.e. no key wrapping. Both use the shared client_secret.
+ *
+ * @return a serialized access token response.
+ *
+ * @throws Exception on error.
+ */
+ private String createAccessTokenResponseJSONSignedAndEncrypted() throws Exception {
+ final var header = new JWSHeader.Builder(JWSAlgorithm.HS256)
+ .type(JOSEObjectType.JWT)
+ .build();
+ final var payload = new JWTClaimsSet.Builder()
+ .issuer(OP_ISSUER_ID)
+ .audience(List.of(CLIENT_ID,"demo_rp2"))
+ .subject("jdoe")
+ .claim("nonce", "abadnonce")
+ .claim("azp", CLIENT_ID)
+ .claim("name","jdoe")
+ .expirationTime(Date.from(Instant.now().plusSeconds(120)))
+ .build();
+ payload.getClaims().forEach((k,v) -> log.debug("{}:{}",k,v));
+ final var signedJWT = new SignedJWT(header,payload);
+ signedJWT.sign(new MACSigner(CLIENT_SECRET));
+
+ final JWEObject jweObject =
+ new JWEObject(new JWEHeader.Builder(JWEAlgorithm.DIR, EncryptionMethod.A256GCM)
+ .contentType("JWT")
+ .build(),
+ new Payload(signedJWT));
+ jweObject.encrypt(new DirectEncrypter(CLIENT_SECRET.getBytes(StandardCharsets.UTF_8)));
+ final EncryptedJWT jwe = EncryptedJWT.parse(jweObject.serialize());
+
+ final String accessTokenSerialized = "{\n"
+ + " \"access_token\": \"W0y5aDNAzEPNpSzu1cuMG904BZuQFZJUUwG5F3ct0zydZWy1ji\",\n"
+ + " \"token_type\": \"Bearer\",\n"
+ + " \"id_token\": \""+jwe.serialize()+"\",\n"
+ + " \"scope\": \"openid\"\n"
+ + "}";
+ log.debug("Access token: \n {}",accessTokenSerialized);
+ return accessTokenSerialized;
+ }
+
/**
* Create a signed UserInfo response JWT.
*
@@ -578,10 +638,36 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
final RelyingPartyContext partyContext = new RelyingPartyContext();
final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
partyContext.setProfileConfig(partyConfig);
- partyConfig.setClientAuthenticationLookupStrategy(p ->
- new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+ partyConfig.setClientAuthenticationMethod("client_secret_basic");
+ partyConfig.setClientId(CLIENT_ID);
+ partyConfig.setClientCredential(createCredentialFromSharedSecret(CLIENT_SECRET));
+ // Set a default security config for the profile config
+ final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
+ final BasicSignatureValidationConfiguration<SignedJWT> sigValidation =
+ new BasicSignatureValidationConfiguration<>();
+ sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
+ new CredentialResolver() {
+
+ @Override
+ public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+ final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+ jwkCredential.setAlgorithm(JWSAlgorithm.HS256);
+ jwkCredential.setKid("secret_key");
+ jwkCredential.setSecretKey(new SecretKeySpec(
+ JWSAssemblyUtils.getSecretBytes(CLIENT_SECRET), "NONE"));
+ return jwkCredential;
+ }
+
+ @Override
+ public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+ return List.of(resolveSingle(criteria));
+ }
+ }));
+
+ secConfig.setIdTokenJwtSignatureValidationConfig(sigValidation);
+ partyConfig.setSecurityConfiguration(secConfig);
- final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+ final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
rPartyConfig.setResponderId("http://idp.example.com/");
partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
@@ -691,7 +777,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
partyContext.setProfileConfig(partyConfig);
partyConfig.setSecurityConfiguration(createSecurityConfigAndValidationParamsForHMAC(
- ID_TOKEN_HMAC_SECRET.getBytes(), "HS256"));
+ CLIENT_SECRET.getBytes(), "HS256"));
partyConfig.setClientAuthenticationLookupStrategy(p ->
new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
@@ -737,7 +823,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
}
@Test
- public void testAuthnFlowFromAuthorizationCallback_UsingEncryptedJWTUserInfoResponse()
+ public void testAuthnFlowFromAuthorizationCallback_Using_SignedAndEncrypted_JWTIDTokenAndUserInfoResponse()
throws Exception {
setFlowPath(FLOW);
@@ -756,7 +842,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// First is token exchange
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/json")
- .setBody(createAccessTokenResponseJSON()));
+ .setBody(createAccessTokenResponseJSONSignedAndEncrypted()));
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
@@ -778,8 +864,38 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
final RelyingPartyContext partyContext = new RelyingPartyContext();
final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
partyContext.setProfileConfig(partyConfig);
- partyConfig.setClientAuthenticationLookupStrategy(p ->
- new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
+ partyConfig.setClientAuthenticationMethod("client_secret_basic");
+ partyConfig.setClientId(CLIENT_ID);
+ partyConfig.setClientCredential(createCredentialFromSharedSecret(CLIENT_SECRET));
+ // Set a default security config for the profile config
+ final OIDCSecurityConfiguration secConfig = new OIDCSecurityConfiguration();
+ final BasicSignatureValidationConfiguration<SignedJWT> sigValidation =
+ new BasicSignatureValidationConfiguration<>();
+ sigValidation.setSignatureTrustEngine(new ExplicitKeySignedJWTTrustEngine(
+ new CredentialResolver() {
+
+ @Override
+ public Credential resolveSingle(final CriteriaSet criteria) throws ResolverException {
+ final BasicJWKCredential jwkCredential = new BasicJWKCredential();
+ jwkCredential.setAlgorithm(JWSAlgorithm.HS256);
+ jwkCredential.setKid("secret_key");
+ jwkCredential.setSecretKey(new SecretKeySpec(
+ JWSAssemblyUtils.getSecretBytes(CLIENT_SECRET), "NONE"));
+ return jwkCredential;
+ }
+
+ @Override
+ public Iterable<Credential> resolve(final CriteriaSet criteria) throws ResolverException {
+ return List.of(resolveSingle(criteria));
+ }
+ }));
+
+ secConfig.setIdTokenJwtSignatureValidationConfig(sigValidation);
+ final var decryptConfig = new OIDCDecryptionConfiguration();
+ secConfig.setRequestObjectDecryptionConfiguration(null)
+
+ partyConfig.setSecurityConfiguration(secConfig);
+
final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
rPartyConfig.setResponderId("http://idp.example.com/");
@@ -910,6 +1026,21 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
}
+ /**
+ * Create a basic {@link JWKCredential} from the given shared secret.
+ *
+ * @param secret the secret to convert to a {@link JWKCredential}.
+ *
+ * @return the credential
+ */
+ private JWKCredential createCredentialFromSharedSecret(final String secret) {
+ final BasicExpiringJWKCredential jwkCredential = new BasicExpiringJWKCredential();
+ jwkCredential.setSecretKey(new SecretKeySpec(JWSAssemblyUtils.getSecretBytes(secret), "NONE"));
+ jwkCredential.setCredentialExpiresAt(Duration.ZERO);
+ jwkCredential.setUsageType(UsageType.UNSPECIFIED);
+ return jwkCredential;
+ }
+
/**
* Test the flow from the external authorization request to the end of the flow when an error
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
index ecf2f28..ad811bc 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relying-party-system.xml
@@ -8,6 +8,12 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize" default-destroy-method="destroy">
+
+ <!-- ***Some support beans from the internal rp configuration XML -->
+
+ <!-- Parent bean for RelyingParty overrides based on activation by name(s). -->
+ <bean id="RelyingPartyByName" abstract="true" parent="RelyingParty"
+ class="net.shibboleth.idp.saml.relyingparty.impl.RelyingPartyConfigurationSupport" factory-method="byName" />
<!-- removed, but I do not know what the default should be in this case
p:defaultSecurityConfiguration-ref="%{idp.security.config:shibboleth.DefaultSecurityConfiguration}" -->
@@ -43,7 +49,7 @@
<!-- Container for any overrides you want to add. -->
<util:list id="shibboleth.RelyingPartyOverrides">
-
+
</util:list>
@@ -51,7 +57,7 @@
Map clients to appropriate client authentication - only supports client_secret_basic and client_secret_post
-->
- <util:map id="shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap">
+ <!-- <util:map id="shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap">
<entry key="mytestclient">
<bean parent="shibboleth.authn.oidc.rp.ClientAuthenticationDetails" c:clientSecret="mytestsecret"
c:tokenEndpointAuthMethod="client_secret_basic" />
@@ -60,7 +66,7 @@
<bean parent="shibboleth.authn.oidc.rp.ClientAuthenticationDetails" c:clientSecret="mytestsecret"
c:tokenEndpointAuthMethod="client_secret_basic" />
</entry>
- </util:map>
+ </util:map> -->
</beans>
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list