// Browser-side WebAuthn helpers. The server sends and expects base64url; // the browser's credential APIs need ArrayBuffers. function b64urlToBuffer(value: string): ArrayBuffer { const padded = value.replace(/-/g, "+").replace(/_/g, "/"); const binary = atob(padded.padEnd(padded.length + ((4 - (padded.length % 4)) % 4), "=")); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); return bytes.buffer; } function bufferToB64url(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ""; for (const b of bytes) binary += String.fromCharCode(b); return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } export function isPasskeySupported(): boolean { return typeof window !== "undefined" && !!window.PublicKeyCredential; } export function toCreateOptions(options: any): PublicKeyCredentialCreationOptions { return { ...options, challenge: b64urlToBuffer(options.challenge), user: { ...options.user, id: b64urlToBuffer(options.user.id) }, excludeCredentials: (options.excludeCredentials ?? []).map((c: any) => ({ ...c, id: b64urlToBuffer(c.id), })), }; } export function toRequestOptions(options: any): PublicKeyCredentialRequestOptions { return { ...options, challenge: b64urlToBuffer(options.challenge), allowCredentials: (options.allowCredentials ?? []).map((c: any) => ({ ...c, id: b64urlToBuffer(c.id), })), }; } // credentialToJSON produces the shape go-webauthn's parsers read. export function credentialToJSON(cred: PublicKeyCredential): unknown { const response = cred.response as AuthenticatorAttestationResponse & AuthenticatorAssertionResponse; const json: any = { id: cred.id, rawId: bufferToB64url(cred.rawId), type: cred.type, clientExtensionResults: cred.getClientExtensionResults(), response: { clientDataJSON: bufferToB64url(response.clientDataJSON) }, }; if (response.attestationObject) { json.response.attestationObject = bufferToB64url(response.attestationObject); if (typeof response.getTransports === "function") { json.response.transports = response.getTransports(); } } if (response.authenticatorData) { json.response.authenticatorData = bufferToB64url(response.authenticatorData); json.response.signature = bufferToB64url(response.signature); json.response.userHandle = response.userHandle ? bufferToB64url(response.userHandle) : null; } return json; }