Skip to content

Replace in-field string metadata with resource-level default fields #2280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
110 changes: 85 additions & 25 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ spec: RFC8610; urlPrefix: https://tools.ietf.org/html/rfc8610
type: dfn
text: group sockets; url: section-3.9

spec: String-Meta; urlPrefix: https://www.w3.org/TR/2024/NOTE-string-meta-20241017
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment:

I understand this is the i18n wg proposal, but this approach feels strictly worse, since authenticators have to support it for browsers to take advantage of it.

type: dfn
text: string direction; url: dfn-string-direction
text: resource-wide default; url: dfn-resource-wide-default
</pre> <!-- class=anchors -->

<!-- L128 spec:webappsec-credential-management-1; type:dictionary; for:/; text:CredentialRequestOptions -->
Expand Down Expand Up @@ -1768,6 +1772,9 @@ When this method is invoked, the user agent MUST execute the following algorithm

1. If the length of <code>|pkOptions|.{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialUserEntity/id}}</code> is not between 1 and 64 bytes (inclusive) then throw a {{TypeError}}.

1. If <code>|pkOptions|.{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialUserEntity/language}}</code> is present
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we require language and direction be present if the other one is? i.e. if language is present, direction cannot be undefined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, String-Meta specifically describes these as separate values. It doesn't look like either should require the other.

and is not a [=well-formed language tag=], throw a "{{SyntaxError}}" {{DOMException}}.
Comment on lines +1775 to +1776

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final thought: this requirement feels like it might push implementations that don't support the language field to do extra work. The field is optional and supporting the value is optional, but this clearly says that implementations need to check the tag's structure if the field is present.

String-Meta recommends but does not require that you emit an error here. I'd suggest handling language similar to direction and ignoring the field if the tag is not well-formed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean, but I'm still more inclined to keep the error in the interest of "fail faster". Of course no error will be thrown here if the client implementation predates WebAuthn L3, so there will always be that possibility, but I still think that clients that know the field exists should verify its well-formedness when present - even if the client otherwise ignores the value.

I think this position is also supported by the W3C priority of consituencies: "[...] the needs of web page authors [...] come before the needs of user agent implementors, [...]".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebAuthn typically raises TypeError's when there's an issue with a malformed value, as documented in L3:

We should probably maintain this behavior for this exception too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I see what you mean, but TypeError is not semantically correct for this error case. The user.id error ideally should have been a SyntaxError too, but that's probably too late to change now. SyntaxError is also used by the PRF extension when base64 decoding fails. So I'd prefer to keep this as SyntaxError rather than TypeError.


1. Let |callerOrigin| be {{PublicKeyCredential/[CREATE-METHOD]/origin}}. If |callerOrigin| is an [=opaque origin=], throw a "{{NotAllowedError}}" {{DOMException}}.

1. Let |effectiveDomain| be the |callerOrigin|'s [=effective domain=].
Expand Down Expand Up @@ -2225,6 +2232,10 @@ The following {{DOMException}} exceptions can be raised:
the [=client=] does not support [[#sctn-related-origins|related origin requests]]
or the [$related origins validation procedure$] failed.

: {{SyntaxError}}
:: <code>{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialUserEntity/language}}</code>
was present and was not a [=well-formed language tag=].

Comment on lines +2235 to +2238
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we agree to keep things consistent with TypeError as I call out above, we'll want to take this out.

: {{NotAllowedError}}
:: A catch-all error covering a wide range of possible reasons,
including common ones like the user canceling out of the ceremony.
Expand Down Expand Up @@ -2477,7 +2488,8 @@ When this method is invoked, the user agent MUST execute the following algorithm

1. Prompt the user to optionally select a [=DiscoverableCredentialMetadata=] from |silentlyDiscoveredCredentials|.
The prompt SHOULD display values from the [=DiscoverableCredentialMetadata/otherUI=] of each [=DiscoverableCredentialMetadata=],
such as {{PublicKeyCredentialEntity/name}} and {{PublicKeyCredentialUserEntity/displayName}}.
such as {{PublicKeyCredentialEntity/name}} and {{PublicKeyCredentialUserEntity/displayName}},
taking into account {{PublicKeyCredentialUserEntity/language}} and {{PublicKeyCredentialUserEntity/direction}} if available.

Let |credentialMetadata| be the [=DiscoverableCredentialMetadata=] chosen by the user, if any.

Expand Down Expand Up @@ -2927,6 +2939,8 @@ value and terminate the operation.
required Base64URLString id;
required DOMString name;
required DOMString displayName;
DOMString language;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it decided whether we needed language or not?

DOMString direction;
};

