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/passwordless-defaults #307

Closed MasterKale closed 1 year ago

MasterKale commented 1 year ago

This PR makes SimpleWebAuthn more opinionated about default functionality, and the use case it promotes. In this case the defaults will steer RP's towards implementing passwordless authentication thusly:

These changes were inspired by recent doc updates made to passkeys.dev on how RP's should mark user verification "preferred", but leaves it up to RP's to figure out if UV should be required or not in response validation:

The user verification result (conveyed in authenticator data flags) will reflect the actual user verification result and should always be validated against your requirements on the server.

I'm deciding to make RP's require that user verification is marked true in the response to ensure a reliable passwordless experience.

THIS IS A BREAKING CHANGE!

RP's that do not require support for discoverable credentials from authenticators will need to update their calls to generateRegistrationOptions() accordingly:

Before

const options = generateRegistrationOptions({
  rpName: 'SimpleWebAuthn',
  rpID: 'simplewebauthn.dev',
  userID: '1234',
  userName: 'usernameHere',
});

After

const options = generateRegistrationOptions({
  rpName: 'SimpleWebAuthn',
  rpID: 'simplewebauthn.dev',
  userID: '1234',
  userName: 'usernameHere',
  authenticatorSelection: {
    // See https://www.w3.org/TR/webauthn-2/#enumdef-residentkeyrequirement
    residentKey: 'discouraged',
  },
});

Additionally, RP's implementing a second-factor flow with WebAuthn, where UV is not important (because username+password are provided before WebAuthn is leveraged for the second factor), should not require user verification when verifying responses:

verifyRegistrationResponse()

Before

const verification = await verifyRegistrationResponse({
  credential: attestationFIDOU2F,
  // ...
});

After

const verification = await verifyRegistrationResponse({
  credential: attestationFIDOU2F,
  // ...
  requireUserVerification: false,
});

verifyAuthenticationResponse()

Before

const verification = await verifyAuthenticationResponse({
  credential: assertionResponse,
  // ...
});

After

const verification = await verifyAuthenticationResponse({
  credential: assertionResponse,
  // ...
  requireUserVerification: false,
});