Skip to content

Commit d61caf7

Browse files
committed
fix: always randomize challenge just before use
1 parent d0d6a62 commit d61caf7

File tree

1 file changed

+59
-47
lines changed

1 file changed

+59
-47
lines changed

app.js

Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ let defaultCreateOrReplaceCredOpts = {
6464

6565
userVerification: "discouraged", // implicit register // "preferred", "required"
6666
},
67-
challenge: challenge,
67+
challenge: challenge, // for attestation
6868
// don't create for
6969
excludeCredentials: [], // { id, transports, type }
7070
// https://caniuse.com/mdn-api_credentialscontainer_create_publickey_option_extensions
@@ -160,7 +160,7 @@ let defaultGetCredOpts = {
160160
// type: "public-key",
161161
// },
162162
],
163-
challenge: challenge,
163+
challenge: challenge, // for signature
164164
// extensions: [],
165165
// hints: [],
166166
// can make Cross-Origin requests
@@ -205,6 +205,53 @@ Object.assign(window, {
205205
setAttachment,
206206
});
207207

208+
/**
209+
* Converts a WebAuthn Public Key Credential response to plain JSON with base64 encoding for byte array fields.
210+
* @param {CredentialCreationOptions} pubkeyRegOpts
211+
* https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAttestationResponse
212+
* https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions
213+
*/
214+
async function setPasskey(pubkeyRegOpts) {
215+
console.log("createOrReplacePublicKey() pubkeyRegOpts", pubkeyRegOpts);
216+
217+
abortController.abort(currentAbort);
218+
abortController = new AbortController();
219+
pubkeyRegOpts.signal = abortController.signal;
220+
221+
void globalThis.crypto.getRandomValues(challenge);
222+
if (!pubkeyRegOpts.publicKey) {
223+
throw new Error(".publicKey must exist");
224+
}
225+
pubkeyRegOpts.publicKey.challenge = challenge;
226+
227+
const pubkeyRegResp = await navigator.credentials
228+
.create(pubkeyRegOpts)
229+
.catch(function (err) {
230+
// this may never fire
231+
console.warn("Error: navigator.credentials.create():");
232+
console.log(err);
233+
return null;
234+
});
235+
236+
if (!pubkeyRegResp) {
237+
console.warn("WebAuthn was changed, restarted, failed, or canceled");
238+
if (currentMediation === "conditional") {
239+
let authRequestOpts = globalThis.structuredClone(defaultGetCredOpts);
240+
authRequestOpts.mediation = currentMediation;
241+
void (await authorizePasskey(authRequestOpts));
242+
}
243+
return;
244+
}
245+
/** @type {PublicKeyCredential} */ //@ts-ignore
246+
let pubkeyRegistration = pubkeyRegResp;
247+
248+
console.log(`createCredential response opaque:`);
249+
console.log(pubkeyRegistration);
250+
let registerResult = pubkeyRegisterToJSON(pubkeyRegistration);
251+
console.log(`createCredential response JSON:`);
252+
console.log(registerResult);
253+
}
254+
208255
/**
209256
* @param {Event} event
210257
*/
@@ -215,19 +262,16 @@ async function createOrReplacePublicKey(event) {
215262
let pubkeyRegOpts = globalThis.structuredClone(
216263
defaultCreateOrReplaceCredOpts,
217264
);
265+
if (!pubkeyRegOpts.publicKey) {
266+
throw new Error(".publicKey must exist");
267+
}
218268

219269
if (currentAttachment) {
220270
let attachment = currentAttachment;
221271
//@ts-ignore
222272
pubkeyRegOpts.publicKey.authenticatorSelection.attachment = attachment;
223273
}
224274

225-
void globalThis.crypto.getRandomValues(challenge);
226-
if (!pubkeyRegOpts.publicKey) {
227-
throw new Error(".publicKey must exist");
228-
}
229-
pubkeyRegOpts.publicKey.challenge = challenge;
230-
231275
//@ts-ignore
232276
let username = $("input[name=username]").value;
233277
if (!username) {
@@ -254,38 +298,7 @@ async function createOrReplacePublicKey(event) {
254298
// excludeCredentials: [ { type: 'public-key', id: 'base64id' }]
255299
pubkeyRegOpts.publicKey.excludeCredentials = [];
256300

257-
console.log("createOrReplacePublicKey() pubkeyRegOpts", pubkeyRegOpts);
258-
259-
abortController.abort(currentAbort);
260-
abortController = new AbortController();
261-
pubkeyRegOpts.signal = abortController.signal;
262-
263-
const pubkeyRegResp = await navigator.credentials
264-
.create(pubkeyRegOpts)
265-
.catch(function (err) {
266-
// this may never fire
267-
console.warn("Error: navigator.credentials.create():");
268-
console.log(err);
269-
return null;
270-
});
271-
272-
if (!pubkeyRegResp) {
273-
console.warn("WebAuthn was changed, restarted, failed, or canceled");
274-
if (currentMediation === "conditional") {
275-
let authRequestOpts = globalThis.structuredClone(defaultGetCredOpts);
276-
authRequestOpts.mediation = currentMediation;
277-
void (await authorizePasskey(authRequestOpts).catch(catchUiError));
278-
}
279-
return;
280-
}
281-
/** @type {PublicKeyCredential} */ //@ts-ignore
282-
let pubkeyRegistration = pubkeyRegResp;
283-
284-
console.log(`createCredential response opaque:`);
285-
console.log(pubkeyRegistration);
286-
let registerResult = pubkeyRegisterToJSON(pubkeyRegistration);
287-
console.log(`createCredential response JSON:`);
288-
console.log(registerResult);
301+
await setPasskey(pubkeyRegOpts).catch(catchUiError);
289302
}
290303

291304
/**
@@ -297,6 +310,12 @@ async function createOrReplacePublicKey(event) {
297310
async function authorizePasskey(authRequestOpts) {
298311
console.log("getPublicKey() authRequestOpts", authRequestOpts);
299312

313+
void globalThis.crypto.getRandomValues(challenge);
314+
if (!authRequestOpts.publicKey) {
315+
throw new Error(".publicKey must exist");
316+
}
317+
authRequestOpts.publicKey.challenge = challenge;
318+
300319
abortController.abort(currentAbort);
301320
abortController = new AbortController();
302321
authRequestOpts.signal = abortController.signal;
@@ -337,13 +356,6 @@ async function getPublicKey(event) {
337356

338357
let authRequestOpts = globalThis.structuredClone(defaultGetCredOpts);
339358

340-
if (!authRequestOpts.publicKey) {
341-
throw new Error(".publicKey must exist");
342-
}
343-
344-
authRequestOpts.publicKey.challenge = challenge;
345-
void globalThis.crypto.getRandomValues(challenge);
346-
347359
authRequestOpts.mediation = currentMediation;
348360

349361
// to prevent overwriting the ID / public key when creating

0 commit comments

Comments
 (0)