dictionary PublicKeyCredentialDescriptorJSON {
Expand Down Expand Up @@ -3007,6 +3021,8 @@ value and terminate the operation.
required Base64URLString userId;
required DOMString name;
required DOMString displayName;
DOMString language;
DOMString direction;
};
</xmp>

Expand Down Expand Up @@ -3213,14 +3229,19 @@ accidentally omits valid [=credential IDs=] from
#### {{PublicKeyCredential/signalCurrentUserDetails(options)}} #### {#sctn-signalCurrentUserDetails}

The {{PublicKeyCredential/signalCurrentUserDetails(options)|signalCurrentUserDetails}} method signals the user's
current {{PublicKeyCredentialEntity/name}} and {{PublicKeyCredentialUserEntity/displayName}}.
current {{PublicKeyCredentialEntity/name}} and {{PublicKeyCredentialUserEntity/displayName}},
and accompanying {{PublicKeyCredentialUserEntity/language}}
and {{PublicKeyCredentialUserEntity/direction}} metadata if applicable.

Upon invocation of {{PublicKeyCredential/signalCurrentUserDetails(options)}}, the
[=client=] executes these steps:

1. If the result of [=base64url encoding | base64url decoding=]
<code>|options|.{{CurrentUserDetailsOptions/userId}}</code> is an error,
then return [=a promise rejected with=] a {{TypeError}}.
1. If <code>|options|.{{CurrentUserDetailsOptions/language}}</code> is present
and is not a [=well-formed language tag=],
then return [=a promise rejected with=] a "{{SyntaxError}}" {{DOMException}}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tagging another SyntaxError pending a decision about sticking with TypeError as called out above.

1. Let |p| be the result of executing the [$Asynchronous RP ID validation
algorithm$] with
<code>|options|.{{CurrentUserDetailsOptions/rpId}}</code>.
Expand All @@ -3241,8 +3262,10 @@ The <dfn for="signal method/authenticator actions">currentUserDetails</dfn>
<code>[=credentials map=][|options|.{{CurrentUserDetailsOptions/rpId}}, |userId|]</code>.
1. If |credential| does not exist, abort these steps.
1. Update the |credential|'s [=public key credential source/otherUI=] to match
<code>|options|.{{CurrentUserDetailsOptions/name}}</code> and
<code>|options|.{{CurrentUserDetailsOptions/displayName}}</code>.
<code>|options|.{{CurrentUserDetailsOptions/name}}</code>,
<code>|options|.{{CurrentUserDetailsOptions/displayName}}</code>,
<code>|options|.{{CurrentUserDetailsOptions/language}}</code>
and <code>|options|.{{CurrentUserDetailsOptions/direction}}</code>.

<div class="example">

