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

Breaking change in Chrome 95/W10 #1677

Closed
cyberphone opened this issue Oct 24, 2021 · 9 comments
Closed

Breaking change in Chrome 95/W10 #1677

cyberphone opened this issue Oct 24, 2021 · 9 comments

Comments

@cyberphone
Copy link

cyberphone commented Oct 24, 2021

To date it seems that RPs do not use the transports argument because at least Windows and Android clients work fine without it. However, starting with Chrome 95 this has changed because otherwise you get an extra authenticator dialog also for logins using Windows.

That getTransports() fails on Firefox is another indication that transports is currently not used.

Testing:

  • go to https://webauthn.io/
  • set Authenticator Type to "platform"
  • register. Uses Windows Hello only
  • login. Now you get an additional authenticator selection dialog not related to Windows Hello. It should not be there in a proper RP implementation

chrome

Although the change is compliant with the spec., the WebAuthn ecosystem is apparently unprepared for this update. I have not tested on Mac OS or iOS.

The enhanced behavior shows up on GitHub as well.

I'm probably stupid, but isn't requiring RP servers to support transports for get() in conflict with discoverable authenticators?

@sbweeden
Copy link
Contributor

sbweeden commented Oct 25, 2021

I don't see a spec issue here. If a bug is suspected in Chrome, perhaps that is better reported at https://bugs.chromium.org/

The use of transports in WebAuthn is a hint to browsers, optionally available as part of the credential descriptors in an allowCredentials list during navigator.credentials.get(). The idea is that the browser may be able to optimise the UX based on the transports value of credential descriptors defined in allowCredentials. If no allowCredentials list is supplied, then:

  • by definition there are no transports available to optimise the UX based on the characteristics of the authenticator that the user may have and
  • only discoverable credentials (not authenticators) may be used

Not all browsers support getTransports() - that will come down to the version of WebAuthn supported in a browser-specific implementation (getTransports() was only introduced in L2). It is not required that transports be included as part of public key credential descriptors in an allowCredentials list (this is an optional member). When however these things are available and included with an allowCredentials list in an authentication ceremony, it does permit an optimised UX.

@cyberphone
Copy link
Author

I was initially thinking about reporting this as a Chromium bug but after researching the issue more including testing with adding transports to allowCredentials (which worked as expected), it would be technically incorrect calling this a bug.

However, due to Chrome's massive market-share, their implementation can be regarded as "normative". IMO you actually need a browser/OS-local list of registered credentials in order to get a reasonable and predictable UX. That is, transports should remain redundant for get() which effectively would be a specification change or clarification.

Requiring RPs to supply additional information to feed get() is a clear disadvantage and incompatible with the current WebAuthn ecosystem. With a proper WebAuthn implementation credentialId should be sufficient for targeting a specific credential, while the absence of credentialId should provide the user with a list of applicable credentials to choose from.

I would like to hear what GitHub and their likes think about the Chrome update.

@emlun
Copy link
Member

emlun commented Oct 25, 2021

IMO you actually need a browser/OS-local list of registered credentials in order to get a reasonable and predictable UX.

That isn't sufficient, though, because the browser cannot assume that the user has not at some point used a different browser or machine to register some credentials.

With a proper WebAuthn implementation credentialId should be sufficient for targeting a specific credential

You're right that this is sufficient to allow client to conclude that a particular credential is available, but it is not sufficient to allow the client to conclude that a particular credential is not available, which is what allows for the UX optimizations @sbweeden mentioned. The prime example being if allowCredentials: [{ id: "A", transports: ["internal"] }] and no platform authenticator has that credential, then the client can conclude that there's no need to ask the user to plug in a security key they don't have. But it is impossible in general for the client to have this information on its own since some credentials may have been registered elsewhere.

Even with a client-side cache of transport hints, that still wouldn't remove the need for the RP to initialize new cache entries whenever a credential is added. And at that point there's no reason the RP can't just always add the transport hints.

@cyberphone
Copy link
Author

@emlun So in your opinion GitHub and thousands of other RPs need to rewrite their WebAuthn code and add a transports column to their User DBs? Personally, I would rather consider deprecating the transports attribute. It has obviously not been field-tested. Well, now it has 😁

@emlun
Copy link
Member

emlun commented Oct 25, 2021

If they want to make use of these UX optimizations, then yes, there's no other way to do it reliably. But of course it's not required, and they're no worse off than they were before if they don't.

@cyberphone
Copy link
Author

cyberphone commented Oct 25, 2021

But of course it's not required, and they're no worse off than they were before if they don't.

Users of WebAuthn are actually forced updating their WebAuthn implementations if the Chrome95/W10 interpretation of the spec. becomes "normative", otherwise they are indeed UX-wise worse off than they were before.

@cyberphone
Copy link
Author

cyberphone commented Oct 26, 2021

Additional testing, this time with Edge, revealed that edge-cases (no pun intended) do not work particularly well, but they should be possible dealing with in an improved WebAuthn GUI without forcing the use of the transports attribute. A "Find Credential" wizard should do the trick. Since the "normal" use cases work flawlessly, there is a credential registry somewhere, right?
@timcappalli @akshayku

@cyberphone
Copy link
Author

After digging into the Chrome source, I found some interesting lines:

  if (win_native_api_enabled()) {
    const std::u16string desc = l10n_util::GetStringUTF16(
        IDS_WEBAUTHN_TRANSPORT_POPUP_DIFFERENT_AUTHENTICATOR_WIN);
    mechanisms_.emplace_back(
        Mechanism::WindowsAPI(/*unused*/ true), desc, desc,
        GetTransportIcon(AuthenticatorTransport::kUsbHumanInterfaceDevice),
        base::BindRepeating(&AuthenticatorRequestDialogModel::StartWinNativeApi,
                            base::Unretained(this), mechanisms_.size()),
        // The Windows API should have priority unless caBLE does or if there
        // are linked phones.
        !priority_transport.has_value() && paired_phones_.empty());
  }

Since I have no registered Bluetooth pairings or linked phones in my computer, I don't understand what is happing here. For services like GitHub and webauthn.io which specifies credentialId during get(), it does IMO not work as expected because caBLE or linked phones are equally much external tokens as one connected via USB.

https://bugs.chromium.org/p/chromium/issues/detail?id=1265063

@sbweeden
Copy link
Contributor

sbweeden commented Nov 3, 2021

Not a spec issue, closing per WebAuthn call 2021-11-03.

@sbweeden sbweeden closed this as completed Nov 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants