-
Notifications
You must be signed in to change notification settings - Fork 180
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
Conversation
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. |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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|.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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") { |
There was a problem hiding this comment.
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") { |
There was a problem hiding this comment.
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 () { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
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}}. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}) |
There was a problem hiding this comment.
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
})
..?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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=] |
There was a problem hiding this comment.
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.
@equalsJeffH @jyasskin @jcjones please review. This is now fully polished. Previously when I pushed commit, they were work in progress. |
There was a problem hiding this 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; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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}}" |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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=] |
There was a problem hiding this comment.
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}}" |
There was a problem hiding this comment.
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}}. |
There was a problem hiding this comment.
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}) |
There was a problem hiding this comment.
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. | ||
|
There was a problem hiding this comment.
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.
further review: #544 (review) Thanks for tackling this @AngeloKai -- it's a gnarly one. |
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.
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/ |
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. |
There was a problem hiding this 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.
I think this needs your re-review, @equalsJeffH, and then merge it if you're good with it. |
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: |
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!) |
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