From 7a7f5d717a40d606989e777d4da2718acae75546 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 11:12:35 -0700 Subject: [PATCH 01/11] actually improve #254, and fix #661 --- index.bs | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/index.bs b/index.bs index f1395a821..90640ec1d 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,7 @@ 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) for: CredentialsContainer type: method text: create(); url: dom-credentialscontainer-create @@ -537,7 +539,7 @@ 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)}}. +{{PublicKeyCredential/[[Create]](origin, options)}}. ### `CredentialCreationOptions` Extension ### {#credentialcreationoptions-extension} @@ -562,26 +564,31 @@ 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} -
+
{{PublicKeyCredential}}'s [=interface object=]'s implementation of the -\[[Create]](options) method allows scripts to call +\[[Create]](origin, options) [=internal method=] allows [=[RP]=] scripts to call {{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=credential key pair=] -and {{PublicKeyCredential}}, managed by an [=authenticator=]. +and {{PublicKeyCredential}}, bound to an [=authenticator=]. On success, the returned {{promise}} will be resolved with a {{PublicKeyCredential}} containing an {{AuthenticatorAttestationResponse}} object. -Note: This algorithm is synchronous; the {{Promise}} resolution/rejection is handled by +Note: This algorithm is synchronous: the {{Promise}} resolution/rejection is handled by {{CredentialsContainer/create()|navigator.credentials.create()}}. -This method accepts a single argument: +This method accepts a two arguments: + +
+ + : origin + :: This argument is the [=relevant settings object=]'s [=environment settings object/origin=], as determined by the + calling {{CredentialsContainer/create()}} implementation. -
: options :: This argument is a {{CredentialCreationOptions}} object whose - |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=].
When this method is invoked, the user agent MUST execute the following algorithm: @@ -604,8 +611,7 @@ When this method is invoked, the user agent MUST execute the following algorithm 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=]. @@ -2252,7 +2258,7 @@ When registering a new credential, represented by a {{AuthenticatorAttestationRe 13. If the attestation statement |attStmt| verified successfully and is found to be trustworthy, then register the new credential with the account that was denoted in the - {{PublicKeyCredential/[[Create]](options)/options}}.{{MakePublicKeyCredentialOptions/user}} passed to + {{PublicKeyCredential/[[Create]](origin, options)/options}}.{{MakePublicKeyCredentialOptions/user}} passed to {{CredentialsContainer/create()}}, by associating it with the [=credentialId=] and [=credentialPublicKey=] in the [=attestedCredentialData=] in |authData|, as appropriate for the [=[RP]=]'s system. From 24aea5a80863dba0a7148b1f8bf7c6906f8144fe Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 12:06:19 -0700 Subject: [PATCH 02/11] DiscoFrmExtSource(options) -> (origin, options) --- index.bs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.bs b/index.bs index 90640ec1d..53f753655 100644 --- a/index.bs +++ b/index.bs @@ -538,7 +538,7 @@ 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 +implementation of {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options)}} and {{PublicKeyCredential/[[Create]](origin, options)}}. ### `CredentialCreationOptions` Extension ### {#credentialcreationoptions-extension} @@ -568,7 +568,7 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede
{{PublicKeyCredential}}'s [=interface object=]'s implementation of the -\[[Create]](origin, options) [=internal method=] allows [=[RP]=] scripts to call +\[[Create]](origin, options) [=internal method=] [[CREDENTIAL-MANAGEMENT-1]] allows [=[RP]=] scripts to call {{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=credential key pair=] and {{PublicKeyCredential}}, bound to an [=authenticator=]. On success, the returned {{promise}} will be resolved with a {{PublicKeyCredential}} containing an @@ -814,7 +814,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=]. The 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 @@ -832,10 +832,10 @@ Since this specification requires an [=authorization gesture=] to create any [=c for="PublicKeyCredential" method>\[[CollectFromCredentialStore]](options) inherits the default behavior of {{Credential/[[CollectFromCredentialStore]]()|Credential.[[CollectFromCredentialStore]]()}}, of returning an empty set. -
PublicKeyCredential's `[[DiscoverFromExternalSource]](options)` method
+
PublicKeyCredential's `[[DiscoverFromExternalSource]](origin, options)` method
-
-When the PublicKeyCredential.\[[DiscoverFromExternalSource]](options) +
+When the PublicKeyCredential.\[[DiscoverFromExternalSource]](origin, options) method is invoked, the user agent MUST: 1. Assert: |options|.{{CredentialRequestOptions/publicKey}} is [=present=]. From 33905b7a3397affeb28d06cd7ededb9b8d0b38f2 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 14:49:25 -0700 Subject: [PATCH 03/11] make [[DiscoFrmExtSource]]'s exposition match [[Create]]'s --- index.bs | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/index.bs b/index.bs index 53f753655..780f79251 100644 --- a/index.bs +++ b/index.bs @@ -90,6 +90,7 @@ spec: credential-management-1; urlPrefix: https://w3c.github.io/webappsec-creden for: Credential type: method text: [[Create]](origin, options) + text: [[DiscoverFromExternalSource]](origin, options) for: CredentialsContainer type: method text: create(); url: dom-credentialscontainer-create @@ -574,10 +575,7 @@ and {{PublicKeyCredential}}, bound to an [=authenticator=]. On success, the returned {{promise}} will be resolved with a {{PublicKeyCredential}} containing an {{AuthenticatorAttestationResponse}} object. -Note: This algorithm is synchronous: the {{Promise}} resolution/rejection is handled by -{{CredentialsContainer/create()|navigator.credentials.create()}}. - -This method accepts a two arguments: +This method accepts two arguments:
@@ -591,6 +589,9 @@ This method accepts a two arguments: object specifying the desired attributes of the to-be-created [=public key credential=].
+Note: This algorithm is synchronous: the {{Promise}} resolution/rejection is handled by +{{CredentialsContainer/create()|navigator.credentials.create()}}. + When this method is invoked, the user agent MUST execute the following algorithm: 1. Assert: |options|.{{CredentialCreationOptions/publicKey}} is [=present=]. @@ -814,7 +815,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 consent|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 @@ -824,19 +825,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's `[[DiscoverFromExternalSource]](origin, options)` method
-When the PublicKeyCredential.\[[DiscoverFromExternalSource]](origin, options) -method is invoked, the user agent MUST: + +The {{PublicKeyCredential}}.\[[DiscoverFromExternalSource]](origin, options) [=internal method=] accepts two arguments: + +
+ + : origin + :: This argument is the [=relevant settings object=]'s [=environment settings object/origin=], as determined by the + calling {{CredentialsContainer/get()}} implementation, i.e., {{CredentialsContainer}}'s Request a `Credential` abstract operation. + + : options + :: This argument is a {{CredentialRequestOptions}} object whose + |options|.{{CredentialRequestOptions/publicKey}} member contains a {{PublicKeyCredentialRequestOptions}} + object specifying the desired attributes of the [=public key credential=] to discover. +
+ +Note: This algorithm is synchronous: the {{Promise}} resolution/rejection is handled by +{{CredentialsContainer/get()|navigator.credentials.get()}}. + +When this method is invoked, the user agent MUST execute the following algorithm: 1. Assert: |options|.{{CredentialRequestOptions/publicKey}} is [=present=]. @@ -849,9 +868,8 @@ method is invoked, the user agent MUST: 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 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 From 6b09e67ca12e0e575c92bcc2e42d79dda16e395d Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 14:50:23 -0700 Subject: [PATCH 04/11] deal with yet another fix #254 straggler in [[Create]] --- index.bs | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.bs b/index.bs index 780f79251..dc4426bc6 100644 --- a/index.bs +++ b/index.bs @@ -609,9 +609,6 @@ 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 {{PublicKeyCredential/[[Create]](origin, options)/origin}}. If |callerOrigin| is an [=opaque origin=], return a {{DOMException}} whose name is "{{NotAllowedError}}", and terminate this algorithm. From 4db4e448205a8644285bf3d59fea2a8b4c8973b9 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 14:58:03 -0700 Subject: [PATCH 05/11] get rid of |global| in [[DiscoFrmExtSource]] --- index.bs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.bs b/index.bs index dc4426bc6..ee0567798 100644 --- a/index.bs +++ b/index.bs @@ -863,9 +863,7 @@ When this method is invoked, the user agent MUST execute the following algorithm 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 be {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options)/origin}}. If |callerOrigin| is +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=]. From f6148728d75b6c0ece61280e95b813c5809b2c3c Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 15:36:43 -0700 Subject: [PATCH 06/11] remove 'in parallel' and 'global' stuff from #discover-from-external-source alg --- index.bs | 100 +++++++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/index.bs b/index.bs index ee0567798..0439e221f 100644 --- a/index.bs +++ b/index.bs @@ -954,9 +954,9 @@ When this method is invoked, the user agent MUST execute the following algorithm : [=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}} and set its value to |allowCredentialDescriptorList|[0].id's + value. 1. [=list/For each=] credential descriptor |C| in |allowCredentialDescriptorList|, [=set/append=] each value, if any, of |C|.{{transports}} to |distinctTransports|. @@ -971,19 +971,19 @@ When this method is invoked, the user agent MUST execute the following algorithm 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 @@ -993,56 +993,54 @@ When this method is invoked, the user agent MUST execute the following algorithm 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: - -
- - : If |lifetimeTimer| expires, - :: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on - |authenticator| and [=set/remove=] |authenticator| from |issuedRequests|. - - : 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|. +
- : If any |authenticator| returns an error status, - :: [=set/Remove=] |authenticator| from |issuedRequests|. + : If |lifetimeTimer| expires, + :: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on + |authenticator| and [=set/remove=] |authenticator| from |issuedRequests|. - : If any |authenticator| indicates success, - :: 1. [=set/Remove=] |authenticator| from |issuedRequests|. - 2. Let |value| be a new {{PublicKeyCredential}} associated with |global| whose fields are: + : 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|. - : {{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]]. + : If any |authenticator| returns an error status, + :: [=set/Remove=] |authenticator| from |issuedRequests|. - : {{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. -
+ : If any |authenticator| indicates success, + :: 1. [=set/Remove=] |authenticator| from |issuedRequests|. + 2. Let |value| be a new {{PublicKeyCredential}} 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]]. + + : {{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. +
1. Return a {{DOMException}} whose name is "{{NotAllowedError}}". From 9bc663032568ca9ad7a9ae1128d317b9e313b474 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 27 Oct 2017 16:38:16 -0700 Subject: [PATCH 07/11] work on #discover-from-external-source alg to improve #254 --- index.bs | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/index.bs b/index.bs index 0439e221f..dcf7a54d1 100644 --- a/index.bs +++ b/index.bs @@ -752,15 +752,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 @@ -955,9 +955,14 @@ When this method is invoked, the user agent MUST execute the following algorithm :: 1. Let |distinctTransports| be a new [=ordered set=]. 1. If |allowCredentialDescriptorList| has exactly one value, let |savedCredentialId| be a new - {{PublicKeyCredentialDescriptor}} and set its value to |allowCredentialDescriptorList|[0].id's + {{PublicKeyCredentialDescriptor}}.{{PublicKeyCredentialDescriptor/id}} and set its value to |allowCredentialDescriptorList|[0].id's value. + Issue: I suspect the foregoing step is incorrect. Before this, it (also incorrectly) called for creating an + {{ArrayBuffer}} using PublicKeyCredential's interface object's relevant global object, but we do not have + access to said global object because this entire alg is running in parallel. How do we now create, save, + and subsequently access (see below) |savedCredentialId|? + 1. [=list/For each=] credential descriptor |C| in |allowCredentialDescriptorList|, [=set/append=] each value, if any, of |C|.{{transports}} to |distinctTransports|. @@ -1004,7 +1009,7 @@ When this method is invoked, the user agent MUST execute the following algorithm : 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 + 1. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator| and [=set/remove=] it from |issuedRequests|. : If any |authenticator| returns an error status, @@ -1012,14 +1017,36 @@ When this method is invoked, the user agent MUST execute the following algorithm : If any |authenticator| indicates success, :: 1. [=set/Remove=] |authenticator| from |issuedRequests|. + + 1. Let |assertionCreationData| be a [=struct=] whose [=items=] are: + + : credentialId + :: If |savedCredentialId| exists, set the value of [=credentialId=] to be the bytes of + |savedCredentialId|. Otherwise, set the value of [=credentialId=] 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|. + + : authenticatorDataResult + :: whose value is the bytes of the [=authenticator data=] returned by the [=authenticator=]. + + : signatureResult + :: whose value is the bytes of the signature value returned by the [=authenticator=]. + + : userHandleResult + :: whose value is the bytes of the [=user handle=] returned by the [=authenticator=]. + + : clientExtensionResults + :: whose value is the bytes of ... + + 2. Let |value| be a new {{PublicKeyCredential}} 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]]. + : {{PublicKeyCredential/response}} :: A new {{AuthenticatorAssertionResponse}} object associated with |global| whose fields are: @@ -1916,7 +1943,8 @@ When this method is invoked, the [=authenticator=] must perform the following pr 1. If any error occurred while generating the [=assertion signature=], return an error code equivalent to "{{UnknownError}}" and terminate the operation. -1. Return to the user agent: +
  • + Return to the user agent: - |selectedCredential|'s credential ID, if either a list of credentials of length 2 or greater was supplied by the client, or no such list was supplied. @@ -1926,6 +1954,7 @@ When this method is invoked, the [=authenticator=] must perform the following pr - |authenticatorData| - |signature| - The [=user handle=] associated with |selectedCredential|. +
  • If the authenticator cannot find any credential corresponding to the specified [=[RP]=] that matches the specified criteria, it terminates the operation and returns an error. From 0cda621bac20e25f780776ca53c5598fc9600983 Mon Sep 17 00:00:00 2001 From: JeffH Date: Mon, 30 Oct 2017 11:24:54 -0700 Subject: [PATCH 08/11] finish (one hopes) work on #discover-from-external-source alg to fix #254 --- index.bs | 104 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/index.bs b/index.bs index dcf7a54d1..75ab746fc 100644 --- a/index.bs +++ b/index.bs @@ -784,14 +784,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|. @@ -956,12 +956,10 @@ When this method is invoked, the user agent MUST execute the following algorithm 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. + value (see [here](#authenticatorGetAssertion-return-values) in [[#op-get-assertion]] for more information). - Issue: I suspect the foregoing step is incorrect. Before this, it (also incorrectly) called for creating an - {{ArrayBuffer}} using PublicKeyCredential's interface object's relevant global object, but we do not have - access to said global object because this entire alg is running in parallel. How do we now create, save, - and subsequently access (see below) |savedCredentialId|? + Issue: The foregoing step _may_ be incorrect, in that we are attempting to create |savedCredentialId| + here and use it later below. 1. [=list/For each=] credential descriptor |C| in |allowCredentialDescriptorList|, [=set/append=] each value, if any, of |C|.{{transports}} to |distinctTransports|. @@ -1018,11 +1016,11 @@ When this method is invoked, the user agent MUST execute the following algorithm : If any |authenticator| indicates success, :: 1. [=set/Remove=] |authenticator| from |issuedRequests|. - 1. Let |assertionCreationData| be a [=struct=] whose [=items=] are: + 1. Let assertionCreationData be a [=struct=] whose [=items=] are: - : credentialId - :: If |savedCredentialId| exists, set the value of [=credentialId=] to be the bytes of - |savedCredentialId|. Otherwise, set the value of [=credentialId=] to be the bytes of the + : 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]]. @@ -1039,34 +1037,49 @@ When this method is invoked, the user agent MUST execute the following algorithm :: whose value is the bytes of the [=user handle=] returned by the [=authenticator=]. : clientExtensionResults - :: whose value is the bytes of ... - - - 2. Let |value| be a new {{PublicKeyCredential}} associated with |global| whose fields are: - - : {{PublicKeyCredential/[[identifier]]}} - :: Create a new {{ArrayBuffer}}, using |global|'s [=%ArrayBuffer%=]. - - - : {{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 + :: 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]]}} + :: 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 + |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|. - 4. Return |value| and terminate this algorithm. + + 1. Return |constructAssertionAlg| and terminate this algorithm. 1. Return a {{DOMException}} whose name is "{{NotAllowedError}}". @@ -1945,15 +1958,16 @@ When this method is invoked, the [=authenticator=] must perform the following pr
  • Return to the user agent: - - |selectedCredential|'s credential ID, if either a list of credentials of length 2 or greater was supplied by the client, - or no such list was supplied. + - |selectedCredential|'s credential ID, if either a list of credentials of length 2 or greater was supplied by the + client, or no such list was supplied. Otherwise, return only the below values. - Note: If the client supplies a list of exactly one credential and it was successfully employed, then its credential ID - is not returned since the client already knows it. + Note: If the client supplies a list of exactly one credential and it was successfully employed, then its credential + ID is not returned since the client already knows it. This saves transmitting these bytes over what may be + a constrained connection in what is likely a common case. - - |authenticatorData| - - |signature| - - The [=user handle=] associated with |selectedCredential|. + - |authenticatorData| + - |signature| + - The [=user handle=] associated with |selectedCredential|.
  • If the authenticator cannot find any credential corresponding to the specified [=[RP]=] that matches the specified criteria, it From 1a606eb2851520971da7c2e8be0c0b8896b77319 Mon Sep 17 00:00:00 2001 From: JeffH Date: Mon, 30 Oct 2017 11:33:01 -0700 Subject: [PATCH 09/11] minor editorial --- index.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 75ab746fc..11828001f 100644 --- a/index.bs +++ b/index.bs @@ -959,7 +959,8 @@ When this method is invoked, the user agent MUST execute the following algorithm 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. + 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? 1. [=list/For each=] credential descriptor |C| in |allowCredentialDescriptorList|, [=set/append=] each value, if any, of |C|.{{transports}} to |distinctTransports|. From 2d3f3f5a89b2d48a0cc11da4f6be9f93f1a92f71 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 3 Nov 2017 13:24:20 -0700 Subject: [PATCH 10/11] repair #createCredential intro parag, improves issue #671 --- index.bs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 08921a432..0977f3b15 100644 --- a/index.bs +++ b/index.bs @@ -619,10 +619,8 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede
    {{PublicKeyCredential}}'s [=interface object=]'s implementation of the \[[Create]](origin, options) [=internal method=] [[CREDENTIAL-MANAGEMENT-1]] allows [=[RP]=] scripts to call -{{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=credential key pair=] -and {{PublicKeyCredential}}, bound to an [=authenticator=]. -On success, the returned {{promise}} will be resolved with a {{PublicKeyCredential}} containing an -{{AuthenticatorAttestationResponse}} object. +{{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new +[=public key credential source=], bound to an [=authenticator=]. This method accepts two arguments: From 4c0562a405feb115af39f3a6b15b38083074dbe9 Mon Sep 17 00:00:00 2001 From: JeffH Date: Fri, 3 Nov 2017 13:40:27 -0700 Subject: [PATCH 11/11] complete fix #671 --- index.bs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 0977f3b15..2e263ed10 100644 --- a/index.bs +++ b/index.bs @@ -1007,7 +1007,7 @@ When this method is invoked, the user agent MUST execute the following algorithm 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? + 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|. @@ -3189,9 +3189,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