[java-idp-plugin-webauthn] branch main updated: Add Attestation Conveyance Preference to options
Phil Smart
philip.smart at jisc.ac.uk
Mon Feb 5 18:39:05 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=8a0dca01a13ff1cdbb3c228ba1b4fc5683ad77b3
The following commit(s) were added to refs/heads/main by this push:
new 8a0dca0 Add Attestation Conveyance Preference to options
8a0dca0 is described below
commit 8a0dca01a13ff1cdbb3c228ba1b4fc5683ad77b3
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Mon Feb 5 18:39:02 2024 +0000
Add Attestation Conveyance Preference to options
- Does not yet store the attestation if returned
---
.../admin/CredentialCreationOptionsParameters.java | 48 +++++++++++--
.../context/WebAuthnRegistrationContext.java | 29 +++++++-
.../impl/AddAttestationConveyancePreference.java | 78 ++++++++++++++++++++++
.../CreatePublicKeyCredentialCreationOptions.java | 11 ++-
.../impl/YubicoWebauthnAuthenticationClient.java | 1 +
.../client/impl/YubicoWebauthnClientFactory.java | 37 +++++-----
.../webauthn-registration-beans.xml | 4 ++
.../webauthn-registration-flow.xml | 1 +
.../authn/webauthn/conf/authn/webauthn.properties | 5 +-
.../authn/webauthn/views/webauthn-registered.vm | 2 +-
10 files changed, 187 insertions(+), 29 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/CredentialCreationOptionsParameters.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/CredentialCreationOptionsParameters.java
index 0e1a7ae..f2ace6e 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/CredentialCreationOptionsParameters.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/CredentialCreationOptionsParameters.java
@@ -20,6 +20,7 @@ import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import com.yubico.webauthn.data.AttestationConveyancePreference;
import com.yubico.webauthn.data.AuthenticatorAttachment;
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
import com.yubico.webauthn.data.ResidentKeyRequirement;
@@ -53,7 +54,10 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
@Nonnull final ResidentKeyRequirement residentKeyRequirement;
/** The userhandle supplied to the authenticator during registration. As generated by the IdP.*/
- @Nonnull final byte[] userHandle;
+ @Nonnull final byte[] userHandle;
+
+ /** The Attestation conveyance preference.*/
+ @Nonnull private final AttestationConveyancePreference attestationConveyancePreference;
/**
*
@@ -71,6 +75,8 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
"Resident key requirement can not be null");
this.userHandle = Constraint.isNotNull(builder.userHandle, "UserHandle can not be null");
this.authenticatorAttachment = builder.authenticatorAttachment;
+ this.attestationConveyancePreference = Constraint.isNotNull(builder.attestationConveyancePreference,
+ "AttestationConveyancePreference can not be null");
}
/**
@@ -121,6 +127,15 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
@Nonnull public final byte[] getUserHandle() {
return userHandle;
}
+
+ /**
+ * Get the attestation conveyance preference.
+ *
+ * @return the attestation conveyance preference.
+ */
+ public AttestationConveyancePreference getAttestationConveyancePreference() {
+ return attestationConveyancePreference;
+ }
/**
@@ -204,7 +219,20 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
* @param userHandle the user handle
* @return the next stage
*/
- public IBuildStage withUserHandle(@Nonnull final byte[] userHandle);
+ public IAttestationConveyancePreferenceStage withUserHandle(@Nonnull final byte[] userHandle);
+ }
+
+
+ /** Stage interface.*/
+ public interface IAttestationConveyancePreferenceStage {
+ /**
+ * The attestation conveyance preference.
+ *
+ * @param preference the preference
+ * @return the next stage
+ */
+ public IBuildStage withAttestationConveyancePreference(
+ @Nonnull final AttestationConveyancePreference preference);
}
@@ -230,7 +258,8 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
/** Builder class.*/
public static final class Builder implements IUserVerificationRequirementStage, IChallengeStage,
- IExcludeCredentialsStage, IUsernameStage, IResidentKeyRequirementStage, IUserHandleStage, IBuildStage {
+ IExcludeCredentialsStage, IUsernameStage, IResidentKeyRequirementStage, IUserHandleStage,
+ IAttestationConveyancePreferenceStage, IBuildStage {
/** Does the authentication/registration require user verification.*/
private UserVerificationRequirement userVerificationRequirement;
@@ -252,6 +281,9 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
/** The AuthenticatorAttachment requirement. {@code null} would represent either possibility.*/
private AuthenticatorAttachment authenticatorAttachment;
+
+ /** The Attestation conveyance preference.*/
+ private AttestationConveyancePreference attestationConveyancePreference;
/** Constructor.*/
private Builder() {
@@ -291,10 +323,18 @@ public class CredentialCreationOptionsParameters extends BaseOptionsParameters {
}
@Override
- public IBuildStage withUserHandle(@Nonnull final byte[] userHandle) {
+ public IAttestationConveyancePreferenceStage withUserHandle(@Nonnull final byte[] userHandle) {
this.userHandle = userHandle;
return this;
}
+
+
+ @Override
+ public IBuildStage withAttestationConveyancePreference(
+ @Nonnull final AttestationConveyancePreference preference) {
+ attestationConveyancePreference = preference;
+ return this;
+ }
@Override
public IBuildStage withAuthenticatorAttachment(
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
index dccbb42..78aefef 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/context/WebAuthnRegistrationContext.java
@@ -5,6 +5,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import com.yubico.webauthn.RegistrationResult;
+import com.yubico.webauthn.data.AttestationConveyancePreference;
import com.yubico.webauthn.data.AuthenticatorAttachment;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
@@ -57,7 +58,10 @@ public final class WebAuthnRegistrationContext extends BaseWebAuthnContext {
@Nullable private ResidentKeyRequirement residentKeyRequirement;
/** The AuthenticatorAttachment requirement. {@code null} would represent either possibility.*/
- @Nullable private AuthenticatorAttachment authenticatorAttachmentRequirement;
+ @Nullable private AuthenticatorAttachment authenticatorAttachmentRequirement;
+
+ /** The attestation preference.*/
+ @Nullable private AttestationConveyancePreference attestationConveyancePreference;
/**
* Set the AuthenticatorAttachment requirement. {@code null} would represent either possibility.
@@ -237,4 +241,27 @@ public final class WebAuthnRegistrationContext extends BaseWebAuthnContext {
@Nullable public ResidentKeyRequirement getResidentKeyRequirement() {
return residentKeyRequirement;
}
+
+
+ /**
+ * Set the attestation conveyance preference.
+ *
+ * @param preference the attestation conveyance preference
+ *
+ * @return this context
+ */
+ @Nonnull public BaseWebAuthnContext setAttestationConveyancePreference(
+ @Nullable final AttestationConveyancePreference preference) {
+ attestationConveyancePreference = preference;
+ return this;
+ }
+
+ /**
+ * Get the attestation conveyance preference.
+ *
+ * @return the attestationConveyancePreference.
+ */
+ @Nullable public AttestationConveyancePreference getAttestationConveyancePreference() {
+ return attestationConveyancePreference;
+ }
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java
new file mode 100644
index 0000000..ac585bd
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed 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.webauthn.admin.impl;
+
+import java.util.stream.Stream;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.yubico.webauthn.data.AttestationConveyancePreference;
+
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.ConstraintViolationException;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Add a Attestation Preference to the {@link WebAuthnAuthenticationContext context}.
+ */
+public class AddAttestationConveyancePreference extends AbstractWebAuthnRegistrationAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddAttestationConveyancePreference.class);
+
+ /** The attestation conveyance preference. Default is 'none'. */
+ @Nonnull private AttestationConveyancePreference attestationConveyancePreference;
+
+ /** Constructor.*/
+ public AddAttestationConveyancePreference() {
+ attestationConveyancePreference = AttestationConveyancePreference.NONE;
+ }
+
+ /**
+ * Set the UserVerification requirement.
+ *
+ * @param requirement The userVerificationRequirement to set.
+ */
+ public void setAttestationConveyancePreference(@Nonnull @NotEmpty final String preference) {
+ checkSetterPreconditions();
+ Constraint.isNotEmpty(preference, "AttestationConveyancePreference can not be null or empty");
+
+ final AttestationConveyancePreference attestationPreference =
+ Stream.of(AttestationConveyancePreference.values())
+ .filter(uv -> uv.getValue().equals(preference))
+ .findAny()
+ .orElseThrow(() ->
+ new ConstraintViolationException("Attestation conveyance preference '"+preference+"' unknown"));
+ assert attestationPreference != null;
+ attestationConveyancePreference = attestationPreference;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nonnull final WebAuthnRegistrationContext context) {
+
+ log.debug("{} Attestation conveyance preference is '{}'",getLogPrefix(), attestationConveyancePreference);
+ context.setAttestationConveyancePreference(attestationConveyancePreference);
+
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
index e08b9ee..6ca356f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
@@ -28,6 +28,7 @@ import org.slf4j.Logger;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yubico.webauthn.data.AttestationConveyancePreference;
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
import com.yubico.webauthn.data.ResidentKeyRequirement;
@@ -113,6 +114,12 @@ public class CreatePublicKeyCredentialCreationOptions extends AbstractWebAuthnRe
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return;
}
+ final AttestationConveyancePreference attestationPreference = context.getAttestationConveyancePreference();
+ if (attestationPreference == null) {
+ log.error("{} AttestationConveyancePreference is null",getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
try {
@@ -130,7 +137,9 @@ public class CreatePublicKeyCredentialCreationOptions extends AbstractWebAuthnRe
.withExcludeCredentials(existingCredentialDescriptors)
.withUsername(username)
.withResidentKeyRequirement(residentKeyRequirement)
- .withUserHandle(userHandle).withAuthenticatorAttachment(
+ .withUserHandle(userHandle)
+ .withAttestationConveyancePreference(attestationPreference)
+ .withAuthenticatorAttachment(
context.getAuthenticatorAttachmentRequirement())
.build();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java
index 25e0f61..3277c78 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClient.java
@@ -138,6 +138,7 @@ public class YubicoWebauthnAuthenticationClient implements WebAuthnAuthenticatio
.challenge(new ByteArray(creationOptions.getChallenge()))
.pubKeyCredParams(preferredPublickeyParams)
.excludeCredentials(creationOptions.getExcludeCredentials())
+ .attestation(creationOptions.getAttestationConveyancePreference())
.authenticatorSelection(AuthenticatorSelectionCriteria.builder()
.userVerification(creationOptions.getUserVerificationRequirement())
.residentKey(creationOptions.getResidentKeyRequirement())
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 7685d01..f57fe1e 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
@@ -65,7 +65,10 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
/** The credential repository to store valid credentials in.*/
@GuardedBy("this") @NonnullAfterInit private CredentialRepository credentialRepository;
- /** Allowable origins for this Relying Party. Overrides the origin derived from the relyingPartyId if used.*/
+ /**
+ * Allowable origins the authenticator response can contain.
+ * Overrides an origin derived from the relyingPartyId if set.
+ */
@GuardedBy("this") @Nonnull @NonnullElements private Set<String> origins;
/** Constructor.*/
@@ -96,31 +99,23 @@ public class YubicoWebauthnClientFactory extends AbstractInitializableComponent
@Override
public WebAuthnAuthenticationClient getObject() throws Exception {
- // FIXME: There is a bug in the builder here than prevents origins from being set as null
+ // FIXME: There is an issue in the builder here than prevents origins from being set as null
// once that is fixed, we only need one builder statement here.
+ final var builder = RelyingParty.builder().identity(
+ RelyingPartyIdentity
+ .builder()
+ .id(getRelyingPartyId())
+ .name(getRelyingPartyName())
+ .build()).credentialRepository(getCredentialRepository())
+ .allowOriginPort(isAllowOriginPort())
+ .allowOriginSubdomain(isAllowOriginSubdomain());
+
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();
+ final RelyingParty rp = builder.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();
+ final RelyingParty rp = builder.build();
assert rp != null;
return new YubicoWebauthnAuthenticationClient(rp, getObjectMapper());
}
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
index 0933b6c..3b92309 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
@@ -26,6 +26,10 @@
class="net.shibboleth.idp.plugin.authn.webauthn.impl.UsernameFromAuthenticationContextLookupStrategy" />
</property>
</bean>
+
+ <bean id="AddAttestationConveyancePreference" scope="prototype" parent="AbstractWebAuthnRegistrationAction"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AddAttestationConveyancePreference"
+ p:attestationConveyancePreference="%{idp.authn.webauthn.registration.attestationConveyancePreference:none}"/>
<bean id="AddResidentKeyRequirement" scope="prototype" parent="AbstractWebAuthnRegistrationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AddResidentKeyRequirement"
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
index 2531e61..f8c01cf 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
@@ -30,6 +30,7 @@
<evaluate expression="GenerateUserHandle"/>
<evaluate expression="AddResidentKeyRequirement"/>
<evaluate expression="AddAuthenticatorAttachmentRequirement"/>
+ <evaluate expression="AddAttestationConveyancePreference"/>
<evaluate expression="AddUserVerificationRequired"/>
<evaluate expression="CreatePublicKeyCredentialCreationOptions"/>
<evaluate expression="'proceed'" />
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 ae6f003..aadccd2 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
@@ -7,7 +7,7 @@ idp.authn.webauthn.relyingPartyName = Shibboleth
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)
+## An override of origins this RP allows in a response from an authenticator
#idp.authn.webauthn.origins = https://localhost
## Which type of flow is supported? Usernameless or passwordless
@@ -20,6 +20,9 @@ idp.authn.webauthn.allowOriginSubdomain = false
# idp.authn.webauthn.registration.authenticatorAttachment = any
### Require User Verification
# idp.authn.webauthn.registration.userVerification = discouraged
+### State the preference of the IdP during registration to receive an authenticator attestation. One-of 'none',
+### 'indirect', 'direct', or 'enterprise'.
+idp.authn.webauthn.registration.attestationConveyancePreference = direct
## Settings for allowing 2FA usage of the WebAuthn flow.
idp.authn.webauthn.2fa.allowedPreviousFactors = authn/Password
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registered.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registered.vm
index 60938f8..64067ee 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registered.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registered.vm
@@ -59,7 +59,7 @@
<th>#springMessageText("idp.webauthn.register.table.keyName", "Key Name")</th>
<th>#springMessageText("idp.webauthn.register.table.transports", "Transports")</th>
<th>#springMessageText("idp.webauthn.register.table.passkey", "Passkey?")</th>
- <th>#springMessageText("idp.webauthn.register.table.userVerifiedOnRegistration", "User Verified On Registration")</th>
+ <th>#springMessageText("idp.webauthn.register.table.userVerifiedOnRegistration", "User Verified?")</th>
<th>#springMessageText("idp.webauthn.register.table.registrationTime", "Registration Time")</th>
</tr>
#foreach($cred in $webauthnRegContext.existingCredentials)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list