MasterKale / SimpleWebAuthn

WebAuthn, Simplified. A collection of TypeScript-first libraries for simpler WebAuthn integration. Supports modern browsers, Node, Deno, and more.
https://simplewebauthn.dev
MIT License
1.63k stars 137 forks source link

feat/conditional-ui #214

Closed MasterKale closed 2 years ago

MasterKale commented 2 years ago

This PR adds support for upcoming browsers' conditional UI capability to @simplewebauthn/browser's startAuthentication().

To use it, pass in true for the new second useBrowserAutofill positional argument to startAuthentication(), then specify Promise resolution (or rejection) logic to handle the user possibly, eventually choosing a WebAuthn credential via the browser's autofill mechanism:

<head>
  <!-- ... -->
  <script>
    const { startAuthentication } = SimpleWebAuthnBrowser;

    fetch('/generate-authentication-options')
      .then((options) => {
        startAuthentication(options, true)
          .then(authResp => sendToServerForVerificationAndLogin)
          .catch(err => handleError);
      });
  </script>
</head>

Guidance from platform vendors indicates that it is important to initialize WebAuthn's Conditional UI experience as soon as possible. Placing this logic in <head> should give browsers enough time to query authenticators for any discoverable credentials to display to the user. It may also work to delay painting UI For N milliseconds to achieve the same thing in something like a single-page app that would trigger this experience some time after page load. It's still early days for this capability so ymmv.

A new asynchronous browserSupportsWebAuthnAutofill() helper is also being added in this PR. This method can be used independently of the call that's made in startAuthentication() when initializing browser autofill support.

Existing authentication UI and logic should not need to be refactored. This new "pending" WebAuthn request will be automatically cancelled on a subsequent execution of startAuthentication() in, say, an existing click handler that triggers the browser's typical "modal" WebAuthn experience.

Conditional UI is still a nascent capability, but this method should be pretty reliable since the API is largely settled. This new logic has been successfully tested in both Chrome Canary[1][2] and Safari in the macOS Ventura beta.

Screenshots

Chrome Canary:

Screen Shot 2022-06-17 at 4 18 17 PM

Safari in the macOS Ventura beta:

FVfczGlUsAI7kYb

Addresses #209.


[1] The following flag must be specified when launching Chrome Canary to enable Conditional UI:

open -a /Applications/Google\ Chrome\ Canary.app --args --enable-features=WebAuthenticationConditionalUI

[2] I've been informed there's a race condition deep within Chrome Canary that sometimes prevents the autofill from populating with WebAuthn credentials. If they aren't showing up just keep refreshing and eventually they'll appear. This is a temporary issue and will definitely be resolved before Chrome's Conditional UI support lands in Chrome Stable.

P4sca1 commented 2 years ago

@MasterKale How is this supposed to work? Right now, fetch('/generate-authentication-options') requires the user to be signed in, so that their registered authenticators can be fetched and filled into allowCredentials.

MasterKale commented 2 years ago

@P4sca1 good question, let's take this into a Discussion: https://github.com/MasterKale/SimpleWebAuthn/discussions/225