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 makeCredential() more precise. #347

Merged
merged 8 commits into from
Mar 1, 2017

Conversation

jyasskin
Copy link
Member

@jyasskin jyasskin commented Feb 15, 2017

I've linked a lot more terms, reordered explanations to be clearer, and
specified some missing behavior.

This fixes #263 and #273, and improves #254 and #270.

@jyasskin jyasskin changed the title Fix up makeCredential() to be as precise as a web spec. Make makeCredential() more precise. Feb 15, 2017
@jyasskin jyasskin force-pushed the clean-make-credential branch 2 times, most recently from a1c674c to f06fd29 Compare February 15, 2017 04:33
@annevk
Copy link
Member

annevk commented Feb 15, 2017

It seems #273 is the more elaborate issue.

index.bs Outdated
: {{challenge}}
:: The [=base64url encoding=] of {{attestationChallenge}}
: {{origin}}
:: |rpId|
Copy link
Member

Choose a reason for hiding this comment

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

rpId is not an origin though. It can be a host per the above steps.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, I haven't tackled #259 yet, and I think that'll be easier once w3c/html#769 (splitting the document.domain setter) is fixed. So fixing this will probably be a follow-up. That said, it's probably a good idea to specify this as one of the serializations of an origin, pretending |rpId| is already an origin.

Copy link
Member

Choose a reason for hiding this comment

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

w3c/html o_O

Copy link
Member Author

Choose a reason for hiding this comment

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

We'll get the fix in whatwg/html; w3c is just where the bug happened to get filed due to #257.

index.bs Outdated
: {{ClientData/extensions}}
:: <code>{{options}}.{{ScopedCredentialOptions/extensions}}</code>

10. Let |clientDataJSON| be the UTF-8 encoding of the result of calling the
Copy link
Member

Choose a reason for hiding this comment

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

You should link UTF-8 encoding to https://encoding.spec.whatwg.org/#utf-8-encode.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

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.

thanks @jyasskin, this seems to me to be an overall improvement to the prior state. I have some questions/suggestions inline.

@@ -62,6 +66,7 @@ spec: WebCryptoAPI; urlPrefix: https://www.w3.org/TR/WebCryptoAPI/
</pre> <!-- class=anchors -->

<pre class="link-defaults">
spec:infra; type:dfn; text:list
Copy link
Contributor

Choose a reason for hiding this comment

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

