[java-idp-plugin-oidc-rp] branch main updated: JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver
Phil Smart
philip.smart at jisc.ac.uk
Thu Mar 24 10:36:18 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=1dd27e4f890344f72c1ddd0409c6ace3c7a5fc7c
The following commit(s) were added to refs/heads/main by this push:
new 1dd27e4 JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver
1dd27e4 is described below
commit 1dd27e4f890344f72c1ddd0409c6ace3c7a5fc7c
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Mar 24 10:36:12 2022 +0000
JOIDCRP-14 and JOIDCRP-13 - Client Authentication/Identifier Resolver
- Remove the client authentication/identifier resolvers in favour of
strategies applied to the profile configuration.
- By default, these strategies take values from the oidc properties
file for quick configuration of a single client.
- If values are not supplied in the properties file, a further strategy
looks them up from a storage service which allows multiple clients to be
defined.
- The default storage services are based on maps which need to be
defined in the relying-party.xml file - and are then auto-reloaded etc.
by the relyingparty resolver.
https://shibboleth.atlassian.net/browse/JOIDCRP-13
https://shibboleth.atlassian.net/browse/JOIDCRP-14
---
.../config/navigate/RedirectUriLookupFunction.java | 3 +-
idp-oidc-rp-impl/pom.xml | 5 +
...AbstractClientAuthenticationLookupStrategy.java | 74 ++++++
.../DefaultClientAuthenticationLookupStrategy.java | 111 ++++++++
.../DefaultClientIdentifierLookupStrategy.java | 77 ++++++
.../MapBackedMemoryStorageServiceFactoryBean.java | 23 +-
...ceBackedClientAuthenticationLookupStrategy.java | 291 +++++++++++++++++++++
...erviceBackedClientIdentifierLookupStrategy.java | 122 +++++++++
.../plugin/authn/oidc/rp/config/package-info.java | 21 ++
.../impl/AbstractHttpOIDCAuthenticationAction.java | 5 +-
.../oidc/rp/impl/AddOIDCAuthenticationRequest.java | 17 +-
.../oidc/rp/impl/ExchangeCodeForAccessToken.java | 2 +-
...nitializeOAuth2ClientAuthenticationContext.java | 123 ++++-----
.../rp/impl/InitializeOAuth2ClientContext.java | 96 ++++---
.../impl/OAuth2ClientAuthenticationContainer.java | 147 -----------
...lientAuthenticationResolverServiceStrategy.java | 65 -----
.../impl/OAuth2ClientIdentifierContainer.java | 148 -----------
...th2ClientIdentifierResolverServiceStrategy.java | 67 -----
...eloadingOAuth2ClientAuthenticationProvider.java | 107 --------
.../ReloadingOAuth2ClientIdentifierProvider.java | 105 --------
.../META-INF/net.shibboleth.idp/postconfig.xml | 247 +++++------------
.../oidc-relying-party-authn-beans.xml | 2 -
.../oidc-relying-party-authn-flow.xml | 2 +-
.../idp/service/relying-party/postconfig.xml | 93 +++++++
.../authn/clientauthentication-resolver-system.xml | 33 ---
.../authn/clientidentifier-resolver-system.xml | 30 ---
.../authn/oidc/rp/conf/authn/oidc-rp.properties | 26 +-
...aultClientAuthenticationLookupStrategyTest.java | 97 +++++++
...ckedClientAuthenticationLookupStrategyTest.java | 202 ++++++++++++++
.../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java | 63 +++--
.../resources/conf/test-relying-party-system.xml | 16 ++
.../conf/test-relyingparty-resolver-service.xml | 22 +-
32 files changed, 1380 insertions(+), 1062 deletions(-)
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
index fdeece6..6cc382f 100644
--- a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/navigate/RedirectUriLookupFunction.java
@@ -41,7 +41,8 @@ public class RedirectUriLookupFunction extends AbstractRelyingPartyLookupFunctio
final ProfileConfiguration pc = rpc.getProfileConfig();
if (pc instanceof OIDCAuthorizationConfiguration) {
try {
- return new URI(((OIDCAuthorizationConfiguration) pc).getRedirectUriOverride(input));
+ final String uriString = ((OIDCAuthorizationConfiguration) pc).getRedirectUriOverride(input);
+ return uriString != null ? new URI(uriString): null;
} catch (final URISyntaxException e) {
return null;
}
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 69a5e33..6ad016c 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -111,6 +111,11 @@
<artifactId>idp-admin-impl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>jakarta.json</groupId>
+ <artifactId>jakarta.json-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<!-- Test dependency -->
<dependency>
<groupId>net.shibboleth.idp.plugin.authn.test</groupId>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java
new file mode 100644
index 0000000..63330fc
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/AbstractClientAuthenticationLookupStrategy.java
@@ -0,0 +1,74 @@
+/*
+ * 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.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+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.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+
+/** Base function to support lookup of a suitable client authentication methods.*/
+public abstract class AbstractClientAuthenticationLookupStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<ProfileRequestContext, ClientAuthentication> {
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(AbstractClientAuthenticationLookupStrategy.class);
+
+ /**
+ * Construct the client authentication from the given client authentication record.
+ *
+ * @param clientId the client_id
+ * @param tokenEndpointAuthMethod the token endpoint authentication method
+ * @param clientSecret the client secret
+ *
+ * @return the constructed client authentication
+ */
+ @Nullable protected ClientAuthentication constructClientAuthentication(
+ @Nonnull final String clientId, @Nonnull final String tokenEndpointAuthMethod,
+ @Nonnull final Secret clientSecret) {
+
+ if (clientSecret.expired()) {
+ log.warn("{} Client secret has expired for client '{}'", getId(), clientId);
+ return null;
+ }
+ final ClientAuthenticationMethod method = new ClientAuthenticationMethod(tokenEndpointAuthMethod);
+ if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {
+ return new ClientSecretBasic(new ClientID(clientId), clientSecret);
+ } else if (method.equals(ClientAuthenticationMethod.CLIENT_SECRET_POST)) {
+ return new ClientSecretPost(new ClientID(clientId), clientSecret);
+ }
+ log.warn("{}: Client authentication method '{}' not supported for client '{}'", getId(),
+ tokenEndpointAuthMethod, clientId);
+ return null;
+
+ }
+
+}
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
new file mode 100644
index 0000000..189b5f7
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategy.java
@@ -0,0 +1,111 @@
+/*
+ * 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.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.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+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.
+ */
+ at ThreadSafe
+public class DefaultClientAuthenticationLookupStrategy extends AbstractClientAuthenticationLookupStrategy {
+
+ /** A fixed client_id to use.*/
+ @Nullable private final String clientId;
+
+ /** A fixed client_secret to use over any injected strategy to locate one .*/
+ @Nullable private final String clientSecret;
+
+ /** A fixed client authentication method to use.*/
+ @Nullable private final String clientAuthenticationMethod;
+
+ /** A fixed client secret expiry in seconds since UNIX EPOCH to use.*/
+ @Nullable private final Duration clientSecretExpiresAt;
+
+ /** The strategy used to locate the client authentication method if a fixed one is not already supplied.*/
+ @Nullable private final Function<ProfileRequestContext, ClientAuthentication> clientAuthenticationLookupStrategy;
+
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param id the client_id
+ * @param secret the client_secret
+ * @param authenticationMethod the client authentication method
+ * @param secretExpiresAt when the client secret expires, or 0 for no expiry
+ * @param strategy the strategy to lookup the client authentication
+ * @throws ComponentInitializationException
+ */
+ public DefaultClientAuthenticationLookupStrategy(
+ @ParameterName(name="clientId") @Nullable final String id,
+ @ParameterName(name="clientSecret") @Nullable final String secret,
+ @ParameterName(name="clientAuthenticationMethod") @Nullable final String authenticationMethod,
+ @ParameterName(name="clientSecretExpiresAt") @Nullable final Duration secretExpiresAt,
+ @ParameterName(name="clientIdLookupStrategy")
+ @Nullable final Function<ProfileRequestContext, ClientAuthentication> strategy)
+ throws ComponentInitializationException {
+
+
+ if (strategy == null && secret == null && authenticationMethod == null && secretExpiresAt == null) {
+
+ throw new ComponentInitializationException("Must supply a client_secret, client_authentication_method,"
+ + " and client_secret_expires_at if a client authentication lookup strategy is not supplied");
+ }
+ clientId = StringSupport.trimOrNull(id);
+ clientSecret = StringSupport.trimOrNull(secret);
+ clientAuthenticationMethod = StringSupport.trimOrNull(authenticationMethod);
+ clientSecretExpiresAt = secretExpiresAt;
+ clientAuthenticationLookupStrategy = strategy;
+ }
+
+ @Override
+ @Nullable public ClientAuthentication apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ // Use supplied client authentication parameters first
+ if (clientSecret != null && clientAuthenticationMethod != null && clientSecretExpiresAt != null
+ && clientId != null) {
+ if (clientSecretExpiresAt.toSeconds() == 0) {
+ // No expiry if 0, so do not set.
+ return constructClientAuthentication(clientId, clientAuthenticationMethod, new Secret(clientSecret));
+
+ }
+ return constructClientAuthentication(clientId, clientAuthenticationMethod, new Secret(clientSecret,
+ Date.from(Instant.ofEpochSecond(clientSecretExpiresAt.toSeconds()))));
+ }
+ return clientAuthenticationLookupStrategy.apply(profileRequestContext);
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java
new file mode 100644
index 0000000..784e674
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientIdentifierLookupStrategy.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * A strategy that produces a client_id either directly from the one supplied, or derived from the
+ * lookup strategy.
+ */
+ at ThreadSafe
+public class DefaultClientIdentifierLookupStrategy implements Function<ProfileRequestContext, String> {
+
+ /** A fixed client_id to use over any injected strategy to locate one.*/
+ @Nullable private final String clientId;
+
+ /** The strategy used to locate the client_id if a fixed one is not already supplied.*/
+ @Nullable private final Function<ProfileRequestContext, String> clientIdLookupStrategy;
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param id a client_id that takes precedence over one derived from the lookup strategy.
+ * @param strategy the strategy used to derive a client_id for the input profile request context.
+ *
+ * @throws ComponentInitializationException if both id and strategy are null, one is required.
+ */
+ public DefaultClientIdentifierLookupStrategy(@ParameterName(name="clientId") @Nullable final String id,
+ @ParameterName(name="clientIdLookupStrategy")
+ @Nullable final Function<ProfileRequestContext, String> strategy)
+ throws ComponentInitializationException {
+
+ if (id == null && strategy == null) {
+ throw new ComponentInitializationException("Must supply either a fixed client_id value or a "
+ + "client_id lookup strategy");
+ }
+ clientId = StringSupport.trimOrNull(id);
+ clientIdLookupStrategy = strategy;
+ }
+
+ @Override
+ @Nullable public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ // Use supplied client_id first
+ if (clientId != null) {
+ return clientId;
+ }
+ return clientIdLookupStrategy.apply(profileRequestContext);
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
similarity index 84%
rename from idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java
rename to idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
index 10f5328..469c9d3 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/MapBackedMemoryStorageServiceFactoryBean.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/MapBackedMemoryStorageServiceFactoryBean.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
import java.util.Collections;
import java.util.Map;
@@ -23,8 +23,12 @@ import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opensaml.storage.StorageService;
import org.opensaml.storage.impl.MemoryStorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -33,13 +37,16 @@ import com.fasterxml.jackson.databind.ObjectWriter;
import net.shibboleth.utilities.java.support.annotation.ParameterName;
import net.shibboleth.utilities.java.support.logic.Constraint;
-
/**
- * A factory that creates and populates a map backed MemoryStorageService from an injected map.
+ * A factory that creates and populates a map backed {@link MemoryStorageService} from an injected map.
* Values which are *not* Strings are serialized into JSON using the supplied object mapper before they are added
* to the storage service. Values which are already Strings are just passed in without modification.
*/
-public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBean<MemoryStorageService> {
+ at ThreadSafe
+public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBean<StorageService> {
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(MapBackedMemoryStorageServiceFactoryBean.class);
/** The Map to inject values into the storage service with.*/
@Nonnull private final Map<String, Object> mapToInject;
@@ -47,7 +54,7 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
/** The ID to give the created memory service.*/
@Nonnull private final String identifier;
- /** The partition name to use in the injected storage service.*/
+ /** The partition name to use in the storage service.*/
@Nonnull private final String storageServiceContext;
/** JSON object mapper. */
@@ -94,11 +101,11 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
@Override
public Class<?> getObjectType() {
- return MemoryStorageService.class;
+ return StorageService.class;
}
@Override
- protected MemoryStorageService createInstance() throws Exception {
+ protected StorageService createInstance() throws Exception {
final MemoryStorageService service = new MemoryStorageService();
service.setId(identifier);
service.initialize();
@@ -113,6 +120,8 @@ public class MapBackedMemoryStorageServiceFactoryBean extends AbstractFactoryBea
}
service.create(storageServiceContext, entry.getKey(), objectAsString, null);
}
+ log.debug("Created a map based memory storage service '{}' with '{}' records",
+ identifier, mapToInject.size());
return service;
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java
new file mode 100644
index 0000000..eaa81d2
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategy.java
@@ -0,0 +1,291 @@
+/*
+ * 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.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.json.Json;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonStructure;
+import javax.json.stream.JsonGenerator;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.storage.StorageRecord;
+import org.opensaml.storage.StorageSerializer;
+import org.opensaml.storage.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+
+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.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+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;
+
+/**
+ * Default strategy for locating a configured client authentication method appropriate for the client_id
+ * stashed in the {@link OAuth2ClientContext}.
+ */
+public class StorageServiceBackedClientAuthenticationLookupStrategy
+ extends AbstractClientAuthenticationLookupStrategy {
+
+ /** The context name in the {@link StorageService}. */
+ @Nonnull @NotEmpty public static final String CONTEXT_NAME = "oauth2ClientAuthentication";
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(StorageServiceBackedClientAuthenticationLookupStrategy.class);
+
+ /** The strategy used to lookup or create the {@link OAuth2ClientContext}.*/
+ @NonnullAfterInit
+ private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
+
+ /** The {@link StorageService} used to store a client identifier for a given Issuer. */
+ @NonnullAfterInit private StorageService storageService;
+
+ /**
+ * A storage service serializer for serializing and deserializing ClientAuthenticationRecords out of
+ * storage records.
+ */
+ @Nonnull private StorageSerializer<ClientAuthenticationRecord> storageSerializer;
+
+ /** Constructor. */
+ public StorageServiceBackedClientAuthenticationLookupStrategy() {
+ oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
+ new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
+ new OutboundMessageContextLookup()));
+
+ storageSerializer = new ClientAuthenticationStorageRecordSerializer();
+ }
+
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (oauth2ClientContextLookupStrategy == null) {
+ throw new ComponentInitializationException("OAuth2ClientContextLookupStrategy cannot be null");
+ }
+ if (storageService == null) {
+ throw new ComponentInitializationException("StorageService cannot be null");
+ }
+ }
+
+ /**
+ * Set the storage serializer to use with the storage service.
+ *
+ * @param serializer the storage serializer
+ */
+ public void setStorageSerializer(final StorageSerializer<ClientAuthenticationRecord> serializer) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ storageSerializer = Constraint.isNotNull(serializer, "Storage serializer can not be null");
+ }
+
+ /**
+ * 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 {@link StorageService} back-end to use.
+ *
+ * @param storage the back-end to use
+ */
+ public void setStorageService(@Nonnull final StorageService storage) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ storageService = Constraint.isNotNull(storage, "StorageService cannot be null");
+ }
+
+ @Override
+ @Nullable public ClientAuthentication apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final OAuth2ClientContext clientCtx = oauth2ClientContextLookupStrategy.apply(profileRequestContext);
+ if (clientCtx == null || StringSupport.trimOrNull(clientCtx.getClientId()) == null) {
+ return null;
+ }
+ try {
+ final StorageRecord<ClientAuthenticationRecord> record =
+ storageService.read(CONTEXT_NAME, clientCtx.getClientId());
+ if (record != null) {
+ final ClientAuthenticationRecord clientAuthRecord =
+ record.getValue(storageSerializer, CONTEXT_NAME, clientCtx.getClientId());
+ return constructClientAuthentication(clientCtx.getClientId(),
+ clientAuthRecord.getTokenEndpointAuthMethod(),
+ clientAuthRecord.getClientSecret());
+ }
+ } catch (final IOException e) {
+ log.debug("{}: Error retrieving client_authentication record from the storage service", getId(), e);
+ return null;
+ }
+ return null;
+ }
+
+
+
+ /** A Storage service serializer for converting between ClientAuthenticationRecords and JSON.*/
+ private static class ClientAuthenticationStorageRecordSerializer
+ implements StorageSerializer<ClientAuthenticationRecord> {
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(ClientAuthenticationStorageRecordSerializer.class);
+
+ @Override
+ public boolean isInitialized() {
+ // Is initialized once created
+ return true;
+ }
+
+ @Override
+ public void initialize() throws ComponentInitializationException {
+ //no-op here, does not need to be called.
+ }
+
+ @Override
+ public String serialize(final ClientAuthenticationRecord instance) throws IOException {
+
+ final StringWriter sink = new StringWriter(128);
+
+ try (final JsonGenerator gen = Json.createGenerator(sink)) {
+
+ final String secretExpiryTimeSinceEpochUTC = Long.toString(
+ instance.getClientSecret().getExpirationDate().toInstant().getEpochSecond());
+ gen.writeStartObject()
+ .write("token_endpoint_auth_method", instance.getTokenEndpointAuthMethod())
+ .write("client_secret", instance.getClientSecret().getValue())
+ .write("client_secret_expires_at", secretExpiryTimeSinceEpochUTC);
+
+ gen.writeEnd().close();
+
+ return sink.toString();
+ } catch (final JsonException e) {
+ log.error("Exception while serializing ClientAuthenticationRecord: {}", e.getMessage());
+ throw new IOException("Exception while serializing ClientAuthenticationRecord", e);
+ }
+ }
+
+ @Override
+ public ClientAuthenticationRecord deserialize(
+ final long version, final String context, final String key, final String value,
+ final Long expiration) throws IOException {
+
+ try (final JsonReader reader = Json.createReader(new StringReader(value))) {
+
+ final JsonStructure st = reader.read();
+ if (!(st instanceof JsonObject)) {
+ throw new IOException("Found invalid data structure while parsing ClientAuthenticationRecord");
+ }
+ final JsonObject obj = (JsonObject) st;
+
+ final String tokenEndpointAuthMethod = obj.getString("token_endpoint_auth_method");
+ final String secret = obj.getString("client_secret");
+ final long expiryInSeconds = obj.getJsonNumber("client_secret_expires_at").longValueExact();
+
+ if (expiryInSeconds == 0) {
+ // Then no expiry
+ return new ClientAuthenticationRecord(tokenEndpointAuthMethod, new Secret(secret));
+ } else {
+ final Instant secretExpiration = Instant.ofEpochSecond(expiryInSeconds);
+ return new ClientAuthenticationRecord(
+ tokenEndpointAuthMethod, new Secret(secret, Date.from(secretExpiration)));
+ }
+
+
+
+
+ } catch (final NullPointerException | ClassCastException | ArithmeticException | JsonException e) {
+ log.error("Exception while parsing ClientAuthenticationRecord: {}", e.getMessage());
+ throw new IOException("Found invalid data structure while parsing ClientAuthenticationRecord", e);
+ }
+ }
+
+ }
+
+ /**
+ * A client authentication record that is serialized in and out of a storage service.
+ */
+ @Immutable
+ private static class ClientAuthenticationRecord {
+
+ /** The client authentication method for the token endpoint.*/
+ @Nonnull private final String tokenEndpointAuthMethod;
+
+ /** the client secret.*/
+ @Nonnull private final Secret clientSecret;
+
+ /**
+ * Constructor.
+ *
+ * @param authMethod the client authentication method
+ * @param secret the client secret
+ */
+ public ClientAuthenticationRecord(@Nonnull final String authMethod, @Nonnull final Secret secret) {
+ tokenEndpointAuthMethod =
+ Constraint.isNotEmpty(authMethod, "Authentication method type can not be null or empty");
+ clientSecret = Constraint.isNotNull(secret, "Client Secrete can not be null");
+ }
+
+ /**
+ * Get the token_endpoint_auth_method.
+ *
+ * @return Returns the tokenEndpointAuthMethod.
+ */
+ public final String getTokenEndpointAuthMethod() {
+ return tokenEndpointAuthMethod;
+ }
+
+ /**
+ * Get the client_sercret.
+ *
+ * @return Returns the clientSecret.
+ */
+ public final Secret getClientSecret() {
+ return clientSecret;
+ }
+
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java
new file mode 100644
index 0000000..8270037
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientIdentifierLookupStrategy.java
@@ -0,0 +1,122 @@
+/*
+ * 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.io.IOException;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.StorageRecord;
+import org.opensaml.storage.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+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;
+
+/**
+ * Default strategy for locating a client_id in the storage service based on the issuer_id found
+ * in the provider metadata.
+ */
+public class StorageServiceBackedClientIdentifierLookupStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<ProfileRequestContext, String> {
+
+ /** The context name in the {@link StorageService}. */
+ @Nonnull @NotEmpty public static final String CONTEXT_NAME = "oauth2ClientIdentifier";
+
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(StorageServiceBackedClientIdentifierLookupStrategy.class);
+
+ /** Lookup strategy to locate the provider metadata to use.*/
+ @NonnullAfterInit
+ private Function<ProfileRequestContext, OIDCProviderMetadataContext> providerMetadataLookupStrategy;
+
+ /** The {@link StorageService} used to store a client_id for a given issuer_id. */
+ @NonnullAfterInit private StorageService storageService;
+
+
+ /**
+ * Set the lookup strategy to locate the OpenID providers metadata.
+ *
+ * @param strategy the strategy.
+ */
+ public void setProviderMetadataLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, OIDCProviderMetadataContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ providerMetadataLookupStrategy =
+ Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
+ }
+
+ @Override
+ public void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (storageService == null) {
+ throw new ComponentInitializationException(getId() + ": StorageService cannot be null");
+ }
+ if (providerMetadataLookupStrategy == null) {
+ throw new ComponentInitializationException(
+ getId() + ": OIDC Provider metadata lookup strategy cannot be null");
+ }
+ }
+
+ /**
+ * Set the {@link StorageService} back-end to use.
+ *
+ * @param storage the back-end to use
+ */
+ public void setStorageService(@Nonnull final StorageService storage) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ storageService = Constraint.isNotNull(storage, "StorageService cannot be null");
+ }
+
+ @Override
+ @Nullable public String apply(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ final OIDCProviderMetadataContext providerMetadataContext =
+ providerMetadataLookupStrategy.apply(profileRequestContext);
+ if (providerMetadataContext == null || providerMetadataContext.getProviderInformation() == null) {
+ log.warn("{} No provider metadata found for peer, nothing to do", getId());
+ return null;
+ }
+
+ try {
+ final StorageRecord<?> record =
+ storageService.read(CONTEXT_NAME,
+ providerMetadataContext.getProviderInformation().getIssuer().getValue());
+ if (record != null) {
+ return record.getValue();
+ }
+ } catch (final IOException e) {
+ log.warn(getId() + "{}: StorageService read failed, could not acquire client_id for issuer {}", getId(),
+ providerMetadataContext.getProviderInformation().getIssuer(), e);
+ }
+ return null;
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java
new file mode 100644
index 0000000..a1e4a77
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Configuration implementation classes.
+ */
+package net.shibboleth.idp.plugin.authn.oidc.rp.config;
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
index 742bfc6..f3007eb 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AbstractHttpOIDCAuthenticationAction.java
@@ -18,7 +18,6 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
import java.io.IOException;
-import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -41,7 +40,7 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
/**
- * An abstract class for actions that want to make synchronous HTTP requests.
+ * An abstract class for OIDC actions that want to make synchronous HTTP requests.
*
* @param <T> the response type of the object returned as a result of the request.
*/
@@ -166,7 +165,7 @@ public class AbstractHttpOIDCAuthenticationAction<T> extends AbstractOIDCAuthent
final T responseObject = getHttpResponseDecoderStrategy().apply(response);
if (responseObject == null) {
throw new OIDCRPException(
- "Unable to decode HTTP response");
+ "Unable to process HTTP response, likely error response");
}
return responseObject;
} catch (final IOException e) {
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
index 9ed4ba5..96e3ea5 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AddOIDCAuthenticationRequest.java
@@ -82,7 +82,7 @@ public class AddOIDCAuthenticationRequest extends AbstractAuthenticationAction {
/** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
@Nonnull
- private final Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
+ private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
/** The stashed OAuth2 client context.*/
@Nullable private OAuth2ClientContext oauth2ClientContext;
@@ -124,6 +124,21 @@ public class AddOIDCAuthenticationRequest extends AbstractAuthenticationAction {
Constraint.isNotNull(strategy,"Provider metadata lookup strategy can not be null");
}
+ /**
+ * 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 lookup strategy to locate the response type and mode context.
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
index 8d5934d..1879970 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ExchangeCodeForAccessToken.java
@@ -102,7 +102,7 @@ public class ExchangeCodeForAccessToken extends AbstractHttpOIDCAuthenticationAc
log.trace("{}: Token request response '{}'",getLogPrefix(), responseObject);
} catch (final OIDCRPException e) {
- log.error("{} Unable to exchange authorisation code for token result",getLogPrefix(),e);
+ log.error("{} Failed to exchange authorisation code for token result: {}",getLogPrefix(), e.getMessage());
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.AUTHN_EXCEPTION);
}
}
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 9ff116a..a2e4867 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
@@ -31,100 +31,75 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.id.ClientID;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.oidc.authn.context.OAuth2ClientAuthenticationContext;
-import net.shibboleth.oidc.metadata.criterion.ClientIDCriterion;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
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.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
/**
* An {@link AbstractProfileAction action} that resolves the client authentication method for the chosen
* downstream provider (issuer).
*/
+//TODO simply some of these with a base class e.g. client context lookups.
public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfileAction {
/** Class logger. */
@Nonnull
private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientAuthenticationContext.class);
- /** The resolver to use to find the client authentication for the given (client_id). */
- @NonnullAfterInit private OAuth2ClientAuthenticationResolver clientAuthenticationResovler;
-
- /** The strategy used to lookup or create the {@link OAuth2ClientContext}.*/
- @Nonnull private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
-
- /** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client authentication.*/
- @Nonnull private
- Function<ProfileRequestContext, OAuth2ClientAuthenticationContext> oauth2ClientAuthenticationContextLookupStrategy;
-
- /** The stashed OAuth2 client context.*/
- @Nullable private OAuth2ClientContext oauth2ClientContext;
-
+
+ /**
+ * The strategy used to lookup or create the {@link OAuth2ClientAuthenticationContext}
+ * for storing the client authentication.*/
+ @Nonnull private Function<ProfileRequestContext, OAuth2ClientAuthenticationContext>
+ oauth2ClientAuthenticationContextLookupStrategy;
+
/** The stashed OAuth2 client authentication context.*/
@Nullable private OAuth2ClientAuthenticationContext oauth2ClientAuthenticationContext;
+
+ /** Lookup function for relying party context. */
+ @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+ /** Applicable stashed profile configuration. */
+ @Nullable private OIDCAuthorizationConfiguration profileConfiguration;
/** Constructor.*/
public InitializeOAuth2ClientAuthenticationContext() {
- // Default under OIDCPeerEntityContext in the outbound context (create true).
- oauth2ClientContextLookupStrategy = new ChildContextLookup<>(OAuth2ClientContext.class).compose(
- new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
- new OutboundMessageContextLookup()));
-
+
+ // Default under the OIDC Peer Entity Context, create is true
oauth2ClientAuthenticationContextLookupStrategy =
new ChildContextLookup<>(OAuth2ClientAuthenticationContext.class, true).compose(
new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
new OutboundMessageContextLookup()));
+
+ relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
}
@Override
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
-
- if (clientAuthenticationResovler == null) {
- throw new ComponentInitializationException("OAuth2 Client Authentication Resovler cannot be null");
- }
- }
-
- /**
- * Set the OAuth2 client authentication resolver.
- *
- * @param resolver the resolver
- */
- public void setClientAuthenticationResolver(@Nonnull final OAuth2ClientAuthenticationResolver resolver) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
- clientAuthenticationResovler = Constraint.isNotNull(resolver,
- "OAuth2 Client Authentication Resovler cannot be null");
}
/**
- * Set the strategy to lookup the {@link OAuth2ClientContext}
- * from the {@link ProfileRequestContext}.
+ * Set lookup strategy for relying party context.
*
- * @param strgy the strategy.
+ * @param strategy lookup 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");
+ public void setRelyingPartyContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+ relyingPartyContextLookupStrategy =
+ Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
}
+
/**
* Set the strategy to lookup the {@link OAuth2ClientAuthenticationContext}
* from the {@link ProfileRequestContext}.
@@ -147,12 +122,6 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
return false;
}
- oauth2ClientContext = oauth2ClientContextLookupStrategy.apply(profileRequestContext);
- if (oauth2ClientContext == null) {
- log.error("{} No OAuth2 client context found or created", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
oauth2ClientAuthenticationContext =
oauth2ClientAuthenticationContextLookupStrategy.apply(profileRequestContext);
if (oauth2ClientAuthenticationContext == null) {
@@ -161,6 +130,17 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
return false;
}
+ final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+ if (rpCtx != null && rpCtx.getConfiguration() != null &&
+ rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+ profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+ }
+ if (profileConfiguration == null) {
+ log.error("{} OIDCAuthorizationConfiguration not found", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return false;
+ }
+
return true;
}
@@ -169,28 +149,17 @@ public class InitializeOAuth2ClientAuthenticationContext extends AbstractProfile
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
super.doExecute(profileRequestContext);
- try {
- final ClientAuthentication clientAuth =
- clientAuthenticationResovler.resolveSingle(
- new CriteriaSet(new ClientIDCriterion(new ClientID(oauth2ClientContext.getClientId()))));
-
- if (clientAuth == null) {
- log.error("{} No client authentication mode found from resolver", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
- return;
- }
-
- log.trace("{} Found client authentication mode '{}' for client '{}'",
- getLogPrefix(), clientAuth.getMethod(), oauth2ClientContext.getClientId());
-
- oauth2ClientAuthenticationContext.setClientAuthentication(clientAuth);
- log.debug("{} Initialized OAuth2 Client Authentication Context for client '{}'",
- getLogPrefix(), oauth2ClientContext.getClientId());
- } catch (final ResolverException e) {
- log.warn("{} client context could not be initialized", getLogPrefix(), e);
+ final ClientAuthentication clientAuth = profileConfiguration.getClientAuthentication(profileRequestContext);
+
+ if (clientAuth == null) {
+ log.error("{} No client authentication mode found from profile configuration", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
return;
}
+
+ oauth2ClientAuthenticationContext.setClientAuthentication(clientAuth);
+ log.debug("{} Initialized OAuth2 Client Authentication Context: Found client authentication mode "
+ + "'{}' for client '{}'",getLogPrefix(), clientAuth.getMethod(), clientAuth.getClientID());
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
index 6415634..fab32ee 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientContext.java
@@ -18,9 +18,6 @@
package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -34,26 +31,18 @@ import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.id.ClientID;
-
import net.shibboleth.idp.plugin.authn.oidc.rp.config.navigate.RedirectUriLookupFunction;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.oidc.metadata.criterion.ClientIDCriterion;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
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;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
/**
* An {@link AbstractProfileAction action} that resolves the client identifier for the chosen
@@ -65,9 +54,6 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
@Nonnull
private final Logger log = LoggerFactory.getLogger(InitializeOAuth2ClientContext.class);
- /** The resolver to use to find the client identifier (client_id). */
- @NonnullAfterInit private OAuth2ClientIdentifierResolver clientIdResovler;
-
/** The strategy used to lookup or create the {@link OAuth2ClientContext} for storing the client_id.*/
@NonnullAfterInit private Function<ProfileRequestContext, OAuth2ClientContext> oauth2ClientContextLookupStrategy;
@@ -77,6 +63,12 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
/** A redirect_uri lookup strategy which can pull out an override redirect_uri from the profile request context.*/
@Nonnull private Function<ProfileRequestContext, URI> redirectUriOverrideLookupStrategy;
+ /** Lookup function for relying party context. */
+ @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+ /** Applicable stashed profile configuration. */
+ @Nullable private OIDCAuthorizationConfiguration profileConfiguration;
+
/** Constructor.*/
public InitializeOAuth2ClientContext() {
// Default under OIDCPeerEntityContext in the outbound context (create true).
@@ -84,9 +76,22 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
new ChildContextLookup<>(OIDCPeerEntityContext.class).compose(
new OutboundMessageContextLookup()));
+ relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+
redirectUriOverrideLookupStrategy = new RedirectUriLookupFunction();
}
+ /**
+ * Set lookup strategy for relying party context.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setRelyingPartyContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+ relyingPartyContextLookupStrategy =
+ Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+ }
+
/**
* Set the redirect_uri lookup strategy to locate an explicitly set redirect_uri.
*
@@ -104,24 +109,7 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
@Override
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
-
- if (clientIdResovler == null) {
- throw new ComponentInitializationException("OAuth2 Client Resovler cannot be null");
- }
- }
-
- /**
- * Set the OAuth2 client identifier (client_id) resolver.
- *
- * @param resolver the resolver
- */
- public void setClientIdResolver(@Nonnull final OAuth2ClientIdentifierResolver resolver) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
- clientIdResovler = Constraint.isNotNull(resolver, "OAuth2 Client Resovler cannot be null");
}
-
/**
* Set the strategy to lookup the {@link OAuth2ClientContext}
@@ -152,33 +140,39 @@ public class InitializeOAuth2ClientContext extends AbstractProfileAction {
return false;
}
+ final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+ if (rpCtx != null && rpCtx.getConfiguration() != null &&
+ rpCtx.getProfileConfig() instanceof OIDCAuthorizationConfiguration) {
+ profileConfiguration = (OIDCAuthorizationConfiguration) rpCtx.getProfileConfig();
+ }
+ if (profileConfiguration == null) {
+ log.error("{} OIDCAuthorizationConfiguration not found", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return false;
+ }
+
return true;
}
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
super.doExecute(profileRequestContext);
-
- try {
- final String clientId = clientIdResovler.resolveSingle(profileRequestContext);
- if (clientId == null) {
- log.error("{} No client_id found from resolver", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
- return;
- }
- oauth2ClientContext.setClientId(clientId);
-
- final URI redirectUri = redirectUriOverrideLookupStrategy.apply(profileRequestContext);
- if (redirectUri != null) {
- log.debug("{} Redirect_uri has been explicitly set as '{}'", getLogPrefix(), redirectUri);
- oauth2ClientContext.setRedirectUriOverride(redirectUri);
- }
- log.debug("{} Initialized OAuth2 Client Context for client '{}'", getLogPrefix(), clientId);
- } catch (final ResolverException e) {
- log.warn("{} client context could not be initialized", getLogPrefix(), e);
+
+ final String clientId = profileConfiguration.getClientId(profileRequestContext);
+ if (StringSupport.trimOrNull(clientId) == null) {
+ log.error("{} No client_id found from profile configuration", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CONFIG);
return;
}
+ oauth2ClientContext.setClientId(clientId);
+
+ final URI redirectUri = redirectUriOverrideLookupStrategy.apply(profileRequestContext);
+ if (redirectUri != null) {
+ log.debug("{} Redirect_uri has been explicitly set as '{}'", getLogPrefix(), redirectUri);
+ oauth2ClientContext.setRedirectUriOverride(redirectUri);
+ }
+ log.debug("{} Initialized OAuth2 Client Context for client '{}'", getLogPrefix(), clientId);
+
}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java
deleted file mode 100644
index 463c1af..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationContainer.java
+++ /dev/null
@@ -1,147 +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.authn.oidc.rp.spring.impl;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * This class is a sortable container of {@link OAuth2ClientAuthenticationResolver}s, wrapped into a serviceable component.
- */
-public class OAuth2ClientAuthenticationContainer extends AbstractServiceableComponent<OAuth2ClientAuthenticationResolver>
- implements Comparable<OAuth2ClientAuthenticationContainer> {
-
- /** If we autogenerate a sort key it comes from this count. */
- private static int sortKeyValue;
-
- /** Class logger. */
- private final Logger log = LoggerFactory.getLogger(OAuth2ClientAuthenticationContainer.class);
-
- /** The embedded resolver. */
- @NonnullAfterInit
- private OAuth2ClientAuthenticationResolver resolver;
-
- /** The key by which we sort the provider. */
- @NonnullAfterInit
- private Integer sortKey;
-
- /**
- * Set the sort key.
- *
- * @param key what to set
- */
- public void setSortKey(final int key) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- sortKey = key;
- }
-
- /**
- * Set the {@link ProviderMetadataResolver} to embed.
- *
- * @param theResolver The {@link ProviderMetadataResolver} to embed.
- */
- @Nonnull
- public void setEmbeddedResolver(@Nonnull final OAuth2ClientAuthenticationResolver theResolver) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- resolver = Constraint.isNotNull(theResolver, "OAuth2ClientAuthenticationResolver cannot be null");
- }
-
- /**
- * Return what we are build around. Used for testing.
- *
- * @return the parameter we got as a constructor
- */
- @Nonnull
- public OAuth2ClientAuthenticationResolver getEmbeddedResolver() {
- return resolver;
- }
-
-
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- setId(resolver.getId());
- super.doInitialize();
- if (null == resolver) {
- throw new ComponentInitializationException("OAuth2ClientAuthenticationResolver cannot be null");
- }
-
- if (null == sortKey) {
- synchronized (OAuth2ClientAuthenticationContainer.class) {
- sortKeyValue++;
- setSortKey(sortKeyValue);
- }
- log.info("Top level OAuth2ClientAuthentication Provider '{}' "
- + "did not have a sort key; giving it value '{}'", getId(),
- sortKey);
- }
- }
-
-
- @Override
- @Nonnull
- public OAuth2ClientAuthenticationResolver getComponent() {
- return getEmbeddedResolver();
- }
-
- @Override
- public int compareTo(final OAuth2ClientAuthenticationContainer other) {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- final int result = sortKey.compareTo(other.sortKey);
- if (result != 0) {
- return result;
- }
- if (equals(other)) {
- return 0;
- }
- return getId().compareTo(other.getId());
- }
-
- /**
- * {@inheritDoc}. We are within a spring context and so equality can be determined by ID, however we also test by
- * sortKey just in case.
- */
- @Override
- public boolean equals(final Object other) {
- if (null == other) {
- return false;
- }
- if (!(other instanceof OAuth2ClientAuthenticationContainer)) {
- return false;
- }
- final OAuth2ClientAuthenticationContainer otherRp = (OAuth2ClientAuthenticationContainer) other;
-
- return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(sortKey, getId());
- }
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java
deleted file mode 100644
index 7b7ffaa..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientAuthenticationResolverServiceStrategy.java
+++ /dev/null
@@ -1,65 +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.authn.oidc.rp.spring.impl;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.context.ApplicationContext;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.service.ServiceException;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-public class OAuth2ClientAuthenticationResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
- implements Function<ApplicationContext, ServiceableComponent<OAuth2ClientAuthenticationResolver>> {
-
- @Override
- public ServiceableComponent<OAuth2ClientAuthenticationResolver> apply(
- @Nullable final ApplicationContext appContext) {
- final Collection<OAuth2ClientAuthenticationContainer> resolvers =
- appContext.getBeansOfType(OAuth2ClientAuthenticationContainer.class).values();
-
- if (resolvers.isEmpty()) {
- throw new ServiceException(
- "Reload did not produce any bean of type " + OAuth2ClientAuthenticationContainer.class.getName());
- }
- if (1 == resolvers.size()) {
- // done
- return resolvers.iterator().next();
- }
- // initialize so we can sort
- for (final OAuth2ClientAuthenticationContainer resolver : resolvers) {
- try {
- resolver.initialize();
- } catch (final ComponentInitializationException e) {
- throw new BeanCreationException("Could not preinitialize "
- + resolver.getId(), e);
- }
- }
-
- throw new BeanCreationException("could not preinitialize the Client Authentication Provider, "
- + "mulitple Client Authentication Providers not supported");
- }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java
deleted file mode 100644
index 601200d..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierContainer.java
+++ /dev/null
@@ -1,148 +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.authn.oidc.rp.spring.impl;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Objects;
-
-import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
-import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-/**
- * This class is a sortable container of {@link OAuth2ClientAuthenticationResolver}s, wrapped into a serviceable component.
- */
-public class OAuth2ClientIdentifierContainer extends AbstractServiceableComponent<OAuth2ClientIdentifierResolver>
- implements Comparable<OAuth2ClientIdentifierContainer> {
-
- /** If we autogenerate a sort key it comes from this count. */
- private static int sortKeyValue;
-
- /** Class logger. */
- private final Logger log = LoggerFactory.getLogger(OAuth2ClientIdentifierContainer.class);
-
- /** The embedded resolver. */
- @NonnullAfterInit
- private OAuth2ClientIdentifierResolver resolver;
-
- /** The key by which we sort the provider. */
- @NonnullAfterInit
- private Integer sortKey;
-
- /**
- * Set the sort key.
- *
- * @param key what to set
- */
- public void setSortKey(final int key) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- sortKey = key;
- }
-
- /**
- * Set the {@link OAuth2ClientIdentifierResolver} to embed.
- *
- * @param theResolver The {@link OAuth2ClientIdentifierResolver} to embed.
- */
- @Nonnull
- public void setEmbeddedResolver(@Nonnull final OAuth2ClientIdentifierResolver theResolver) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- resolver = Constraint.isNotNull(theResolver, "OAuth2ClientAuthenticationResolver cannot be null");
- }
-
- /**
- * Return what we are built around. Used for testing.
- *
- * @return the embedded resolver.
- */
- @Nonnull
- public OAuth2ClientIdentifierResolver getEmbeddedResolver() {
- return resolver;
- }
-
-
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- setId(resolver.getId());
- super.doInitialize();
- if (null == resolver) {
- throw new ComponentInitializationException("OAuth2ClientAuthenticationResolver cannot be null");
- }
-
- if (null == sortKey) {
- synchronized (OAuth2ClientIdentifierContainer.class) {
- sortKeyValue++;
- setSortKey(sortKeyValue);
- }
- log.info("Top level OAuth2ClientAuthentication Provider '{}' "
- + "did not have a sort key; giving it value '{}'", getId(),
- sortKey);
- }
- }
-
-
- @Override
- @Nonnull
- public OAuth2ClientIdentifierResolver getComponent() {
- return getEmbeddedResolver();
- }
-
- @Override
- public int compareTo(final OAuth2ClientIdentifierContainer other) {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- final int result = sortKey.compareTo(other.sortKey);
- if (result != 0) {
- return result;
- }
- if (equals(other)) {
- return 0;
- }
- return getId().compareTo(other.getId());
- }
-
- /**
- * {@inheritDoc}. We are within a spring context and so equality can be determined by ID, however we also test by
- * sortKey just in case.
- */
- @Override
- public boolean equals(final Object other) {
- if (null == other) {
- return false;
- }
- if (!(other instanceof OAuth2ClientIdentifierContainer)) {
- return false;
- }
- final OAuth2ClientIdentifierContainer otherRp = (OAuth2ClientIdentifierContainer) other;
-
- return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(sortKey, getId());
- }
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.java
deleted file mode 100644
index 0820475..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/OAuth2ClientIdentifierResolverServiceStrategy.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.authn.oidc.rp.spring.impl;
-
-import java.util.Collection;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import org.springframework.beans.factory.BeanCreationException;
-import org.springframework.context.ApplicationContext;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.service.ServiceException;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-public class OAuth2ClientIdentifierResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
- implements Function<ApplicationContext, ServiceableComponent<OAuth2ClientIdentifierResolver>> {
-
- @Override
- public ServiceableComponent<OAuth2ClientIdentifierResolver> apply(
- @Nullable final ApplicationContext appContext) {
-
- final Collection<OAuth2ClientIdentifierContainer> resolvers =
- appContext.getBeansOfType(OAuth2ClientIdentifierContainer.class).values();
-
- if (resolvers.isEmpty()) {
- throw new ServiceException(
- "Reload did not produce any bean of type " + OAuth2ClientAuthenticationContainer.class.getName());
- }
- if (1 == resolvers.size()) {
- // done
- return resolvers.iterator().next();
- }
- // initialize so we can sort
- for (final OAuth2ClientIdentifierContainer resolver : resolvers) {
- try {
- resolver.initialize();
- } catch (final ComponentInitializationException e) {
- throw new BeanCreationException("Could not preinitialize the Client Identifer Provider "
- + resolver.getId(), e);
- }
- }
-
- throw new BeanCreationException("Could not preinitialize the Client Identifer Provider"
- + ", mulitple client identifer providers not supported");
- }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java
deleted file mode 100644
index 62dee46..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientAuthenticationProvider.java
+++ /dev/null
@@ -1,107 +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.authn.oidc.rp.spring.impl;
-
-import java.util.Collections;
-
-import javax.annotation.Nonnull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-
-/**
- * A service interface to implement the {@link OAuth2ClientAuthenticationResolver}.
- */
-public class ReloadingOAuth2ClientAuthenticationProvider extends AbstractIdentifiableInitializableComponent
- implements OAuth2ClientAuthenticationResolver {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingOAuth2ClientAuthenticationProvider.class);
-
- /** The service which manages the reloading. */
- private final ReloadableService<OAuth2ClientAuthenticationResolver> service;
-
- /**
- * Constructor.
- *
- * @param resolverService the service which will manage the loading.
- */
- public ReloadingOAuth2ClientAuthenticationProvider(
- @Nonnull final ReloadableService<OAuth2ClientAuthenticationResolver> resolverService) {
- service = Constraint.isNotNull(resolverService, "ProviderMetadataResolver Service cannot be null");
- }
-
- @Override
- public Iterable<ClientAuthentication> resolve(@Nonnull final CriteriaSet criteria) throws ResolverException {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- ServiceableComponent<OAuth2ClientAuthenticationResolver> component = null;
- try {
- component = service.getServiceableComponent();
- if (null == component) {
- log.error("OAuth2ClientAuthenticationResolver '{}': Error accessing underlying source: "
- + "Invalid configuration.", getId());
- } else {
- final OAuth2ClientAuthenticationResolver resolver = component.getComponent();
- return resolver.resolve(criteria);
- }
- } catch (final ResolverException e) {
- log.error("OAuth2ClientAuthenticationResolver '{}': Error during resolution", getId(), e);
- } finally {
- if (null != component) {
- component.unpinComponent();
- }
- }
- return Collections.emptySet();
- }
-
- @Override
- public ClientAuthentication resolveSingle(@Nonnull final CriteriaSet criteria) throws ResolverException {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- ServiceableComponent<OAuth2ClientAuthenticationResolver> component = null;
- try {
- component = service.getServiceableComponent();
- if (null == component) {
- log.error("ReloadingProviderMetadataProvider '{}': Error accessing underlying source: "
- + "Invalid configuration.", getId());
- } else {
- final OAuth2ClientAuthenticationResolver resolver = component.getComponent();
- return resolver.resolveSingle(criteria);
- }
- } catch (final ResolverException e) {
- log.error("ProviderMetadataResolver '{}': Error during resolution", getId(), e);
- } finally {
- if (null != component) {
- component.unpinComponent();
- }
- }
- return null;
- }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java
deleted file mode 100644
index 5d4ee68..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/spring/impl/ReloadingOAuth2ClientIdentifierProvider.java
+++ /dev/null
@@ -1,105 +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.authn.oidc.rp.spring.impl;
-
-import java.util.Collections;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
-import net.shibboleth.utilities.java.support.service.ReloadableService;
-import net.shibboleth.utilities.java.support.service.ServiceableComponent;
-
-
-/**
- * A service interface to implement the {@link OAuth2ClientIdentifierResolver}.
- */
-public class ReloadingOAuth2ClientIdentifierProvider extends AbstractIdentifiableInitializableComponent
- implements OAuth2ClientIdentifierResolver {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingOAuth2ClientIdentifierProvider.class);
-
- /** The service which manages the reloading. */
- private final ReloadableService<OAuth2ClientIdentifierResolver> service;
-
- /**
- * Constructor.
- *
- * @param resolverService the service which will manage the loading.
- */
- public ReloadingOAuth2ClientIdentifierProvider(
- @Nonnull final ReloadableService<OAuth2ClientIdentifierResolver> resolverService) {
- service = Constraint.isNotNull(resolverService, "ProviderMetadataResolver Service cannot be null");
- }
-
- @Override
- public Iterable<String> resolve(@Nonnull final ProfileRequestContext criteria) throws ResolverException {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- ServiceableComponent<OAuth2ClientIdentifierResolver> component = null;
- try {
- component = service.getServiceableComponent();
- if (null == component) {
- log.error("OAuth2ClientAuthenticationResolver '{}': Error accessing underlying source: "
- + "Invalid configuration.", getId());
- } else {
- final OAuth2ClientIdentifierResolver resolver = component.getComponent();
- return resolver.resolve(criteria);
- }
- } catch (final ResolverException e) {
- log.error("OAuth2ClientAuthenticationResolver '{}': Error during resolution", getId(), e);
- } finally {
- if (null != component) {
- component.unpinComponent();
- }
- }
- return Collections.emptySet();
- }
-
- @Override
- public String resolveSingle(@Nonnull final ProfileRequestContext criteria) throws ResolverException {
- ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
- ServiceableComponent<OAuth2ClientIdentifierResolver> component = null;
- try {
- component = service.getServiceableComponent();
- if (null == component) {
- log.error("ReloadingProviderMetadataProvider '{}': Error accessing underlying source: "
- + "Invalid configuration.", getId());
- } else {
- final OAuth2ClientIdentifierResolver resolver = component.getComponent();
- return resolver.resolveSingle(criteria);
- }
- } catch (final ResolverException e) {
- log.error("ProviderMetadataResolver '{}': Error during resolution", getId(), e);
- } finally {
- if (null != component) {
- component.unpinComponent();
- }
- }
- return null;
- }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index f9b0fc8..1727532 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -9,70 +9,63 @@
default-init-method="initialize" default-destroy-method="destroy">
- <!--
- System beans needed for extension to function, loaded after global.xml.
- The default template shows an incomplete example authentication flow descriptor which can be
- removed if not needed
- -->
-
+ <!-- System beans needed for extension to function, loaded after global.xml. The default template shows an incomplete
+ example authentication flow descriptor which can be removed if not needed -->
+
<!-- Functions use by the flow and global beans -->
-
+
<bean id="shibboleth.ChildLookup.OIDCProviderMetadataContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext) }" />
-
+
<bean id="shibboleth.ChildLookup.OAuth2ClientContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext) }" />
-
+
<bean id="shibboleth.ChildLookup.OIDCPeerEntityContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext) }" />
-
+
<bean id="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
- <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContext"/>
+ <ref bean="shibboleth.ChildLookup.OIDCProviderMetadataContext" />
</constructor-arg>
<constructor-arg name="f">
- <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound"/>
+ <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" />
</constructor-arg>
</bean>
-
+
<bean id="shibboleth.ChildLookup.OAuth2ClientContextFromOutbound" parent="shibboleth.Functions.Compose">
<constructor-arg name="g">
- <ref bean="shibboleth.ChildLookup.OAuth2ClientContext"/>
+ <ref bean="shibboleth.ChildLookup.OAuth2ClientContext" />
</constructor-arg>
<constructor-arg name="f">
- <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound"/>
+ <ref bean="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" />
</constructor-arg>
</bean>
-
+
<!-- Find OIDCPeerEntity Context under outbound message -->
<bean id="shibboleth.ChildLookup.OIDCPeerEntityFromOutbound" parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.ChildLookup.OIDCPeerEntityContext"
- c:f-ref="shibboleth.MessageContextLookup.Outbound" />
-
-
+ c:g-ref="shibboleth.ChildLookup.OIDCPeerEntityContext" c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+
+
<!-- The authentication flow descriptor -->
-
+
<bean id="authn/OIDCRelyingParty" parent="shibboleth.AuthenticationFlow"
- p:order="%{idp.authn.oidc.rp.order:1000}"
- p:nonBrowserSupported="%{idp.authn.oidc.rp.nonBrowserSupported:true}"
- p:passiveAuthenticationSupported="%{idp.authn.oidc.rp.passiveAuthenticationSupported:true}"
- p:forcedAuthenticationSupported="%{idp.authn.oidc.rp.forcedAuthenticationSupported:true}"
- p:proxyRestrictionsEnforced="%{idp.authn.oidc.rp.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
- p:proxyScopingEnforced="%{idp.authn.oidc.rp.proxyScopingEnforced:false}"
- p:discoveryRequired="%{idp.authn.oidc.rp.discoveryRequired:false}"
- p:lifetime="%{idp.authn.oidc.rp.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
- p:inactivityTimeout="%{idp.authn.oidc.rp.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
- p:reuseCondition-ref="#{'%{idp.authn.oidc.rp.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
- p:activationCondition-ref="#{'%{idp.authn.oidc.rp.activationCondition:shibboleth.Conditions.TRUE}'.trim()}">
+ p:order="%{idp.authn.oidc.rp.order:1000}" p:nonBrowserSupported="%{idp.authn.oidc.rp.nonBrowserSupported:true}"
+ p:passiveAuthenticationSupported="%{idp.authn.oidc.rp.passiveAuthenticationSupported:true}"
+ p:forcedAuthenticationSupported="%{idp.authn.oidc.rp.forcedAuthenticationSupported:true}"
+ p:proxyRestrictionsEnforced="%{idp.authn.oidc.rp.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
+ p:proxyScopingEnforced="%{idp.authn.oidc.rp.proxyScopingEnforced:false}"
+ p:discoveryRequired="%{idp.authn.oidc.rp.discoveryRequired:false}"
+ p:lifetime="%{idp.authn.oidc.rp.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
+ p:inactivityTimeout="%{idp.authn.oidc.rp.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
+ p:reuseCondition-ref="#{'%{idp.authn.oidc.rp.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
+ p:activationCondition-ref="#{'%{idp.authn.oidc.rp.activationCondition:shibboleth.Conditions.TRUE}'.trim()}">
<property name="supportedPrincipals">
<list>
- <bean parent="shibboleth.SAML2AuthnContextClassRef"
- c:classRef="class-ref" />
- <bean parent="shibboleth.SAML1AuthenticationMethod"
- c:method="auth-ref" />
+ <bean parent="shibboleth.SAML2AuthnContextClassRef" c:classRef="class-ref" />
+ <bean parent="shibboleth.SAML1AuthenticationMethod" c:method="auth-ref" />
</list>
</property>
<property name="supportedPrincipalsByString">
@@ -80,63 +73,19 @@
c:_0="#{'%{idp.authn.oidc.rp.supportedPrincipals:}'.trim()}" />
</property>
</bean>
-
+
<bean id="issuer" class="java.lang.String" c:_0="%{idp.authn.oidc.rp.issuer:%{idp.entityID}}" />
-
- <bean id="AbstractOIDCProfile" abstract="true"
- p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.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}"
- p:forcePKCE="%{idp.authn.oidc.rp.forcePKCE:false}"
- p:allowPKCEPlain="%{idp.authn.oidc.rp.allowPKCEPlain:false}"
- p:iDTokenLifetime="%{idp.authn.oidc.rp.idToken.defaultLifetime:PT1H}"
- p:accessTokenLifetime="%{idp.authn.oidc.rp.accessToken.defaultLifetime:PT10M}"
- p:refreshTokenLifetime="%{idp.authn.oidc.rp.refreshToken.defaultLifetime:PT2H}"
- p:alwaysIncludedAttributes="%{idp.authn.oidc.rp.alwaysIncludedAttributes:}" />
-
- <bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
- class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
- p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"
- p:authorizeCodeLifetime="%{idp.authn.oidc.rp.authorizeCode.defaultLifetime:PT5M}"
- 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:}" />
-
- <!--
- Security Configuration Defaults. These settings establish the default security
- configurations for signatures and loads the default credentials used.
- -->
-
- <bean id="shibboleth.oidc.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()}" />
- </property>
- <property name="encryptionConfiguration">
- <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}" />
- </property>
- <property name="requestObjectDecryptionConfiguration">
- <ref bean="#{'%{idp.oidc.rodecrypt.config:shibboleth.oidc.requestObjectDecryptionConfiguration}'.trim()}" />
- </property>
- <property name="requestObjectSignatureValidationConfiguration">
- <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}" />
- </property>
- <property name="tokenEndpointJwtSignatureValidationConfiguration">
- <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}" />
- </property> -->
- </bean>
-
+
+
+
<!-- Necessary for encoder parsing and claims mapping to function. -->
-
+
<bean parent="shibboleth.RegistryNamingFunction" c:claz="net.minidev.json.JSONObject">
<constructor-arg name="function">
<bean class="net.shibboleth.oidc.attribute.transcoding.AbstractOIDCAttributeTranscoder.NamingFunction" />
- </constructor-arg>
+ </constructor-arg>
</bean>
-
+
<!-- Controller implementation -->
<bean id="shibboleth.authn.OIDC.externalServletPath" class="java.lang.String"
c:_0="%{idp.authn.oidc.rp.externalAuthnPath:/Authn/OIDC/RP}">
@@ -146,128 +95,60 @@
<bean id="shibboleth.authn.OIDC.externalAuthnPath" class="java.lang.String"
c:_0="servletRelative:#{getObject('shibboleth.authn.OIDC.externalServletPath')}#{T(net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController).AUTHORIZE_PATH_SEGMENT}" />
-
-
+
+
<bean id="shibboleth.oidc.rp.AuthorizationController"
p:redirectUriCreationStrategy="#{getObject('shibboleth.oidc.rp.RedirectUriCreationStrategy') ?: getObject('shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy')}"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
-
-
- <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
+
+ <bean id="shibboleth.oidc.rp.DefaultRedirectUriCreationStrategy"
c:callbackPath="#{getObject('shibboleth.authn.OIDC.externalServletPath')}/callback"
- c:allowedOrigins="%{idp.oidc.rp.redirecturl.allowedOrigins:}"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction"/>
-
- <!-- TODO these bean names need to change - otherwise they may class with the OP plugin if installed
- at the same time -->
-
+ c:allowedOrigins="%{idp.authn.oidc.rp.client.redirecturl.allowedOrigins:}"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.DefaultRedirectUriCreationFunction" />
+
+
<!-- OIDC OP information resolver service beans. -->
-
+
<bean id="shibboleth.oidc.rp.ProviderMetadataResolver"
class="net.shibboleth.oidc.metadata.impl.ReloadingProviderMetadataProvider"
c:resolverService-ref="shibboleth.oidc.rp.ProviderMetadataResolverService" />
-
- <!-- TODO add this back? depends-on="shibboleth.AttributeResolverService"-->
+
+ <!-- TODO add this back? depends-on="shibboleth.AttributeResolverService" -->
<bean id="shibboleth.oidc.rp.ProviderMetadataResolverService"
class="net.shibboleth.ext.spring.service.ReloadableSpringService"
-
+
p:serviceConfigurations-ref="ExtendedProviderMetadataResolverResources"
p:failFast="%{idp.service.providermetadata.failFast:%{idp.service.failFast:false}}"
p:reloadCheckDelay="%{idp.service.providermetadata.checkInterval:PT0S}"
p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
- <constructor-arg name="claz"
- value="net.shibboleth.oidc.metadata.ProviderMetadataResolver" />
+ <constructor-arg name="claz" value="net.shibboleth.oidc.metadata.ProviderMetadataResolver" />
<constructor-arg name="strategy">
- <bean class="net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ProviderMetadataResolverServiceStrategy" />
+ <bean
+ class="net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ProviderMetadataResolverServiceStrategy" />
</constructor-arg>
</bean>
- <!-- TODO ADD BACK THE CONDITION (GET TO WORK WITH TEST) <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
- <value>conditional:%{idp.home}/conf/oidc-providermetadata-resolvers.xml</value>
- OR <value>classpath:/conf/oidc-providermetadata-resolvers.xml</value>
- </util:list> -->
+ <!-- TODO ADD BACK THE CONDITION (GET TO WORK WITH TEST) <util:list id="shibboleth.DefaultProviderMetadataResolverResources">
+ <value>conditional:%{idp.home}/conf/oidc-providermetadata-resolvers.xml</value> OR <value>classpath:/conf/oidc-providermetadata-resolvers.xml</value>
+ </util:list> -->
<util:list id="shibboleth.DefaultProviderMetadataResolverResources">
<value>%{idp.home}/conf/authn/oidc-providermetadata-resolvers.xml</value> <!-- should be a conditional:? -->
</util:list>
<!-- Auto-append system config file to resource set. -->
- <bean id ="ExtendedProviderMetadataResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
- p:firstList="#{getObject('%{idp.service.providermetadata.resources:shibboleth.ProviderMetadataResolverResources}'.trim()) ?:
+ <bean id="ExtendedProviderMetadataResolverResources"
+ class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
+ p:firstList="#{getObject('%{idp.service.providermetadata.resources:shibboleth.ProviderMetadataResolverResources}'.trim()) ?:
getObject('shibboleth.DefaultProviderMetadataResolverResources')}">
<property name="secondList">
- <util:list >
- <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml</value>
- </util:list>
- </property>
- </bean>
-
-
- <!-- Client authentication resolvers -->
- <!-- TODO add this back? depends-on="shibboleth.AttributeResolverService"-->
-
- <bean id="shibboleth.oidc.rp.ClientAuthenticationResolverService"
- class="net.shibboleth.ext.spring.service.ReloadableSpringService"
- p:serviceConfigurations-ref="ExtendedClientAuthenticationResolverResources"
- p:failFast="%{idp.service.clientinfo.failFast:%{idp.service.failFast:false}}"
- p:reloadCheckDelay="%{idp.service.clientinfo.checkInterval:PT0S}"
- p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
- p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
- <constructor-arg name="claz"
- value="net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolver" />
- <constructor-arg name="strategy">
- <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientAuthenticationResolverServiceStrategy" />
- </constructor-arg>
- </bean>
-
- <bean id ="ExtendedClientAuthenticationResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
- p:firstList="#{getObject('%{idp.service.clientinfo.resources:shibboleth.ClientInformationResolverResources}'.trim()) ?:
- getObject('shibboleth.DefaultClientAuthenticationResolverResources')}">
- <property name="secondList">
- <util:list >
- <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml</value>
- </util:list>
- </property>
- </bean>
- <util:list id="shibboleth.DefaultClientAuthenticationResolverResources">
- <value>%{idp.home}/conf/authn/oidc-client-registration-authentication.xml</value> <!-- should be a conditional:? -->
- </util:list>
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolver"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.ReloadingOAuth2ClientAuthenticationProvider"
- c:resolverService-ref="shibboleth.oidc.rp.ClientAuthenticationResolverService" />
-
-
- <!-- Client Identifier resolvers -->
- <bean id="shibboleth.oidc.rp.ClientIdentifierResolverService"
- class="net.shibboleth.ext.spring.service.ReloadableSpringService"
- p:serviceConfigurations-ref="ExtendedClientIdentifierResolverResources"
- p:failFast="%{idp.service.clientinfo.failFast:%{idp.service.failFast:false}}"
- p:reloadCheckDelay="%{idp.service.clientinfo.checkInterval:PT0S}"
- p:beanPostProcessors-ref="shibboleth.IdentifiableBeanPostProcessor"
- p:beanFactoryPostProcessors-ref="shibboleth.PropertySourcesPlaceholderConfigurer">
- <constructor-arg name="claz"
- value="net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolver" />
- <constructor-arg name="strategy">
- <bean class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientIdentifierResolverServiceStrategy" />
- </constructor-arg>
- </bean>
-
- <bean id ="ExtendedClientIdentifierResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
- p:firstList="#{getObject('%{idp.service.clientinfo.resources:shibboleth.ClientInformationResolverResources}'.trim()) ?:
- getObject('shibboleth.DefaultClientIdentifierResolverResources')}">
- <property name="secondList">
- <util:list >
- <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml</value>
+ <util:list>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/providermetadata-resolver-system.xml
+ </value>
</util:list>
</property>
</bean>
- <util:list id="shibboleth.DefaultClientIdentifierResolverResources">
- <value>%{idp.home}/conf/authn/oidc-client-registration-clientid.xml</value> <!-- should be a conditional:? -->
- </util:list>
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierResolver"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.ReloadingOAuth2ClientIdentifierProvider"
- c:resolverService-ref="shibboleth.oidc.rp.ClientIdentifierResolverService" />
-
-
+
+
+
+
</beans>
\ No newline at end of file
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 84de395..df7f839 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
@@ -96,7 +96,6 @@
<bean id="InitializeOAuth2ClientContext" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientContext"
- p:clientIdResolver-ref="shibboleth.oidc.rp.OAuth2ClientIdentifierResolver"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
@@ -190,7 +189,6 @@
<bean id="InitializeOAuth2ClientAuthenticationContext" scope="prototype"
class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.InitializeOAuth2ClientAuthenticationContext"
- p:clientAuthenticationResolver-ref="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolver"
p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"/>
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 cb825fc..6e5cd49 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
@@ -29,8 +29,8 @@
<!-- <evaluate expression="PostLookupPopulateAuditContext" /> -->
<evaluate expression="InitializeOutboundMessageContext" />
- <evaluate expression="InitializeOAuth2ClientContext" />
<evaluate expression="SelectProfileConfiguration" />
+ <evaluate expression="InitializeOAuth2ClientContext" />
<evaluate expression="PopulateResponseTypeAndModeContext"/>
<evaluate expression="AddOIDCAuthenticationRequest"/>
<!-- <evaluate expression="PostRequestPopulateAuditContext" />
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
new file mode 100644
index 0000000..573f224
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <!-- OIDC RP Profile Configurations. -->
+
+ <bean id="AbstractOIDCProfile" abstract="true"
+ p:securityConfiguration-ref="%{idp.security.authn.oidc.rp.config:shibboleth.oidc.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}"
+ p:forcePKCE="%{idp.authn.oidc.rp.forcePKCE:false}" p:allowPKCEPlain="%{idp.authn.oidc.rp.allowPKCEPlain:false}"
+ p:iDTokenLifetime="%{idp.authn.oidc.rp.idToken.defaultLifetime:PT1H}"
+ p:accessTokenLifetime="%{idp.authn.oidc.rp.accessToken.defaultLifetime:PT10M}"
+ p:refreshTokenLifetime="%{idp.authn.oidc.rp.refreshToken.defaultLifetime:PT2H}"
+ 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 -->
+ <bean id="OIDC.SSO" parent="AbstractOIDCSSOProfile" lazy-init="true"
+ class="net.shibboleth.oidc.profile.config.OIDCAuthorizationConfiguration"
+ p:httpRequestMethod="%{idp.authn.oidc.rp.httpRequestMethod:GET}"
+ p:authorizeCodeLifetime="%{idp.authn.oidc.rp.authorizeCode.defaultLifetime:PT5M}"
+ 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')}" />
+
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultClientAuthenticationLookStrategy"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.DefaultClientAuthenticationLookupStrategy"
+ c:clientId="%{idp.authn.oidc.rp.client.clientId:#{null}}"
+ c:clientSecret="%{idp.authn.oidc.rp.client.clientSecret:#{null}}"
+ c:clientAuthenticationMethod="%{idp.authn.oidc.rp.client.clientAuthenticationMethod:client_secret_basic}"
+ c:clientSecretExpiresAt="%{idp.authn.oidc.rp.client.clientSecretExpiresAt:PT0S}"
+ c:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.EmbeddedClientAuthenticationLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.StorageServiceBackedClientAuthenticationLookStrategy')}"/>
+
+
+ <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:clientIdLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.EmbeddedClientIdentifierLookupStrategy') ?: getObject('shibboleth.authn.oidc.rp.StorageServiceBackedClientIdentifierLookupStrategy')}"/>
+
+ <bean id="shibboleth.authn.oidc.rp.StorageServiceBackedClientIdentifierLookupStrategy" lazy-init="true"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientIdentifierLookupStrategy"
+ p:providerMetadataLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
+ p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('shibboleth.authn.oidc.rp.DefaultMapBackedClientIdStorageService')}" />
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultMapBackedClientIdStorageService" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.MapBackedMemoryStorageServiceFactoryBean"
+ c:context="#{T(net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientIdentifierLookupStrategy).CONTEXT_NAME}"
+ c:map="#{getObject('shibboleth.authn.oidc.rp.IssuerToClientIdMap')}"
+ c:id="DefaultIssuerToClientIDMapStorageService" />
+
+ <bean id="shibboleth.authn.oidc.rp.StorageServiceBackedClientAuthenticationLookStrategy" lazy-init="true"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy"
+ p:storageService="#{getObject('shibboleth.authn.oidc.rp.ClientAuthenticationStorageService') ?: getObject('shibboleth.authn.oidc.rp.DefaultMapBackedClientAuthenticationStorageService')}" />
+
+ <bean id="shibboleth.authn.oidc.rp.DefaultMapBackedClientAuthenticationStorageService" lazy-init="true"
+ class="net.shibboleth.idp.plugin.authn.oidc.rp.config.MapBackedMemoryStorageServiceFactoryBean"
+ c:context="#{T(net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy).CONTEXT_NAME}"
+ c:map="#{getObject('shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap')}"
+ c:id="DefaultClientIDToClientAuthenticationMapStorageService" />
+
+ <!-- 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"
+ 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()}"
+ /> </property> <property name="encryptionConfiguration"> <ref bean="#{'%{idp.oidc.encryption.config:shibboleth.oidc.EncryptionConfiguration}'.trim()}"
+ /> </property> <property name="requestObjectDecryptionConfiguration"> <ref bean="#{'%{idp.oidc.rodecrypt.config:shibboleth.oidc.requestObjectDecryptionConfiguration}'.trim()}"
+ /> </property> <property name="requestObjectSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.requestObjectSignatureValidationConfiguration}'.trim()}"
+ /> </property> <property name="tokenEndpointJwtSignatureValidationConfiguration"> <ref bean="#{'%{idp.oidc.rovalid.config:shibboleth.oidc.tokenEndpointJwtSignatureValidationConfiguration}'.trim()}"
+ /> </property> -->
+ </bean>
+
+
+
+</beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml
deleted file mode 100644
index db1fc48..0000000
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientauthentication-resolver-system.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
-
- default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationProvider" lazy-init="false"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientAuthenticationContainer"
- p:embeddedResolver-ref="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolverImpl">
- </bean>
-
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientAuthenticationResolverImpl"
- class="net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolverImpl"
- p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('DefaultMapBackedClientAuthenticationStorageService')}" />
-
-
- <bean id="DefaultMapBackedClientAuthenticationStorageService" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.MapBackedMemoryStorageServiceFactoryBean"
- c:context="#{T(net.shibboleth.oidc.security.impl.OAuth2ClientAuthenticationResolverImpl).CONTEXT_NAME}"
- c:map="#{getObject('shibboleth.authn.oidc.rp.ClientIdToClientAuthenticationMap')}"
- c:id="DefaultClientIDToClientAuthenticationMapStorageService" />
-
- <!-- 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"/>
-
-</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml
deleted file mode 100644
index ac5f449..0000000
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/clientidentifier-resolver-system.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
-
- default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierProvider" lazy-init="false"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.spring.impl.OAuth2ClientIdentifierContainer"
- p:embeddedResolver-ref="shibboleth.oidc.rp.OAuth2ClientIdentifierResolverImpl">
- </bean>
-
- <bean id="shibboleth.oidc.rp.OAuth2ClientIdentifierResolverImpl"
- class="net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolverImpl"
- p:providerMetadataLookupStrategy-ref="shibboleth.ChildLookup.OIDCProviderMetadataContextFromOutbound"
- p:storageService="#{getObject('shibboleth.authn.oidc.rp.StorageService') ?: getObject('DefaultMapBackedClientIdStorageService')}" />
-
-
- <bean id="DefaultMapBackedClientIdStorageService" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.MapBackedMemoryStorageServiceFactoryBean"
- c:context="#{T(net.shibboleth.oidc.security.impl.OAuth2ClientIdentifierResolverImpl).CONTEXT_NAME}"
- c:map="#{getObject('shibboleth.authn.oidc.rp.IssuerToClientIdMap')}"
- c:id="DefaultIssuerToClientIDMapStorageService" />
-
-
-
-</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
index 7e1b3ce..993518c 100644
--- a/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
+++ b/idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
@@ -1,7 +1,21 @@
-idp.oidc.rp.clientID=client_id
-idp.oidc.rp.clientSecret=secret
-##does not need the .well-known/openid-configuration path.
-idp.oidc.rp.providerConfigurationDocument=https://hostname
-idp.oidc.rp.redirectURI=https://localhost:8443/idp/profile/Authn/OIDC/RP/callback
+## The downstream OP if discovery is not used
+idp.authn.oidc.rp.proxyIssuer= <issuerId>
+
+
+# If a redirect_uri is not explicitly declared above, one can be inferred from each
+# request's Host header. To avoid Host header injection attacks, the allowed origins
+# must be specified here. Origins are comma seperated. Do not specify the port when
+# using the default ports.
+idp.authn.oidc.rp.client.redirecturl.allowedOrigins = https://localhost
+
+
## openid is defaulted. Other scopes could be; profile etc.
-#idp.oidc.rp.scope=openid
\ No newline at end of file
+#idp.oidc.rp.scope=openid
+
+## A fixed client_id that can be used with the given proxy issuer.
+idp.authn.oidc.rp.client.clientId = client_id
+## A set of fixed client authentication parameters, which can be used when a single client is configured. If the secret
+## is set here, these settings will be enabled for client_authentication.
+idp.authn.oidc.rp.client.clientSecret = client_secret
+#idp.authn.oidc.rp.client.clientSecretExpiresAt = PT0S
+#idp.authn.oidc.rp.client.clientAuthenticationMethod = client_secret_basic
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.java
new file mode 100644
index 0000000..60d257e
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/DefaultClientAuthenticationLookupStrategyTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.time.Duration;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+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.utilities.java.support.component.ComponentInitializationException;
+
+/** Tests for the DefaultClientAuthenticationLookupStrategy.*/
+public class DefaultClientAuthenticationLookupStrategyTest {
+
+ /** Strategy to test.*/
+ private DefaultClientAuthenticationLookupStrategy strategy;
+
+ @Test
+ public void testBuildFromProperties() throws ComponentInitializationException {
+ strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", "client_secret_basic",
+ Duration.ofSeconds(0), s -> null);
+ strategy.setId("MockAuthenticationLookupStrategy");
+ strategy.initialize();
+
+ final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+ assertEquals(clientAuth.getClientID().getValue(), "client_id");
+ assertTrue(clientAuth instanceof ClientSecretBasic);
+ assertEquals(((ClientSecretBasic)clientAuth).getClientSecret().getValue(), "client_secret");
+ assertFalse(((ClientSecretBasic)clientAuth).getClientSecret().expired());
+ }
+
+ @Test
+ public void testBuildFromProperties_SecretExpired() throws ComponentInitializationException {
+ strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", "client_secret_basic",
+ Duration.ofSeconds(1), s -> null);
+ strategy.setId("MockAuthenticationLookupStrategy");
+ strategy.initialize();
+
+ final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+ assertNull(clientAuth);
+ }
+
+ @Test
+ public void testBuildFromStrategy() throws ComponentInitializationException {
+ strategy = new DefaultClientAuthenticationLookupStrategy(null, null, null,
+ null, s -> new ClientSecretPost(new ClientID("client_id"), new Secret("client_secret")));
+ strategy.setId("MockAuthenticationLookupStrategy");
+ strategy.initialize();
+
+ final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+ assertEquals(clientAuth.getClientID().getValue(), "client_id");
+ assertTrue(clientAuth instanceof ClientSecretPost);
+ assertEquals(((ClientSecretPost)clientAuth).getClientSecret().getValue(), "client_secret");
+ assertFalse(((ClientSecretPost)clientAuth).getClientSecret().expired());
+ }
+
+ @Test
+ public void testBuildFromStrategy_PartialClientProperties() throws ComponentInitializationException {
+ strategy = new DefaultClientAuthenticationLookupStrategy("client_id", "client_secret", null,
+ null, s -> new ClientSecretPost(new ClientID("client_id"), new Secret("client_secret")));
+ strategy.setId("MockAuthenticationLookupStrategy");
+ strategy.initialize();
+
+ final ClientAuthentication clientAuth = strategy.apply(new ProfileRequestContext());
+ assertEquals(clientAuth.getClientID().getValue(), "client_id");
+ assertTrue(clientAuth instanceof ClientSecretPost);
+ assertEquals(((ClientSecretPost)clientAuth).getClientSecret().getValue(), "client_secret");
+ assertFalse(((ClientSecretPost)clientAuth).getClientSecret().expired());
+ }
+
+}
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java
new file mode 100644
index 0000000..6e0336b
--- /dev/null
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/config/StorageServiceBackedClientAuthenticationLookupStrategyTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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 static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.impl.MemoryStorageService;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.storage.ClientAuthenticationDetails;
+
+/** Tests for the StorageServiceBackedClientAuthenticationLookupStrategy.*/
+public class StorageServiceBackedClientAuthenticationLookupStrategyTest {
+
+ /** The profile request context.*/
+ private ProfileRequestContext prc;
+
+ /** Setup.*/
+ @BeforeMethod
+ public void setup() {
+
+ prc = new ProfileRequestContext();
+ final var oauth2Context = new OAuth2ClientContext();
+ oauth2Context.setClientId("client_id");
+ final var outboundMsg = new MessageContext();
+ outboundMsg.getSubcontext(OIDCPeerEntityContext.class, true).addSubcontext(oauth2Context);
+ prc.setOutboundMessageContext(outboundMsg);
+ }
+
+ /**
+ * Test for a successful client authentication lookup.
+ *
+ * @throws Exception on error
+ */
+ @Test
+ public void testLookupSuccess() throws Exception {
+
+
+ final var memStore = new MemoryStorageService();
+ memStore.setId("MockMemoryStorageService");
+ memStore.initialize();
+
+ final var mapper = new ObjectMapper();
+ final var record =
+ new ClientAuthenticationDetails("secret".toCharArray(), 0L, "client_secret_basic");
+
+ memStore.create(
+ StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",
+ mapper.writer().writeValueAsString(record), null);
+
+ final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+ strategy.setId("MockStrategy");
+ strategy.setStorageService(memStore);
+ strategy.initialize();
+
+ final var clientAuth = strategy.apply(prc);
+ assertNotNull(clientAuth);
+ assertTrue(clientAuth instanceof ClientSecretBasic);
+ assertEquals(((ClientSecretBasic)clientAuth).getClientID().getValue(), "client_id");
+ assertEquals(((ClientSecretBasic)clientAuth).getClientSecret().getValue(), "secret");
+ }
+
+ /**
+ * Test for an unsuccessful client authentication lookup, record does not exist.
+ *
+ * @throws Exception on error
+ */
+ @Test
+ public void testLookupNoRecord() throws Exception {
+
+ final var memStore = new MemoryStorageService();
+ memStore.setId("MockMemoryStorageService");
+ memStore.initialize();
+
+ final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+ strategy.setId("MockStrategy");
+ strategy.setStorageService(memStore);
+ strategy.initialize();
+
+ final var clientAuth = strategy.apply(prc);
+ assertNull(clientAuth);
+
+ }
+
+
+ /**
+ * Test for an unsuccessful client authentication lookup, wrong record exists.
+ *
+ * @throws Exception on error
+ */
+ @Test
+ public void testLookupWrongRecord() throws Exception {
+
+ final var memStore = new MemoryStorageService();
+ memStore.setId("MockMemoryStorageService");
+ memStore.initialize();
+
+ final var mapper = new ObjectMapper();
+ final var record =
+ new ClientAuthenticationDetails("secret".toCharArray(), 0L, "client_secret_basic");
+
+ memStore.create(
+ StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "different_client",
+ mapper.writer().writeValueAsString(record), null);
+
+ final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+ strategy.setId("MockStrategy");
+ strategy.setStorageService(memStore);
+ strategy.initialize();
+
+ final var clientAuth = strategy.apply(prc);
+ assertNull(clientAuth);
+
+ }
+
+ /**
+ * Test for an unsuccessful client authentication lookup, secret has expired.
+ *
+ * @throws Exception on error
+ */
+ @Test
+ public void testLookupSecretExpired() throws Exception {
+
+ final var memStore = new MemoryStorageService();
+ memStore.setId("MockMemoryStorageService");
+ memStore.initialize();
+
+ final var mapper = new ObjectMapper();
+ final var record =
+ new ClientAuthenticationDetails("secret".toCharArray(), 1L, "client_secret_basic");
+
+ memStore.create(
+ StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",
+ mapper.writer().writeValueAsString(record), null);
+
+ final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+ strategy.setId("MockStrategy");
+ strategy.setStorageService(memStore);
+ strategy.initialize();
+
+ final var clientAuth = strategy.apply(prc);
+ assertNull(clientAuth);
+
+ }
+
+ /**
+ * Test for an unsuccessful client authentication lookup, incompatible record. Will log stack and return
+ * null.
+ *
+ * @throws Exception on error
+ */
+ @Test
+ public void testLookupIncompatibleRecord() throws Exception {
+
+ final var memStore = new MemoryStorageService();
+ memStore.setId("MockMemoryStorageService");
+ memStore.initialize();
+
+ final var record = "{\"key\":\"value\"}";
+
+ memStore.create(
+ StorageServiceBackedClientAuthenticationLookupStrategy.CONTEXT_NAME, "client_id",
+ record, null);
+
+ final var strategy = new StorageServiceBackedClientAuthenticationLookupStrategy();
+ strategy.setId("MockStrategy");
+ strategy.setStorageService(memStore);
+ strategy.initialize();
+
+ final var clientAuth = strategy.apply(prc);
+ assertNull(clientAuth);
+
+ }
+
+
+}
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 1806afd..e49611f 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
@@ -70,6 +70,8 @@ import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.ResponseMode;
import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
@@ -82,6 +84,7 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
import net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl;
+import net.shibboleth.idp.plugin.authn.oidc.rp.config.StorageServiceBackedClientAuthenticationLookupStrategy;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OAuth2ClientContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.OIDCPeerEntityContext;
import net.shibboleth.idp.plugin.authn.oidc.rp.context.ResponseTypeAndModeContext;
@@ -106,6 +109,8 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
private static final String OP_ISSUER_ID = "https://localhost:9918";
private final String RP_ALLOWED_ORIGINS = "https://localhost";
+
+ private static final String CLIENT_ID = "demo_rp";
/**
* Example of good provider metadata. Endpoints are localhost to support the
@@ -257,22 +262,23 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
} catch (final Exception e) {
log.error("Could not mock HTTP response",e);
}
-
-
+
loadBeanDefinitionsFromXmlFile(builderContext,
- new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"));
+ new ClassPathResource("META-INF/net.shibboleth.idp/postconfig.xml"),
+ null);
loadBeanDefinitionsFromXmlFile(builderContext,
- new ClassPathResource("conf/test-relyingparty-resolver-service.xml"));
+ new ClassPathResource("conf/test-relyingparty-resolver-service.xml"),
+ Map.of("idp.authn.oidc.rp.client.clientId", CLIENT_ID));
loadBeanDefinitionsFromXmlFile(builderContext,
- new ClassPathResource("conf/additional-system-beans.xml"));
+ new ClassPathResource("conf/additional-system-beans.xml"), null);
loadBeanDefinitionsFromXmlFile(builderContext,
- new ClassPathResource("attribute/registry/postconfig.xml"));
+ new ClassPathResource("attribute/registry/postconfig.xml"), null);
loadBeanDefinitionsFromXmlFile(builderContext,
- new ClassPathResource("attribute/filter/attribute-filter-system.xml"));
+ new ClassPathResource("attribute/filter/attribute-filter-system.xml"), null);
}
/**
@@ -291,10 +297,10 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
.build();
final var payload = new JWTClaimsSet.Builder()
.issuer(OP_ISSUER_ID)
- .audience(List.of("demo_rp","demo_rp2"))
+ .audience(List.of(CLIENT_ID,"demo_rp2"))
.subject("jdoe")
.claim("nonce", "abadnonce")
- .claim("azp", "demo_rp")
+ .claim("azp", CLIENT_ID)
.claim("name","jdoe")
.expirationTime(Date.from(Instant.now().plusSeconds(120)))
.build();
@@ -561,10 +567,15 @@ 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")));
+
final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
rPartyConfig.setResponderId("http://idp.example.com/");
partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
+
+
// Setup outbound context
final MessageContext outMsgCtx = new MessageContext();
@@ -572,7 +583,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
outMsgCtx.addSubcontext(createPeerContext());
outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
nestPrc.setOutboundMessageContext(outMsgCtx);
- outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+ outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
// Setup inbound context.
final MessageContext inMsgCtx = new MessageContext();
@@ -625,7 +636,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
- .setBody(createSignedUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp").serialize()));
+ .setBody(createSignedUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID).serialize()));
mockOPServer.start(9918);
@@ -642,7 +653,15 @@ 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")));
+
+ final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+ rPartyConfig.setResponderId("http://idp.example.com/");
+ partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
+
+
// Setup outbound context
final MessageContext outMsgCtx = new MessageContext();
@@ -650,7 +669,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
outMsgCtx.addSubcontext(createPeerContext());
outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
nestPrc.setOutboundMessageContext(outMsgCtx);
- outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+ outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
// Setup inbound context.
final MessageContext inMsgCtx = new MessageContext();
@@ -704,7 +723,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
- .setBody(createSignedAndEncryptedUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp")
+ .setBody(createSignedAndEncryptedUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
.serialize()));
mockOPServer.start(9918);
@@ -722,7 +741,14 @@ 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")));
+
+ final RelyingPartyConfiguration rPartyConfig = new RelyingPartyConfiguration();
+ rPartyConfig.setResponderId("http://idp.example.com/");
+ partyContext.setConfiguration(rPartyConfig);
nestPrc.addSubcontext(partyContext);
+
// Setup outbound context
final MessageContext outMsgCtx = new MessageContext();
@@ -730,7 +756,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
outMsgCtx.addSubcontext(createPeerContext());
outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
nestPrc.setOutboundMessageContext(outMsgCtx);
- outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+ outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
// Setup inbound context.
final MessageContext inMsgCtx = new MessageContext();
@@ -785,7 +811,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// Second is userInfo
mockOPServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader("content-type", "application/jwt")
- .setBody(createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,"demo_rp")
+ .setBody(createPlainUserInfoJWTResponseJSON(OP_ISSUER_ID,CLIENT_ID)
.serialize()));
mockOPServer.start(9918);
@@ -801,8 +827,11 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
// Add under nest PRC
final RelyingPartyContext partyContext = new RelyingPartyContext();
- final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
+ final OIDCAuthorizationConfiguration partyConfig = new OIDCAuthorizationConfiguration();
+ partyConfig.setClientAuthenticationLookupStrategy(new StorageServiceBackedClientAuthenticationLookupStrategy());
partyContext.setProfileConfig(partyConfig);
+ partyConfig.setClientAuthenticationLookupStrategy(p ->
+ new ClientSecretBasic(new ClientID(CLIENT_ID), new Secret("secret")));
nestPrc.addSubcontext(partyContext);
// Setup outbound context
@@ -811,7 +840,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
outMsgCtx.addSubcontext(createPeerContext());
outMsgCtx.addSubcontext(createResponseTypeAndModeContext());
nestPrc.setOutboundMessageContext(outMsgCtx);
- outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext("demo_rp",null));
+ outMsgCtx.getSubcontext(OIDCPeerEntityContext.class).addSubcontext(createOAuth2ClientContext(CLIENT_ID,null));
// Setup inbound context.
final MessageContext inMsgCtx = new MessageContext();
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 d1e28d8..8fbf822 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
@@ -45,6 +45,22 @@
<util:list id="shibboleth.RelyingPartyOverrides">
</util:list>
+
+
+ <!--
+ Map clients to appropriate client authentication - only supports client_secret_basic and client_secret_post
+ -->
+
+ <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" />
+ </entry>
+ <entry key="demo_rp">
+ <bean parent="shibboleth.authn.oidc.rp.ClientAuthenticationDetails" c:clientSecret="mytestsecret"
+ c:tokenEndpointAuthMethod="client_secret_basic" />
+ </entry>
+ </util:map>
</beans>
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml b/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
index 7f7e263..bbdd4c1 100644
--- a/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
+++ b/idp-oidc-rp-impl/src/test/resources/conf/test-relyingparty-resolver-service.xml
@@ -24,26 +24,28 @@
c:serviceableClaz="net.shibboleth.idp.relyingparty.impl.DefaultRelyingPartyConfigurationResolver" />
</constructor-arg>
</bean>
-
+
<!-- Auto-append system config file to resource set. -->
- <bean id ="ExtendedRelyingPartyResolverResources" class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
- p:firstList-ref="#{'%{idp.service.relyingparty.resources:shibboleth.RelyingPartyResolverResources}'.trim()}" >
+ <bean id="ExtendedRelyingPartyResolverResources"
+ class="net.shibboleth.ext.spring.factory.CombiningListFactoryBean"
+ p:firstList-ref="#{'%{idp.service.relyingparty.resources:shibboleth.RelyingPartyResolverResources}'.trim()}">
<property name="secondList">
- <util:list >
+ <util:list>
<value>classpath:/conf/test-relying-party-system.xml</value>
</util:list>
</property>
</bean>
-
+
<bean id="shibboleth.RelyingPartyConfigurationResolver"
class="net.shibboleth.idp.relyingparty.impl.ReloadingRelyingPartyConfigurationResolver"
c:resolverService-ref="shibboleth.RelyingPartyResolverService" />
-
+
<util:list id="shibboleth.RelyingPartyResolverResources">
- <!-- <value>%{idp.home}/conf/relying-party.xml</value>
- <value>%{idp.home}/conf/credentials.xml</value> -->
+ <!-- <value>%{idp.home}/conf/relying-party.xml</value> <value>%{idp.home}/conf/credentials.xml</value> -->
</util:list>
-
-
+
+ <!-- Wildcard import hook for plugins. -->
+ <import resource="classpath*:/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml" />
+
</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