Skip to content

Commit 362f1b4

Browse files
authored
Merge pull request #363 from Yubico/credProps-authenticatorDisplayName
Added credProps.authenticatorDisplayName
2 parents b670381 + a1a3e2f commit 362f1b4

File tree

10 files changed

+309
-3
lines changed

10 files changed

+309
-3
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ New features:
5151
* (Experimental) Added property `RegisteredCredential.transports`.
5252
** NOTE: Experimental features may receive breaking changes without a major
5353
version increase.
54+
* (Experimental) Added property `credProps.authenticatorDisplayName`.
55+
** NOTE: Experimental features may receive breaking changes without a major
56+
version increase.
57+
* (Experimental) Added `credProps` extension to assertion extension outputs.
5458

5559

5660
== Version 2.5.2 ==

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.yubico.webauthn.data.AuthenticatorResponse;
3636
import com.yubico.webauthn.data.ByteArray;
3737
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
38+
import com.yubico.webauthn.data.Extensions;
3839
import com.yubico.webauthn.data.PublicKeyCredential;
3940
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
4041
import com.yubico.webauthn.data.UserIdentity;
@@ -281,4 +282,33 @@ public Optional<AuthenticatorAssertionExtensionOutputs> getAuthenticatorExtensio
281282
return AuthenticatorAssertionExtensionOutputs.fromAuthenticatorData(
282283
credentialResponse.getResponse().getParsedAuthenticatorData());
283284
}
285+
286+
/**
287+
* Retrieve a suitable nickname for this credential, if one is available. This MAY differ from
288+
* {@link RegistrationResult#getAuthenticatorDisplayName() the value returned during
289+
* registration}, if any. In that case the application may want to offer the user to update the
290+
* previously stored value, if any.
291+
*
292+
* <p>This returns the <code>authenticatorDisplayName</code> output from the <a
293+
* href="https://w3c.github.io/webauthn/#sctn-authenticator-credential-properties-extension">
294+
* <code>credProps</code></a> extension.
295+
*
296+
* @return A user-chosen or vendor-default display name for the credential, if available.
297+
* Otherwise empty.
298+
* @see <a
299+
* href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
300+
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
301+
* (credProps)</a>
302+
* @see RegistrationResult#getAuthenticatorDisplayName()
303+
* @see Extensions.CredentialProperties.CredentialPropertiesOutput#getAuthenticatorDisplayName()
304+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
305+
* the standard matures.
306+
*/
307+
@JsonIgnore
308+
@Deprecated
309+
public Optional<String> getAuthenticatorDisplayName() {
310+
return getClientExtensionOutputs()
311+
.flatMap(outputs -> outputs.getCredProps())
312+
.flatMap(credProps -> credProps.getAuthenticatorDisplayName());
313+
}
284314
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.yubico.webauthn.data.AuthenticatorResponse;
3636
import com.yubico.webauthn.data.ByteArray;
3737
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
38+
import com.yubico.webauthn.data.Extensions;
3839
import com.yubico.webauthn.data.PublicKeyCredential;
3940
import java.util.Optional;
4041
import lombok.AccessLevel;
@@ -243,4 +244,33 @@ public Optional<AuthenticatorAssertionExtensionOutputs> getAuthenticatorExtensio
243244
return AuthenticatorAssertionExtensionOutputs.fromAuthenticatorData(
244245
credentialResponse.getResponse().getParsedAuthenticatorData());
245246
}
247+
248+
/**
249+
* Retrieve a suitable nickname for this credential, if one is available. This MAY differ from
250+
* {@link RegistrationResult#getAuthenticatorDisplayName() the value returned during
251+
* registration}, if any. In that case the application may want to offer the user to update the
252+
* previously stored value, if any.
253+
*
254+
* <p>This returns the <code>authenticatorDisplayName</code> output from the <a
255+
* href="https://w3c.github.io/webauthn/#sctn-authenticator-credential-properties-extension">
256+
* <code>credProps</code></a> extension.
257+
*
258+
* @return A user-chosen or vendor-default display name for the credential, if available.
259+
* Otherwise empty.
260+
* @see <a
261+
* href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
262+
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
263+
* (credProps)</a>
264+
* @see RegistrationResult#getAuthenticatorDisplayName()
265+
* @see Extensions.CredentialProperties.CredentialPropertiesOutput#getAuthenticatorDisplayName()
266+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
267+
* the standard matures.
268+
*/
269+
@JsonIgnore
270+
@Deprecated
271+
public Optional<String> getAuthenticatorDisplayName() {
272+
return getClientExtensionOutputs()
273+
.flatMap(outputs -> outputs.getCredProps())
274+
.flatMap(credProps -> credProps.getAuthenticatorDisplayName());
275+
}
246276
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.yubico.webauthn.data.AuthenticatorResponse;
4040
import com.yubico.webauthn.data.ByteArray;
4141
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
42+
import com.yubico.webauthn.data.Extensions;
4243
import com.yubico.webauthn.data.PublicKeyCredential;
4344
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
4445
import java.io.IOException;
@@ -367,6 +368,33 @@ public Optional<Boolean> isDiscoverable() {
367368
.flatMap(credProps -> credProps.getRk());
368369
}
369370

