Skip to content
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

Make create() and get() abortable #544

Merged
merged 44 commits into from
Nov 8, 2017
Merged

Make create() and get() abortable #544

merged 44 commits into from
Nov 8, 2017

Conversation

AngeloKai
Copy link
Contributor

@AngeloKai AngeloKai commented Sep 6, 2017

NOTE: this is closely related to: w3c/webappsec-credential-management#110 (and formerly #104), which is now (as of 2-Nov-2017) merged to master.

As @leshi explained to me, Google's current two step verification step involves prompting a wizard such as this.. If the user clicks cancel on the wizard, the UA needs to withdraw all the calls to authenticators, which should stop blinking subsequently. I'd call out the PR is not only to fulfill this niche use case but to give us a clean interface overall.

The PR makes use of the https://dom.spec.whatwg.org/#abortsignal-aborted-flag) object to achieve such functionality. This would be an addition to the API interface. AFAIK, both Edge and Firefox have implemented AbortSignal. Chrome is working on the feature too.

There is a corresponding PR standing at CredMan land: w3c/webappsec-credential-management#104. The CredMan PR adds the abort functionality to the CredMan surface, though this may not be needed depending on @mikewest and Dominic's preference.

The AbortSignal feature is new to HTML overall. Fetch is the first one to utilize the feature: whatwg/fetch#447. However, we can't completely follow fetch because there are still nuances here and there.


Preview | Diff

JeffH and others added 26 commits June 16, 2017 14:43
@AngeloKai
Copy link
Contributor Author

The PR is based on JeffH's recent changes so there are many commits attached to this. After JeffH's PR is merged, I will rebase to make things clearer.

@AngeloKai
Copy link
Contributor Author

The PR aims at addressing the following issues: #537, #380, #283, #316, and finally #292.

#292 will need both this PR and #498

Copy link
Member

@jyasskin jyasskin left a comment

Choose a reason for hiding this comment

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

I probably won't get a chance to review the fixes to my comments before TPAC. @jcjones or one of the other reviewers is welcome to declare my comments fixed.

index.bs Outdated
@@ -570,7 +570,9 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede
{{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=credential key pair=]
and {{PublicKeyCredential}}, managed by an [=authenticator=].
On success, the returned {{promise}} will be resolved with a {{PublicKeyCredential}} containing an
{{AuthenticatorAttestationResponse}} object.
{{AuthenticatorAttestationResponse}} object. If an {{AbortSignal}} object is passed in,
the developer can abort on going {{CredentialsContainer/create()|navigator.credentials.create()}} operation with
Copy link
Member

Choose a reason for hiding this comment

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

"can abort an ongoing"?

index.bs Outdated
@@ -735,6 +737,12 @@ When this method is invoked, the user agent MUST execute the following algorithm
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.

: If the <code>|options|.{{MakePublicKeyCredentialOptions/signal}}</code> is [=present=] and and the
Copy link
Member

@jyasskin jyasskin Nov 1, 2017

Choose a reason for hiding this comment

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

"If <code>|options|..." and remove the second "and and".

index.bs Outdated
@@ -735,6 +737,12 @@ When this method is invoked, the user agent MUST execute the following algorithm
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.

: If the <code>|options|.{{MakePublicKeyCredentialOptions/signal}}</code> is [=present=] and and the
[=AbortSignal/aborted flag=] is set to false,
Copy link
Member

Choose a reason for hiding this comment

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

If this is the step intended to handle the developer aborting the create() call, I think you mean "is set to true" here.

index.bs Outdated
@@ -735,6 +737,12 @@ When this method is invoked, the user agent MUST execute the following algorithm
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.

: If the <code>|options|.{{MakePublicKeyCredentialOptions/signal}}</code> is [=present=] and and the
Copy link
Member

Choose a reason for hiding this comment

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

Assuming you keep Jeff's suggestion to put the AbortSignal in CredentialCreationOptions instead of MakePublicKeyCredentialOptions (which I agree with, as mentioned in w3c/webappsec-credential-management#104), you'll need to pull the signal out before step 2 where you overwrite |options|.

Copy link
Contributor

Choose a reason for hiding this comment

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

AbortSignal was put in credman's CredentialCreationOptions via credman PR w3c/webappsec-credential-management#110

index.bs Outdated
: If the <code>|options|.{{MakePublicKeyCredentialOptions/signal}}</code> is [=present=] and and the
[=AbortSignal/aborted flag=] is set to false,
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.</dd> Set the [=AbortSignal/aborted flag=] be true. Then
Copy link
Member

Choose a reason for hiding this comment

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

But then I'm not sure why you'd change the aborted flag in this step.

index.bs Outdated
@@ -3754,6 +3781,39 @@ extension for transaction authorization.
});
</pre>

## Aborting Authentication Operations ## {#sample-aborting}

The below example showcases how a developer may use the AbortSignal parameter to abort an
Copy link
Member

Choose a reason for hiding this comment

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

s/showcases/shows/?

