function base64ToUint8Array(base64String) { console.log("b64 string", base64String) // Convert URL-safe base64 to standard base64 base64String = base64String.replace(/-/g, '+').replace(/_/g, '/'); // Add padding if necessary while (base64String.length % 4 !== 0) { base64String += '='; } const binaryString = atob(base64String); const binaryLength = binaryString.length; const bytes = new Uint8Array(binaryLength); for (let i = 0; i < binaryLength; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } function arrayBufferToBase64Url(buffer) { const bytes = new Uint8Array(buffer); let binary = ''; bytes.forEach((b) => binary += String.fromCharCode(b)); const base64 = btoa(binary); return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } async function register() { const username = document.querySelector("#username").value try { // Step 1: Fetch the registration options from your server const response = await fetch('/register/options', { method: "POST", body: JSON.stringify({ user: username }), }); const data = await response.json(); const options = data.publicKey console.log(options) // Step 2: Convert some options to Uint8Array from buffer, base64, etc. as needed options.challenge = base64ToUint8Array(options.challenge); options.user.id = base64ToUint8Array(options.user.id); // Step 3: Create a new credential with the PublicKeyCredential.create() method const credential = await navigator.credentials.create({ publicKey: options }); // Step 4: Prepare the data to be sent to the server for verification const credentialData = { id: credential.id, rawId: arrayBufferToBase64Url(credential.rawId), type: credential.type, response: { attestationObject: arrayBufferToBase64Url(credential.response.attestationObject), clientDataJSON: arrayBufferToBase64Url(credential.response.clientDataJSON) } }; // Step 5: Send the credential data back to the server for registration verification const registerResponse = await fetch(`/register/complete/${username}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentialData) }); if (registerResponse.ok) { console.log('Registration successful!'); window.location.href = "/" } else { console.error('Registration failed:', await registerResponse.text()); } } catch (error) { console.error('Error during registration:', error); } } async function login() { const username = document.querySelector("#login-username").value try { // Step 1: Fetch the login options from your server const response = await fetch('/login/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, // Include any user-identifying information if necessary body: JSON.stringify({ user: username }), }); if (!response.ok) throw new Error('Failed to fetch options'); const data = await response.json(); const options = data.publicKey; // Step 2: Convert some options to Uint8Array as needed options.challenge = base64ToUint8Array(options.challenge); options.allowCredentials = options.allowCredentials.map(cred => ({ ...cred, id: base64ToUint8Array(cred.id) })); // Step 3: Request a credential from the browser using PublicKeyCredential.get() const credential = await navigator.credentials.get({ publicKey: options }); // Step 4: Prepare the credential response to send to the server console.log(credential.response.authenticatorData) const credentialData = { id: credential.id, rawId: arrayBufferToBase64Url(credential.rawId), type: credential.type, response: { authenticatorData: arrayBufferToBase64Url(credential.response.authenticatorData), clientDataJSON: arrayBufferToBase64Url(credential.response.clientDataJSON), signature: arrayBufferToBase64Url(credential.response.signature), userHandle: credential.response.userHandle ? arrayBufferToBase64Url(credential.response.userHandle) : null } }; // Step 5: Send the credential data back to the server for verification const verifyResponse = await fetch(`/login/complete/${username}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentialData), credentials: "include", }); if (verifyResponse.ok) { console.log('Login successful!'); window.location.href = "/" } else { console.error('Login failed:', await verifyResponse.text()); } } catch (error) { console.error('Error during login:', error); } }