From 8784f4c1d6f41a0f5b5958554ac62e8eacf56def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Gon=C3=A7alves?= Date: Sun, 12 Apr 2026 14:39:01 +0200 Subject: [PATCH] Demo: Use PublicKeyCredential parse functions instead of converting by hand --- Demo/wwwroot/js/custom.login.js | 25 ++------- Demo/wwwroot/js/custom.register.js | 31 ++--------- Demo/wwwroot/js/helpers.js | 69 +----------------------- Demo/wwwroot/js/mfa.login.js | 25 ++------- Demo/wwwroot/js/mfa.register.js | 31 ++--------- Demo/wwwroot/js/passwordless.login.js | 25 ++------- Demo/wwwroot/js/passwordless.register.js | 31 ++--------- Demo/wwwroot/js/usernameless.login.js | 27 ++-------- Demo/wwwroot/js/usernameless.register.js | 29 ++-------- 9 files changed, 36 insertions(+), 257 deletions(-) diff --git a/Demo/wwwroot/js/custom.login.js b/Demo/wwwroot/js/custom.login.js index 7fd650e4a..4590f7dfe 100644 --- a/Demo/wwwroot/js/custom.login.js +++ b/Demo/wwwroot/js/custom.login.js @@ -37,10 +37,8 @@ async function handleSignInSubmit(event) { return; } - makeAssertionOptions.challenge = coerceToArrayBuffer(makeAssertionOptions.challenge); - makeAssertionOptions.allowCredentials.forEach(function (listItem) { - listItem.id = coerceToArrayBuffer(listItem.id); - }); + // Parse Base64Url into ArrayBuffers + makeAssertionOptions = PublicKeyCredential.parseRequestOptionsFromJSON(makeAssertionOptions); console.log("Assertion options", makeAssertionOptions); @@ -74,23 +72,8 @@ async function handleSignInSubmit(event) { * @param {any} assertedCredential */ async function verifyAssertionWithServer(assertedCredential) { - - // Move data into Arrays incase it is super long - let authData = new Uint8Array(assertedCredential.response.authenticatorData); - let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); - let rawId = new Uint8Array(assertedCredential.rawId); - let sig = new Uint8Array(assertedCredential.response.signature); - const data = { - id: assertedCredential.id, - rawId: coerceToBase64Url(rawId), - type: assertedCredential.type, - extensions: assertedCredential.getClientExtensionResults(), - response: { - authenticatorData: coerceToBase64Url(authData), - clientDataJSON: coerceToBase64Url(clientDataJSON), - signature: coerceToBase64Url(sig) - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = assertedCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/custom.register.js b/Demo/wwwroot/js/custom.register.js index e0199423a..3e0c5ffd5 100644 --- a/Demo/wwwroot/js/custom.register.js +++ b/Demo/wwwroot/js/custom.register.js @@ -34,7 +34,7 @@ async function handleRegisterSubmit(event) { } catch (e) { console.error(e); - let msg = "Something wen't really wrong"; + let msg = "Something went really wrong"; showErrorAlert(msg); } @@ -48,15 +48,8 @@ async function handleRegisterSubmit(event) { return; } - // Turn the challenge back into the accepted format of padded base64 - makeCredentialOptions.challenge = coerceToArrayBuffer(makeCredentialOptions.challenge); - // Turn ID into a UInt8Array Buffer for some reason - makeCredentialOptions.user.id = coerceToArrayBuffer(makeCredentialOptions.user.id); - - makeCredentialOptions.excludeCredentials = makeCredentialOptions.excludeCredentials.map((c) => { - c.id = coerceToArrayBuffer(c.id); - return c; - }); + // Parse Base64Url into ArrayBuffers + makeCredentialOptions = PublicKeyCredential.parseCreationOptionsFromJSON(makeCredentialOptions); if (makeCredentialOptions.authenticatorSelection.authenticatorAttachment === null) makeCredentialOptions.authenticatorSelection.authenticatorAttachment = undefined; @@ -113,22 +106,8 @@ async function fetchMakeCredentialOptions(formData) { // This should be used to verify the auth data with the server async function registerNewCredential(newCredential) { - // Move data into Arrays incase it is super long - let attestationObject = new Uint8Array(newCredential.response.attestationObject); - let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); - let rawId = new Uint8Array(newCredential.rawId); - - const data = { - id: newCredential.id, - rawId: coerceToBase64Url(rawId), - type: newCredential.type, - extensions: newCredential.getClientExtensionResults(), - response: { - attestationObject: coerceToBase64Url(attestationObject), - clientDataJSON: coerceToBase64Url(clientDataJSON), - transports: newCredential.response.getTransports() - }, - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = newCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/helpers.js b/Demo/wwwroot/js/helpers.js index 92b8d7933..8c7830516 100644 --- a/Demo/wwwroot/js/helpers.js +++ b/Demo/wwwroot/js/helpers.js @@ -1,71 +1,4 @@ -coerceToArrayBuffer = function (thing, name) { - if (typeof thing === "string") { - // base64url to base64 - thing = thing.replace(/-/g, "+").replace(/_/g, "/"); - - // base64 to Uint8Array - var str = window.atob(thing); - var bytes = new Uint8Array(str.length); - for (var i = 0; i < str.length; i++) { - bytes[i] = str.charCodeAt(i); - } - thing = bytes; - } - - // Array to Uint8Array - if (Array.isArray(thing)) { - thing = new Uint8Array(thing); - } - - // Uint8Array to ArrayBuffer - if (thing instanceof Uint8Array) { - thing = thing.buffer; - } - - // error if none of the above worked - if (!(thing instanceof ArrayBuffer)) { - throw new TypeError("could not coerce '" + name + "' to ArrayBuffer"); - } - - return thing; -}; - - -coerceToBase64Url = function (thing) { - // Array or ArrayBuffer to Uint8Array - if (Array.isArray(thing)) { - thing = Uint8Array.from(thing); - } - - if (thing instanceof ArrayBuffer) { - thing = new Uint8Array(thing); - } - - // Uint8Array to base64 - if (thing instanceof Uint8Array) { - var str = ""; - var len = thing.byteLength; - - for (var i = 0; i < len; i++) { - str += String.fromCharCode(thing[i]); - } - thing = window.btoa(str); - } - - if (typeof thing !== "string") { - throw new Error("could not coerce to string"); - } - - // base64 to base64url - // NOTE: "=" at the end of challenge is optional, strip it off here - thing = thing.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, ""); - - return thing; -}; - - - -// HELPERS +// HELPERS function showErrorAlert(message, error) { let footermsg = ''; diff --git a/Demo/wwwroot/js/mfa.login.js b/Demo/wwwroot/js/mfa.login.js index a200a6a60..e3c745418 100644 --- a/Demo/wwwroot/js/mfa.login.js +++ b/Demo/wwwroot/js/mfa.login.js @@ -42,10 +42,8 @@ async function handleSignInSubmit(event) { return; } - makeAssertionOptions.challenge = coerceToArrayBuffer(makeAssertionOptions.challenge); - makeAssertionOptions.allowCredentials.forEach(function (listItem) { - listItem.id = coerceToArrayBuffer(listItem.id); - }); + // Parse Base64Url into ArrayBuffers + makeAssertionOptions = PublicKeyCredential.parseRequestOptionsFromJSON(makeAssertionOptions); console.log("Assertion options", makeAssertionOptions); @@ -79,23 +77,8 @@ async function handleSignInSubmit(event) { * @param {any} assertedCredential */ async function verifyAssertionWithServer(assertedCredential) { - - // Move data into Arrays incase it is super long - let authData = new Uint8Array(assertedCredential.response.authenticatorData); - let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); - let rawId = new Uint8Array(assertedCredential.rawId); - let sig = new Uint8Array(assertedCredential.response.signature); - const data = { - id: assertedCredential.id, - rawId: coerceToBase64Url(rawId), - type: assertedCredential.type, - extensions: assertedCredential.getClientExtensionResults(), - response: { - authenticatorData: coerceToBase64Url(authData), - clientDataJSON: coerceToBase64Url(clientDataJSON), - signature: coerceToBase64Url(sig) - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = assertedCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/mfa.register.js b/Demo/wwwroot/js/mfa.register.js index 463aa5aeb..bd6c34447 100644 --- a/Demo/wwwroot/js/mfa.register.js +++ b/Demo/wwwroot/js/mfa.register.js @@ -38,7 +38,7 @@ async function handleRegisterSubmit(event) { } catch (e) { console.error(e); - let msg = "Something wen't really wrong"; + let msg = "Something went really wrong"; showErrorAlert(msg); } @@ -52,15 +52,8 @@ async function handleRegisterSubmit(event) { return; } - // Turn the challenge back into the accepted format of padded base64 - makeCredentialOptions.challenge = coerceToArrayBuffer(makeCredentialOptions.challenge); - // Turn ID into a UInt8Array Buffer for some reason - makeCredentialOptions.user.id = coerceToArrayBuffer(makeCredentialOptions.user.id); - - makeCredentialOptions.excludeCredentials = makeCredentialOptions.excludeCredentials.map((c) => { - c.id = coerceToArrayBuffer(c.id); - return c; - }); + // Parse Base64Url into ArrayBuffers + makeCredentialOptions = PublicKeyCredential.parseCreationOptionsFromJSON(makeCredentialOptions); if (makeCredentialOptions.authenticatorSelection.authenticatorAttachment === null) makeCredentialOptions.authenticatorSelection.authenticatorAttachment = undefined; @@ -118,22 +111,8 @@ async function fetchMakeCredentialOptions(formData) { // This should be used to verify the auth data with the server async function registerNewCredential(newCredential) { - // Move data into Arrays incase it is super long - let attestationObject = new Uint8Array(newCredential.response.attestationObject); - let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); - let rawId = new Uint8Array(newCredential.rawId); - - const data = { - id: newCredential.id, - rawId: coerceToBase64Url(rawId), - type: newCredential.type, - extensions: newCredential.getClientExtensionResults(), - response: { - attestationObject: coerceToBase64Url(attestationObject), - clientDataJSON: coerceToBase64Url(clientDataJSON), - transports: newCredential.response.getTransports() - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = newCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/passwordless.login.js b/Demo/wwwroot/js/passwordless.login.js index a10573da2..88434feb9 100644 --- a/Demo/wwwroot/js/passwordless.login.js +++ b/Demo/wwwroot/js/passwordless.login.js @@ -35,10 +35,8 @@ async function handleSignInSubmit(event) { return; } - makeAssertionOptions.challenge = coerceToArrayBuffer(makeAssertionOptions.challenge); - makeAssertionOptions.allowCredentials.forEach(function (listItem) { - listItem.id = coerceToArrayBuffer(listItem.id); - }); + // Parse Base64Url into ArrayBuffers + makeAssertionOptions = PublicKeyCredential.parseRequestOptionsFromJSON(makeAssertionOptions); console.log("Assertion options", makeAssertionOptions); @@ -72,23 +70,8 @@ async function handleSignInSubmit(event) { * @param {any} assertedCredential */ async function verifyAssertionWithServer(assertedCredential) { - - // Move data into Arrays incase it is super long - let authData = new Uint8Array(assertedCredential.response.authenticatorData); - let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); - let rawId = new Uint8Array(assertedCredential.rawId); - let sig = new Uint8Array(assertedCredential.response.signature); - const data = { - id: assertedCredential.id, - rawId: coerceToBase64Url(rawId), - type: assertedCredential.type, - extensions: assertedCredential.getClientExtensionResults(), - response: { - authenticatorData: coerceToBase64Url(authData), - clientDataJSON: coerceToBase64Url(clientDataJSON), - signature: coerceToBase64Url(sig) - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = assertedCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/passwordless.register.js b/Demo/wwwroot/js/passwordless.register.js index b287bc4b5..d0b180a16 100644 --- a/Demo/wwwroot/js/passwordless.register.js +++ b/Demo/wwwroot/js/passwordless.register.js @@ -35,7 +35,7 @@ async function handleRegisterSubmit(event) { } catch (e) { console.error(e); - let msg = "Something wen't really wrong"; + let msg = "Something went really wrong"; showErrorAlert(msg); } @@ -49,15 +49,8 @@ async function handleRegisterSubmit(event) { return; } - // Turn the challenge back into the accepted format of padded base64 - makeCredentialOptions.challenge = coerceToArrayBuffer(makeCredentialOptions.challenge); - // Turn ID into a UInt8Array Buffer for some reason - makeCredentialOptions.user.id = coerceToArrayBuffer(makeCredentialOptions.user.id); - - makeCredentialOptions.excludeCredentials = makeCredentialOptions.excludeCredentials.map((c) => { - c.id = coerceToArrayBuffer(c.id); - return c; - }); + // Parse Base64Url into ArrayBuffers + makeCredentialOptions = PublicKeyCredential.parseCreationOptionsFromJSON(makeCredentialOptions); if (makeCredentialOptions.authenticatorSelection.authenticatorAttachment === null) makeCredentialOptions.authenticatorSelection.authenticatorAttachment = undefined; @@ -115,22 +108,8 @@ async function fetchMakeCredentialOptions(formData) { // This should be used to verify the auth data with the server async function registerNewCredential(newCredential) { - // Move data into Arrays incase it is super long - let attestationObject = new Uint8Array(newCredential.response.attestationObject); - let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); - let rawId = new Uint8Array(newCredential.rawId); - - const data = { - id: newCredential.id, - rawId: coerceToBase64Url(rawId), - type: newCredential.type, - extensions: newCredential.getClientExtensionResults(), - response: { - attestationObject: coerceToBase64Url(attestationObject), - clientDataJSON: coerceToBase64Url(clientDataJSON), - transports: newCredential.response.getTransports() - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = newCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/usernameless.login.js b/Demo/wwwroot/js/usernameless.login.js index d9b7905f4..9ecf0913f 100644 --- a/Demo/wwwroot/js/usernameless.login.js +++ b/Demo/wwwroot/js/usernameless.login.js @@ -35,10 +35,8 @@ async function handleSignInSubmit(event) { return; } - makeAssertionOptions.challenge = coerceToArrayBuffer(makeAssertionOptions.challenge); - makeAssertionOptions.allowCredentials.forEach(function (listItem) { - listItem.id = coerceToArrayBuffer(listItem.id); - }); + // Parse Base64Url into ArrayBuffers + makeAssertionOptions = PublicKeyCredential.parseRequestOptionsFromJSON(makeAssertionOptions); console.log("Assertion options", makeAssertionOptions); @@ -72,25 +70,8 @@ async function handleSignInSubmit(event) { * @param {any} assertedCredential */ async function verifyAssertionWithServer(assertedCredential) { - - // Move data into Arrays incase it is super long - let authData = new Uint8Array(assertedCredential.response.authenticatorData); - let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); - let rawId = new Uint8Array(assertedCredential.rawId); - let sig = new Uint8Array(assertedCredential.response.signature); - let userHandle = assertedCredential.response.userHandle ? new Uint8Array(assertedCredential.response.userHandle) : null; - const data = { - id: assertedCredential.id, - rawId: coerceToBase64Url(rawId), - type: assertedCredential.type, - extensions: assertedCredential.getClientExtensionResults(), - response: { - authenticatorData: coerceToBase64Url(authData), - clientDataJSON: coerceToBase64Url(clientDataJSON), - userHandle: userHandle ? coerceToBase64Url(userHandle): null, - signature: coerceToBase64Url(sig) - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = assertedCredential.toJSON(); let response; try { diff --git a/Demo/wwwroot/js/usernameless.register.js b/Demo/wwwroot/js/usernameless.register.js index 581425b60..8d6575615 100644 --- a/Demo/wwwroot/js/usernameless.register.js +++ b/Demo/wwwroot/js/usernameless.register.js @@ -50,15 +50,8 @@ async function handleRegisterSubmit(event) { return; } - // Turn the challenge back into the accepted format of padded base64 - makeCredentialOptions.challenge = coerceToArrayBuffer(makeCredentialOptions.challenge); - // Turn ID into a UInt8Array Buffer for some reason - makeCredentialOptions.user.id = coerceToArrayBuffer(makeCredentialOptions.user.id); - - makeCredentialOptions.excludeCredentials = makeCredentialOptions.excludeCredentials.map((c) => { - c.id = coerceToArrayBuffer(c.id); - return c; - }); + // Parse Base64Url into ArrayBuffers + makeCredentialOptions = PublicKeyCredential.parseCreationOptionsFromJSON(makeCredentialOptions); if (makeCredentialOptions.authenticatorSelection.authenticatorAttachment === null) makeCredentialOptions.authenticatorSelection.authenticatorAttachment = undefined; @@ -116,22 +109,8 @@ async function fetchMakeCredentialOptions(formData) { // This should be used to verify the auth data with the server async function registerNewCredential(newCredential) { - // Move data into Arrays incase it is super long - let attestationObject = new Uint8Array(newCredential.response.attestationObject); - let clientDataJSON = new Uint8Array(newCredential.response.clientDataJSON); - let rawId = new Uint8Array(newCredential.rawId); - - const data = { - id: newCredential.id, - rawId: coerceToBase64Url(rawId), - type: newCredential.type, - extensions: newCredential.getClientExtensionResults(), - response: { - attestationObject: coerceToBase64Url(attestationObject), - clientDataJSON: coerceToBase64Url(clientDataJSON), - transports: newCredential.response.getTransports() - } - }; + // Convert ArrayBuffers to Base64Url for HTTP transport + const data = newCredential.toJSON(); let response; try {