diff --git a/NEWS b/NEWS
index bce20dc26..4b68b0b29 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,19 @@
+== Version 1.1.0 ==
+
+Changed behaviours:
+
+* `AssertionExtensionInputsBuilder.appid(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 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)); + } } } diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionOptions.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionOptions.java index 59a7b7253..4e0064081 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionOptions.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/FinishAssertionOptions.java @@ -61,14 +61,15 @@ public class FinishAssertionOptions { * @see The Token Binding Protocol Version 1.0 */ @NonNull - @Builder.Default - private final 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/main/java/com/yubico/webauthn/StartAssertionOptions.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/StartAssertionOptions.java index 5aac5060b..5d958f020 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/StartAssertionOptions.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/StartAssertionOptions.java @@ -54,8 +54,7 @@ public class StartAssertionOptions { * credential */ @NonNull - @Builder.Default - private final 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 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/StartRegistrationOptions.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/StartRegistrationOptions.java index 086d16420..3660c86c9 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/StartRegistrationOptions.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/StartRegistrationOptions.java @@ -49,8 +49,7 @@ public class StartRegistrationOptions { * Constraints on what kind of authenticator the user is allowed to use to create the credential. */ @NonNull - @Builder.Default - private final 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(@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(@NonNull AppId appid) { + return this.appid(Optional.of(appid)); + } + } } diff --git a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAssertionResponse.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAssertionResponse.java index 27dcd29b2..471d4faa4 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAssertionResponse.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAssertionResponse.java @@ -133,6 +133,25 @@ public AuthenticatorAssertionResponseBuilder signature(ByteArray signature) { } } } + + /** + * The user handle returned from the authenticator, or empty if the authenticator did not return a user handle. See + * §6.3.3 The authenticatorGetAssertion + * Operation. + */ + public AuthenticatorAssertionResponseBuilder userHandle(@NonNull Optionaltrue
, the
@@ -82,4 +81,26 @@ private AuthenticatorSelectionCriteria(
this(Optional.ofNullable(authenticatorAttachment), requireResidentKey, userVerification);
}
+ public static class AuthenticatorSelectionCriteriaBuilder {
+ private @NonNull 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(@NonNull 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/main/java/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.java index ce3e45715..8abadc09b 100644 --- a/webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.java +++ b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredentialCreationOptions.java @@ -87,8 +87,7 @@ public class PublicKeyCredentialCreationOptions { * and MAY be overridden by the client. */ @NonNull - @Builder.Default - private final Optional+ * 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 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. + *
+ */ @NonNull - @Builder.Default @Getter(onMethod = @__({ @Override })) - private final 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 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/main/java/com/yubico/webauthn/data/UserIdentity.java b/webauthn-server-core/src/main/java/com/yubico/webauthn/data/UserIdentity.java index 7cf55949f..2eea7716c 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,10 +113,18 @@ 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 })) - private final 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 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/FinishAssertionOptionsTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/FinishAssertionOptionsTest.java new file mode 100644 index 000000000..85a3fe6ba --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/FinishAssertionOptionsTest.java @@ -0,0 +1,26 @@ +package com.yubico.webauthn; + +import com.yubico.webauthn.data.ByteArray; +import com.yubico.webauthn.data.exception.HexException; +import java.util.Optional; +import org.junit.Test; + +public class FinishAssertionOptionsTest { + + @Test(expected = NullPointerException.class) + public void itHasANonOptionalCallerTokenBindingIdMethod() throws HexException { + FinishAssertionOptions.builder() + .request(null) + .response(null) + .callerTokenBindingId(ByteArray.fromHex("aa")); + } + + @Test(expected = NullPointerException.class) + public void itHasAnOptionalCallerTokenBindingIdMethod() throws HexException { + FinishAssertionOptions.builder() + .request(null) + .response(null) + .callerTokenBindingId(Optional.of(ByteArray.fromHex("aa"))); + } + +} diff --git a/webauthn-server-core/src/test/java/com/yubico/webauthn/FinishRegistrationOptionsTest.java b/webauthn-server-core/src/test/java/com/yubico/webauthn/FinishRegistrationOptionsTest.java new file mode 100644 index 000000000..c6fe88494 --- /dev/null +++ b/webauthn-server-core/src/test/java/com/yubico/webauthn/FinishRegistrationOptionsTest.java @@ -0,0 +1,26 @@ +package com.yubico.webauthn; + +import com.yubico.webauthn.data.ByteArray; +import com.yubico.webauthn.data.exception.HexException; +import java.util.Optional; +import org.junit.Test; + +public class FinishRegistrationOptionsTest { + + @Test(expected = NullPointerException.class) + public void itHasANonOptionalCallerTokenBindingIdMethod() throws HexException { + FinishRegistrationOptions.builder() + .request(null) + .response(null) + .callerTokenBindingId(ByteArray.fromHex("aa")); + } + + @Test(expected = NullPointerException.class) + public void itHasAnOptionalCallerTokenBindingIdMethod() throws HexException { + FinishAssertionOptions.builder() + .request(null) + .response(null) + .callerTokenBindingId(Optional.of(ByteArray.fromHex("aa"))); + } + +} 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(List