index.bs Outdated
.then(function (attestation) {
// Register the user.
}).catch(function (error) {
if (error = "AbortError") {
Copy link
Member

Choose a reason for hiding this comment

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

error.name ==, right?

index.bs Outdated
});

// Assume widget shows up whenever auth occurs.
if (widget = "disappear") {
Copy link
Member

Choose a reason for hiding this comment

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

==

index.bs Outdated
if (widget = "disappear") {
authAbortSignal.abort();

authAbortSignal.onabort = function () {
Copy link
Member

Choose a reason for hiding this comment

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

This function will never run, since https://dom.spec.whatwg.org/#abortsignal-signal-abort fired the event 2 lines up.

index.bs Outdated
// Register the user.
}).catch(function (error) {
if (error = "AbortError") {
// Let the server know a key hasn't been created.
Copy link
Member

Choose a reason for hiding this comment

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

The exact error type probably doesn't affect what the server gets told. It might affect the UI shown to the user?

@AngeloKai
Copy link
Contributor Author

Btw the corresponding credman PR w3c/webappsec-credential-management#110 is merged.

index.bs Outdated
[=AbortSignal/aborted flag=] is set to false,
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|.</dd> Set the [=AbortSignal/aborted flag=] be true. Then
reject the promise with an {{AbortError}} {{DOMException}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

whoops, we can't reject promises here because we don't have any promises -- this is a synchronous alg that's called on a background thread and (now) returns a (callback) alg to the caller. unfortunately, we've been working at a hectic pace and we have some loose ends in the spec, like mentioning promises where we now ought not. See issue #671.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like a bigger problem to fix here. If we do this here, it may be inconsistent. As jake mentioned in the other comment, our parallel calls here are problematic. I can work with you to submit a PR to clean this up.

Copy link
Contributor

@equalsJeffH equalsJeffH left a comment

Choose a reason for hiding this comment

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

have some additional comments, plus agree with @jyasskin's comments/review. It seems this abort notion is a tricky thing to incorporate.

index.bs Outdated
}

var options = {
// A list of options.
Copy link
Contributor

Choose a reason for hiding this comment

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

should signal be in options like so:

   var options = {
       signal: authAbortSignal`,
        // additional options....
   }

..? i.e., per credman now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@equalsJeffH I think authenticatorCancel should be sufficient to address the issue for now. We may need a more polished algorithm overall to describe the life cycle. But that is something we can do at PR time. I can collaborate with you and @jyasskin on that.


navigator.credentials.create({
publicKey: options,
signal: authAbortSignal})
Copy link
Contributor

Choose a reason for hiding this comment

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

signal is a part of options yes?
so this would be..

     navigator.credentials.create({
         publicKey: options
     })

..?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the current design is that the signal can be a separate field (as described in the example and in my other comment).

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, doh, nevermind, sorry.

index.bs Outdated
@@ -974,7 +982,8 @@ 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. Execute the following steps [=in parallel=] but abort if the steps are [=terminated=]. The [=task source=] for these [=tasks=]
Copy link
Contributor

Choose a reason for hiding this comment

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

agree w/@jyasskin that we may need to employ https://dom.spec.whatwg.org/#abortsignal-add techniques similar to how fetch handles aborting. It also seems that fetch has a notion of "An ongoing fetch can be terminated
with flag aborted
" which is local to the fetch spec, and not part of the DOM's "aborting ongoing activities". I wonder whether we might need to implement something similar.

@AngeloKai
Copy link
Contributor Author

@equalsJeffH @jyasskin @jcjones please review. This is now fully polished. Previously when I pushed commit, they were work in progress.

Copy link
Contributor

@equalsJeffH equalsJeffH left a comment

Choose a reason for hiding this comment

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

Is getting closer, but there is impedance mismatch between proposed changes here and how credman is invoking webauthn (webauthn does not have promises to reject). credman has abortSignal nominally incorporated, but its algs may need to employ it in a more fine grained fashion.

index.bs Outdated
@@ -547,6 +557,7 @@ the {{CredentialCreationOptions}} dictionary as follows:
<pre class="idl">
partial dictionary CredentialCreationOptions {
MakePublicKeyCredentialOptions publicKey;
AbortSignal signal;
Copy link
Contributor

Choose a reason for hiding this comment

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

hm, webappsec-credential-management/#dictdef-credentialcreationoptions already has a ssignal member of type AbortSignal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added this to deal with a bikeshed linking error. I can take it out.

index.bs Outdated
@@ -558,6 +569,7 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede
<pre class="idl">
partial dictionary CredentialRequestOptions {
PublicKeyCredentialRequestOptions publicKey;
AbortSignal signal;
Copy link
Contributor

Choose a reason for hiding this comment

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

index.bs Outdated
@@ -688,6 +702,10 @@ When this method is invoked, the user agent MUST execute the following algorithm

1. Let |clientDataHash| be the [=hash of the serialized client data=] represented by |clientDataJSON|.

1. If the <code>|options|.{{CredentialCreationOptions/signal}}</code> is [=present=] and its
[=AbortSignal/aborted flag=] is set to true, return [=a promise rejected with=] an "{{AbortError}}"
Copy link
Contributor

Choose a reason for hiding this comment

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

we cannot return a promise here because we're just a synchronous algorithm; the Promise resolution/rejection is handled by navigator.credentials.create(). see w3c/webappsec-credential-management#100

webappsec-credential-management/#algorithm-create already is checking for options.signal's aborted flag being set, but may need to also be doing it in the "in parallel" section. maybe we need to have webauthn's [[Create]]() alg simply return as quickly as possible if an abort situation arises, and then have webappsec-credential-management/#algorithm-create check for abort right after webauthn's [[Create]]() alg returns to it ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does the note in the abort section help explain this? The reality is abort is a new concept to web platform APIs and our current spec languages don't handle this that well. This is why the section mentions we are checking the aborted flag three times to "fake" the effect of rejecting immediately.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure. this will ostensibly get review from folks who are more familiar with specifying this new abort hotness and we will see.

index.bs Outdated
: If the <code>|options|.{{CredentialCreationOptions/signal}}</code> is [=present=] and its
[=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 promise rejected with=]
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto: can't reject promise here. may need to use https://dom.spec.whatwg.org/#abortsignal-add magic here a la how fetch is doing it. e.g. see step 8 of https://fetch.spec.whatwg.org/#fetch-method

index.bs Outdated
@@ -906,6 +930,10 @@ method is invoked, the user agent MUST:

1. Let |clientDataHash| be the [=hash of the serialized client data=] represented by |clientDataJSON|.

1. If the <code>|options|.{{CredentialRequestOptions/signal}}</code> is [=present=] and its
[=AbortSignal/aborted flag=] is set to true, return [=a promise rejected with=] an "{{AbortError}}"
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

webappsec-credential-management/#algorithm-request is checking options.signal's aborted flag. same thoughts as above, i.e., [[DiscoverFromExternalSource]]() needs to promptly return and webappsec-credential-management/#algorithm-request ought to check immediately for abort situation?

index.bs Outdated
true,
:: [=set/For each=] |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator|
and [=set/remove=] |authenticator| from |issuedRequests|. Then
return [=a promise rejected with=] an "{{AbortError}}" {{DOMException}}.
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto wrt no promise to return and perhaps needing abortsignal-add magic


navigator.credentials.create({
publicKey: options,
signal: authAbortSignal})
Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, doh, nevermind, sorry.

Issue: The WHATWG HTML WG is discussing whether to provide a hook when a browsing context gains or
loses focuses. If a hook is provided, the above paragraph will be updated to include the hook.
See [WHATWG HTML WG Issue #2711](https://github.com/whatwg/html/issues/2711) for more details.

Copy link
Contributor

Choose a reason for hiding this comment

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

above is helpful (nice!) but will need updating per however we end up polishing this abort functionality between webauthn & credman.

@equalsJeffH
Copy link
Contributor

further review: #544 (review) Thanks for tackling this @AngeloKai -- it's a gnarly one.

@AngeloKai AngeloKai dismissed jyasskin’s stale review November 3, 2017 23:27

jeffrey said he likely would get to the review in near term but anyone else can review this on behalf of him to check if his points got addressed.

@AngeloKai
Copy link
Contributor Author

s/jeffrey said he likely would get to the review in near term/jeffrey said he won't able to get the review in near term/

@AngeloKai
Copy link
Contributor Author

As a related note, Firefox has PRs in review to abort webauthn codes when the user switches tabs: https://bugzilla.mozilla.org/show_bug.cgi?id=1383799 and https://bugzilla.mozilla.org/show_bug.cgi?id=1409202. Edge kind of has an abort mechanism in place as well. This PR is just exposing the API to developers to let them abort.

Copy link
Contributor

@jcjones jcjones left a comment

Choose a reason for hiding this comment

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

This looks ready to merge to me. Major kudos to Angelo for this.

@jcjones
Copy link
Contributor

jcjones commented Nov 7, 2017

I think this needs your re-review, @equalsJeffH, and then merge it if you're good with it.

@equalsJeffH
Copy link
Contributor

given @jcjones' approval, i suppose we can merge this -- though, there are IMV unresolved aspects of it such as @jyasskin's comment #544 (comment) and these other ones:

#544 (comment)
#544 (comment)
#544 (comment)

@equalsJeffH equalsJeffH dismissed their stale review November 8, 2017 23:40

this seems good enough for now

@equalsJeffH equalsJeffH merged commit 931b46e into w3c:master Nov 8, 2017
@equalsJeffH equalsJeffH deleted the abort branch November 8, 2017 23:42
@jcjones
Copy link
Contributor

jcjones commented Nov 9, 2017

Thanks, I agree. It gets the functionality in here, and IMO the right wording for what should happen on aborts will be more clear when we actually try to do the thing. (Which is ... soon!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants