[java-idp-plugin-duo] branch main updated: JDUO-103 - Forward max_age requirement for forced authentication
Codeberg
noreply at shibboleth.net
Fri Sep 18 17:14:40 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-duo.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-duo/commit/31beeb4e629d58f2ee9b46dde16a44daeadacc13
The following commit(s) were added to refs/heads/main by this push:
new 31beeb4e JDUO-103 - Forward max_age requirement for forced authentication
31beeb4e is described below
commit 31beeb4e629d58f2ee9b46dde16a44daeadacc13
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Sep 18 18:14:31 2026 +0100
JDUO-103 - Forward max_age requirement for forced authentication
- Added Duo WebSDK version 1.4.0
- Updated certificate handling to be compatible with their new
expectations for cert pinning. That is, PEM-encoded X.509 certs rather
than public key pins. This is backward incompatible, but we are driven
by changes to Duo's SDK.
- Now forwarding max_age and prompt to Duo via their SDK changes
- Moved the Nimbus PEM certs from the Nimbus plugin to the impl module,
because both Nimbus and the WebSDK version now use those.
https://shibboleth.atlassian.net/browse/JDUO-103
---
.../shibboleth/idp/plugin/authn/duo/trust/5718.crt | 0
.../shibboleth/idp/plugin/authn/duo/trust/5842.crt | 0
.../idp/plugin/authn/duo/trust/AmazonRootCA1.crt | 0
.../idp/plugin/authn/duo/trust/AmazonRootCA2.crt | 0
.../idp/plugin/authn/duo/trust/AmazonRootCA3.crt | 0
.../idp/plugin/authn/duo/trust/AmazonRootCA4.crt | 0
.../duo/trust/DigiCertHighAssuranceEVRootCA.crt | 0
.../authn/duo/trust/DigiCertTLSECCP384RootG5.crt | 0
.../authn/duo/trust/DigiCertTLSRSA4096RootG5.crt | 0
.../idp/plugin/authn/duo/trust/SFSRootCAG2.crt | 0
.../shibboleth/idp/plugin/authn/duo/trust/r2.crt | 0
.../shibboleth/idp/plugin/authn/duo/trust/r4.crt | 0
.../idp/plugin/authn/duo/trust/roote46.crt | 0
.../idp/plugin/authn/duo/trust/rootr46.crt | 0
.../idp/plugin/authn/duo/trust/sfroot-g2.crt | 0
.../plugin/authn/duo/nimbus/impl/NimbusClient.java | 4 +-
.../authn/duo/sdk/impl/DuoSDKClientAdaptor.java | 61 ++++++++++++++----
.../authn/duo/sdk/impl/DuoSDKClientFactory.java | 75 ++++++++++++++++++++--
.../plugin/authn/duo/duo-client-factory-bean.xml | 42 ++++++------
.../duo/sdk/impl/DuoSDKClientFactoryTest.java | 35 ++++++----
.../src/test/resources/trust/example-bad.crt | 3 +
.../src/test/resources/trust/example.crt | 17 +++++
pom.xml | 2 +-
23 files changed, 189 insertions(+), 50 deletions(-)
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5718.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5718.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5718.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5718.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5842.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5842.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5842.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/5842.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA1.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA1.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA1.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA1.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA2.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA2.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA2.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA2.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA3.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA3.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA3.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA3.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA4.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA4.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA4.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA4.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertHighAssuranceEVRootCA.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertHighAssuranceEVRootCA.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertHighAssuranceEVRootCA.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertHighAssuranceEVRootCA.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSECCP384RootG5.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSECCP384RootG5.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSECCP384RootG5.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSECCP384RootG5.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSRSA4096RootG5.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSRSA4096RootG5.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSRSA4096RootG5.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSRSA4096RootG5.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/SFSRootCAG2.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/SFSRootCAG2.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/SFSRootCAG2.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/SFSRootCAG2.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r2.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r2.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r2.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r2.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r4.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r4.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r4.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/r4.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/roote46.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/roote46.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/roote46.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/roote46.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/rootr46.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/rootr46.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/rootr46.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/rootr46.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/sfroot-g2.crt b/idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/sfroot-g2.crt
similarity index 100%
rename from idp-duo-nimbus-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/sfroot-g2.crt
rename to idp-duo-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/trust/sfroot-g2.crt
diff --git a/idp-duo-nimbus-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/nimbus/impl/NimbusClient.java b/idp-duo-nimbus-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/nimbus/impl/NimbusClient.java
index adcdc2b7..e47e11c4 100644
--- a/idp-duo-nimbus-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/nimbus/impl/NimbusClient.java
+++ b/idp-duo-nimbus-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/nimbus/impl/NimbusClient.java
@@ -138,7 +138,9 @@ public final class NimbusClient extends AbstractDuoOIDCClient{
}
@Override
- @Nonnull public String createAuthUrl(@Nonnull final AuthenticationRequestOptions requestOptions) throws DuoClientException {
+ @Nonnull public String createAuthUrl(@Nonnull final AuthenticationRequestOptions requestOptions)
+ throws DuoClientException {
+ Constraint.isNotNull(requestOptions, "Authentication request options can not be null");
Constraint.isNotEmpty(requestOptions.getUsername(), "Username can not be null or empty");
Constraint.isNotEmpty(requestOptions.getState(), "State can not be null or empty");
Constraint.isNotEmpty(requestOptions.getNonce(), "Nonce can not be null or empty for this client");
diff --git a/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientAdaptor.java b/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientAdaptor.java
index 18ab9009..8bd38afc 100644
--- a/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientAdaptor.java
+++ b/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientAdaptor.java
@@ -27,6 +27,8 @@ import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Logger;
+import com.duosecurity.AuthUrlOptions;
+import com.duosecurity.AuthUrlOptions.Prompt;
import com.duosecurity.Client;
import com.duosecurity.exception.DuoException;
import com.duosecurity.model.HealthCheckResponse;
@@ -98,6 +100,7 @@ public final class DuoSDKClientAdaptor extends AbstractDuoOIDCClient{
try {
if (caCerts == null) {
+ log.trace("Creating a new Duo client without pinned certificates");
//will use the default certs in the Client if the caCerts are null
final Client newClient = new Client.Builder(integration.getClientId(), integration.getSecretKey(),
integration.getAPIHost(), integration.getRedirectURI())
@@ -106,6 +109,7 @@ public final class DuoSDKClientAdaptor extends AbstractDuoOIDCClient{
assert newClient != null;
client = newClient;
} else {
+ log.trace("Creating a new Duo client with pinned certificates");
final Client newClient = new Client.Builder(integration.getClientId(), integration.getSecretKey(),
integration.getAPIHost(), integration.getRedirectURI()).setCACerts(
caCerts.toArray(new String[caCerts.size()]))
@@ -209,8 +213,7 @@ public final class DuoSDKClientAdaptor extends AbstractDuoOIDCClient{
/**
* {@inheritDoc}
*
- * <p>The Duo WebSDK Client does not support either the {@code nonce} or {@code redirectURIOverride}
- * parameters.</p>
+ * <p>The Duo WebSDK Client does not support the {@code redirectURIOverride} parameter.</p>
*
*/
@Override
@@ -218,21 +221,57 @@ public final class DuoSDKClientAdaptor extends AbstractDuoOIDCClient{
@Nonnull @NotEmpty final String state, @Nullable final String nonce,
@Nullable final String redirectURIOverride)
throws DuoClientException {
- Constraint.isNotEmpty(username, "Username can not be null or empty");
- Constraint.isNotEmpty(state, "State can not be null or empty");
- //does not support the nonce or redirect_uri override
+ // Duo client does not support the redirectURIOverride
+ final AuthenticationRequestOptions options = new AuthenticationRequestOptions()
+ .setUsername(username)
+ .setState(state)
+ .setNonce(nonce);
+
+ return createAuthUrl(options);
+ }
+
+ @Override
+ @Nonnull public String createAuthUrl(@Nonnull final AuthenticationRequestOptions requestOptions)
+ throws DuoClientException {
+ Constraint.isNotNull(requestOptions, "Authentication request options can not be null");
+ final String username =
+ Constraint.isNotEmpty(requestOptions.getUsername(), "Username can not be null or empty");
+ final String state =
+ Constraint.isNotEmpty(requestOptions.getState(), "State can not be null or empty");
+ Constraint.isGreaterThan(21, state.length(), "State must be at least 22 characters");
+ Constraint.isLessThan(1025, state.length(),"State must be at maximum 1024 characters");
- // TODO remove warning when it does.
- log.debug("Underlying DuoSDK does not support max_age or prompt, do not try to verfiy auth_time");
-
try {
- final String authUrl = client.createAuthUrl(username, state);
+ final AuthUrlOptions.Builder options = new AuthUrlOptions.Builder(username, state);
+
+ final var maxAge = requestOptions.getMaxAge();
+ final var prompt = requestOptions.getPrompt();
+ final var nonce = requestOptions.getNonce();
+
+ if (nonce != null) {
+ options.setNonce(nonce);
+ }
+ if (maxAge != null) {
+ options.setMaxAge(Math.toIntExact(maxAge.toSeconds()));
+ }
+ // Duo only support login, so an easy conversion
+ if (prompt != null) {
+ if ("login".equals(prompt)) {
+ options.setPrompt(Prompt.LOGIN);
+ } else {
+ log.error("Attempting to set prompt '{}' which is not supported by Duo", prompt);
+ throw new DuoClientException("Unsupported Duo prompt: " + prompt);
+ }
+ }
+
+ final String authUrl = client.createAuthUrl(options.build());
assert authUrl != null;
return authUrl;
- } catch (final DuoException e) {
+ } catch (final DuoException | ArithmeticException e) {
//wrap duo specific exception.
throw new DuoClientException(e);
}
+
}
/**
@@ -322,7 +361,7 @@ public final class DuoSDKClientAdaptor extends AbstractDuoOIDCClient{
@Override
public boolean isSupportsNonce() {
- return false;
+ return true;
}
}
diff --git a/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java b/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
index fe7de05b..93f6689e 100644
--- a/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
+++ b/idp-duo-sdk-client-impl/src/main/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactory.java
@@ -14,6 +14,10 @@
package net.shibboleth.idp.plugin.authn.duo.sdk.impl;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
@@ -21,6 +25,9 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
+import org.opensaml.security.x509.X509Support;
+import org.slf4j.Logger;
+
import net.shibboleth.idp.plugin.authn.duo.DuoClientException;
import net.shibboleth.idp.plugin.authn.duo.DuoOIDCClient;
import net.shibboleth.idp.plugin.authn.duo.DuoOIDCClientFactory;
@@ -29,15 +36,21 @@ import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.component.AbstractInitializableComponent;
+import net.shibboleth.shared.logic.ConstraintViolationException;
import net.shibboleth.shared.primitive.DeprecationSupport;
import net.shibboleth.shared.primitive.DeprecationSupport.ObjectType;
+import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resource.Resource;
/** Abstract factory implementation for the {@link DuoSDKClientAdaptor}. */
@ThreadSafe
-public final class DuoSDKClientFactory extends AbstractInitializableComponent implements DuoOIDCClientFactory{
+public final class DuoSDKClientFactory extends AbstractInitializableComponent implements DuoOIDCClientFactory{
+
+ /** Class logger. */
+ @Nonnull private static final Logger log = LoggerFactory.getLogger(DuoSDKClientFactory.class);
/** List of CA Certificate pins. If null, the client's default set are used.*/
@GuardedBy("this") @Nullable @NonnullElements @Unmodifiable private List<String> caCerts;
@@ -53,16 +66,64 @@ public final class DuoSDKClientFactory extends AbstractInitializableComponent im
* to the API host. If {@code null}, the client will use the internal defaults.
*
* @param certs the list of certificate pins.
+ *
+ * @deprecated use {@link #setPinnedCertificates(List)}
*/
+ @Deprecated(since = "2.4.0", forRemoval = true)
public synchronized void setCaCerts(@Nullable final List<String> certs) {
checkSetterPreconditions();
- //check if null, as the native duo client uses the internal defaults if null
- //but will respect an empty list.
if (certs != null) {
- caCerts = List.copyOf(StringSupport.normalizeStringCollection(certs));
- } else {
- caCerts = null;
- }
+ log.warn("Public key hash pinning is no longer supported");
+ DeprecationSupport.warnOnce(ObjectType.CONFIGURATION, "Public key hash pinning is no longer supported" ,
+ null, "PEM encoded certificates: shibboleth.authn.DuoOIDC.sdk.TrustedCertificateResources");
+ throw new ConstraintViolationException("Public key hash pinning not supported");
+ }
+ }
+
+ /**
+ * Set the resources which we will convert into PEM encoded strings.
+ *
+ * @param certResources the resources
+ */
+ public synchronized void setPinnedCertificates(@Nullable final List<Resource> certResources) {
+ checkSetterPreconditions();
+
+ if (certResources != null) {
+ caCerts = new ArrayList<String>(certResources.size());
+ for (final Resource f : certResources) {
+ try (InputStream inputStream = f.getInputStream()) {
+ final String certificate = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+ validateCertificate(certificate);
+ caCerts.add(certificate);
+ } catch (final IOException e) {
+ log.error("Failed to read certificate resource", e);
+ throw new ConstraintViolationException("Failed to read certificate resource: " + f.getDescription());
+ }
+ }
+ }
+ }
+
+ /**
+ * Check the certificate strings is a valid X.509 certificates, and not, for example, a public key pin.
+ *
+ * @param certificate the certificate to check
+ */
+ private static void validateCertificate(@Nonnull final String certificate) {
+
+ // Quick check this is a PEM and not a DER encoded certificate
+ if (!certificate.contains("-----BEGIN CERTIFICATE-----") ||
+ !certificate.contains("-----END CERTIFICATE-----")) {
+ log.error("Pinned CA certificate must be PEM-encoded");
+ throw new ConstraintViolationException("Pinned CA certificate must be PEM-encoded");
+ }
+ try {
+ // We don't do anything, we just try to parse it
+ X509Support.decodeCertificate(certificate.getBytes(StandardCharsets.UTF_8));
+ } catch (final Exception e) {
+ log.error("Pinned CA certificate is of the wrong type, expected PEM encoded X.509 certificate", e);
+ throw new ConstraintViolationException(
+ "Pinned CA certificate is of the wrong type, expected PEM encoded X.509 certificate");
+ }
}
/**
diff --git a/idp-duo-sdk-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/duo-client-factory-bean.xml b/idp-duo-sdk-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/duo-client-factory-bean.xml
index 8d941ac2..418f2a6e 100644
--- a/idp-duo-sdk-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/duo-client-factory-bean.xml
+++ b/idp-duo-sdk-client-impl/src/main/resources/net/shibboleth/idp/plugin/authn/duo/duo-client-factory-bean.xml
@@ -12,30 +12,34 @@
<!-- Client factory with is own unique ID but common (default) alias/name -->
<bean id="shibboleth.authn.DuoOIDC.sdk.clientFactory" name="shibboleth.authn.DuoOIDC.clientFactory"
class="net.shibboleth.idp.plugin.authn.duo.sdk.impl.DuoSDKClientFactory" scope="singleton"
- p:caCerts="#{getObject('shibboleth.authn.DuoOIDC.sdk.TrustedCertificates') ?: getObject('shibboleth.authn.DuoOIDC.sdk.DefaultTrustedCertificates')}"
+ p:caCerts="#{getObject('shibboleth.authn.DuoOIDC.sdk.TrustedCertificates')}"
+ p:pinnedCertificates="#{getObject('shibboleth.authn.DuoOIDC.sdk.TrustedCertificateResources')
+ ?: getObject('shibboleth.authn.DuoOIDC.sdk.DefaultTrustedCertificateResources')}"
p:proxyPort="%{idp.duo.oidc.http.proxy.port:#{null}}"
p:proxyHost="%{idp.duo.oidc.http.proxy.host:#{null}}">
</bean>
-
- <!-- The Default set of CA Certificate Pins used by the Duo SDK -->
- <util:list id="shibboleth.authn.DuoOIDC.sdk.DefaultTrustedCertificates" value-type="java.lang.String"
+
+ <!-- Default set of trusted CAs -->
+ <util:list id="shibboleth.authn.DuoOIDC.sdk.DefaultTrustedCertificateResources" value-type="java.lang.String"
list-class="java.util.ArrayList">
- <value>sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=</value>
- <value>sha256/++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=</value>
- <value>sha256/f0KW/FtqTjs108NpYj42SrGvOB2PpxIVM8nWxjPqJGE=</value>
- <value>sha256/NqvDJlas/GRcYbcWE8S/IceH9cq77kg0jVhZeAPXq8k=</value>
- <value>sha256/9+ze1cZgR9KO1kZrVDxA4HQ6voHRCSVNz4RdTCx4U8U=</value>
- <value>sha256/KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=</value>
- <value>sha256/oC+voZLIy4HLE0FVT5wFtxzKKokLDRKY1oNkfJYe+98=</value>
- <value>sha256/ape1HIIZ6T5d7GS61YBs3rD4NVvkfnVwELcCRW4Bqv0=</value>
- <value>sha256/rn+WLLnmp9v3uDP7GPqbcaiRdd+UnCMrap73yz3yu/w=</value>
- <value>sha256/4EoCLOMvTM8sf2BGKHuCijKpCfXnUUR/g/0scfb9gXM=</value>
- <value>sha256/Vfd95BwDeSQo+NUYxVEEIlvkOlWY2SalKK1lPhzOx78=</value>
- <value>sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=</value>
- <value>sha256/B+hU8mp8vTiZJ6oEG/7xts0h3RQ4GK2UfcZVqeWH/og=</value>
- <value>sha256/uu5PB+MS9L3/ffB/PuTG6A+WjsTtTaF52qqjrcHFXRU=</value>
- <value>sha256/gI1os/q0iEpflxrOfRBVDXqVoWN3Tz7Dav/7IT++THQ=</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA1.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA2.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA3.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/AmazonRootCA4.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/5718.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/5842.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSECCP384RootG5.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertTLSRSA4096RootG5.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/r2.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/r4.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/roote46.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/rootr46.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/sfroot-g2.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/SFSRootCAG2.crt</value>
+ <value>classpath:/net/shibboleth/idp/plugin/authn/duo/trust/DigiCertHighAssuranceEVRootCA.crt</value>
</util:list>
+
+
</beans>
\ No newline at end of file
diff --git a/idp-duo-sdk-client-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactoryTest.java b/idp-duo-sdk-client-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactoryTest.java
index bed64710..d9359f02 100644
--- a/idp-duo-sdk-client-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactoryTest.java
+++ b/idp-duo-sdk-client-impl/src/test/java/net/shibboleth/idp/plugin/authn/duo/sdk/impl/DuoSDKClientFactoryTest.java
@@ -33,6 +33,7 @@ import static org.testng.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.List;
+import org.springframework.core.io.ClassPathResource;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -40,6 +41,9 @@ import net.shibboleth.idp.plugin.authn.duo.DefaultDuoOIDCIntegration;
import net.shibboleth.idp.plugin.authn.duo.DuoClientException;
import net.shibboleth.idp.plugin.authn.duo.DuoOIDCClient;
import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.resource.Resource;
+import net.shibboleth.shared.spring.resource.ResourceHelper;
/** Test for the DuoSDKClientFactory.*/
public class DuoSDKClientFactoryTest {
@@ -57,20 +61,28 @@ public class DuoSDKClientFactoryTest {
factory = new DuoSDKClientFactory();
}
-
- /** Test succesful set of null CA certs.*/
- @Test
- public final void testSetNullCaCerts() {
- factory.setCaCerts(null);
- }
- /** Test succesful set of CA certs.*/
- @Test
- public final void testSetCaCerts() {
+ /** Test unsuccesful set invalid X.509 cert.*/
+ @Test(expectedExceptions = ConstraintViolationException.class)
+ public final void testOldSetCaCerts() {
final List<String> certs = new ArrayList<>();
certs.add("sha256/I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=");
factory.setCaCerts(certs);
}
+
+ @Test
+ public final void testSetPinnedCertificates() {
+ final List<Resource> certs = new ArrayList<>();
+ certs.add(ResourceHelper.of(new ClassPathResource("trust/example.crt")));
+ factory.setPinnedCertificates(certs);
+ }
+
+ @Test(expectedExceptions = ConstraintViolationException.class)
+ public final void testSetBadPinnedCertificates() {
+ final List<Resource> certs = new ArrayList<>();
+ certs.add(ResourceHelper.of(new ClassPathResource("trust/example-bad.crt")));
+ factory.setPinnedCertificates(certs);
+ }
/**
* Test creation.
@@ -80,8 +92,9 @@ public class DuoSDKClientFactoryTest {
*/
@Test
public final void testCreateInstance() throws DuoClientException, ComponentInitializationException {
- final List<String> certs = new ArrayList<>();
- certs.add("sha256/I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=");
+ final List<Resource> certs = new ArrayList<>();
+ certs.add(ResourceHelper.of(new ClassPathResource("trust/example.crt")));
+ factory.setPinnedCertificates(certs);
final DefaultDuoOIDCIntegration integ = new DefaultDuoOIDCIntegration();
integ.setAPIHost("api-c9f24c5a.duosecurity.com");
integ.setClientId("DIU6GEFWG5LIUTVV2M3N");
diff --git a/idp-duo-sdk-client-impl/src/test/resources/trust/example-bad.crt b/idp-duo-sdk-client-impl/src/test/resources/trust/example-bad.crt
new file mode 100644
index 00000000..dd3aaff4
--- /dev/null
+++ b/idp-duo-sdk-client-impl/src/test/resources/trust/example-bad.crt
@@ -0,0 +1,3 @@
+-----BEGIN CERTIFICATE-----
+bad
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/idp-duo-sdk-client-impl/src/test/resources/trust/example.crt b/idp-duo-sdk-client-impl/src/test/resources/trust/example.crt
new file mode 100644
index 00000000..31d70b8d
--- /dev/null
+++ b/idp-duo-sdk-client-impl/src/test/resources/trust/example.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAZoCCQCaPHbdhD7mkDANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9l
+eGFtcGxlLmludmFsaWQwIBcNMjYwOTE4MDg0OTM0WhgPMjA1NTAyMDMwODQ5MzRa
+MBoxGDAWBgNVBAMMD2V4YW1wbGUuaW52YWxpZDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAJj0PkmYpHQP8GDBXoNI99xUIYE43aTn0tw2nJ/vWumXuEY1
+7XuBTlrRObAKOVzZjp9aCfLlDSoHxOSzDE8wen8P6PKxcDtfM8UIWDyLBuZ7U5nL
+DB6KzGr+QV7cPNDycciA06sl8EqRIloHpVO+yjNSMcaDc9STLqpHxNsgPES79JlH
+10xkOBbrOqxwkVGrjrttLv7YeombAZlx77ZYn2HUkWMlDCQ5VZHXu5VU2grIuLwG
+cHNeTxlNo7M00Y4Drm2xvde8i1krK7Y61iNTgxHwK8oN7kZMtS27kK/xBIPArwXV
+S3sPmB0btCHAM41lfCp/O+NB9qJ5Z/zqwDmSdHUCAwEAATANBgkqhkiG9w0BAQsF
+AAOCAQEAVIZQmO1ubKvet5rnGGQjoIbE9QhMCK/fQks2qHlIf+L6Zuw4hCVmgAab
+1lEGCyEoH5TYfyT5ChjjBb1f3uEPqjP2NRgE9UBl5LF0iJibM7sKKW4rTT24/wo5
+aX+/fSWV1tsanvELLu0IulXThIkkzQ3rNqczjHk1L8yCi5GkWAW3+WM3WmYdj2Ij
+HJxBT2cZ3cpC+wYLBswEIPuOHJL/2W3rttVRqCQ5Og2Szy/RuhYIE0sHxF/PywBW
+OlsSZ5Em9YHhlc7/jMdOcL0jykFwS3v3PE4SpqUqewPOqU/OsR0m4alIqVs9IjFV
+YeFD9XrQprhNawlqgTV7831mmifYtA==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index afe650c5..a0fb47b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
<shibboleth.projectName>java-idp-plugin-duo</shibboleth.projectName>
<idp.groupId>net.shibboleth.idp</idp.groupId>
<idp.version>5.0.0</idp.version>
- <duo.client.version>1.3.2</duo.client.version>
+ <duo.client.version>1.4.0</duo.client.version>
<okhttp3.mockserver.version>4.9.3</okhttp3.mockserver.version>
<okhttp3.tls.version>4.9.3</okhttp3.tls.version>
<opensaml.groupId>org.opensaml</opensaml.groupId>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list