export function buildDataString(assertedCredential: PublicKeyCredential) { const response = assertedCredential.response as AuthenticatorAssertionResponse; const authData = new Uint8Array(response.authenticatorData); const clientDataJSON = new Uint8Array(response.clientDataJSON); const rawId = new Uint8Array(assertedCredential.rawId); const sig = new Uint8Array(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), }, }; return JSON.stringify(data); } export function parseWebauthnJson(jsonString: string) { const json = JSON.parse(jsonString); const challenge = json.challenge.replace(/-/g, "+").replace(/_/g, "/"); json.challenge = Uint8Array.from(atob(challenge), (c) => c.charCodeAt(0)); json.allowCredentials.forEach((listItem: any) => { // eslint-disable-next-line const fixedId = listItem.id.replace(/\_/g, "/").replace(/\-/g, "+"); listItem.id = Uint8Array.from(atob(fixedId), (c) => c.charCodeAt(0)); }); return json; } // From https://github.com/abergs/fido2-net-lib/blob/b487a1d47373ea18cd752b4988f7262035b7b54e/Demo/wwwroot/js/helpers.js#L34 // License: https://github.com/abergs/fido2-net-lib/blob/master/LICENSE.txt function coerceToBase64Url(thing: any) { // 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) { let str = ""; const len = thing.byteLength; for (let 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; }