Skip to content

Commit a1a3e2f

Browse files
committed
Add credProps and authenticatorDisplayName to assertion extension outputs
1 parent 02a10f6 commit a1a3e2f

File tree

8 files changed

+193
-0
lines changed

8 files changed

+193
-0
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ New features:
5454
* (Experimental) Added property `credProps.authenticatorDisplayName`.
5555
** NOTE: Experimental features may receive breaking changes without a major
5656
version increase.
57+
* (Experimental) Added `credProps` extension to assertion extension outputs.
5758

5859

5960
== 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ public Optional<Boolean> isDiscoverable() {
381381
* href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
382382
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
383383
* (credProps)</a>
384+
* @see AssertionResult#getAuthenticatorDisplayName()
385+
* @see AssertionResultV2#getAuthenticatorDisplayName()
384386
* @see Extensions.CredentialProperties.CredentialPropertiesOutput#getAuthenticatorDisplayName()
385387
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
386388
* the standard matures.

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
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;
911
import com.yubico.webauthn.RegistrationResult;
1012
import com.yubico.webauthn.StartRegistrationOptions;
1113
import com.yubico.webauthn.extension.uvm.KeyProtectionType;
@@ -131,6 +133,8 @@ public Optional<Boolean> getRk() {
131133
* <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
132134
* (credProps)</a>
133135
* @see RegistrationResult#getAuthenticatorDisplayName()
136+
* @see AssertionResult#getAuthenticatorDisplayName()
137+
* @see AssertionResultV2#getAuthenticatorDisplayName()
134138
* @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change
135139
* as the standard matures.
136140
*/

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/RelyingPartyV2AssertionSpec.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
@@ -2920,6 +2921,55 @@ class RelyingPartyV2AssertionSpec
29202921
)
29212922
}
29222923
}
2924+
2925+
describe("exposes the credProps.authenticatorDisplayName extension output as getAuthenticatorDisplayName()") {
2926+
val pkcTemplate =
2927+
TestAuthenticator.createAssertion(
2928+
challenge =
2929+
request.getPublicKeyCredentialRequestOptions.getChallenge,
2930+
credentialKey = credentialKeypair,
2931+
credentialId = credential.getId,
2932+
)
2933+
2934+
it("""when set to "hej".""") {
2935+
val pkc = pkcTemplate.toBuilder
2936+
.clientExtensionResults(
2937+
pkcTemplate.getClientExtensionResults.toBuilder
2938+
.credProps(
2939+
CredentialPropertiesOutput
2940+
.builder()
2941+
.authenticatorDisplayName("hej")
2942+
.build()
2943+
)
2944+
.build()
2945+
)
2946+
.build()
2947+
val result = rp.finishAssertion(
2948+
FinishAssertionOptions
2949+
.builder()
2950+
.request(request)
2951+
.response(pkc)
2952+
.build()
2953+
)
2954+
2955+
result.getAuthenticatorDisplayName.toScala should equal(
2956+
Some("hej")
2957+
)
2958+
}
2959+
2960+
it("when not available.") {
2961+
val pkc = pkcTemplate
2962+
val result = rp.finishAssertion(
2963+
FinishAssertionOptions
2964+
.builder()
2965+
.request(request)
2966+
.response(pkc)
2967+
.build()
2968+
)
2969+
2970+
result.getAuthenticatorDisplayName.toScala should equal(None)
2971+
}
2972+
}
29232973
}
29242974
}
29252975
}

0 commit comments

Comments
 (0)