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

Add isPlatformAuthenticatorReady function to the API surface #379

Merged
merged 21 commits into from Aug 2, 2017
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
86c561b
Added first draft of the requestPlatformAuthenticator
AngeloKai Mar 14, 2017
156d2a4
Merge branch 'master' into angelo-findAuthnr
AngeloKai Mar 14, 2017
d188369
Added second draft of the isAuthnrReady function
AngeloKai Mar 14, 2017
30d18a2
Made suggestion based on feedback about leaving out getA + add TUI bit
AngeloKai Mar 16, 2017
45f7c56
Cleaned up algorithm based on feedbacks + remove mention of requireUs…
Mar 20, 2017
d3a8147
Refined languages in describing the intent of the method
Mar 20, 2017
4e3df5d
Merged with upstream master
AngeloKai Jun 3, 2017
0c930fa
Merged remote master
AngeloKai Jun 9, 2017
bf70a75
Added example to PR
AngeloKai Jun 12, 2017
9d073c0
Added IDL interface
AngeloKai Jun 16, 2017
8661baf
Merge branch 'angelo-findAuthnr' of https://github.com/AngeloKai/weba…
selfissued Jul 28, 2017
e22cb12
Incorporated comments by @equalsJeffH and @jcjones
selfissued Jul 28, 2017
bb139cd
Attempt to fix bikeshed markdown errors
selfissued Jul 28, 2017
7f58862
Merge branch 'master' into angelo-findAuthnr
Jul 31, 2017
8941f84
fix BS linking errors
Jul 31, 2017
6c5a055
restore spacing in '<dl>...</dl>' section
Jul 31, 2017
d131f64
spacing fixups
Jul 31, 2017
f7c7da5
Applied Jeff Hodges' comments
selfissued Aug 2, 2017
61826b1
Merge branch 'angelo-findAuthnr' of https://github.com/AngeloKai/weba…
selfissued Aug 2, 2017
f310667
Applied a missed change and corrected a syntax error
selfissued Aug 2, 2017
e924709
Merge branch 'master' of https://github.com/w3c/webauthn into angelo-…
selfissued Aug 2, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 77 additions & 7 deletions index.bs
Expand Up @@ -72,9 +72,11 @@ spec: WebCryptoAPI; urlPrefix: https://www.w3.org/TR/WebCryptoAPI/
text: AlgorithmIdentifier; url: dfn-AlgorithmIdentifier

<!-- Remove these once Shepherd picks up the changes -->
spec: CREDENTIAL-MANAGEMENT-1; urlPrefix: https://w3c.github.io/webappsec-credential-management/
spec: credential-management-1; urlPrefix: https://w3c.github.io/webappsec-credential-management/
type: dictionary
text: CredentialCreationOptions; url: dictdef-credentialcreationoptions
type: dictionary
text: CredentialRequestOptions; url: dictdef-credentialrequestoptions
for: Credential
type: method
text: [[Create]](options)
Expand All @@ -88,7 +90,7 @@ spec:html; type:dfn; for:environment settings object; text:global object
spec:infra; type:dfn; text:list
spec:url; type:dfn; text:domain
spec:url; type:dfn; text:valid domain;
spec:webappsec-credential-management-1; type:dictionary; for:/; text:CredentialRequestOptions
<!-- spec:webappsec-credential-management-1; type:dictionary; for:/; text:CredentialRequestOptions -->
spec:webidl; type:interface; text:Promise
</pre>

Expand Down Expand Up @@ -920,9 +922,9 @@ When this method is invoked, the user agent MUST execute the following algorithm
entries created by running each extension's [=client extension processing=] algorithm to create the [=client
extension outputs=], for each [=client extension=] in {{AuthenticatorResponse/clientDataJSON}}.clientExtensions.

3. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on
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.
4. Return |value| and terminate this algorithm.
</dd>
</dl>

Expand All @@ -933,6 +935,35 @@ authorizing an authenticator with which to complete the operation.
</div>


### Platform Authenticator Availability - PublicKeyCredential's `isPlatformAuthenticatorAvailable()` method ### {#isPlatformAuthenticatorAvailable}

<div link-for-hint="WebAuthentication/isPlatformAuthenticatorAvailable">

