Re: Splitting "Credential Management"?

On Tue, Apr 4, 2017 at 6:34 AM, Mike West <mkwst@google.com> wrote:

> On Sat, Apr 1, 2017 at 12:59 AM, Jeffrey Yasskin <jyasskin@google.com>
> wrote:
>
>> Thoughts on the new merged version at https://w3c.github.io/webap
>> psec-credential-management/:
>>
>
> Thanks! I appreciate the thoughtful feedback!
>
>
>> 1) I'd like to start coming up with terms to distinguish "concept 1", the
>> thing you possess that gives you the right to do something, say, log in as
>> User X; and "concept 2", the proof that you have that thing. We're going to
>> need this for full federated credentials, SMS auth, and WebAuthn. Some
>> options: "credential"/"credential proof", "credential"/"attestation",
>> "credential generator"/"credential", "identity"/"credential". I lean toward
>> the first pair and will use those here, but I'm happy to take anything that
>> avoids conflating them.
>>
>
> I agree with you that "concept 1" and "concept 2" are different, but it's
> not clear to me that a developer would care. That is, if I'm understanding
> the distinction you're drawing, websites are always looking for a "concept
> 2", and it just happens to be identical to the "concept 1" in the case of
> passwords.
>

I can buy that, in which case using "Credential" for the only concept the
page needs would be really nice. I'd still like to have the spec *talk*
about the distinction between the concepts so that devs and implementers
reading it easily understand that passing around your webauthn credential
isn't intended to hand out the private key.


>   a) Does store() merely record that a particular CredentialProof worked
>> for a site, or does it save a whole Credential? The first is consistent
>> with the current behavior for "federated", and would imply that a WebAuthn
>> CredentialProof could be stored too. If the second, we'd change store()'s
>> parameter to be Credential, and passwords might be the only valid argument.
>>
>
> `store()` delegates to an internal method on the interface, so it can do
> whatever makes sense for a particular credential type.
>

While that's true, it'd be nice if we had a consistent story for the
meaning instead of a bunch of one-off behaviors. I'm getting more
comfortable with "this proof worked for this site", and then we can figure
out how to implement that for each credential type.


> For `PasswordCredential`, this is https://w3c.github.io/
> webappsec-credential-management/#store-passwordcredential, and it stores
> the ID, name, icon, origin, and password. For `FederatedCredential`, it's
> https://w3c.github.io/webappsec-credential-management/#store-
> federatedcredential, and stores the ID, name, icon, origin, provider, and
> protocol.
>
> For these types of credentials, `store()` is meant to update the contents
> of the credential store with whatever the website tells us is working. We
> expect `store()` to be used for password change forms, for instance.
>
> For WebAuthn, the model is probably different. Currently, the PR at
> https://github.com/w3c/webauthn/pull/384 defines the internal method as a
> noop, but there are two scenarios to think through:
>
> 1. The website is registering a webauthn authenticator via
> `ScopedCredential.create()`. In this case, we have all the information from
> the RP, and we have the whole response from the device. It's arguable that
> the user's interaction with the device would be enough to justify storing
> the relationship for future use in a chooser.
>
> 2. The website is asking for a `ScopedCredential`, but the UA has no idea
> whether one exists. So we ask the user, they insert their security key, and
> it responds in a reasonable way to a challenge. In this case, it's probably
> also reasonable to treat the user's interaction with the device as
> justification to store the relationship, but we don't have the information
> we would have in the registration step. In this case, `store()` might still
> be useful.
>

Yep.


> 4) I think we'll need the UA to store a notion of:
>>   a) Which credentials successfully log into which identities for a given
>> site. Call these "known" credentials. An "identity" is just the content
>> of CredentialUserData.
>>   b) Which credentials have been used successfully since the most recent
>> requireUserMediation(). Call these the "active" credentials?
>>
>>   When get() is called with options that match exactly one "active"
>> credential, the UA should try to get a proof for that credential, whether
>> it's a password, a federated identity, or a WebAuthn authenticator. Some of
>> these will do their own mediation, but the UA shouldn't show an extra
>> chooser.
>>
>
> How would a user change accounts in this world? Like, consider Google's
> (absurd) multi-login? If `accounts.google.com` wanted to allow you to
> sign in as `user2` when you're already signed in as `user1`, we support
> that today by forcing a chooser in cases where we don't have a single,
> unambigious credential of the type you're asking for.
>

A user would *change* accounts by having the site call
requireUserMediation(). However, I think I've broken the ability to *add*
an account. Will think on this more. *Maybe* an extra argument to get()
that forces mediation but doesn't otherwise clear the active set? ....

This is where a .cancel() operation
>> <https://github.com/w3c/webauthn/issues/380> would be useful.
>>
>
> This seems like it would be generally useful, yes. I wish that TC39 hadn't
> punted on the problem of cancellable promises, as it's what we need here.
>
> Fetch has a potentially-more-complicated-than-we-need controller model,
> which we could certainly copy. That is:
>
> ```
> var controller = new CredentialRequestController();
> navigator.credentials.get({ ..., signal: observer.signal }).then(...);
> controller.abort();
> ```
>
> There's probably some value in just following along with the path they're
> cutting, even if it allows more flexibility than we require. It also has
> the advantage of not changing anyone's existing code if they don't actually
> care about cancellation.
>

+1. Maybe we can get a generic CancelController object so that each spec
doesn't have to create its own.


> When get() is called with options that match zero or >=2 active
>> credentials, the UA may need to show a chooser. It should prioritize active
>> credentials over known credentials over unknown credentials, but it needs
>> to let the user select an unknown credential. In some cases, the get()
>> options might narrow down the set of credentials so that the UA can call a
>> single OS API to do the operation (e.g. "publicKey" with no active
>> credentials). In that case, the UA should still tell the OS to insist on
>> some sort of mediation.
>>
>
> I think this makes sense, but I think we might also need a chooser in
> cases where there's exactly 1 "active" credential of a given type. See the
> multi-login example above.
>
>
>> SMS and email auth might be interesting cases here: users might want one
>> of them to be the active credential, but still want to confirm receiving an
>> SMS or email. I guess we could treat that as the credential type doing its
>> own mediation?
>>
>
> If the browser does the work without asking the user, how would either be
> considered "mediated"?
>

I was suggesting that SMS might always need to be mediated, even if it's
the only active credential.


> 5) The credential store's different pieces might have different lifetimes:
>>   a) Passwords and UA-stored private keys need to survive "clear browsing
>> data", and so they need more user approval to be stored.
>>   b) Knowledge that the user has a removable WebAuthn authenticator or
>> has used SMS auth with a particular site can be cleared with a site's
>> cookies, and so maybe doesn't need any user approval to be stored.
>>
>
> What's the relevant distinction between things in a) and b) that would
> lead you to treat them differently?
>

If you delete (a), the user's ability to log into the site completely goes
away. If you delete (b), the user might have to do more navigation to get
back to the site, but they can still log in.

Jeffrey

Received on Tuesday, 4 April 2017 15:24:32 UTC