- From: Dirk Balfanz <balfanz@google.com>
- Date: Fri, 07 Apr 2017 21:10:18 +0000
- To: Jeffrey Yasskin <jyasskin@google.com>, public-webauthn@w3.org
- Message-ID: <CADHfa2DLyxkLni+pc+332RtMwjsTSnKJbcQTNjVPwFSXkY4ZKg@mail.gmail.com>
Ok, I'll bite and argue the counter-point. :-) To be sure, I'm not arguing against exposing webauthn though navigator.credentials, I'm arguing against accepting https://github.com/w3c/webauthn/pull/384 as is, because I believe it will create a lot of future work for us that will slow us down. Just one small example: you're highlighting in your message (which is a "pitch to accept #384") the benefits of a createAndStore() method. But such a method isn't part of #384. Presumably, we would have to do some work to get from the state in #384 to having consensus around createAndStore(). There are quite a few open questions that accepting #384 would raise; I'll list some below: - It seems that requireUserMediation() is a no-op for ScopedCredentials. But webauthn *does* have a notion of different kinds of tests of user presence, they're just expressed differently in the API. In particular, we distinguish between a simple test of user presence, and "user verification". We're toying with the idea of introducing another "level" of user-presence-test (user presence not required), but it's not clear whether requireUserMediation() and its specific semantics are a good match for that. For starters, in webauthn, whether or no a test-of-user presence is required will likely be an attribute of an authenticator, not of an origin (and there are other open questions). To summarize, how requireUserMediation() and the various levels of tests-of-user-presence in webauthn will collide or merge is not clear. - We're thinking of introducing new methods to webauthn, such as a cancel() method, and a promoteAuthenticatorIfAvailable() method. For each of those, we'd have to think through what they mean for the other type of credentials, and will have a problem if we realize that they don't apply well to non-ScopedCredentials. - Similar to requireUserMediation(), store() seems to be a no-op for SopedCredentials. I think Jeffrey is trying to address this with this proposal for a createAndStore() method, but the necessity to even come up, and consider, and discuss, and argue over, and get consensus on, these kinds of new proposals arises only if we accept #384 as is. - Create() is a no-op for passwords. In #384, it's therefore a static method on ScopedCredential. That seems a bit strange. Jeffrey seems to agree, and therefore proposes a createAndStore() method. But that means more arguing, etc. (see above) To level this up a little: as y'all know, I agree with the goal of de-confusing developers. I think we should make sure that the relative positioning of these two APIs is clear. I appreciate that one possible way to explain the relative positioning is to say "they're really the same thing; if you squint you won't even notice the difference between these types of credentials". But if #384 is our best attempt at taking that position, I'm afraid it looks to me like that that story doesn't hold together. Another approach to de-confusing would be to say "these are different things - they're related, but different in fundamental ways". We can tell *that* story by re-naming things to stress the differences (SiteBoundCredential and ScopedCredential is way too similar, for example), by having a common root (both in inheritance and namespace), but by having different sets of method that behave differently. Which brings me to my last point - how important is it to have a single get() method that can deal with passwords and Authenticators, and a single createAndStore() method that also makes sense for passwords and Authenticators? I don't want to argue that it's fundamentally a bad idea to try for that unification, but I also don't think that it will really help that much. Take Jeffrey's example for createAndStore() below. I don't believe an RP would actually ever make such a call. Creating a publicKey credential is typically something done *after* the user signed in, let alone created an account, whereas creating a password credential is presumably something that happens as the user is signing up for/creating an account. So I would anticipate that those two calls (createAndStore({password}) and createAndStore({publicKey})) would happen in different contexts, on different pages, and that it wouldn't be the end of the world if they looked slightly different. Similar arguments apply to a unified get() - I believe that password users and Authenticator users follow sufficiently different paths through a web site's UI that the benefits of a unified get() methods aren't that huge. Ok, I'll stop now - the post is already long enough. :-) But I do worry that we add months to our timeline by incorporating #384. Thanks, Dirk. On Wed, Apr 5, 2017 at 9:58 AM Jeffrey Yasskin <jyasskin@google.com> wrote: I'm not going to be able to call in today, so I figured I'd send my pitch to accept https://github.com/w3c/webauthn/pull/384 by email. I think the biggest benefit of exposing webauthn credentials through the same interface as passwords is that it lets us achieve Jeff Hodges' goal of a single interface for webpages to use for sign-in, and with a small change to #384 another single interface for credential creation. For credential creation, the page needs to give the user a choice of which kind of credential to create. Adding a couple more credential types, that could look something like: navigator.credentials.createAndStore({ accountInfo: {userid, username, userImage, siteName}, acceptableCredentials: { password: {minEntropy: 64}, federated: {providers:["https://accounts.google.com", " https://www.facebook.com", ...]}, sms: { checkBy: { send: function(number) {/*Ask the server to send an SMS*/}, vouch: [googleKey, samsungKey, appleKey, ...], }, }, publicKey: { // <-- Better name for webauthn credentials. attestationChallenge: crypto.getRandomValues(sixteen_byte_buffer), cryptoParameters: [{algorithm: "ES256"}], // Do we need a filter for acceptable attestation certificates? }, } }); The UI flow would be: 1) Get and validate a username, possibly taking advantage of autocomplete="username" or autocomplete="email". 2) Call .createAndStore(). 3) Potentially call .createAndStore() again to get a second factor. You need a library to help manage the different credential types on the server, but using a single call lets the browser guide the user through their choice of credential types instead of needing to expose several "can I use this" functions to the website. Sign-in needs this unification a bit less, since most users have exactly one credential for a given account, and in those cases, the site can dispatch to the single call that works for that credential type. But some users have multiple credentials for a given account, and in those cases, letting the browser manage which credential to use would be useful. navigator.credentials.get({ username, password: true, federated: {providers:["https://accounts.google.com", " https://www.facebook.com", ...]}, sms: { checkBy: { send: function(number) {/*Ask the server to send an SMS*/}, }, }, publicKey: { // <-- Better name for webauthn credentials. challenge: crypto.getRandomValues(sixteen_byte_buffer), allowList: {id: id_for_username}, }, } }); Jeffrey
Received on Friday, 7 April 2017 21:11:04 UTC