Add "infra" to the list of dependencies in {#dependencies}?

Copy link
Member Author

Choose a reason for hiding this comment

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

Bikeshed does provide an [INFRA] in https://api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/webauthn/clean-make-credential/index.bs#normative with the used terms listed in https://api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/webauthn/clean-make-credential/index.bs#index-defined-elsewhere. I can add it redundantly to #dependencies, or we could plan to remove that whole section in favor of the automated sections.

Copy link
Member

Choose a reason for hiding this comment

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

I usually state them upfront so readers know what kind of things to expect, but maybe we should stop doing that? Probably worth revisiting at some point.

Copy link
Contributor

Choose a reason for hiding this comment

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

@annevk said:

I usually state them upfront so readers know what kind of things to expect, but maybe we should stop doing that? Probably worth revisiting at some point

yeah, something to think about. of course i was lame and didn't search further down in doc for mentions/links to [INFRA]. [ perhaps also bikeshed could eventually construct a {#dependencies} section nearer front of specs. ]

Copy link
Member Author

Choose a reason for hiding this comment

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

Would y'all file an issue in https://github.com/tabatkins/bikeshed/issues about generating a dependencies section earlier in the document? For now ... I'm going to take this thread as an indication not to add "infra" to #dependencies unless y'all say otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jyasskin

Would y'all file an issue in https://github.com/tabatkins/bikeshed/issues about generating a dependencies section earlier in the document?

done: speced/bikeshed#937

I'm going to take this thread as an indication not to add "infra" to #dependencies unless y'all say otherwise.

ok, tho perhaps Dependencies section ought to have link to #index-defined-elsewhere ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

index.bs Outdated
Then asynchronously continue executing the following steps. If any fatal error is encountered in this process other than the
ones enumerated below, cancel the timer, reject |promise| with a DOMException whose name is "{{UnknownError}}", and terminate
this algorithm.
Issue: Put some constraints on the "reasonable range".
Copy link
Contributor

Choose a reason for hiding this comment

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

hm, maybe we should only add issues such as this to the spec if there are corresponding issues filed in https://github.com/w3c/webauthn/issues/ and said in-spec-issue explicitly links to the issue-in-github -- i.e., if any in-spec-issues remain at time of merge, they should have corresponding webauthn/issues/ filed.

e.g., some of the below in-spec-issues might be resolved by incorporating review feedback.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, that sounds right. I'm hoping to resolve most of the new Issues using your feedback during the review, but I'll file official issues for anything we can't resolve here.

Copy link
Contributor

Choose a reason for hiding this comment

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

sounds good, thx.

index.bs Outdated
cancel the timer started in step 2, return [=a promise rejected with=] with
a {{DOMException}} whose name is "{{NotSupportedError}}", and terminate this
algorithm.

Copy link
Contributor

Choose a reason for hiding this comment

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

add..
Issue(#266): is WRT the above step.
..?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've incorporated a fix for #266.

Copy link
Contributor

Choose a reason for hiding this comment

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

yay!

index.bs Outdated
an error occurs during this procedure, then [=continue=].
- [=list/Append=] a new object of type {{ScopedCredentialParameters}} to
|normalizedParameters|, with |type| set to
<code>|current|.{{ScopedCredentialParameters/type}}</code> and
Copy link
Contributor

Choose a reason for hiding this comment

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

add..
Issue(#265): is WRT the above step.
..? (and perhaps just go ahead and incorp the proposed solution?)

Copy link
Member Author

Choose a reason for hiding this comment

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

I've incorporated the proposed solution.

Copy link
Contributor

Choose a reason for hiding this comment

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

yay, tho there might be a modest bug in last substep: https://github.com/w3c/webauthn/pull/347/files#r103042454 ?

index.bs Outdated
<a>present</a>, then [=map/for each=] |extension| → |argument| of
<code>{{options}}.{{ScopedCredentialOptions/extensions}}</code>:
1. If |extension| is not supported by this client platform, [=continue=].
2. Let |result| be the result of running |extension|'s [=client processing=] algorithm on |argument|.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/algorithm/algorithm, if any,/

Copy link
Member Author

Choose a reason for hiding this comment

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

We'd need to define what to do with clientExtensions for an extension with no client processing algorithm. https://w3c.github.io/webauthn/#extensions says

Clients wishing to support the widest possible range of extensions may choose to pass through any extensions that they do not recognize to authenticators, generating the authenticator argument by simply encoding the client argument in CBOR.

I'd read that as a default definition of an extension's client processing, but I guess it's actually one of 3 alternatives for this step:

  1. The extension's defined client processing.
  2. CBOR-encode argument.
  3. Continue.

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
:: <code>{{options}}.{{ScopedCredentialOptions/extensions}}</code>

10. Let |clientDataJSON| be the UTF-8 encoding of the result of calling the
original value of {{JSON/stringify|JSON.stringify}} on |clientData|.
Copy link
Contributor

Choose a reason for hiding this comment

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

what is meant by "calling the original value of JSON.stringify"? The term "original" does not appear in or around https://tc39.github.io/ecma262/#sec-json.stringify

Copy link
Member Author

Choose a reason for hiding this comment

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

It should have been "initial", matching https://www.w3.org/2001/tag/doc/promises-guide/#a-new-promise. It's a fuzzy term, but it's meant to defend against the web page assigning a new value to window.JSON or to JSON.stringify.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, ok, thx.

index.bs Outdated
14. [=set/For each=] |authenticator| in |currentlyAvailableAuthenticators|:
1. Let |excludeList| be a new [=list=].
2. [=list/For each=] credential |C| in <code>{{options}}.{{ScopedCredentialOptions/excludeList}}</code>:
1. If |C| has an empty {{transports}} list, [=list/append=] it to
Copy link
Contributor

Choose a reason for hiding this comment

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

s/it/|C|/

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

index.bs Outdated
2. If |authenticator| is connected over a transport not mentioned in
<code>|C|.{{transports}}</code>, the client MAY [=continue=].

Issue: I'm not sure this captures the intent of the original wording.
Copy link
Contributor

Choose a reason for hiding this comment

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

upon review it seems to me it does capture the original intent, tho would be good to get @vijaybh's (at least) review.

index.bs Outdated
this client platform, to produce the extension data that needs to be sent to the authenticator. If an error is encountered
while processing an extension, skip that extension and do not produce any extension data for it. Call the result of this
processing |clientExtensions|.
Issue: Should "process" be "algorithm", or does it actually mean an OS
Copy link
Contributor

Choose a reason for hiding this comment

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

seems to me "process" should be "algorithm".

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed, but @vijaybh should double-check.

index.bs Outdated
while processing an extension, skip that extension and do not produce any extension data for it. Call the result of this
processing |clientExtensions|.
Issue: Should "process" be "algorithm", or does it actually mean an OS
process? What kinds of fatal errors are you worried about? I suggest we just
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me a case can be made that any possible errors are covered by the "If any authenticator returns an error status", but perhaps @vijaybh was worried about various platform-level errors that might percolate up in real life, eg when calling authenticatorCancel (e.g., authnr is now unreachable) and intends for that sentence to be a catch-all.

@equalsJeffH equalsJeffH added this to the WD-05 milestone Feb 23, 2017
I've linked a lot more terms, reordered explanations to be clearer, and
specified some missing behavior.

This fixes w3c#273 and improves w3c#270.
@jyasskin jyasskin force-pushed the clean-make-credential branch 6 times, most recently from c403edc to 3268d0c Compare February 24, 2017 20:25
index.bs Outdated
:: UA-chosen

Issue: We need *some* constraints on the possible hash algorithms, or
else sites will fail on unusual UAs.
Copy link
Contributor

Choose a reason for hiding this comment

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

pick from this set: {"SHA-256", "SHA-384", "SHA-512"}, c.f. webcrypto ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm inclined to keep that policy decision out of this PR. :) I've filed an issue and referred to it from here.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK

Copy link
Contributor

Choose a reason for hiding this comment

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

OK

index.bs Outdated
an error occurs during this procedure, then [=continue=].
1. [=list/Append=] the pair of
<code>|current|.{{ScopedCredentialParameters/type}}</code> and
|algorithm| to |normalizedAlgorithm|.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/normalizedAlgorithm/normalizedParameters/ ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Argh, yes.

index.bs Outdated
1. If the {{ScopedCredentialOptions/extensions}} member of {{options}} is
<a>present</a>, then [=map/for each=] |extension| → |argument| of
<code>{{options}}.{{ScopedCredentialOptions/extensions}}</code>:
1. If |extension| is not supported by this client platform, [=continue=].
Copy link
Contributor

Choose a reason for hiding this comment

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

given #347 (comment), should this substep be something like..

If |extension| is not supported by this client platform, either [=continue=], or optionally CBOR-encode |argument| as a byte string, and go to Step 4.

..@vijaybh ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added some text along those lines, and a new issue to make it better.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK.

:: |clientExtensions|

1. Let |clientDataJSON| be the [=UTF-8 encoding=] of the result of calling the
initial value of {{JSON/stringify|JSON.stringify}} on |clientData|.
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that this fixes #274 by precisely specifying the JSON format. Do UAs need to use the flexibility that was here before?

Copy link
Member

Choose a reason for hiding this comment

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

No, we don't want to allow for multiple JSON implementations (and then later have that end up being required due to some silliness/webness).

Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to make this change centrally in https://w3c.github.io/webauthn/#clientdata-clientdatajson and reference that here. Maybe we should also change the variable name so that we can say something like "Generate the [=clientDataJSON=] and call it |J|."

@annevk
Copy link
Member

annevk commented Feb 25, 2017

(Please don't use usernames in commit messages anymore. The spam is annoying and can persist rather long.)

@jyasskin
Copy link
Member Author

I'm at a C++ committee meeting this week and on vacation next week, so if you want any more changes to this PR, either please feel free to make them yourself, or I'll get to them the week of March 13.

@equalsJeffH
Copy link
Contributor

LGTM, thanks @jyasskin.

Then asynchronously continue executing the following steps. If any fatal error is encountered in this process other than the
ones enumerated below, cancel the timer, reject |promise| with a {{DOMException}} whose name is "{{UnknownError}}", and terminate
this algorithm.
Issue: Put some constraints on the "reasonable range".
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use Github to track issues and not put them in the document? Over time it becomes painful to track these inline issues.

Copy link
Contributor

Choose a reason for hiding this comment

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

see thread here: #347 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Straw man: I chose [15,120] seconds as the range for timeouts in the first Firefox implementation. Under 15 seconds seems too short to dig out a security key, and making things blink for more than two minutes seemed cruel.


1. Set |callerOrigin| to the <a>current settings object</a>'s <a>origin</a>. If |callerOrigin| is
an <a>opaque origin</a>, reject |promise| with a {{DOMException}} whose name is "{{NotAllowedError}}", and
an <a>opaque origin</a>, [=reject=] |promise| with a {{DOMException}} whose name is "{{NotAllowedError}}", and
Copy link
Contributor

Choose a reason for hiding this comment

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

But you deleted the previous step where |promise| was instantiated, so at this point it is undefined. I suspect what you want to do is keep the part about creating |promise| in the step above this one, and only go async by returning |promise| later on.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, just get rid of |promise| entirely, use [=a promise rejected with=] here and in other error cases, and use [=a new promise=] when going async below.

<code>{{options}}.{{ScopedCredentialOptions/extensions}}</code>:
1. If |extension| is not supported by this client platform, then either:
* [=Continue=], or
* Let |result| be a CBOR ([[!RFC7049]]) encoding of |extension|.
Copy link
Contributor

Choose a reason for hiding this comment

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

The consensus among implementers was that we do not want to blindly pass extensions along since this may break a contract the UA has with the user, e.g. you may blindly pass along a location extension when the user has asked to block location info. So maybe we should have [=Continue=] as the only option.

: {{origin}}
:: The [=unicode serialization of an origin|unicode serialization=] of |rpId|
: {{hashAlg}}
:: UA-chosen
Copy link
Contributor

Choose a reason for hiding this comment

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

We've intentionally avoided using "UA" anywhere in this spec. Suggest "Hash algorithm for generating clientDataHash, by the client". Maybe we could recommend SHA-256 for maximum interop.

initial value of {{JSON/stringify|JSON.stringify}} on |clientData|.

Issue: Some extensions contain ArrayBuffers, which don't stringify well.
What's the intent here?
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should base64url encode those just as we do with the challenge. Need to update client processing sections of extensions, maybe this should be part of #363.

:: |clientExtensions|

1. Let |clientDataJSON| be the [=UTF-8 encoding=] of the result of calling the
initial value of {{JSON/stringify|JSON.stringify}} on |clientData|.
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to make this change centrally in https://w3c.github.io/webauthn/#clientdata-clientdatajson and reference that here. Maybe we should also change the variable name so that we can say something like "Generate the [=clientDataJSON=] and call it |J|."

1. [=list/For each=] credential |C| in <code>{{options}}.{{ScopedCredentialOptions/excludeList}}</code>:
1. If |C| has an empty {{transports}} list, [=list/append=] |C| to
|excludeList| and [=continue=].
1. If |authenticator| is connected over a transport not mentioned in
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an "else" from the previous point, right? This seems a bit unwieldy.

Perhaps we could replace all these three sub-bullets with two:

  1. If |C|.{{transports}} is non-empty and does not contain the transport that |authenticator| is connected over, the client MAY [=continue=].
  2. [=list/Append=] |C| to |excludeList|.

any), to create a {{ClientData}} structure representing this request. Choose a hash algorithm for {{ClientData/hashAlg}} and
compute the {{ScopedCredentialInfo/clientDataJSON}} and its <a>clientDataHash</a>.
Issue: What kinds of fatal errors are you worried about? I suggest we just
remove that sentence.
Copy link
Contributor

Choose a reason for hiding this comment

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

OK. The initial intention was to guard against things like low-level communication breakdowns between client and authenticator, but maybe that becomes part of "any authenticator returns an error" now.

"{{NotAllowedError}}".

Issue: {{NotAllowedError}} seems incorrect for at least the timeout and
cancelled exit conditions above.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested alternative? If you have one we can just put it in with this PR or open a new issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've filed #376 for this.

@@ -542,7 +643,7 @@ When this method is invoked, the user agent MUST execute the following algorithm
execute a platform-specific procedure to determine which, if any, credentials listed in {{AssertionOptions/allowList}}
might be present on this authenticator, and set |credentialList| to this filtered list. If no such filtering is
possible, set |credentialList| to an empty list.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should that be [=list=]? Or were you planning to make getAssertion more precise along similar lines in a new PR?

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.

On call we agreed to address the comments in a follow-up PR. This looks good to merge, so we don't have to rebase it.

@vijaybh
Copy link
Contributor

vijaybh commented Mar 1, 2017

As discussed on the call, merging this now. I will send out a new PR with changes reflecting my comments.

@vijaybh vijaybh merged commit 546f82f into w3c:master Mar 1, 2017
vijaybh added a commit that referenced this pull request Mar 2, 2017
@jyasskin jyasskin deleted the clean-make-credential branch March 3, 2017 05:50
vijaybh added a commit that referenced this pull request Mar 3, 2017
* Refine makeCredential description

As per my comments on #347

* Incorporate feedback from JeffH, part 1

* Incorporate feedback from JeffH, part 2

Use "client data" and "authenticator data" instead of "ClientData" and
"authenticatorData".

* Incorporate feedback from JeffH, part 3

Tag all instances of "client data", "authenticator data" and
"attestation data".
vijaybh pushed a commit that referenced this pull request Mar 4, 2017
* Refine makeCredential description

As per my comments on #347

* Incorporate feedback from JeffH, part 1

* [=scoped credential(s)=]

* [=authenticator=]

* [=[RP]=]

* [=Conforming User Agent=]

* [=[RPS]=]

* [=authenticators=]

* [=Web Authentication API=]

* [=Registration=]

* [=Authentication=]

* {{makeCredential()}} and {{getAssertion()}}

* [=User Verification=]

* [=Authentication Assertion=]

* [=authorization gesture=]

* [=user consent=]

* '<a>...</a>' to be '[=...=]' thru terminology section

* Incorporate feedback from JeffH, part 2

Use "client data" and "authenticator data" instead of "ClientData" and
"authenticatorData".

* Incorporate feedback from JeffH, part 3

Tag all instances of "client data", "authenticator data" and
"attestation data".

* [=present=]

* [=attestation object=]

* [=effective domain=]

* <a>...</a> replacement up to {#iface-credentialInfo}

* <a>...</a> replacement up to {#attestation-formats}

* <a>...</a> replacement thru rest of spec

* refined regex and caught <a>...</a> stragglers

* Fix two typos and some locally anomalous line lengths

Typos - weird character in "Subsequently" on line 84, "user
verification" missing on line 104 (old) / 102 (new).
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.

WebCryptoAPI's "normalizing an algorithm" cannot be done as part of async steps
6 participants