Skip to content

Commit 424520f

Browse files
committed
Release 2.3.0
New features: - (Experimental) Added `authenticatorAttachment` property to response objects: - NOTE: Experimental features may receive breaking changes without a major version increase. - Added method `getAuthenticatorAttachment()` to `PublicKeyCredential` and corresponding builder method `authenticatorAttachment(AuthenticatorAttachment)`. - Added method `getAuthenticatorAttachment()` to `RegistrationResult` and `AssertionResult`, which echo `getAuthenticatorAttachment()` from the corresponding `PublicKeyCredential`. - Thanks to GitHub user luisgoncalves for the contribution, see #250 Other: - Fixed the README description of SemVer exceptions: `@Deprecated` features are still part of the public API unless they also have an `EXPERIMENTAL:` tag in JavaDoc. - Brought `com.yubico.webauthn` package JavaDoc up to date with new library features.
2 parents ab1fac4 + 640c5a4 commit 424520f

File tree

15 files changed

+382
-184
lines changed

15 files changed

+382
-184
lines changed

NEWS

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
== Version 2.3.0 ==
2+
3+
New features:
4+
5+
* (Experimental) Added `authenticatorAttachment` property to response objects:
6+
** NOTE: Experimental features may receive breaking changes without a major
7+
version increase.
8+
** Added method `getAuthenticatorAttachment()` to `PublicKeyCredential` and
9+
corresponding builder method
10+
`authenticatorAttachment(AuthenticatorAttachment)`.
11+
** Added method `getAuthenticatorAttachment()` to `RegistrationResult` and
12+
`AssertionResult`, which echo `getAuthenticatorAttachment()` from the
13+
corresponding `PublicKeyCredential`.
14+
** Thanks to GitHub user luisgoncalves for the contribution, see
15+
https://github.com/Yubico/java-webauthn-server/pull/250
16+
17+
Other:
18+
19+
* Fixed the README description of SemVer exceptions: `@Deprecated` features are
20+
still part of the public API unless they also have an `EXPERIMENTAL:` tag in
21+
JavaDoc.
22+
* Brought `com.yubico.webauthn` package JavaDoc up to date with new library
23+
features.
24+
25+
126
== Version 2.2.0 ==
227

328
`webauthn-server-core`:
@@ -44,7 +69,7 @@ Fixes:
4469

4570
Fixes:
4671

47-
* Improved documentation of guaranteed provided by `FidoMetadataDownloader` and
72+
* Improved documentation of guarantees provided by `FidoMetadataDownloader` and
4873
required of its parameters.
4974

5075

README

Lines changed: 76 additions & 74 deletions
Large diffs are not rendered by default.

build.gradle

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ buildscript {
44
}
55
dependencies {
66
classpath 'com.cinnober.gradle:semver-git:2.5.0'
7-
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.11.0'
7+
classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.12.1'
88
classpath 'io.github.cosmicsilence:gradle-scalafix:0.1.13'
99
}
1010
}
@@ -257,6 +257,19 @@ subprojects { project ->
257257

258258
}
259259

260+
// Configure cross-links from webauthn-server-attestation JavaDoc to core JavaDoc
261+
project(':webauthn-server-attestation').tasks.javadoc {
262+
var coreProj = project(':webauthn-server-core')
263+
var coreJavadoc = coreProj.tasks.javadoc
264+
inputs.files coreJavadoc.outputs.files
265+
266+
// These links won't work locally, but they will work on developers.yubico.com
267+
options.linksOffline("../../webauthn-server-core/${coreProj.version}", "${coreJavadoc.destinationDir}")
268+
269+
// Use this instead for local testing
270+
//options.linksOffline("file://${coreJavadoc.destinationDir}", "${coreJavadoc.destinationDir}")
271+
}
272+
260273
// The root project has no sources, but the dependency platform also needs to be published as an artifact
261274
// See https://docs.gradle.org/current/userguide/java_platform_plugin.html
262275
// See https://github.com/Yubico/java-webauthn-server/issues/93#issuecomment-822806951

buildSrc/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ repositories {
77
}
88

