[java-idp-plugin-webauthn] branch main updated: Add internal Registration and Assertion result objects
Phil Smart
philip.smart at jisc.ac.uk
Fri Mar 22 17:43:32 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=4574618441368c4bbfca0243431207ffb4710efa
The following commit(s) were added to refs/heads/main by this push:
new 4574618 Add internal Registration and Assertion result objects
4574618 is described below
commit 4574618441368c4bbfca0243431207ffb4710efa
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Mar 22 17:43:29 2024 +0000
Add internal Registration and Assertion result objects
- We can not instantiate the Yubico variants, so we need our own
- Also add more tests
- Create new Mock WebAuthn clients (so as not to rely as much on
Yubico's client for the tests). Still WIP.
---
.../authn/webauthn/authn/AssertionResult.java | 102 ++++++++++
.../authn/webauthn/authn/RegistrationResult.java | 221 +++++++++++++++++++++
.../client/WebAuthnAuthenticationClient.java | 9 +-
.../context/WebAuthnRegistrationContext.java | 3 +-
.../admin/impl/StorePublicKeyCredential.java | 2 +-
.../ValidateAuthenticatorAttestationResponse.java | 8 +-
.../impl/YubicoWebAuthnAuthenticationClient.java | 25 ++-
.../webauthn/impl/ValidateWebAuthnAssertion.java | 6 +-
.../impl/ExceptionThrowingMockWebAuthnClient.java | 5 +-
.../webauthn/client/impl/MockWebAuthnClient.java | 179 +++++++++++++++++
.../YubicoWebauthnAuthenticationClientTest.java | 4 +-
.../authn/webauthn/impl/AbstractWebAuthnTest.java | 35 +++-
.../impl/ValidateWebAuthnAssertionTest.java | 133 +++++++++++++
13 files changed, 703 insertions(+), 29 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java
new file mode 100644
index 0000000..7bbed4e
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/AssertionResult.java
@@ -0,0 +1,102 @@
+/*
+ * 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.authn;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * The result of calling {@link WebAuthnAuthenticationClient#validateAuthenticatorAssertionResponse(String, byte[],
+ * com.yubico.webauthn.data.PublicKeyCredentialRequestOptions, com.yubico.webauthn.data.PublicKeyCredential)}
+ */
+//TODO javadoc etc. when finalised
+public class AssertionResult {
+
+ /** Is this assertion valid?*/
+ private final boolean success;
+
+ /** The username of the user this result corresponds to.*/
+ @Nonnull @NotEmpty private final String username;
+
+ /** Is the signature count valid?*/
+ private final boolean signatureCounterValid;
+
+ /**
+ * @return Returns the success.
+ */
+ public final boolean isSuccess() {
+ return success;
+ }
+
+
+ /**
+ * @return Returns the username.
+ */
+ public final String getUsername() {
+ return username;
+ }
+
+
+ /**
+ * @return Returns the signatureCounterValid.
+ */
+ public final boolean isSignatureCounterValid() {
+ return signatureCounterValid;
+ }
+
+
+ private AssertionResult(final Builder builder) {
+ this.success = builder.success;
+ this.username = builder.username;
+ this.signatureCounterValid = builder.signatureCounterValid;
+ }
+
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+
+ public static final class Builder {
+ private boolean success;
+ private String username;
+ private boolean signatureCounterValid;
+
+ private Builder() {
+ }
+
+ public Builder withSuccess(final boolean success) {
+ this.success = success;
+ return this;
+ }
+
+ public Builder withUsername(final String username) {
+ this.username = username;
+ return this;
+ }
+
+ public Builder withSignatureCounterValid(final boolean signatureCounterValid) {
+ this.signatureCounterValid = signatureCounterValid;
+ return this;
+ }
+
+ public AssertionResult build() {
+ return new AssertionResult(this);
+ }
+ }
+
+}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java
new file mode 100644
index 0000000..3d6f915
--- /dev/null
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/authn/RegistrationResult.java
@@ -0,0 +1,221 @@
+/*
+ * 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.authn;
+
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import com.yubico.webauthn.data.AttestationType;
+import com.yubico.webauthn.data.AttestedCredentialData;
+import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
+import com.yubico.webauthn.data.ByteArray;
+import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
+import com.yubico.webauthn.data.PublicKeyCredential;
+import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
+
+import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
+
+/**
+ * The result of a call to {@link WebAuthnAuthenticationClient#validateAuthenticatorAttestationResponse
+ * (com.yubico.webauthn.data.PublicKeyCredentialCreationOptions, com.yubico.webauthn.data.PublicKeyCredential)}
+ */
+ at ThreadSafe
+ at Immutable
+//TODO JavaDoc etc. once finalised
+//TODO credential is just held here, it comes from the original response. In which case this class is mostly convient to
+// access fields inside the credential.
+public class RegistrationResult {
+
+ /**
+ * Is the attestation signature valid. Does it link to a trusted root attestation.
+ *
+ * <p>Note, this is different than if the assertion signature is valid.</p>
+ */
+ private final boolean attestationTrusted;
+
+ /**
+ * The attestation type that was used for this credential. This only applies to attestation statements
+ * if requested.
+ */
+ @Nonnull private final AttestationType attestationType;
+
+ /** The verified attestation response.*/
+ @Nonnull private final
+ PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> credential;
+
+
+ private RegistrationResult(final Builder builder) {
+ this.attestationTrusted = builder.attestationTrusted;
+ this.attestationType = builder.attestationType;
+ this.credential = builder.credential;
+ }
+
+ /**
+ * Get the builder used to construct this object.
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+
+ /**
+ * Is the attestation signature valid. Does it link to a trusted root attestation.
+ *
+ * <p>Note, this is different than if the assertion signature is valid.</p>
+ *
+ * @return true if the attestation signature is valid, false otherwise.
+ */
+ public final boolean isAttestationTrusted() {
+ return attestationTrusted;
+ }
+
+ /**
+ * The attestation type that was used for this credential. This only applies to attestation statements
+ * iff requested.
+ *
+ * @return the attestation type.
+ */
+ public final AttestationType getAttestationType() {
+ return attestationType;
+ }
+
+ /**
+ * Get the credential ID and transports of the created credential.
+ *
+ * @return the credential ID and transports of the created credential.
+ */
+ public PublicKeyCredentialDescriptor getKeyId() {
+ return PublicKeyCredentialDescriptor.builder()
+ .id(credential.getId())
+ .type(credential.getType())
+ .transports(credential.getResponse().getTransports())
+ .build();
+ }
+
+ /**
+ * Get the public key of the created credential in COSE format.
+ *
+ * @return the public key, <code>null</code> if not found.
+ */
+ @Nullable public ByteArray getPublicKeyCose() {
+ final Optional<AttestedCredentialData> attestedCredentialData = credential
+ .getResponse()
+ .getAttestation()
+ .getAuthenticatorData()
+ .getAttestedCredentialData();
+ if (attestedCredentialData.isPresent()) {
+ return attestedCredentialData.get().getCredentialPublicKey();
+ }
+ return null;
+ }
+
+
+ /**
+ * Get the AAGUID of the authenticator.
+ *
+ * @return the AAGUID of the authenticator, <code>null</code> if not found.
+ */
+ @Nullable public ByteArray getAaguid() {
+ final Optional<AttestedCredentialData> attestedCredentialData = credential
+ .getResponse()
+ .getAttestation()
+ .getAuthenticatorData()
+ .getAttestedCredentialData();
+
+ if (attestedCredentialData.isPresent()) {
+ return attestedCredentialData.get().getAaguid();
+ }
+ return null;
+
+ }
+
+
+ /**
+ * Try to determine if this credential is a discoverable (passkey) type by inspecting the ResidentKey flag inside
+ * the credential properties extension.
+ *
+ * @return true if the credential is disoverable (a passkey), false if it is not, or empty if not know e.g.
+ * the extensions were not returned.
+ */
+ @Nonnull public Optional<Boolean> isDiscoverable() {
+ final ClientRegistrationExtensionOutputs clientExtensions = credential.getClientExtensionResults();
+ if (!clientExtensions.getExtensionIds().isEmpty() && clientExtensions.getCredProps().isPresent()) {
+ return clientExtensions.getCredProps().flatMap(credProps -> credProps.getRk());
+ }
+ return Optional.empty();
+ }
+
+
+ /**
+ * Was the user verified by a suitable authorization guester during the registration ceremony?
+ *
+ * @return true if the authenticator claims to have performed user verification, false otherwise.
+ */
+ public boolean isUserVerified() {
+ return credential.getResponse().getParsedAuthenticatorData().getFlags().UV;
+ }
+
+
+ /**
+ * Get the attestation response.
+ *
+ * @return the credential
+ */
+ public final
+ PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> getCredential() {
+ return credential;
+ }
+
+ /** The builder.*/
+ public static final class Builder {
+ private boolean attestationTrusted;
+ private AttestationType attestationType;
+ private PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> credential;
+
+ private Builder() {
+ }
+
+ public Builder withAttestationTrusted(final boolean attestationTrusted) {
+ this.attestationTrusted = attestationTrusted;
+ return this;
+ }
+
+ public Builder withAttestationType(final AttestationType attestationType) {
+ this.attestationType = attestationType;
+ return this;
+ }
+
+ public Builder withCredential(
+ final PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> credential) {
+ this.credential = credential;
+ return this;
+ }
+
+ public RegistrationResult build() {
+ return new RegistrationResult(this);
+ }
+ }
+
+
+
+
+
+}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/WebAuthnAuthenticationClient.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/WebAuthnAuthenticationClient.java
index a248160..8b55117 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/WebAuthnAuthenticationClient.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/client/WebAuthnAuthenticationClient.java
@@ -4,8 +4,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
-import com.yubico.webauthn.AssertionResult;
-import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
@@ -15,7 +13,9 @@ import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
import net.shibboleth.idp.plugin.authn.webauthn.admin.CredentialCreationOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
import net.shibboleth.idp.plugin.authn.webauthn.authn.CredentialRequestOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.exception.WebAuthnAuthenticationClientException;
@@ -72,9 +72,8 @@ public interface WebAuthnAuthenticationClient {
*
* @throws AssertionFailureException if the assertion is not valid
*/
- //TODO would need our own AssertionResult to make this usuable beyond Yubico.
//TODO do we need a userId supplied here?
- AssertionResult validateAuthenticatorAssertionResponse(@Nullable final String username,
+ @Nonnull AssertionResult validateAuthenticatorAssertionResponse(@Nullable final String username,
@Nullable final byte[] userId,
@Nonnull final PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions,
@Nonnull final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
@@ -91,7 +90,7 @@ public interface WebAuthnAuthenticationClient {
*
* @throws RegistrationFailureException if the registration is not valid
*/
- RegistrationResult validateAuthenticatorAttestationResponse(
+ @Nonnull RegistrationResult validateAuthenticatorAttestationResponse(
@Nonnull final PublicKeyCredentialCreationOptions publicKeyCredentialCreationOptions,
@Nonnull final PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs>
authenticatorAttestationResponse) throws RegistrationFailureException;
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 54d88db..5051c25 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
@@ -4,7 +4,6 @@ import javax.annotation.Nonnull;
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;
@@ -13,6 +12,8 @@ import com.yubico.webauthn.data.PublicKeyCredential;
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.ResidentKeyRequirement;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
+
/**
* Registration context for processing WebAuthn Registration Ceremonies. This context is intended for registration of
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
index 90e40bd..0255a32 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
@@ -35,11 +35,11 @@ import com.yubico.fido.metadata.AAGUID;
import com.yubico.fido.metadata.FidoMetadataService;
import com.yubico.fido.metadata.MetadataBLOBPayloadEntry;
import com.yubico.webauthn.RegisteredCredential;
-import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.UserIdentity;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
import net.shibboleth.idp.plugin.authn.webauthn.storage.impl.CredentialRegistrationSerializer;
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
index e30a2f7..b01436c 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
@@ -23,13 +23,14 @@ import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
-import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
+import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
import com.yubico.webauthn.data.PublicKeyCredential;
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
@@ -93,9 +94,10 @@ public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRe
// If untrusted attestations are not allowed, it will not get this far. This is only useful to log if
// untrusted attestations are allowed and the metadata has been loaded and the attestation has been
// checked.
+ final ByteArray aaguid = credentialPublicKey.getAaguid();
+ final String athenticator = aaguid != null ? aaguid.getHex() : "unknown";
log.debug("{} Was attestation for authenticator '{}' trusted? {}", getLogPrefix(),
- credentialPublicKey.getAaguid().getHex(),
- credentialPublicKey.isAttestationTrusted() ? "Yes" : "No");
+ athenticator, credentialPublicKey.isAttestationTrusted() ? "Yes" : "No");
// If valid. Add back to context
context.setRegistrationResult(credentialPublicKey);
log.info("{} Public Key Registration was valid", getLogPrefix());
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 b76f216..84e826b 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
@@ -24,10 +24,8 @@ import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Logger;
import com.yubico.webauthn.AssertionRequest;
-import com.yubico.webauthn.AssertionResult;
import com.yubico.webauthn.FinishAssertionOptions;
import com.yubico.webauthn.FinishRegistrationOptions;
-import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.RelyingParty;
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
@@ -44,7 +42,9 @@ import com.yubico.webauthn.data.UserIdentity;
import com.yubico.webauthn.exception.RegistrationFailedException;
import net.shibboleth.idp.plugin.authn.webauthn.admin.CredentialCreationOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
import net.shibboleth.idp.plugin.authn.webauthn.authn.CredentialRequestOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
@@ -175,7 +175,7 @@ public class YubicoWebAuthnAuthenticationClient implements WebAuthnAuthenticatio
username, userId);
}
- final AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder()
+ final com.yubico.webauthn.AssertionResult result = rp.finishAssertion(FinishAssertionOptions.builder()
.request(requestAssertion)
.response(authenticatorAssertionResponse)
.build());
@@ -187,7 +187,13 @@ public class YubicoWebAuthnAuthenticationClient implements WebAuthnAuthenticatio
// I think this is *always* true if valid, and will throw if not valid. But just in case.
throw new AssertionFailureException("Authenticator assertion was not valid");
}
- return result;
+ final AssertionResult assertionResult = AssertionResult.builder()
+ .withSignatureCounterValid(result.isSignatureCounterValid())
+ .withSuccess(result.isSuccess())
+ .withUsername(result.getUsername())
+ .build();
+ assert assertionResult != null;
+ return assertionResult;
} catch (final Exception e) {
throw new AssertionFailureException(e);
@@ -204,10 +210,19 @@ public class YubicoWebAuthnAuthenticationClient implements WebAuthnAuthenticatio
try {
log.trace("Public Key Credential to validate '{}'", authenticatorAttestationResponse);
- return rp.finishRegistration(FinishRegistrationOptions.builder()
+ final com.yubico.webauthn.RegistrationResult result = rp.finishRegistration(FinishRegistrationOptions.builder()
.request(publicKeyCredentialCreationOptions)
.response(authenticatorAttestationResponse)
.build());
+
+ final RegistrationResult registrationResult = RegistrationResult.builder()
+ .withAttestationTrusted(result.isAttestationTrusted())
+ .withAttestationType(result.getAttestationType())
+ .withCredential(authenticatorAttestationResponse)
+ .build();
+
+ assert registrationResult != null;
+ return registrationResult;
} catch (final RegistrationFailedException e) {
throw new RegistrationFailureException(e);
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
index fddc8f0..11e6e55 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertion.java
@@ -10,7 +10,6 @@ import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
-import com.yubico.webauthn.AssertionResult;
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
import com.yubico.webauthn.data.PublicKeyCredential;
@@ -20,6 +19,7 @@ import net.shibboleth.idp.authn.AbstractValidationAction;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.authn.principal.UsernamePrincipal;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
@@ -125,6 +125,10 @@ public class ValidateWebAuthnAssertion extends AbstractValidationAction {
final AssertionResult result = webAuthnClient.validateAuthenticatorAssertionResponse(
context.getUsername(), context.getUserId(), publicKeyCredentialRequestOptions, assertion);
+ if (!result.isSuccess()) {
+ throw new AssertionFailureException("Assestion was not valid");
+ }
+
log.info("{} WebAuthn authentication succeeded for '{}'",getLogPrefix(),result.getUsername());
context.setUsername(result.getUsername());
buildAuthenticationResult(profileRequestContext, authenticationContext);
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/ExceptionThrowingMockWebAuthnClient.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/ExceptionThrowingMockWebAuthnClient.java
index 176c35e..e8c9a1b 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/ExceptionThrowingMockWebAuthnClient.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/ExceptionThrowingMockWebAuthnClient.java
@@ -14,8 +14,7 @@
package net.shibboleth.idp.plugin.authn.webauthn.client.impl;
-import com.yubico.webauthn.AssertionResult;
-import com.yubico.webauthn.RegistrationResult;
+
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
@@ -25,7 +24,9 @@ import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
import net.shibboleth.idp.plugin.authn.webauthn.admin.CredentialCreationOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
import net.shibboleth.idp.plugin.authn.webauthn.authn.CredentialRequestOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/MockWebAuthnClient.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/MockWebAuthnClient.java
new file mode 100644
index 0000000..0131aa1
--- /dev/null
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/MockWebAuthnClient.java
@@ -0,0 +1,179 @@
+/*
+ * 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.client.impl;
+
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+
+import com.yubico.webauthn.RelyingParty;
+import com.yubico.webauthn.data.AttestationType;
+import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
+import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
+import com.yubico.webauthn.data.AuthenticatorSelectionCriteria;
+import com.yubico.webauthn.data.ByteArray;
+import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
+import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
+import com.yubico.webauthn.data.PublicKeyCredential;
+import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
+import com.yubico.webauthn.data.PublicKeyCredentialParameters;
+import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
+import com.yubico.webauthn.data.RegistrationExtensionInputs;
+import com.yubico.webauthn.data.UserIdentity;
+
+import net.shibboleth.idp.plugin.authn.webauthn.admin.CredentialCreationOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.CredentialRequestOptionsParameters;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
+import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
+import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
+import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
+import net.shibboleth.idp.plugin.authn.webauthn.exception.WebAuthnAuthenticationClientException;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A Mock WebAuthnClient
+ */
+//TODO can we mock the options request etc? seems harder
+public class MockWebAuthnClient implements WebAuthnAuthenticationClient {
+
+ /** Is the assertion response valid.*/
+ private final boolean assertionResponseSuccess;
+
+ /** Is the signature count valid.*/
+ private final boolean signatureCount;
+
+ /** Information pertaining to the relying party.*/
+ @Nonnull private final RelyingParty rp;
+
+ @Nonnull @NonnullElements private final List<PublicKeyCredentialParameters> preferredPublickeyParams =
+ CollectionSupport.listOf(
+ PublicKeyCredentialParameters.ES256,
+ PublicKeyCredentialParameters.EdDSA,
+ PublicKeyCredentialParameters.ES384,
+ PublicKeyCredentialParameters.ES512,
+ PublicKeyCredentialParameters.RS256,
+ PublicKeyCredentialParameters.RS384,
+ PublicKeyCredentialParameters.RS512);
+
+
+ /**
+ * Constructor.
+ *
+ * @param assertionResponseSuccess Is the assertion response valid
+ * @param signatureCount Is the signature count in the assertion response valid
+ */
+ public MockWebAuthnClient(@Nonnull final RelyingParty relyingParty,
+ final boolean assertionResponseSuccess, final boolean signatureCount) {
+ super();
+ rp = Constraint.isNotNull(relyingParty, "The reyling party configuration can not be null");
+ this.assertionResponseSuccess = assertionResponseSuccess;
+ this.signatureCount = signatureCount;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PublicKeyCredentialRequestOptions createAuthenticationRequest(
+ final CredentialRequestOptionsParameters requestParams) throws WebAuthnAuthenticationClientException {
+
+ final PublicKeyCredentialRequestOptions request = PublicKeyCredentialRequestOptions.builder()
+ .challenge(new ByteArray(requestParams.getChallenge()))
+ .rpId(rp.getIdentity().getId())
+ .allowCredentials(Optional.ofNullable(requestParams.getAllowCredentials()))
+ .userVerification(requestParams.getUserVerificationRequirement())
+ .timeout(Optional.of(60000l))
+ .build();
+ if (request == null) {
+ throw new WebAuthnAuthenticationClientException("Unable to build public key credential request options");
+ }
+ return request;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PublicKeyCredentialCreationOptions createRegistrationRequest(
+ final CredentialCreationOptionsParameters creationOptions) throws WebAuthnAuthenticationClientException {
+
+ final UserIdentity identity =
+ UserIdentity.builder().name(creationOptions.getUsername())
+ .displayName(creationOptions.getUsername())
+ .id(new ByteArray(creationOptions.getUserId()))
+ .build();
+
+ RegistrationExtensionInputs extensions;
+ if (creationOptions.isEnableCredProperties()) {
+ extensions = RegistrationExtensionInputs.builder().credProps().build();
+ } else {
+ extensions = RegistrationExtensionInputs.builder().build();
+ }
+
+ final PublicKeyCredentialCreationOptions creation = PublicKeyCredentialCreationOptions.builder()
+ .rp(rp.getIdentity())
+ .user(identity)
+ .challenge(new ByteArray(creationOptions.getChallenge()))
+ .pubKeyCredParams(preferredPublickeyParams)
+ .excludeCredentials(creationOptions.getExcludeCredentials())
+ .attestation(creationOptions.getAttestationConveyancePreference())
+ .authenticatorSelection(AuthenticatorSelectionCriteria.builder()
+ .userVerification(creationOptions.getUserVerificationRequirement())
+ .residentKey(creationOptions.getResidentKeyRequirement())
+ .authenticatorAttachment(creationOptions.getAuthenticatorAttachment())
+ .build())
+ .extensions(extensions)
+ .timeout(Optional.empty()).build();
+ if (creation == null) {
+ throw new WebAuthnAuthenticationClientException("Unable to build public key credential creation options");
+ }
+ return creation;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public AssertionResult validateAuthenticatorAssertionResponse(final String username, final byte[] userId,
+ final PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions,
+ final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> authenticatorAssertionResponse)
+ throws AssertionFailureException {
+
+ final AssertionResult assertionResult = AssertionResult.builder()
+ .withSignatureCounterValid(signatureCount)
+ .withSuccess(assertionResponseSuccess)
+ .withUsername(username)
+ .build();
+ assert assertionResult != null;
+ return assertionResult;
+
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegistrationResult validateAuthenticatorAttestationResponse(
+ final PublicKeyCredentialCreationOptions publicKeyCredentialCreationOptions,
+ final PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> authenticatorAttestationResponse)
+ throws RegistrationFailureException {
+
+ return RegistrationResult.builder()
+ .withAttestationTrusted(true)
+ .withAttestationType(AttestationType.NONE)
+ .withCredential(authenticatorAttestationResponse)
+ .build();
+
+ }
+
+}
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
index b4c0cbc..eaf1eda 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/client/impl/YubicoWebauthnAuthenticationClientTest.java
@@ -25,9 +25,7 @@ import java.util.TreeSet;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import com.yubico.webauthn.AssertionResult;
import com.yubico.webauthn.RegisteredCredential;
-import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.RelyingParty;
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
@@ -42,6 +40,8 @@ import com.yubico.webauthn.data.RelyingPartyIdentity;
import com.yubico.webauthn.data.UserIdentity;
import com.yubico.webauthn.data.UserVerificationRequirement;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AssertionResult;
+import net.shibboleth.idp.plugin.authn.webauthn.authn.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.exception.AssertionFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnTest;
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
index 4bbc26b..e4ca99f 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
@@ -19,6 +19,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Random;
import javax.annotation.Nonnull;
@@ -39,9 +40,10 @@ import com.yubico.webauthn.data.PublicKeyCredentialParameters;
import com.yubico.webauthn.data.RelyingPartyIdentity;
import com.yubico.webauthn.data.UserIdentity;
+import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
-import net.shibboleth.idp.plugin.authn.webauthn.client.impl.YubicoWebauthnClientFactory;
+import net.shibboleth.idp.plugin.authn.webauthn.client.impl.MockWebAuthnClient;
import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
@@ -97,6 +99,9 @@ public abstract class AbstractWebAuthnTest {
/** A mocked credential repository to use.*/
protected StorageServiceCredentialRepository credentialRepo;
+ /** The relying party.*/
+ protected RelyingParty rp;
+
/** List of acceptable public key algorithms.*/
@Nonnull protected final List<PublicKeyCredentialParameters> preferredPublickeyParams =
CollectionSupport.listOf(
@@ -129,6 +134,9 @@ public abstract class AbstractWebAuthnTest {
webAuthnContext = new WebAuthnAuthenticationContext();
assert null != prc;
ac = new AuthenticationContext();
+ final AuthenticationFlowDescriptor afd = new AuthenticationFlowDescriptor();
+ afd.setId("authn/WebAuthn");
+ ac.setAttemptedFlow(afd);
ac.setAuthenticatingAuthority("https://idp.example.com");
assert null != ac;
prc.addSubcontext(ac);
@@ -139,7 +147,7 @@ public abstract class AbstractWebAuthnTest {
prc.addSubcontext(webAuthnRegContext);
//Move this test to one of the client, this should use a mock and less specific data types
- final RelyingParty rp = RelyingParty.builder().identity(
+ rp = RelyingParty.builder().identity(
RelyingPartyIdentity
.builder()
.id("idp.example.com")
@@ -164,13 +172,8 @@ public abstract class AbstractWebAuthnTest {
webAuthnRegContext.setPublicKeyCredentialCreationOptions(credentialCreationOptions);
// TODO Should create a mock factory
- final YubicoWebauthnClientFactory factory = new YubicoWebauthnClientFactory();
- factory.setPreferredPublickeyParams(preferredPublickeyParams.stream().map(alg -> alg.getAlg().name()).toList());
- factory.setCredentialRepository(new InMemoryRegistrationStorage());
- factory.setRelyingPartyId("idp.example.com");
- factory.setRelyingPartyName("Demo IdP as a WebAuthn RP");
- factory.initialize();
- client = factory.getObject();
+
+ client = new MockWebAuthnClient(rp, true, true);
// The im-memory repo is for testing only
credentialRepo = new InMemoryRegistrationStorage();
@@ -235,6 +238,20 @@ public abstract class AbstractWebAuthnTest {
.setBody(body));
}
+ /**
+ * Generate a 'number' of random bytes
+ *
+ * @param number the number of random bytes
+ *
+ * @return the random bytes
+ */
+ protected byte[] generateRandomBytes(final int number) {
+ final Random random = new Random();
+ final byte[] byteArray = new byte[number];
+ random.nextBytes(byteArray);
+ return byteArray;
+ }
+
/**
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertionTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertionTest.java
new file mode 100644
index 0000000..859a2af
--- /dev/null
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ValidateWebAuthnAssertionTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.webflow.execution.Event;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
+import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
+import com.yubico.webauthn.data.ByteArray;
+import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
+import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
+import com.yubico.webauthn.data.PublicKeyCredential;
+import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
+import com.yubico.webauthn.data.UserIdentity;
+import com.yubico.webauthn.data.UserVerificationRequirement;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.client.impl.MockWebAuthnClient;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.DecodingException;
+
+/**
+ * Tests for {@link ValidateWebAuthnAssertion}
+ */
+public class ValidateWebAuthnAssertionTest extends AbstractWebAuthnTest {
+
+ private ValidateWebAuthnAssertion action;
+
+ private WebAuthnAuthenticationContext context;
+
+ private PublicKeyCredentialRequestOptions credentialRequestOptions;
+
+ private UserIdentity userIdentity;
+
+ private PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> attestation;
+
+ /**
+ * {@inheritDoc}
+ *
+ * Need to register a credential to user first, be before we can test a valid assertion (authentication) response.
+ */
+ @Override
+ @BeforeMethod
+ public void setup() throws Exception {
+ super.setup();
+ context = addWebAuthnAuthenticationContext();
+ mockAuthenticator = new MockAuthenticator(RPID);
+
+ action = new ValidateWebAuthnAssertion();
+ action.setWebAuthnClient(new MockWebAuthnClient(rp, true, true));
+
+ final Map<String, String> clientDataCreate = createClientData("webauthn.create", ORIGIN, CHALLENGE_B64);
+
+ attestation =
+ mockAuthenticator.createAuthenticatorAttestationResponse(CHALLENGE_B64, clientDataCreate,
+ Base64Support.decode(USER_HANDLE_B64));
+
+ credentialRequestOptions =
+ PublicKeyCredentialRequestOptions.builder()
+ .challenge(new ByteArray(Base64Support.decode(CHALLENGE_B64)))
+ .rpId(rp.getIdentity().getId())
+ .userVerification(UserVerificationRequirement.REQUIRED)
+ .timeout(Optional.of(60000l))
+ .build();
+ context.setPublicKeyCredentialRequestOptions(credentialRequestOptions);
+
+
+
+ }
+
+ @Test
+ public void testValidAssertion() throws DecodingException, Exception {
+ context.setUsername(USERNAME);
+ action.initialize();
+
+ final Map<String, String> clientDataGet = createClientData("webauthn.get", ORIGIN, CHALLENGE_B64);
+
+ final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
+ assertion = mockAuthenticator.createAuthenticatorAssertionResponse(attestation.getId().getBytes(),
+ clientDataGet);
+
+ //Set the assertion (authentication) response based on the credential we've already registered
+ context.setAuthenticatorAssertionResponse(assertion);
+
+ final Event event = action.execute(src);
+ assertNull(event);
+ }
+
+ @Test
+ public void testInValidAssertion_NotSuccessful() throws DecodingException, Exception {
+ action.setWebAuthnClient(new MockWebAuthnClient(rp, false, true));
+
+ context.setUsername(USERNAME);
+ action.initialize();
+
+ final Map<String, String> clientDataGet = createClientData("webauthn.get", ORIGIN, CHALLENGE_B64);
+
+ final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
+ assertion = mockAuthenticator.createAuthenticatorAssertionResponse(attestation.getId().getBytes(),
+ clientDataGet);
+
+ //Set the assertion (authentication) response based on the credential we've already registered
+ context.setAuthenticatorAssertionResponse(assertion);
+
+ final Event event = action.execute(src);
+ assertNotNull(event);
+ assert event != null;
+ assertEquals(event.getId(), AuthnEventIds.INVALID_CREDENTIALS);
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list