diff --git a/index.bs b/index.bs index 06e35fea5..0daa4b44b 100644 --- a/index.bs +++ b/index.bs @@ -58,7 +58,9 @@ spec: ECMAScript; urlPrefix: https://tc39.github.io/ecma262/# for: JSON; text: stringify; url: sec-json.stringify type: dfn text: %ArrayBuffer%; url: sec-arraybuffer-constructor - text: internal slot; url: sec-object-internal-methods-and-internal-slots + url: sec-object-internal-methods-and-internal-slots + text: internal method + text: internal slot spec: HTML52; urlPrefix: https://w3c.github.io/html/ @@ -87,7 +89,8 @@ spec: credential-management-1; urlPrefix: https://w3c.github.io/webappsec-creden text: CredentialRequestOptions; url: dictdef-credentialrequestoptions for: Credential type: method - text: [[Create]](options) + text: [[Create]](origin, options) + text: [[DiscoverFromExternalSource]](origin, options) for: CredentialsContainer type: method text: create(); url: dom-credentialscontainer-create @@ -595,8 +598,8 @@ that are returned to the caller when a new credential is created, or a new asser {{PublicKeyCredential}}'s [=interface object=] inherits {{Credential}}'s implementation of {{Credential/[[CollectFromCredentialStore]](options)}} and {{Credential/[[Store]](credential)}}, and defines its own -implementation of {{PublicKeyCredential/[[DiscoverFromExternalSource]](options)}} and -{{PublicKeyCredential/[[Create]](options)}}. +implementation of {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options)}} and +{{PublicKeyCredential/[[Create]](origin, options)}}. ### `CredentialCreationOptions` Extension ### {#credentialcreationoptions-extension} @@ -621,30 +624,35 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede -### Create a new credential - PublicKeyCredential's `[[Create]](options)` method ### {#createCredential} +### Create a new credential - PublicKeyCredential's `[[Create]](origin, options)` method ### {#createCredential} -
|options|.{{CredentialCreationOptions/publicKey}}
member contains a {{MakePublicKeyCredentialOptions}} object
- specifying the desired attributes of the to-be-created [=public key credential=].
+ |options|.{{CredentialCreationOptions/publicKey}}
member contains a {{MakePublicKeyCredentialOptions}}
+ object specifying the desired attributes of the to-be-created [=public key credential=].
|options|.{{CredentialCreationOptions/publicKey}}
is [=present=].
@@ -656,11 +664,7 @@ When this method is invoked, the user agent MUST execute the following algorithm
|lifetimeTimer| to this adjusted value. If the {{MakePublicKeyCredentialOptions/timeout}} member of |options| is [=present|not
present=], then set |lifetimeTimer| to a platform-specific default.
-1. Let |global| be the {{PublicKeyCredential}}'s [=interface object=]'s [=global object|environment settings object's global
- object=].
-
-1. Let |callerOrigin| be the [=environment settings object/origin=] specified by this {{PublicKeyCredential}} [=interface
- object=]'s [=relevant settings object=]. If |callerOrigin| is an [=opaque origin=], return a {{DOMException}} whose name is
+1. Let |callerOrigin| be {{PublicKeyCredential/[[Create]](origin, options)/origin}}. If |callerOrigin| is an [=opaque origin=], return a {{DOMException}} whose name is
"{{NotAllowedError}}", and terminate this algorithm.
1. Let |effectiveDomain| be the |callerOrigin|'s [=effective domain=].
@@ -813,15 +817,15 @@ When this method is invoked, the user agent MUST execute the following algorithm
1. Let |credentialCreationData| be a [=struct=] whose [=items=] are:
- : attestationObjectResult
+ : attestationObjectResult
:: whose value is the bytes returned from the successful [=authenticatorMakeCredential=] operation.
Note: this value is attObj
, as defined in [[#generating-an-attestation-object]].
- : clientDataJSONResult
+ : clientDataJSONResult
:: whose value is the bytes of |clientDataJSON|.
- : clientExtensionResults
+ : clientExtensionResults
:: whose value is an {{AuthenticationExtensions}} object containing [=extension identifier=] →
[=client extension output=] entries. The entries are created by running each extension's
[=client extension processing=] algorithm to create the [=client extension outputs=], for each
@@ -845,14 +849,14 @@ When this method is invoked, the user agent MUST execute the following algorithm
: {{AuthenticatorResponse/clientDataJSON}}
:: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
- |credentialCreationData|.[=clientDataJSONResult=]
.
+ |credentialCreationData|.[=credentialCreationData/clientDataJSONResult=]
.
: {{AuthenticatorAttestationResponse/attestationObject}}
:: |attestationObject|
: {{PublicKeyCredential/[[clientExtensionsResults]]}}
:: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
- |credentialCreationData|.[=clientExtensionResults=]
.
+ |credentialCreationData|.[=credentialCreationData/clientExtensionResults=]
.
1. Return |pubKeyCred|.
@@ -873,7 +877,7 @@ authorizing an authenticator.
### Use an existing credential to make an assertion ### {#getAssertion}
[=[RPS]=] call navigator.credentials.get({publicKey:..., ...})
to
-discover and use an existing [=public key credential=], with the user's consent. The script optionally specifies some criteria
+discover and use an existing [=public key credential=], with the [=user consent|user's consent=]. [=[RP]=] script optionally specifies some criteria
to indicate what [=credential sources=] are acceptable to it. The user agent and/or platform locates [=credential sources=]
matching the specified criteria, and guides the user to pick one that the script will be allowed to use. The user may choose to
decline the entire interaction even if a [=credential source=] is present, for example to maintain privacy. If the user picks a
@@ -883,19 +887,37 @@ decline the entire interaction even if a [=credential source=] is present, for e
The {{CredentialsContainer/get()}} implementation [[CREDENTIAL-MANAGEMENT-1]] calls
PublicKeyCredential.{{PublicKeyCredential/[[CollectFromCredentialStore]]()}}
to collect any [=credentials=] that
-should be available without [=user mediation=] (roughly, this specification's [=authorization gesture=]), and if it doesn't find
-exactly one of those, it calls PublicKeyCredential.{{PublicKeyCredential/[[DiscoverFromExternalSource]]()}}
to have
+should be available without [=user mediation=] (roughly, this specification's [=authorization gesture=]), and if it does not find
+exactly one of those, it then calls PublicKeyCredential.{{PublicKeyCredential/[[DiscoverFromExternalSource]]()}}
to have
the user select a [=credential source=].
-Since this specification requires an [=authorization gesture=] to create any [=credentials=], PublicKeyCredential.\[[CollectFromCredentialStore]](options)
inherits the default behavior of
+Since this specification requires an [=authorization gesture=] to create any [=credentials=], the PublicKeyCredential.\[[CollectFromCredentialStore]](options)
[=internal method=] inherits the default behavior of
{{Credential/[[CollectFromCredentialStore]]()|Credential.[[CollectFromCredentialStore]]()}}, of returning an empty set.
-PublicKeyCredential.\[[DiscoverFromExternalSource]](options)
-method is invoked, the user agent MUST:
+{{PublicKeyCredential}}.\[[DiscoverFromExternalSource]](origin, options)
[=internal method=] accepts two arguments:
+
+|options|.{{CredentialRequestOptions/publicKey}}
member contains a {{PublicKeyCredentialRequestOptions}}
+ object specifying the desired attributes of the [=public key credential=] to discover.
+|options|.{{CredentialRequestOptions/publicKey}}
is [=present=].
@@ -906,11 +928,8 @@ method is invoked, the user agent MUST:
Set a timer |lifetimeTimer| to this adjusted value. If the {{PublicKeyCredentialRequestOptions/timeout}} member of
|options| is [=present|not present=], then set |lifetimeTimer| to a platform-specific default.
-1. Let |global| be the {{PublicKeyCredential}}'s [=interface object=]'s [=relevant global object=].
-
-1. Let |callerOrigin| be the [=environment settings object/origin=] specified by this {{PublicKeyCredential}} [=interface
- object=]'s [=relevant settings object=]. If |callerOrigin| is an [=opaque origin=], return a {{DOMException}} whose name is
- "{{NotAllowedError}}", and terminate this algorithm.
+1. Let |callerOrigin| be {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options)/origin}}. If |callerOrigin| is
+ an [=opaque origin=], return a {{DOMException}} whose name is "{{NotAllowedError}}", and terminate this algorithm.
1. Let |effectiveDomain| be the |callerOrigin|'s [=effective domain=].
If [=effective domain=] is not a [=valid domain=], then return a
@@ -1004,9 +1023,13 @@ method is invoked, the user agent MUST:
: [=list/is not empty=]
:: 1. Let |distinctTransports| be a new [=ordered set=].
- 1. If |allowCredentialDescriptorList| has exactly one value, let |savedCredentialId| be a new {{ArrayBuffer}},
- created using |global|'s [=%ArrayBuffer%=], and containing the bytes of
- |allowCredentialDescriptorList|[0].id
.
+ 1. If |allowCredentialDescriptorList| has exactly one value, let |savedCredentialId| be a new
+ {{PublicKeyCredentialDescriptor}}.{{PublicKeyCredentialDescriptor/id}} and set its value to |allowCredentialDescriptorList|[0].id
's
+ value (see [here](#authenticatorGetAssertion-return-values) in [[#op-get-assertion]] for more information).
+
+ Issue: The foregoing step _may_ be incorrect, in that we are attempting to create |savedCredentialId|
+ here and use it later below, and we do not have a global in which to allocate a place for it. Perhaps this
+ is good enough? addendum: [@jcjones feels the above step is likely good enough](https://github.com/w3c/webauthn/pull/665#discussion_r148130187).
1. [=list/For each=] credential descriptor |C| in |allowCredentialDescriptorList|,
[=set/append=] each value, if any, of |C|.{{transports}}
to |distinctTransports|.
@@ -1021,19 +1044,19 @@ method is invoked, the user agent MUST:
configuration knowledge of the appropriate transport to use with |authenticator| in making its
selection.
- Then, using |transport|, invoke [=in parallel=] the [=authenticatorGetAssertion=] operation on
+ Then, using |transport|, invoke the [=authenticatorGetAssertion=] operation on
|authenticator|, with |rpId|, |clientDataHash|, |allowCredentialDescriptorList|, and
|authenticatorExtensions| as parameters.
: [=list/is empty=]
:: Using local configuration knowledge of the appropriate transport to use with |authenticator|,
- invoke [=in parallel=] the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|,
+ invoke the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|,
|clientDataHash|, |allowCredentialDescriptorList|, and |clientExtensions| as parameters.
: [=list/is empty=]
:: Using local configuration knowledge of the appropriate transport to use with |authenticator|, invoke
- [=in parallel=] the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|, |clientDataHash|,
+ the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|, |clientDataHash|,
and |clientExtensions| as parameters.
Note: In this case, the [=[RP]=] did not supply a list of acceptable credential descriptors. Thus the
@@ -1043,62 +1066,97 @@ method is invoked, the user agent MUST:
1. [=set/Append=] |authenticator| to |issuedRequests|.
-1. Execute the following steps [=in parallel=]. The [=task source=] for these [=tasks=] is the [=dom manipulation task source=].
+1. While |issuedRequests| [=list/is not empty=], perform the following actions depending upon |lifetimeTimer|
+ and responses from the authenticators:
- 1. While |issuedRequests| [=list/is not empty=], perform the following actions depending upon |lifetimeTimer|
- and responses from the authenticators:
+ credentialIdResult
+ :: If |savedCredentialId| exists, set the value of [=credentialIdResult=] to be the bytes of
+ |savedCredentialId|. Otherwise, set the value of [=credentialIdResult=] to be the bytes of the
+ [=credential ID=] returned from the successful [=authenticatorGetAssertion=] operation, as defined in
+ [[#op-get-assertion]].
+
+ : clientDataJSONResult
+ :: whose value is the bytes of |clientDataJSON|.
- : If the {{CredentialRequestOptions/signal}} member is [=present=] and the [=AbortSignal/aborted flag=] is set to
- true,
- :: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
- and [=set/remove=] |authenticator| from |issuedRequests|. Then
- return a {{DOMException}} whose name is "{{AbortError}}" and terminate this algorithm.
+ : authenticatorDataResult
+ :: whose value is the bytes of the [=authenticator data=] returned by the [=authenticator=].
- : If any |authenticator| returns a status indicating that the user cancelled the operation,
- :: 1. [=set/Remove=] |authenticator| from |issuedRequests|.
- 2. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation
- on |authenticator| and [=set/remove=] it from |issuedRequests|.
+ : signatureResult
+ :: whose value is the bytes of the signature value returned by the [=authenticator=].
- : If any |authenticator| returns an error status,
- :: [=set/Remove=] |authenticator| from |issuedRequests|.
+ : userHandleResult
+ :: whose value is the bytes of the [=user handle=] returned by the [=authenticator=].
- : If any |authenticator| indicates success,
- :: 1. [=set/Remove=] |authenticator| from |issuedRequests|.
- 2. Let |value| be a new {{PublicKeyCredential}} associated with |global| whose fields are:
+ : clientExtensionResults
+ :: whose value is an {{AuthenticationExtensions}} object containing [=extension identifier=] →
+ [=client extension output=] entries. The entries are created by running each extension's
+ [=client extension processing=] algorithm to create the [=client extension outputs=], for each
+ [=client extension=] in {{AuthenticatorResponse/clientDataJSON}}.clientExtensions
.
+
+ 1. Let |constructAssertionAlg| be an algorithm that takes a [=global object=]
+ |global|, and whose steps are:
+
+ 1. Let |pubKeyCred| be a new {{PublicKeyCredential}} object associated with |global| whose fields are:
: {{PublicKeyCredential/[[identifier]]}}
- :: Create a new {{ArrayBuffer}}, using |global|'s [=%ArrayBuffer%=].
- If |savedCredentialId| exists, set the value of the new {{ArrayBuffer}} to be the bytes of
- |savedCredentialId|. Otherwise, set the value of the new {{ArrayBuffer}} to be the bytes of the
- [=credential ID=] returned from the successful [=authenticatorGetAssertion=] operation, as defined in
- [[#op-get-assertion]].
+ :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
+ |assertionCreationData|.[=credentialIdResult=]
.
: {{PublicKeyCredential/response}}
:: A new {{AuthenticatorAssertionResponse}} object associated with |global| whose fields are:
- : {{AuthenticatorResponse/clientDataJSON}}
- :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
- |clientDataJSON|.
- : {{AuthenticatorAssertionResponse/authenticatorData}}
- :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of the
- returned {{authenticatorData}}.
- : {{AuthenticatorAssertionResponse/signature}}
- :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of the
- returned {{signature}}.
- : {{AuthenticatorAssertionResponse/userHandle}}
- :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the [=user handle=]
- returned from the successful [=authenticatorGetAssertion=] operation, as defined in
- [[#op-get-assertion]].
-
- 3. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation
- on |authenticator| and [=set/remove=] it from |issuedRequests|.
- 4. Return |value| and terminate this algorithm.
- |assertionCreationData|.[=assertionCreationData/clientDataJSONResult=]
.
+
+ : {{AuthenticatorAssertionResponse/authenticatorData}}
+ :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
+ |assertionCreationData|.[=assertionCreationData/authenticatorDataResult=]
.
+
+ : {{AuthenticatorAssertionResponse/signature}}
+ :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
+ |assertionCreationData|.[=assertionCreationData/signatureResult=]
.
+
+ : {{AuthenticatorAssertionResponse/userHandle}}
+ :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
+ |assertionCreationData|.[=assertionCreationData/userHandleResult=]
.
+
+ : {{PublicKeyCredential/[[clientExtensionsResults]]}}
+ :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of
+ |assertionCreationData|.[=assertionCreationData/clientExtensionResults=]
.
+
+ 1. Return |pubKeyCred|.
+
+ 1. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation
+ on |authenticator| and [=set/remove=] it from |issuedRequests|.
+
+ 1. Return |constructAssertionAlg| and terminate this algorithm.
+ [=credentialId=]
and
[=credentialPublicKey=]
in the [=attestedCredentialData=]
in |authData|, as appropriate for the
[=[RP]=]'s system.
@@ -3182,9 +3243,10 @@ error.
:: A single JSON string specifying a FIDO |appId|.
: Client extension processing
-:: If {{PublicKeyCredentialRequestOptions/rpId}} is present, reject promise with a DOMException
- whose name is "{{NotAllowedError}}", and terminate this algorithm.
- Replace the calculation of |rpId| in Step 3 of [[#getAssertion]] with the
+:: If {{PublicKeyCredentialRequestOptions/rpId}} is present, return a DOMException
+ whose name is "{{NotAllowedError}}", and terminate this algorithm ([[#discover-from-external-source]]).
+
+ Otherwise, replace the calculation of |rpId| in Step 6 of [[#discover-from-external-source]] with the
following procedure: The client uses the value of |appid| to perform
the AppId validation procedure (as defined by [[FIDO-APPID]]). If valid,
the value of |rpId| for all client processing should be replaced by the