From 998b781948c817427501aa769a76935877560092 Mon Sep 17 00:00:00 2001
From: Emil Lundberg
+ * If this is absent, this indicates that this is a request for an assertion by a client-side-resident + * credential, and identification of the user has been deferred until the response is received. + *
+ */ + public AssertionRequestBuilder username(@NonNull Optional+ * If this is absent, this indicates that this is a request for an assertion by a client-side-resident + * credential, and identification of the user has been deferred until the response is received. + *
+ */ + public AssertionRequestBuilder username(@NonNull String username) { + return this.username(Optional.of(username)); + } } } From eb79a00cb0b3cddde2e3d08eb572170a3c6b81fe Mon Sep 17 00:00:00 2001 From: Emil Lundberg+ * This is treated as a hint, and MAY be overridden by the client. + *
+ */ + public PublicKeyCredentialRequestOptionsBuilder timeout(@NonNull Optional+ * This is treated as a hint, and MAY be overridden by the client. + *
+ */ + public PublicKeyCredentialRequestOptionsBuilder timeout(long timeout) { + return this.timeout(Optional.of(timeout)); + } + + /** + * Specifies the relying party identifier claimed by the caller. + *+ * If omitted, its value will be set by the client. + *
+ */ + public PublicKeyCredentialRequestOptionsBuilder rpId(@NonNull Optional+ * If omitted, its value will be set by the client. + *
+ */ + public PublicKeyCredentialRequestOptionsBuilder rpId(@NonNull String rpId) { + return this.rpId(Optional.of(rpId)); + } + + /** + * A list of {@link PublicKeyCredentialDescriptor} objects representing public key credentials acceptable to the + * caller, in descending order of the caller’s preference (the first item in the list is the most preferred + * credential, and so on down the list). + */ + public PublicKeyCredentialRequestOptionsBuilder allowCredentials(@NonNull Optionalappid
extension when initiating authentication operations.
+ *
+ *
+ * If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will automatically set the
+ * appid
extension input, and {@link #finishAssertion(FinishAssertionOptions) finishAssertion} will
+ * adjust its verification logic to also accept this AppID as an alternative to the RP ID.
+ *
+ * By default, this is not set. + *
+ * + * @see AssertionExtensionInputs#getAppid() + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public RelyingPartyBuilder appId(@NonNull Optionalappid
extension when initiating authentication operations.
+ *
+ *
+ * If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will automatically set the
+ * appid
extension input, and {@link #finishAssertion(FinishAssertionOptions) finishAssertion} will
+ * adjust its verification logic to also accept this AppID as an alternative to the RP ID.
+ *
+ * By default, this is not set. + *
+ * + * @see AssertionExtensionInputs#getAppid() + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public RelyingPartyBuilder appId(@NonNull AppId appId) { + return this.appId(Optional.of(appId)); + } + + /** + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} parameter in + * registration operations. + * + *+ * Unless your application has a concrete policy for authenticator attestation, it is recommended to leave this + * parameter undefined. + *
+ * + *+ * By default, this is not set. + *
+ * + * @see PublicKeyCredentialCreationOptions#getAttestation() + * @see §6.4. Attestation + */ + public RelyingPartyBuilder attestationConveyancePreference(@NonNull Optional+ * Unless your application has a concrete policy for authenticator attestation, it is recommended to leave this + * parameter undefined. + *
+ * + *+ * By default, this is not set. + *
+ * + * @see PublicKeyCredentialCreationOptions#getAttestation() + * @see §6.4. Attestation + */ + public RelyingPartyBuilder attestationConveyancePreference(@NonNull AttestationConveyancePreference attestationConveyancePreference) { + return this.attestationConveyancePreference(Optional.of(attestationConveyancePreference)); + } + + /** + * A {@link MetadataService} instance to use for looking up device attestation metadata. This matters only if {@link + * #getAttestationConveyancePreference()} is non-empty and not set to {@link AttestationConveyancePreference#NONE}. + * + *+ * By default, this is not set. + *
+ * + * @see PublicKeyCredentialCreationOptions#getAttestation() + * @see §6.4. Attestation + */ + public RelyingPartyBuilder metadataService(@NonNull Optional+ * By default, this is not set. + *
+ * + * @see PublicKeyCredentialCreationOptions#getAttestation() + * @see §6.4. Attestation + */ + public RelyingPartyBuilder metadataService(@NonNull MetadataService metadataService) { + return this.metadataService(Optional.of(metadataService)); + } } } diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/RelyingPartyTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/RelyingPartyTest.java new file mode 100644 index 000000000..b4382b4ac --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/RelyingPartyTest.java @@ -0,0 +1,41 @@ +package com.yubico.webauthn; + +import com.yubico.webauthn.attestation.Attestation; +import com.yubico.webauthn.attestation.MetadataService; +import com.yubico.webauthn.data.AttestationConveyancePreference; +import com.yubico.webauthn.extension.appid.AppId; +import com.yubico.webauthn.extension.appid.InvalidAppIdException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.junit.Test; + +public class RelyingPartyTest { + + @Test(expected = NullPointerException.class) + public void itHasTheseBuilderMethods() throws InvalidAppIdException { + + final MetadataService metadataService = new MetadataService() { + @Override public Attestation getAttestation(ListThis URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ @NonNull @Builder.Default @Getter(onMethod = @__({ @Override })) diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/UserIdentity.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/UserIdentity.java index 7cf55949f..bc1a5c87a 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/UserIdentity.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/UserIdentity.java @@ -113,6 +113,15 @@ public class UserIdentity implements PublicKeyCredentialEntity { @NonNull private final ByteArray id; + /** + * A URL which resolves to an image associated with the entity. For example, this could be the user’s avatar. + * + *This URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ @NonNull @Builder.Default @Getter(onMethod = @__({ @Override })) From d9c62fb54f1c2132d13f7fee7133652809789a1f Mon Sep 17 00:00:00 2001 From: Emil LundbergThis URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ + public RelyingPartyIdentityBuilder icon(@NonNull OptionalThis URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ + public RelyingPartyIdentityBuilder icon(@NonNull URL icon) { + return this.icon(Optional.of(icon)); + } } } diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/data/RelyingPartyIdentityTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/RelyingPartyIdentityTest.java new file mode 100644 index 000000000..d950c5e8c --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/RelyingPartyIdentityTest.java @@ -0,0 +1,20 @@ +package com.yubico.webauthn.data; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import org.junit.Test; + +public class RelyingPartyIdentityTest { + + @Test + public void itHasTheseBuilderMethods() throws MalformedURLException { + RelyingPartyIdentity.builder() + .id("") + .name("") + .icon(new URL("https://example.com")) + .icon(Optional.of(new URL("https://example.com"))) + ; + } + +} From 2aaa8b8a07c37011c4450729d2086c1ecd2a8eb5 Mon Sep 17 00:00:00 2001 From: Emil LundbergThis URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ + public UserIdentityBuilder icon(@NonNull OptionalThis URL MUST be an a priori authenticated URL. Authenticators MUST accept and store a + * 128-byte minimum length for an icon member’s value. Authenticators MAY ignore an icon member’s value if its + * length is greater than 128 bytes. The URL’s scheme MAY be "data" to avoid fetches of the URL, at the cost of + * needing more storage. + *
+ */ + public UserIdentityBuilder icon(@NonNull URL icon) { + return this.icon(Optional.of(icon)); + } } } diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/data/UserIdentityTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/UserIdentityTest.java new file mode 100644 index 000000000..2bb16fc83 --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/UserIdentityTest.java @@ -0,0 +1,21 @@ +package com.yubico.webauthn.data; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import org.junit.Test; + +public class UserIdentityTest { + + @Test + public void itHasTheseBuilderMethods() throws MalformedURLException { + UserIdentity.builder() + .name("") + .displayName("") + .id(new ByteArray(new byte[]{})) + .icon(new URL("https://example.com")) + .icon(Optional.of(new URL("https://example.com"))) + ; + } + +} From 34414040d9cadfa5b0deaddbab904aee11fa8284 Mon Sep 17 00:00:00 2001 From: Emil Lundbergappid
).
+ *
+ * + * This value should be ignored because its behaviour is underspecified, see: https://github.com/w3c/webauthn/issues/1034. + *
+ * + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public ClientAssertionExtensionOutputsBuilder appid(Optionalappid
).
+ *
+ * + * This value should be ignored because its behaviour is underspecified, see: https://github.com/w3c/webauthn/issues/1034. + *
+ * + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public ClientAssertionExtensionOutputsBuilder appid(boolean appid) { + return this.appid(Optional.of(appid)); + } + } } diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/data/ClientAssertionExtensionOutputsTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/ClientAssertionExtensionOutputsTest.java new file mode 100644 index 000000000..84f3b9d1e --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/ClientAssertionExtensionOutputsTest.java @@ -0,0 +1,16 @@ +package com.yubico.webauthn.data; + +import java.util.Optional; +import org.junit.Test; + +public class ClientAssertionExtensionOutputsTest { + + @Test + public void itHasTheseBuilderMethods() { + ClientAssertionExtensionOutputs.builder() + .appid(false) + .appid(Optional.of(false)) + .build(); + } + +} diff --git a/webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala b/webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala index 6fea04aa9..d20074b18 100644 --- a/webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala +++ b/webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala @@ -780,7 +780,7 @@ class RelyingPartyAssertionSpec extends FunSpec with Matchers with GeneratorDriv describe("client extension outputs in clientExtensionResults are as expected, considering the client extension input values that were given as the extensions option in the get() call. In particular, any extension identifier values in the clientExtensionResults MUST be also be present as extension identifier values in the extensions member of options, i.e., no extensions are present that were not requested. In the general case, the meaning of \"are as expected\" is specific to the Relying Party and which extensions are in use.") { it("Fails if clientExtensionResults is not a subset of the extensions requested by the Relying Party.") { val extensionInputs = AssertionExtensionInputs.builder().build() - val clientExtensionOutputs = ClientAssertionExtensionOutputs.builder().appid(Optional.of(true)).build() + val clientExtensionOutputs = ClientAssertionExtensionOutputs.builder().appid(true).build() // forAll(unrequestedAssertionExtensions, minSuccessful(1)) { case (extensionInputs, clientExtensionOutputs) => val steps = finishAssertion( From 68b801caf6b4f77122f68d78e7575f716948ad4d Mon Sep 17 00:00:00 2001 From: Emil Lundbergappid
).
@@ -97,7 +97,7 @@ public static class ClientAssertionExtensionOutputsBuilder {
* @see §10.1. FIDO AppID Extension
* (appid)
*/
- public ClientAssertionExtensionOutputsBuilder appid(Optionaltrue
, the
@@ -82,4 +81,26 @@ private AuthenticatorSelectionCriteria(
this(Optional.ofNullable(authenticatorAttachment), requireResidentKey, userVerification);
}
+ public static class AuthenticatorSelectionCriteriaBuilder {
+ private @NonNull Optionalappid
).
+ *
+ * + * This extension allows WebAuthn Relying Parties that have previously registered a credential using the legacy FIDO + * JavaScript APIs to request an assertion. The FIDO APIs use an alternative identifier for Relying Parties called + * an AppID, + * and any credentials created using those APIs will be scoped to that identifier. Without this extension, they + * would need to be re-registered in order to be scoped to an RP ID. + *
+ *+ * This extension does not allow FIDO-compatible credentials to be created. Thus, credentials created with WebAuthn + * are not backwards compatible with the FIDO JavaScript APIs. + *
+ * + *+ * {@link RelyingParty#startAssertion(StartAssertionOptions)} sets this extension input automatically if the {@link + * RelyingParty.RelyingPartyBuilder#appId(Optional)} parameter is given when constructing the {@link RelyingParty} + * instance. + *
+ * + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public AssertionExtensionInputsBuilder appid(Optionalappid
).
+ *
+ * + * This extension allows WebAuthn Relying Parties that have previously registered a credential using the legacy FIDO + * JavaScript APIs to request an assertion. The FIDO APIs use an alternative identifier for Relying Parties called + * an AppID, + * and any credentials created using those APIs will be scoped to that identifier. Without this extension, they + * would need to be re-registered in order to be scoped to an RP ID. + *
+ *+ * This extension does not allow FIDO-compatible credentials to be created. Thus, credentials created with WebAuthn + * are not backwards compatible with the FIDO JavaScript APIs. + *
+ * + *+ * {@link RelyingParty#startAssertion(StartAssertionOptions)} sets this extension input automatically if the {@link + * RelyingParty.RelyingPartyBuilder#appId(Optional)} parameter is given when constructing the {@link RelyingParty} + * instance. + *
+ * + * @see §10.1. FIDO AppID Extension + * (appid) + */ + public AssertionExtensionInputsBuilder appid(AppId appid) { + return this.appid(Optional.of(appid)); + } + } } diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/data/AssertionExtensionInputsTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/AssertionExtensionInputsTest.java new file mode 100644 index 000000000..cfa211ce7 --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/data/AssertionExtensionInputsTest.java @@ -0,0 +1,18 @@ +package com.yubico.webauthn.data; + +import com.yubico.webauthn.extension.appid.AppId; +import com.yubico.webauthn.extension.appid.InvalidAppIdException; +import java.util.Optional; +import org.junit.Test; + +public class AssertionExtensionInputsTest { + + @Test + public void itHasTheseBuilderMethods() throws InvalidAppIdException { + AssertionExtensionInputs.builder() + .appid(new AppId("https://example.com")) + .appid(Optional.of(new AppId("https://example.com"))) + .build(); + } + +} From a7d413fca1f4e0e2cca140b5e450ababb3c70c4a Mon Sep 17 00:00:00 2001 From: Emil Lundberg+ * If this is absent, that implies a first-factor authentication operation - meaning identification of the user is + * deferred until after receiving the response from the client. + *
+ * + *+ * The default is empty (absent). + *
+ * + * @see Client-side-resident + * credential + */ + public StartAssertionOptionsBuilder username(@NonNull Optional+ * If this is absent, that implies a first-factor authentication operation - meaning identification of the user is + * deferred until after receiving the response from the client. + *
+ * + *+ * The default is empty (absent). + *
+ * + * @see Client-side-resident + * credential + */ + public StartAssertionOptionsBuilder username(@NonNull String username) { + return this.username(Optional.of(username)); + } + + /** + * The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this authentication operation. + *+ * The default is {@link UserVerificationRequirement#PREFERRED}. + *
+ */ + public StartAssertionOptionsBuilder userVerification(@NonNull Optional+ * The default is {@link UserVerificationRequirement#PREFERRED}. + *
+ */ + public StartAssertionOptionsBuilder userVerification(@NonNull UserVerificationRequirement userVerification) { + return this.userVerification(Optional.of(userVerification)); + } + } } diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AssertionExtensionInputs.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AssertionExtensionInputs.java index b629899af..3176a9301 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AssertionExtensionInputs.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AssertionExtensionInputs.java @@ -75,6 +75,7 @@ public class AssertionExtensionInputs implements ExtensionInputs { * @see §10.1. FIDO AppID Extension * (appid) */ + @NonNull private final Optional