[java-idp-plugin-webauthn] branch main updated: Add allowed origin override support

Phil Smart philip.smart at jisc.ac.uk
Fri Feb 2 14:47:06 UTC 2024


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=1c504c052fb4e3da6971805922e87804d89cf10e

The following commit(s) were added to refs/heads/main by this push:
     new 1c504c0  Add allowed origin override support
1c504c0 is described below

commit 1c504c052fb4e3da6971805922e87804d89cf10e
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Feb 2 14:47:04 2024 +0000

    Add allowed origin override support
---
 .../client/impl/YubicoWebauthnClientFactory.java   | 88 +++++++++++++++++-----
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  6 +-
 .../authn/webauthn/conf/authn/webauthn.properties  |  3 +
 3 files changed, 77 insertions(+), 20 deletions(-)

diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
index e7e8859..7685d01 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnClientFactory.java
@@ -14,6 +14,9 @@
 
 package net.shibboleth.idp.plugin.authn.webauthn.client.impl;
 
+import java.util.Set;
+import java.util.stream.Collectors;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
@@ -22,15 +25,20 @@ import javax.annotation.concurrent.ThreadSafe;
 import org.springframework.beans.factory.FactoryBean;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Predicates;
 import com.yubico.webauthn.CredentialRepository;
 import com.yubico.webauthn.RelyingParty;
 import com.yubico.webauthn.data.RelyingPartyIdentity;
 
 import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.AbstractInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * Spring factory beans for creating a {@link YubicoWebauthnAuthenticationClient}.
