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.62k stars 137 forks source link

feat/530-remove-user-id-footgun #552

Closed MasterKale closed 7 months ago

MasterKale commented 7 months ago

This PR updates @simplewebauthn/server and @simplewebauthn/browser to always base64url-encode the now optional userID bytes. In addition, a 32-byte-long random user identifier will now be generated when calling generateRegistrationOptions() without specifying a value for userID.

Going forward SimpleWebAuthn will encourage new library users to not specify a value for userID! The docs will be updated accordingly once this all goes live.

However, existing users that upgrade to v10 of @simplewebauthn/browser (containing these changes) and wish to continue specifying their own UTF-8 user IDs can continue to do so by accounting for this when generating registration options and parsing userHandle after authentication.

Fixes #530.

Breaking Changes

String values for userID are no longer allowed.

The library will throw an error with a link to https://simplewebauthn.dev/docs/advanced/server/custom-user-ids for refactor guidance. The guidance will boil down to wrapping existing string identifiers in a call to isoUint8Array.fromUTF8String() to get them to Uint8Arrays, and then using isoBase64URL.toString() on userHandle after auth to get back the UTF-8 user identifier.

Before:

// @simplewebauthn/server
const opts = generateRegistrationOptions({
  // ...
  userID: 'randomUserID',
});
// @simplewebauthn/browser
const credential = await startAuthentication(...);
sendToServer(credential);
// @simplewebauthn/server
const credential = await receiveFromBrowser();
console.log(
  credential.response.userhandle,  // 'randomUserID'
);

After:

// @simplewebauthn/server
import { isoUint8Array } from '@simplewebauthn/server/helpers';

const opts = generateRegistrationOptions({
  // ...
  userID: isoUint8Array.fromUTF8String('randomUserID'),
});
// @simplewebauthn/browser
const credential = await startAuthentication(...);
sendToServer(credential);
// @simplewebauthn/server
import { isoBase64URL } from '@simplewebauthn/server/helpers';

const credential = await receiveFromBrowser();
console.log(
  isoBase64URL.toUTF8String(credential.response.userhandle),  // 'randomUserID'
);

isoBase64URL.toString() and isoBase64URL.fromString() have been renamed

The method names have been updated to reflect the use of UTF-8 string encoding.

Before:

const foo = isoBase64URL.toString('...');
const bar = isoBase64URL.fromString('...');

After:

const foo = isoBase64URL.toUTF8String('...');
const bar = isoBase64URL.fromUTF8String('...');