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/arbitrary-data-signing #172

Closed MasterKale closed 2 years ago

MasterKale commented 2 years ago

Issue #166 requested the ability to specify a method for challenge verification to allow for arbitrary data to be included in a registration challenge. I was at first disinclined to support such functionality since it was a use case not specifically defined in the spec, but two months later I figured what the heck, why not?

Now, after generating registration options, the challenge can be augmented with additional data:

const options = generateRegistrationOptions(opts);

// Remember the plain challenge
inMemoryUserDeviceDB[loggedInUserId].currentChallenge = options.challenge;

// Add data to be signed
options.challenge = base64url(JSON.stringify({
  actualChallenge: options.challenge,
  arbitraryData: 'arbitraryDataForSigning',
}));

Then, when invoking verifyRegistrationResponse(), pass in a method for expectedChallenge to parse the challenge and return a boolean:

const expectedChallenge = inMemoryUserDeviceDB[loggedInUserId].currentChallenge;

const verification = await verifyRegistrationResponse({
  expectedChallenge: (challenge: string) => {
    const parsedChallenge = JSON.parse(base64url.decode(challenge));
    return parsedChallenge.actualChallenge === expectedChallenge;
  },
  // ...
});

If you actually want the arbitrary data you'll need to use decodeClientDataJSON() afterwards to get it out:

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

const { challenge } = decodeClientDataJSON(response.clientDataJSON);
const parsedChallenge = JSON.parse(base64url.decode(challenge));
console.log(parsedChallenge.arbitraryData); // 'arbitraryDataForSigning'