99
dependencies {
10-
implementation("info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.9.0")
10+
implementation("info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.9.11")
1111
}

webauthn-server-attestation/README.adoc

Lines changed: 51 additions & 50 deletions
Large diffs are not rendered by default.

webauthn-server-core/src/main/java/com/yubico/webauthn/AssertionResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.fasterxml.jackson.annotation.JsonProperty;
3030
import com.yubico.webauthn.data.AuthenticatorAssertionExtensionOutputs;
3131
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
32+
import com.yubico.webauthn.data.AuthenticatorAttachment;
3233
import com.yubico.webauthn.data.AuthenticatorData;
3334
import com.yubico.webauthn.data.ByteArray;
3435
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
@@ -195,6 +196,20 @@ public boolean isBackedUp() {
195196
return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().BS;
196197
}
197198

199+
/**
200+
* The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator
201+
* attachment modality</a> in effect at the time the asserted credential was used.
202+
*
203+
* @see PublicKeyCredential#getAuthenticatorAttachment()
204+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
205+
* the standard matures.
206+
*/
207+
@Deprecated
208+
@JsonIgnore
209+
public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() {
210+
return credentialResponse.getAuthenticatorAttachment();
211+
}
212+
198213
/**
199214
* The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature
200215
* count</a> of the credential used for the assertion.

webauthn-server-core/src/main/java/com/yubico/webauthn/RegistrationResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.yubico.webauthn.RelyingParty.RelyingPartyBuilder;
3232
import com.yubico.webauthn.attestation.AttestationTrustSource;
3333
import com.yubico.webauthn.data.AttestationType;
34+
import com.yubico.webauthn.data.AuthenticatorAttachment;
3435
import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
3536
import com.yubico.webauthn.data.AuthenticatorRegistrationExtensionOutputs;
3637
import com.yubico.webauthn.data.ByteArray;
@@ -175,6 +176,20 @@ public boolean isBackedUp() {
175176
return credential.getResponse().getParsedAuthenticatorData().getFlags().BS;
176177
}
177178

179+
/**
180+
* The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator
181+
* attachment modality</a> in effect at the time the credential was created.
182+
*
183+
* @see PublicKeyCredential#getAuthenticatorAttachment()
184+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
185+
* the standard matures.
186+
*/
187+
@Deprecated
188+
@JsonIgnore
189+
public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() {
190+
return credential.getAuthenticatorAttachment();
191+
}
192+
178193
/**
179194
* The signature count returned with the created credential.
180195
*

webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAttachment.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import com.fasterxml.jackson.annotation.JsonCreator;
2828
import com.fasterxml.jackson.annotation.JsonValue;
29-
import java.util.Optional;
3029
import java.util.stream.Stream;
3130
import lombok.AllArgsConstructor;
3231
import lombok.Getter;
@@ -73,18 +72,8 @@ public enum AuthenticatorAttachment {
7372

7473
@JsonValue @Getter @NonNull private final String value;
7574

76-
private static Optional<AuthenticatorAttachment> fromString(@NonNull String value) {
77-
return Stream.of(values()).filter(v -> v.value.equals(value)).findAny();
78-
}
79-
8075
@JsonCreator
8176
private static AuthenticatorAttachment fromJsonString(@NonNull String value) {
82-
return fromString(value)
83-
.orElseThrow(
84-
() ->
85-
new IllegalArgumentException(
86-
String.format(
87-
"Unknown %s value: %s",
88-
AuthenticatorAttachment.class.getSimpleName(), value)));
77+
return Stream.of(values()).filter(v -> v.value.equals(value)).findAny().orElse(null);
8978
}
9079
}

webauthn-server-core/src/main/java/com/yubico/webauthn/data/PublicKeyCredential.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@
2525
package com.yubico.webauthn.data;
2626

2727
import com.fasterxml.jackson.annotation.JsonCreator;
28-
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2928
import com.fasterxml.jackson.annotation.JsonProperty;
3029
import com.fasterxml.jackson.core.type.TypeReference;
3130
import com.yubico.internal.util.JacksonCodecs;
31+
import com.yubico.webauthn.AssertionResult;
32+
import com.yubico.webauthn.FinishAssertionOptions;
33+
import com.yubico.webauthn.FinishRegistrationOptions;
34+
import com.yubico.webauthn.RegistrationResult;
35+
import com.yubico.webauthn.RelyingParty;
3236
import java.io.IOException;
37+
import java.util.Optional;
3338
import lombok.AllArgsConstructor;
3439
import lombok.Builder;
3540
import lombok.NonNull;
@@ -46,7 +51,6 @@
4651
*/
4752
@Value
4853
@Builder(toBuilder = true)
49-
@JsonIgnoreProperties({"authenticatorAttachment"})
5054
public class PublicKeyCredential<
5155
A extends AuthenticatorResponse, B extends ClientExtensionOutputs> {
5256

@@ -68,6 +72,8 @@ public class PublicKeyCredential<
6872
*/
6973
@NonNull private final A response;
7074

75+
private final AuthenticatorAttachment authenticatorAttachment;
76+
7177
/**
7278
* A map containing extension identifier → client extension output entries produced by the
7379
* extension’s client extension processing.
@@ -83,6 +89,7 @@ private PublicKeyCredential(
8389
@JsonProperty("id") ByteArray id,
8490
@JsonProperty("rawId") ByteArray rawId,
8591
@NonNull @JsonProperty("response") A response,
92+
@JsonProperty("authenticatorAttachment") AuthenticatorAttachment authenticatorAttachment,
8693
@NonNull @JsonProperty("clientExtensionResults") B clientExtensionResults,
8794
@NonNull @JsonProperty("type") PublicKeyCredentialType type) {
8895
if (id == null && rawId == null) {
@@ -95,16 +102,41 @@ private PublicKeyCredential(
95102

96103
this.id = id == null ? rawId : id;
97104
this.response = response;
105+
this.authenticatorAttachment = authenticatorAttachment;
98106
this.clientExtensionResults = clientExtensionResults;
99107
this.type = type;
100108
}
101109

102110
private PublicKeyCredential(
103111
ByteArray id,
104112
@NonNull A response,
113+
AuthenticatorAttachment authenticatorAttachment,
105114
@NonNull B clientExtensionResults,
106115
@NonNull PublicKeyCredentialType type) {
107-
this(id, null, response, clientExtensionResults, type);
116+
this(id, null, response, authenticatorAttachment, clientExtensionResults, type);
117+
}
118+
119+
/**
120+
* The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator
121+
* attachment modality</a> in effect at the time the credential was created or used.
122+
*
123+
* <p>If parsed from JSON, this will be present if and only if the input was a valid value of
124+
* {@link AuthenticatorAttachment}.
125+
*
126+
* <p>The same value will also be available via {@link
127+
* RegistrationResult#getAuthenticatorAttachment()} or {@link
128+
* AssertionResult#getAuthenticatorAttachment()} on the result from {@link
129+
* RelyingParty#finishRegistration(FinishRegistrationOptions)} or {@link
130+
* RelyingParty#finishAssertion(FinishAssertionOptions)}.
131+
*
132+
* @see RegistrationResult#getAuthenticatorAttachment()
133+
* @see AssertionResult#getAuthenticatorAttachment()
134+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
135+
* the standard matures.
136+
*/
137+
@Deprecated
138+
public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() {
139+
return Optional.ofNullable(authenticatorAttachment);
108140
}
109141

110142
public static <A extends AuthenticatorResponse, B extends ClientExtensionOutputs>

webauthn-server-core/src/main/java/com/yubico/webauthn/package-info.java

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* This package makes up the public API of the webauthn-server-core library.
2+
* This package and its subpackages make up the public API of the webauthn-server-core library.
33
*
44
* <p>The main entry point is the {@link com.yubico.webauthn.RelyingParty} class. It provides
55
* methods for generating inputs to the <code>navigator.credentials.create()</code> and <code>
@@ -82,15 +82,22 @@
8282
* com.yubico.webauthn.data.PublicKeyCredentialCreationOptions} which can be serialized to JSON and
8383
* passed as the <code>publicKey</code> argument to <code>navigator.credentials.create()</code>. You
8484
* can use the {@link com.yubico.webauthn.data.PublicKeyCredentialCreationOptions#toBuilder()
85-
* toBuilder()} method to make any modifications you need. You should store this in temporary
86-
* storage so that it can later be passed as an argument to {@link
87-
* com.yubico.webauthn.RelyingParty#finishRegistration(FinishRegistrationOptions)}.
88-
*
89-
* <p>After receiving the response from the client, construct a {@link
90-
* com.yubico.webauthn.data.PublicKeyCredential}&lt;{@link
91-
* com.yubico.webauthn.data.AuthenticatorAttestationResponse}, {@link
92-
* com.yubico.webauthn.data.ClientRegistrationExtensionOutputs}&gt; from the response and wrap that
93-
* in a {@link com.yubico.webauthn.FinishRegistrationOptions} along with the {@link
85+
* toBuilder()} method to make any modifications you need, then the {@link
86+
* com.yubico.webauthn.data.PublicKeyCredentialCreationOptions#toCredentialsCreateJson()} method is
87+
* suitable for converting the value to JSON to send to the client.
88+
*
89+
* <p>You should also store the {@link com.yubico.webauthn.data.PublicKeyCredentialCreationOptions}
90+
* object in temporary storage so that it can later be passed as an argument to {@link
91+
* com.yubico.webauthn.RelyingParty#finishRegistration(FinishRegistrationOptions)}. If you need to
92+
* serialize the object for storage, the {@link
93+
* com.yubico.webauthn.data.PublicKeyCredentialCreationOptions#toJson()} and {@link
94+
* com.yubico.webauthn.data.PublicKeyCredentialCreationOptions#fromJson(java.lang.String)} methods
95+
* are suitable for serializing to and from a string value.
96+
*
97+
* <p>After receiving the response from the client, use the {@link
98+
* com.yubico.webauthn.data.PublicKeyCredential#parseRegistrationResponseJson(java.lang.String)}
99+
* function to parse the response and wrap it in a {@link
100+
* com.yubico.webauthn.FinishRegistrationOptions} along with the {@link
94101
* com.yubico.webauthn.data.PublicKeyCredentialCreationOptions} used to initiate the request. Pass
95102
* that as the argument to {@link
96103
* com.yubico.webauthn.RelyingParty#finishRegistration(FinishRegistrationOptions)}, which will
@@ -107,14 +114,19 @@
107114
* com.yubico.webauthn.RegistrationResult#getPublicKeyCose() publicKeyCose} as a new
108115
* credential for the user. The {@link com.yubico.webauthn.CredentialRepository} will need to
109116
* look these up for authentication.
117+
* <li>Store the {@link com.yubico.webauthn.RegistrationResult#getSignatureCount() signature
118+
* counter} value in the new credential. If available, this will be used in future
119+
* authentication ceremonies do detect authenticator cloning.
120+
* <li>Optionally, store the {@link com.yubico.webauthn.RegistrationResult#isDiscoverable()
121+
* isDiscoverable} flag, if present, in the new credential. This may help you determine which
122+
* user interaction flows are possible with which credential.
110123
* <li>If you care about authenticator attestation, check that the {@link
111124
* com.yubico.webauthn.RegistrationResult#isAttestationTrusted() attestationTrusted} field
112125
* satisfies your attestation policy. For this you will likely need to configure the {@link
113126
* com.yubico.webauthn.RelyingParty.RelyingPartyBuilder#attestationTrustSource(com.yubico.webauthn.attestation.AttestationTrustSource)
114127
* attestationTrustSource} setting on your {@link com.yubico.webauthn.RelyingParty} instance.
115-
* You may also want to consult some external data source to verify the authenticity of the
116-
* {@link com.yubico.webauthn.data.AuthenticatorAttestationResponse#getAttestationObject()
117-
* attestation object}.
128+
* See also the <code>webauthn-server-attestation</code> for an implementation of such an
129+
* attestation trust and metadata source.
118130
* <li>If you care about authenticator attestation, it is recommended to also store the raw {@link
119131
* com.yubico.webauthn.data.AuthenticatorAttestationResponse#getAttestationObject()
120132
* attestation object} as part of the credential. This enables you to retroactively inspect
@@ -130,11 +142,13 @@
130142
* com.yubico.webauthn.RelyingParty#startAssertion(StartAssertionOptions)}. The main parameter you
131143
* need to set here is the {@link
132144
* com.yubico.webauthn.StartAssertionOptions.StartAssertionOptionsBuilder#username(java.util.Optional)
133-
* username} of the user to authenticate, but even this parameter is optional. If the username is
134-
* not set, then the {@link
145+
* username} or {@link
146+
* com.yubico.webauthn.StartAssertionOptions.StartAssertionOptionsBuilder#userHandle(java.util.Optional)
147+
* user handle} of the user to authenticate, but even these parameters are optional. If neither is
148+
* set, then the {@link
135149
* com.yubico.webauthn.data.PublicKeyCredentialRequestOptions#getAllowCredentials()
136-
* allowCredentials} parameter will not be set. This which means the user must use a <a
137-
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-resident
150+
* allowCredentials} parameter will not be set. This means the user must use a <a
151+
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable
138152
* credential</a> to authenticate; also known as "first-factor authentication". This use case has
139153
* both advantages and disadvantages; see the Web Authentication specification for an extended
140154
* discussion of this.
@@ -143,18 +157,21 @@
143157
* startAssertion} method returns an {@link com.yubico.webauthn.AssertionRequest} containing the
144158
* username, if any, and a {@link com.yubico.webauthn.data.PublicKeyCredentialRequestOptions}
145159
* instance which can be serialized to JSON and passed as the <code>publicKey</code> argument to
146-
* <code>navigator.credentials.get()</code>. Again, store the {@link
147-
* com.yubico.webauthn.AssertionRequest} in temporary storage so it can be passed as an argument to
148-
* {@link
160+
* <code>navigator.credentials.get()</code>. Again, use {@link
161+
* com.yubico.webauthn.AssertionRequest#toBuilder()} to make any necessary changes, {@link
162+
* com.yubico.webauthn.AssertionRequest#toCredentialsGetJson()} to convert it to JSON for sending to
163+
* the client, and store the {@link com.yubico.webauthn.AssertionRequest} in temporary storage so it
164+
* can be passed as an argument to {@link
149165
* com.yubico.webauthn.RelyingParty#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)}.
150-
*
151-
* <p>After receiving the response from the client, construct a {@link
152-
* com.yubico.webauthn.data.PublicKeyCredential}&lt;{@link
153-
* com.yubico.webauthn.data.AuthenticatorAssertionResponse}, {@link
154-
* com.yubico.webauthn.data.ClientAssertionExtensionOutputs}&gt; from the response and wrap that in
155-
* a {@link com.yubico.webauthn.FinishAssertionOptions} along with the {@link
156-
* com.yubico.webauthn.AssertionRequest} used to initiate the request. Pass that as the argument to
157-
* {@link
166+
* Again, {@link com.yubico.webauthn.AssertionRequest#toJson()} and {@link
167+
* com.yubico.webauthn.AssertionRequest#fromJson(java.lang.String)} can be used to convert to and
168+
* from JSON for storage.
169+
*
170+
* <p>After receiving the response from the client, use {@link
171+
* com.yubico.webauthn.data.PublicKeyCredential#parseAssertionResponseJson(java.lang.String)} to
172+
* parse the response, then wrap that in a {@link com.yubico.webauthn.FinishAssertionOptions} along
173+
* with the {@link com.yubico.webauthn.AssertionRequest} used to initiate the request. Pass that as
174+
* the argument to {@link
158175
* com.yubico.webauthn.RelyingParty#finishAssertion(com.yubico.webauthn.FinishAssertionOptions)},
159176
* which will return an {@link com.yubico.webauthn.AssertionResult} if successful and throw an
160177
* exception if not. Regardless of whether it succeeds, you should remove the {@link

0 commit comments

Comments
 (0)