Expand All @@ -3253,7 +3276,9 @@ PublicKeyCredential.signalCurrentUserDetails({
rpId: "example.com",
userId: "aabbcc", // user handle, base64url.
name: "New user name",
displayName: "New display name"
displayName: "New display name",
language: "en", // Optional
direction: "ltr", // Optional
});
```

Expand Down Expand Up @@ -3573,10 +3598,6 @@ associated with or [=scoped=] to, respectively.
[[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]],
when setting {{PublicKeyCredentialEntity/name}}'s value, or displaying the value to the user.

- This string MAY contain language and direction metadata.
[=[RPS]=] SHOULD consider providing this information if setting the member to a value other than the [=RP ID=].
See [[#sctn-strings-langdir]] about how this metadata is encoded.

- [=Clients=] SHOULD perform enforcement, as prescribed in Section 2.3 of
[[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]],
on {{PublicKeyCredentialEntity/name}}'s value prior to displaying the value to the user or
Expand All @@ -3596,8 +3617,6 @@ associated with or [=scoped=] to, respectively.
IdentifierClass [[!RFC8264]], when setting {{PublicKeyCredentialEntity/name}}'s value, or displaying the value
to the user.

- This string MAY contain language and direction metadata. [=[RPS]=] SHOULD consider providing this information. See [[#sctn-strings-langdir]] about how this metadata is encoded.

- [=Clients=] SHOULD perform enforcement, as prescribed in Section 3.4.3 of [[!RFC8265]]
for the UsernameCasePreserved Profile of the PRECIS IdentifierClass [[!RFC8264]],
on {{PublicKeyCredentialEntity/name}}'s value prior to displaying the value to the user or
Expand Down Expand Up @@ -3637,6 +3656,8 @@ credential.
dictionary PublicKeyCredentialUserEntity : PublicKeyCredentialEntity {
required BufferSource id;
required DOMString displayName;
DOMString language;
DOMString direction;
};
</xmp>

Expand Down Expand Up @@ -3671,8 +3692,6 @@ credential.
when setting {{PublicKeyCredentialUserEntity/displayName}}'s value to a non-empty string,
or displaying a non-empty value to the user.

- This string MAY contain language and direction metadata. [=[RPS]=] SHOULD consider providing this information. See [[#sctn-strings-langdir]] about how this metadata is encoded.

- [=Clients=] SHOULD perform enforcement, as prescribed in Section 2.3 of
[[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]],
on {{PublicKeyCredentialUserEntity/displayName}}'s value prior to displaying a non-empty value to the user or
Expand All @@ -3683,6 +3702,23 @@ credential.
When storing a {{PublicKeyCredentialUserEntity/displayName}} member's value,
the value MAY be truncated as described in [[#sctn-strings-truncation]]
using a size limit greater than or equal to 64 bytes.

: <dfn>language</dfn>
:: An OPTIONAL [=resource-wide default=] language field [[!String-Meta]]
for other members of the {{PublicKeyCredentialUserEntity}}.

The [=Client=] SHOULD verify that the value is a [=well-formed language tag=].

This member MAY be ignored if the combination of [=client=] and [=authenticator=] cannot suitably process and store it.

: <dfn>direction</dfn>
:: An OPTIONAL [=resource-wide default=] [=string direction=] field [[!String-Meta]]
for other members of the {{PublicKeyCredentialUserEntity}}.

This SHOULD be one of the values `"ltr"`, `"rtl"` or `"auto"`.
The [=Client=] MUST ignore unknown values, treating an unknown value as if the [=map/exist|member does not exist=].

This member MAY be ignored if the combination of [=client=] and [=authenticator=] cannot suitably process and store it.
</div>


Expand Down Expand Up @@ -5216,6 +5252,8 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o
<code>|rpEntity|.{{PublicKeyCredentialRpEntity/id}}</code>, <code>|rpEntity|.{{PublicKeyCredentialEntity/name}}</code>,
<code>|userEntity|.{{PublicKeyCredentialEntity/name}}</code> and
<code>|userEntity|.{{PublicKeyCredentialUserEntity/displayName}}</code>, if possible.
This display SHOULD take into account <code>|userEntity|.{{PublicKeyCredentialUserEntity/language}}</code>
and <code>|userEntity|.{{PublicKeyCredentialUserEntity/direction}}</code> if possible.

If |requireUserVerification| is [TRUE], the [=authorization gesture=] MUST include [=user verification=].

Expand Down Expand Up @@ -5516,20 +5554,17 @@ but MUST NOT be shorter than the longest prefix substring that satisfies the siz

### Language and Direction Encoding ### {#sctn-strings-langdir}

In order to be correctly displayed in context, the language and base direction of a string [may be required](https://www.w3.org/TR/string-meta/#why-is-this-important). Strings in this API may have to be written to fixed-function [=authenticators=] and then later read back and displayed on a different platform. Thus language and direction metadata is encoded in the string itself to ensure that it is transported atomically.

To encode language and direction metadata in a string that is documented as permitting it, suffix its code points with two sequences of code points:

The first encodes a [=language tag=] with the code point U+E0001 followed by the ASCII values of the [=language tag=] each shifted up by U+E0000. For example, the [=language tag=] “en-US” becomes the code points U+E0001, U+E0065, U+E006E, U+E002D, U+E0055, U+E0053.
In order to be correctly displayed in context, the language and base direction of a string [may be required](https://www.w3.org/TR/string-meta/#why-is-this-important). Strings in this API may have to be written to fixed-function [=authenticators=] and then later read back and displayed on a different platform.

The second consists of a single code point which is either U+200E (“LEFT-TO-RIGHT MARK”), U+200F (“RIGHT-TO-LEFT MARK”), or U+E007F (“CANCEL TAG”). The first two can be used to indicate directionality but SHOULD only be used when neccessary to produce the correct result. (E.g. an RTL string that starts with LTR-strong characters.) The value U+E007F is a direction-agnostic indication of the end of the [=language tag=].
For compatibility with existing fixed-function [=authenticators=] without support for dedicated language and direction metadata fields,
Web Authentication Level 2 included provisions for embedding such metadata in the string itself to ensure that it is transported atomically.
This encoding is NOT RECOMMENDED; [=clients=] and [=authenticators=] MAY ignore such encoding in new values.
[=Clients=] and [=authenticators=] MAY detect and process language and direction metadata encoded in existing strings
as described in [[webauthn-2-20210408#sctn-strings-langdir|Web Authentication Level 2 §6.4.2. Language and Direction Encoding]].

So the string “حبیب الرحمان” could have two different DOMString values, depending on whether the language was encoded or not. (Since the direction is unambiguous a directionality marker is not needed in this example.)
Instead, the Level 3 [=Web Authentication API=] provides [=resource-wide default=] fields for encoding language and direction metadata.
[=[WACS]=] SHOULD use these when provided by the [=[RP]=] and supported by the [=authenticator=].

* Unadorned string: U+062D, U+0628, U+06CC, U+0628, U+0020, U+0627, U+0644, U+0631, U+062D, U+0645, U+0627, U+0646
* With language “ar-SA” encoded: U+062D, U+0628, U+06CC, U+0628, U+0020, U+0627, U+0644, U+0631, U+062D, U+0645, U+0627, U+0646, U+E0001, U+E0061, U+E0072, U+E002D, U+E0053, U+E0041, U+E007F

Consumers of strings that may have language and direction encoded should be aware that truncation could truncate a [=language tag=] into a different, but still valid, language. The final directionality marker or CANCEL TAG code point provide an unambigous indication of truncation.

## Attestation ## {#sctn-attestation}

Expand Down Expand Up @@ -8093,6 +8128,22 @@ The <dfn>Credential Parameters</dfn> is a JSON [=Object=] passed to the [=remote
</td>
<td>string</td>
</tr>
<tr>
<td>|userLanguage|</td>
<td>
The [=resource-wide default=] language tag for the {{PublicKeyCredentialUserEntity|user}} entity associated to the credential.
This property may not be defined.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This property may not be defined.
Note that this property might not be present.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but I'm going to reject this proposal as "This property may not be defined" is the formulation already used for userHandle and largeBlob:

webauthn/index.bs

Lines 8003 to 8006 in 6507c4c

<td>|userHandle|</td>
<td>
The [=public key credential source/userHandle=] associated to the credential encoded using
[=Base64url Encoding=]. This property may not be defined.

webauthn/index.bs

Lines 8016 to 8019 in 6507c4c

<td>|largeBlob|</td>
<td>
The [=large, per-credential blob=] associated to the [=public key credential source=], encoded using [=Base64url Encoding=].
This property may not be defined.

</td>
<td>string</td>
</tr>
<tr>
<td>|userDirection|</td>
<td>
The [=resource-wide default=] [=string direction=] for the {{PublicKeyCredentialUserEntity|user}} entity associated to the credential.
This property may not be defined.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This property may not be defined.
Note that this property might not be present.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</td>
<td>string</td>
</tr>
</tbody>
</table>
</figure>
Expand Down Expand Up @@ -8137,6 +8188,8 @@ The [=remote end steps=] are:
1. If |userName| is not defined, set |userName| to the empty string.
1. Let |userDisplayName| be the |parameters|' |userDisplayName| property.
1. If |userDisplayName| is not defined, set |userDisplayName| to the empty string.
1. Let |userLanguage| be the |parameters|' |userLanguage| property, or `null` if the property is not defined.
1. Let |userDirection| be the |parameters|' |userDirection| property, or `null` if the property is not defined.
1. Let |credential| be a new [=Client-side discoverable Public Key Credential Source=] if |isResidentCredential| is [TRUE]
or a [=Server-side Public Key Credential Source=] otherwise whose items are:
: [=public key credential source/type=]
Expand All @@ -8150,7 +8203,7 @@ The [=remote end steps=] are:
: [=public key credential source/userHandle=]
:: |userHandle|
: [=public key credential source/otherUI=]
:: Construct from |userName| and |userDisplayName|.
:: Construct from |userName|, |userDisplayName|, |userLanguage|, and |userDirection|.
1. Set the |credential|'s [=backup eligibility=] [=credential property=] to |backupEligibility|.
1. Set the |credential|'s [=backup state=] [=credential property=] to |backupState|.
1. Associate a [=signature counter=] |counter| to the |credential| with a starting value equal to the |parameters|'
Expand Down Expand Up @@ -9985,6 +10038,11 @@ Deprecations:
[[#dictionary-pkcredentialentity]]
- [[#sctn-android-safetynet-attestation]]
- [=CollectedClientData/tokenBinding=] was changed to \[RESERVED].
- In-field language and direction metadata are no longer recommended:
- [[#sctn-strings-langdir]]
- <code>{{CredentialCreationOptions/publicKey}}.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialEntity/name}}</code>
- <code>{{CredentialCreationOptions/publicKey}}.{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialEntity/name}}</code>
- <code>{{CredentialCreationOptions/publicKey}}.{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialUserEntity/displayName}}</code>


New features:
Expand Down Expand Up @@ -10014,6 +10072,8 @@ New features:
- Registration parameter
<code>{{CredentialCreationOptions/publicKey}}.{{PublicKeyCredentialCreationOptions/attestationFormats}}</code>:
[[#dictionary-makecredentialoptions]]
- New optional {{PublicKeyCredentialUserEntity}} attributes:
{{PublicKeyCredentialUserEntity/language}} and {{PublicKeyCredentialUserEntity/direction}}


### Editorial Changes ### {#changes-l3-editorial}
Expand Down