[=[RPS]=] use this method to determine whether they can create a new credential using a [=platform authenticator=].
Upon invocation, the [=client=] employs a platform-specific procedure to discover available [=platform authenticators=].
If successful, the [=client=] then assesses whether the user is willing to create a credential using one of the available [=platform authenticators=].
This assessment may include various factors, such as:
- Whether the user is running in private or incognito mode.
- Whether the user has configured the [=client=] to not create such credentials.
- Whether the user has previously expressed an unwillingness to create a new credential for this [=[RP]=],
either through configuration or by declining a user interface prompt.
- The user's explicitly stated intentions, determined through user interaction.
If this assessment is affirmative, the promise is resolved with the value of <i>True</i>.
Otherwise, the promise is resolved with the value of <i>False</i>.
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 that it's possible to both
a) avoid dangling promises
b) preserve privacy in all cases other than the user's explicit consent
by adding wording that mandates that the agent must determine and use a static timeout for reporting any and all false responses. i.e,:
If the user consents, the promise immediately resolves with True.
In any other situation (responds no, ignores the prompt, doesn't have platform authenticator), the promise holds onto that response and resolves with False only at the appointed time.
(Maybe this was what you intended, but it probably should be specifically stated.)

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 am not sure if adding a timer here helps much. Based on past experience, it appears that timer can be quite a battery drain on the platform. @jyasskin can potentially weigh in. Since the promise will be resolved eventually, The timer here also won't buy us much privacy too. The timer simply delays the exposure of the information but doesn't actually preserve privacy. If a bad guy truly wants to fingerprint, s/he can still do so.

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding arbitrary delays seems like it could result in some truly terrible user experiences as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

The delay shouldn't be user visible in any sense, since the rp's action upon receiving "false" is to do nothing. A "true" response, which does require some user action or interaction, would be conveyed immediately. The concept of the delay is just to hide which of the three failure cases is resolving the promise.
(I don't disagree that it might cause possible battery drain.)

I guess my real question might be - have we agreed to give up on the idea that we shouldn't try to prevent fingerprinting?

Copy link
Contributor

Choose a reason for hiding this comment

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

I question whether the RP's action upon receiving "false" is to do nothing. In at least some use cases, I expect that the RP's action would be to immediately try to use non-platform authenticators. This "immediately" would include an intervening long delay, making for a terrible UX, if the proposal to wait is adopted.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that use cases in which someone would use a user-verifying platform authenticator overlap with use cases in which someone would use a non-platform authenticator.

To repeat @kpaulh 's question: Do we no longer have the goal of hiding the existence-vs-non-existence of authenticators from RPs unless the user approves? If we don't care, then I think we should take a different approach altogether - a GetAuthenticatorInfo() call or some such thing. But I think we should care, and that means that the two different error conditions (authenticator-doesn't-exist vs authenticator-exists-but-user-declines-use) should be indistinguishable.

Copy link
Member

Choose a reason for hiding this comment

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

A few small questions:

  1. If the RP is expected to act only in the case of a positive response, then it's not clear that providing a negative response has any impact over and above the fingerprinting risk. I'd suggest that we could simply not send a negative response (e.g. not resolve the Promise).

  2. Is the RP's response to a positive response defined? Talking to @kpaulh, it's not clear that there's agreement between vendors about what question we'd be asking users, and what the response to that question would actually mean. I worry a bit about the UI we'd present in that case, and what we'd actually ask users to approve.

  3. Given the amount of time folks generally spend on login pages, it seems like a delay long enough to be meaningful would be functionally indistinguishable from just not resolving the promise. What kind of timeout were you considering, @kpaulh? (On this point, it's not really clear to me why it would be bad for the promise not to resolve... That's totally something that can happen, and that callers need to deal with (e.g. return new Promise(_ => {});).

Based on the result, the [=[RP]=] can take further actions to guide the user to create a credential.

This method has no arguments and returns a boolean value.

<pre class="idl">
[SecureContext]
partial interface PublicKeyCredential {
[Unscopable] Promise < boolean > isPlatformAuthenticatorAvailable();
};
</pre>

</div>


## Authenticator Responses (interface <dfn interface>AuthenticatorResponse</dfn>) ## {#iface-authenticatorresponse}

[=Authenticators=] respond to [=[RP]=] requests by returning an object derived from the
Expand Down Expand Up @@ -1822,7 +1853,7 @@ WebAuthn supports multiple attestation types:
[[#sec-attestation-privacy]] for futher information.

: <dfn>Self Attestation</dfn>
:: In the case of [=self attestation=], also known as surrogate basic attestation [[UAFProtocol]], the Authenticator doesn't have
:: In the case of [=self attestation=], also known as surrogate basic attestation [[UAFProtocol]], the Authenticator does not have
any specific attestation key. Instead it uses the authentication key itself to create the attestation signature.
Authenticators without meaningful protection measures for an attestation private key typically use this attestation type.

Expand Down Expand Up @@ -1905,7 +1936,7 @@ in several ways, including:

- A WebAuthn Authenticator can implement [=Elliptic Curve based direct anonymous attestation=] (see [[FIDOEcdaaAlgorithm]]).
Using this scheme, the authenticator generates a blinded attestation signature. This allows the [=[RP]=] to verify the
signature using the [=ECDAA-Issuer public key=], but the attestation signature doesn't serve as a global correlation handle.
signature using the [=ECDAA-Issuer public key=], but the attestation signature does not serve as a global correlation handle.


#### Attestation Certificate and Attestation Certificate CA Compromise #### {#ca-compromise}
Expand Down Expand Up @@ -3219,9 +3250,11 @@ platform to enumerate all the authenticator's credentials so that the client can
## Registration ## {#sample-registration}

This is the first-time flow, in which a new credential is created and registered with the server.
In this flow, the [=[RP]=] does not have a preference for [=platform authenticator=] or [=roaming authenticators=].

1. The user visits example.com, which serves up a script. At this point, the user must already be logged in using a legacy
1. The user visits example.com, which serves up a script. At this point, the user may already be logged in using a legacy
username and password, or additional authenticator, or other means acceptable to the [=[RP]=].
Or the user may be in the process of creating a new account.

2. The [=[RP]=] script runs the code snippet below.

Expand Down Expand Up @@ -3292,6 +3325,43 @@ The sample code for generating and registering a new key follows:
});
</pre>

## Registration Specifically with Platform Authenticator ## {#sample-registration-with-platform-authenticator}

This is flow for when the [=[RP]=] is specifically interested in creating a public key credential with
a [=platform authenticator=].

1. The user visits example.com. At this point, the user is likely already logged in.

1. The [=[RP]=] script runs the code snippet below.

1. The user agent asks the user whether they are willing to register with the [=[RP]=] using an available [=platform authenticator=].

1. If the user is not willing, terminate this flow.

1. The user is shown appropriate UI and guided in creating a credential using one of the available platform authenticators.
Upon successful credential creation, the RP script conveys the new credential to the server.

<pre class="example" highlight="js">
if (!PublicKeyCredential) { /* Platform not capable of the API. Handle error. */ }

navigator.credentials.isPlatformAuthenticatorAvailable()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should hang off PublicKeyCredential, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For consistency purposes, yes. But I don't think it actually matters. They mean the same thing anyway.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wat?

Aren't you modifying navigator.credentials in one case (like for all navigator.credentials) and just the webauthn stack in the other case? Or am I totally confused?

Copy link
Member

Choose a reason for hiding this comment

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

  1. It does matter.
  2. They don't mean the same thing.

I'd suggest that we update this example to match the IDL in https://w3c.github.io/webauthn/#isPlatformAuthenticatorAvailable.

Copy link
Contributor

Choose a reason for hiding this comment

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

So we get the proposed change right the first time, can someone who's advocating for the change please propose the exact change wanted - along the lines of s/abc/xyz/ ? Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is someone crafting a new PR with which to propose a resolution to this discussion?

.then(function (userIntent) {

// If the user has affirmed willingness to register with RP using an available platform authenticator
if (userIntent) {
var publicKeyOptions = { /* Public key credential creation options. */};

// Create and register credentials.
return navigator.credentials.create({ "publicKey": publicKeyOptions });
}

// If user is not interested, don't ask the user to register.
}).then(function (newCredentialInfo) {
// Send new credential info to server for verification and registration.
}).catch( function(err) {
// Something went wrong. Handle appropriately.
});
</pre>

## Authentication ## {#sample-authentication}

Expand Down