371+
/**
372+
* Retrieve a suitable nickname for this credential, if one is available.
373+
*
374+
* <p>This returns the <code>authenticatorDisplayName</code> output from the <a
375+
* href="https://w3c.github.io/webauthn/#sctn-authenticator-credential-properties-extension">
376+
* <code>credProps</code></a> extension.
377+
*
378+
* @return A user-chosen or vendor-default display name for the credential, if available.
379+
* Otherwise empty.
380+
* @see <a
381+
* href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
382+
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
383+
* (credProps)</a>
384+
* @see AssertionResult#getAuthenticatorDisplayName()
385+
* @see AssertionResultV2#getAuthenticatorDisplayName()
386+
* @see Extensions.CredentialProperties.CredentialPropertiesOutput#getAuthenticatorDisplayName()
387+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
388+
* the standard matures.
389+
*/
390+
@JsonIgnore
391+
@Deprecated
392+
public Optional<String> getAuthenticatorDisplayName() {
393+
return getClientExtensionOutputs()
394+
.flatMap(outputs -> outputs.getCredProps())
395+
.flatMap(credProps -> credProps.getAuthenticatorDisplayName());
396+
}
397+
370398
/**
371399
* The <a
372400
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-trust-path">attestation

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,18 @@ public class ClientAssertionExtensionOutputs implements ClientExtensionOutputs {
6464
*/
6565
private final Boolean appid;
6666

67+
private final Extensions.CredentialProperties.CredentialPropertiesOutput credProps;
68+
6769
private final Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob;
6870

6971
@JsonCreator
7072
private ClientAssertionExtensionOutputs(
7173
@JsonProperty("appid") Boolean appid,
74+
@JsonProperty("credProps")
75+
Extensions.CredentialProperties.CredentialPropertiesOutput credProps,
7276
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob) {
7377
this.appid = appid;
78+
this.credProps = credProps;
7479
this.largeBlob = largeBlob;
7580
}
7681

@@ -81,6 +86,9 @@ public Set<String> getExtensionIds() {
8186
if (appid != null) {
8287
ids.add(Extensions.Appid.EXTENSION_ID);
8388
}
89+
if (credProps != null) {
90+
ids.add(Extensions.CredentialProperties.EXTENSION_ID);
91+
}
8492
if (largeBlob != null) {
8593
ids.add(Extensions.LargeBlob.EXTENSION_ID);
8694
}
@@ -100,6 +108,24 @@ public Optional<Boolean> getAppid() {
100108
return Optional.ofNullable(appid);
101109
}
102110

111+
/**
112+
* The extension output for the Credential Properties Extension (<code>credProps</code>), if any.
113+
*
114+
* <p>This value MAY be present but have all members empty if the extension was successfully
115+
* processed but no credential properties could be determined.
116+
*
117+
* @see com.yubico.webauthn.data.Extensions.CredentialProperties.CredentialPropertiesOutput
118+
* @see <a
119+
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4.
120+
* Credential Properties Extension (credProps)</a>
121+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
122+
* the standard matures.
123+
*/
124+
@Deprecated
125+
public Optional<Extensions.CredentialProperties.CredentialPropertiesOutput> getCredProps() {
126+
return Optional.ofNullable(credProps);
127+
}
128+
103129
/**
104130
* The extension output for the <a
105131
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">Large blob

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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import com.fasterxml.jackson.annotation.JsonValue;
77
import com.upokecenter.cbor.CBORObject;
88
import com.upokecenter.cbor.CBORType;
9+
import com.yubico.webauthn.AssertionResult;
10+
import com.yubico.webauthn.AssertionResultV2;
11+
import com.yubico.webauthn.RegistrationResult;
912
import com.yubico.webauthn.StartRegistrationOptions;
1013
import com.yubico.webauthn.extension.uvm.KeyProtectionType;
1114
import com.yubico.webauthn.extension.uvm.MatcherProtectionType;
@@ -71,9 +74,15 @@ public static class CredentialPropertiesOutput {
7174
@JsonProperty("rk")
7275
private final Boolean rk;
7376

77+
@JsonProperty("authenticatorDisplayName")
78+
private final String authenticatorDisplayName;
79+
7480
@JsonCreator
75-
private CredentialPropertiesOutput(@JsonProperty("rk") Boolean rk) {
81+
private CredentialPropertiesOutput(
82+
@JsonProperty("rk") Boolean rk,
83+
@JsonProperty("authenticatorDisplayName") String authenticatorDisplayName) {
7684
this.rk = rk;
85+
this.authenticatorDisplayName = authenticatorDisplayName;
7786
}
7887

7988
/**
@@ -105,6 +114,34 @@ private CredentialPropertiesOutput(@JsonProperty("rk") Boolean rk) {
105114
public Optional<Boolean> getRk() {
106115
return Optional.ofNullable(rk);
107116
}
117+
118+
/**
119+
* This OPTIONAL property is a human-palatable description of the credential's managing
120+
* authenticator, chosen by the user.
121+
*
122+
* <p>If the application supports setting "nicknames" for registered credentials, then this
123+
* value may be a suitable default value for such a nickname.
124+
*
125+
* <p>In an authentication ceremony, if this value is different from the stored nickname, then
126+
* the application may want to offer the user to update the stored nickname to match this
127+
* value.
128+
*
129+
* @return A user-chosen or vendor-default display name for the credential, if available.
130+
* Otherwise empty.
131+
* @see <a
132+
* href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
133+
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
134+
* (credProps)</a>
135+
* @see RegistrationResult#getAuthenticatorDisplayName()
136+
* @see AssertionResult#getAuthenticatorDisplayName()
137+
* @see AssertionResultV2#getAuthenticatorDisplayName()
138+
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change
139+
* as the standard matures.
140+
*/
141+
@Deprecated
142+
public Optional<String> getAuthenticatorDisplayName() {
143+
return Optional.ofNullable(authenticatorDisplayName);
144+
}
108145
}
109146
}
110147

webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.yubico.webauthn.data.AuthenticatorTransport
3838
import com.yubico.webauthn.data.ByteArray
3939
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs
4040
import com.yubico.webauthn.data.CollectedClientData
41+
import com.yubico.webauthn.data.Extensions.CredentialProperties.CredentialPropertiesOutput
4142
import com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobAuthenticationInput
4243
import com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobAuthenticationOutput
4344
import com.yubico.webauthn.data.Extensions.Uvm.UvmEntry
@@ -2845,6 +2846,55 @@ class RelyingPartyAssertionSpec
28452846
)
28462847
}
28472848
}
2849+
2850+
describe("exposes the credProps.authenticatorDisplayName extension output as getAuthenticatorDisplayName()") {
2851+
val pkcTemplate =
2852+
TestAuthenticator.createAssertion(
2853+
challenge =
2854+
request.getPublicKeyCredentialRequestOptions.getChallenge,
2855+
credentialKey = credentialKeypair,
2856+
credentialId = credential.getId,
2857+
)
2858+
2859+
it("""when set to "hej".""") {
2860+
val pkc = pkcTemplate.toBuilder
2861+
.clientExtensionResults(
2862+
pkcTemplate.getClientExtensionResults.toBuilder
2863+
.credProps(
2864+
CredentialPropertiesOutput
2865+
.builder()
2866+
.authenticatorDisplayName("hej")
2867+
.build()
2868+
)
2869+
.build()
2870+
)
2871+
.build()
2872+
val result = rp.finishAssertion(
2873+
FinishAssertionOptions
2874+
.builder()
2875+
.request(request)
2876+
.response(pkc)
2877+
.build()
2878+
)
2879+
2880+
result.getAuthenticatorDisplayName.toScala should equal(
2881+
Some("hej")
2882+
)
2883+
}
2884+
2885+
it("when not available.") {
2886+
val pkc = pkcTemplate
2887+
val result = rp.finishAssertion(
2888+
FinishAssertionOptions
2889+
.builder()
2890+
.request(request)
2891+
.response(pkc)
2892+
.build()
2893+
)
2894+
2895+
result.getAuthenticatorDisplayName.toScala should equal(None)
2896+
}
2897+
}
28482898
}
28492899
}
28502900
}

webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyRegistrationSpec.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4289,6 +4289,51 @@ class RelyingPartyRegistrationSpec
42894289
}
42904290
}
42914291

4292+
describe("expose the credProps.authenticatorDisplayName extension output as RegistrationResult.getAuthenticatorDisplayName()") {
4293+
val testDataBase = RegistrationTestData.Packed.BasicAttestation
4294+
val testData = testDataBase.copy(requestedExtensions =
4295+
testDataBase.request.getExtensions.toBuilder.credProps().build()
4296+
)
4297+
4298+
it("""when set to "hej".""") {
4299+
val result = rp.finishRegistration(
4300+
FinishRegistrationOptions
4301+
.builder()
4302+
.request(testData.request)
4303+
.response(
4304+
testData.response.toBuilder
4305+
.clientExtensionResults(
4306+
ClientRegistrationExtensionOutputs
4307+
.builder()
4308+
.credProps(
4309+
CredentialPropertiesOutput
4310+
.builder()
4311+
.authenticatorDisplayName("hej")
4312+
.build()
4313+
)
4314+
.build()
4315+
)
4316+
.build()
4317+
)
4318+
.build()
4319+
)
4320+
4321+
result.getAuthenticatorDisplayName.toScala should equal(Some("hej"))
4322+
}
4323+
4324+
it("when not available.") {
4325+
val result = rp.finishRegistration(
4326+
FinishRegistrationOptions
4327+
.builder()
4328+
.request(testData.request)
4329+
.response(testData.response)
4330+
.build()
4331+
)
4332+
4333+
result.getAuthenticatorDisplayName.toScala should equal(None)
4334+
}
4335+
}
4336+
42924337
describe("support the largeBlob extension") {
42934338
it("being enabled at registration time.") {
42944339
val testData = RegistrationTestData.Packed.BasicAttestation

0 commit comments

Comments
 (0)