@@ -40,10 +48,10 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
             implements FactoryBean<WebAuthnAuthenticationClient> {
 
     /** The relying party identifier.*/
-    @GuardedBy("this") @Nullable private String relyingPartyId;
+    @GuardedBy("this") @NonnullAfterInit private String relyingPartyId;
     
     /** The relying party name.*/
-    @GuardedBy("this") @Nullable private String relyingPartyName;
+    @GuardedBy("this") @NonnullAfterInit private String relyingPartyName;
     
     /** Allow unrestricted origin ports? Default is false.*/
     @GuardedBy("this") private boolean allowOriginPort;
@@ -52,16 +60,19 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     @GuardedBy("this") private boolean allowOriginSubdomain;
     
     /** The JSON object mapper used to JSONify webauthn objects. */
-    @GuardedBy("this") @Nullable private ObjectMapper om;    
+    @GuardedBy("this") @NonnullAfterInit private ObjectMapper om;    
     
     /** The credential repository to store valid credentials in.*/
-    // TODO replace with an adaptor to the storage service?
     @GuardedBy("this") @NonnullAfterInit private CredentialRepository credentialRepository;
     
+    /** Allowable origins for this Relying Party. Overrides the origin derived from the relyingPartyId if used.*/
+    @GuardedBy("this") @Nonnull @NonnullElements private Set<String> origins;
+    
     /** Constructor.*/
     public YubicoWebauthnClientFactory() {
         allowOriginPort = false;
         allowOriginSubdomain = false;
+        origins = CollectionSupport.emptySet();
     }
     
     @Override protected void doInitialize() throws ComponentInitializationException {
@@ -84,19 +95,36 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
     
     @Override
     public WebAuthnAuthenticationClient getObject() throws Exception {
-        final RelyingParty rp = RelyingParty.builder().identity(
-                RelyingPartyIdentity
-                .builder()
-                .id(getRelyingPartyId())
-                .name(getRelyingPartyName())
-                //Use inmemory for now
-                .build()).credentialRepository(getCredentialRepository())
-        .allowOriginPort(isAllowOriginPort())
-        .allowOriginSubdomain(isAllowOriginSubdomain())
-        .build();
         
-        assert rp != null;
-        return new YubicoWebauthnAuthenticationClient(rp, getObjectMapper());
+        // FIXME: There is a bug in the builder here than prevents origins from being set as null
+        // once that is fixed, we only need one builder statement here.
+        if (!getOrigins().isEmpty()) {            
+            final RelyingParty rp = RelyingParty.builder().identity(
+                    RelyingPartyIdentity
+                    .builder()
+                    .id(getRelyingPartyId())
+                    .name(getRelyingPartyName())
+                    .build()).credentialRepository(getCredentialRepository())
+            .allowOriginPort(isAllowOriginPort())
+            .allowOriginSubdomain(isAllowOriginSubdomain())
+            .origins(getOrigins())
+            .build();
+            assert rp != null;
+            return new YubicoWebauthnAuthenticationClient(rp, getObjectMapper());
+        } else {
+            final RelyingParty rp = RelyingParty.builder().identity(
+                    RelyingPartyIdentity
+                    .builder()
+                    .id(getRelyingPartyId())
+                    .name(getRelyingPartyName())
+                    .build()).credentialRepository(getCredentialRepository())
+            .allowOriginPort(isAllowOriginPort())
+            .allowOriginSubdomain(isAllowOriginSubdomain())
+            .build();
+            assert rp != null;
+            return new YubicoWebauthnAuthenticationClient(rp, getObjectMapper());
+        }        
+        
     }
     
     /**
@@ -110,6 +138,30 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
         return credentialRepository;
     }
     
+    /**
+     * Get the allowable origins for this Relying Party. Overrides the origin derived from the relyingPartyId if used
+     * 
+     * @return the origins.
+     */
+    @Nonnull @NonnullElements @NotLive public synchronized Set<String> getOrigins() {
+        return origins;
+    }
+    
+    /**
+     * Set the allowable origins for this Relying Party. Overrides the origin derived from the relyingPartyId if used
+     * 
+     * @param origins the origins to use
+     */
+    public synchronized void setOrigins(@Nullable final Set<String> allowedOrigins) {
+        checkSetterPreconditions();
+        if (allowedOrigins == null || allowedOrigins.isEmpty()) {
+            return;
+        }
+        origins = allowedOrigins.stream().
+            map(StringSupport::trimOrNull).
+            filter(Predicates.notNull()).
+            collect(CollectionSupport.nonnullCollector(Collectors.toSet())).get();
+    }    
     
     /**
      * Set the credential repository used to store the valid webauthn credential.
@@ -170,7 +222,7 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
      * 
      * @return the relying party identifier.
      */
-    @Nullable private synchronized String getRelyingPartyId() {
+    @NonnullAfterInit private synchronized String getRelyingPartyId() {
         return relyingPartyId;
     }
     
@@ -189,7 +241,7 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
      * 
      * @return the relying party name.
      */
-    @Nullable private synchronized String getRelyingPartyName() {
+    @NonnullAfterInit private synchronized String getRelyingPartyName() {
         checkComponentActive();
         return relyingPartyName;
     }
diff --git a/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 272895a..da2fe54 100644
--- a/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -39,6 +39,7 @@
         </property>
     </bean>
     
+    <!-- WebAuthn authentication flow -->
     <bean id="authn/WebAuthn" parent="shibboleth.AuthenticationFlow"
             p:order="%{idp.authn.webauthn.order:1000}"
             p:nonBrowserSupported="%{idp.authn.webauthn.nonBrowserSupported:false}"
@@ -89,8 +90,9 @@
         class="net.shibboleth.idp.plugin.authn.webauthn.client.impl.YubicoWebauthnClientFactory"
         p:relyingPartyId="%{idp.authn.webauthn.relyingPartyId}" 
         p:relyingPartyName="%{idp.authn.webauthn.relyingPartyName}"
-        p:allowOriginPort ="%{idp.authn.webauthn.allowOriginPort:false}"
-        p:allowOriginSubdomain ="%{idp.authn.webauthn.allowOriginSubdomain:false}"
+        p:allowOriginPort="%{idp.authn.webauthn.allowOriginPort:false}"
+        p:allowOriginSubdomain="%{idp.authn.webauthn.allowOriginSubdomain:false}"
+        p:origins="%{idp.authn.webauthn.origins:}"
         p:objectMapper-ref="shibboleth.authn.WebAuthn.JSONObjectMapper" 
         p:credentialRepository-ref="shibboleth.authn.webauthn.DefaultCredentialRepository"/>
     
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
index 0f5c994..ae6f003 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
@@ -5,7 +5,10 @@ idp.authn.webauthn.relyingPartyId = localhost
 idp.authn.webauthn.relyingPartyName = Shibboleth
 ## Allow any port on that origin
 idp.authn.webauthn.allowOriginPort = true
+## Allow any subdomain of that origin
 idp.authn.webauthn.allowOriginSubdomain = false
+## An override of origins this RP is allowed to register and authenticate credentials for (must match that returned by the browser during registration or authentication)
+#idp.authn.webauthn.origins = https://localhost
 
 ## Which type of flow is supported? Usernameless or passwordless
 # idp.authn.webauthn.usernameless.enabled = false

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list