Restructuing

This commit is contained in:
2026-07-05 16:30:58 -04:00
parent 678e20d402
commit 7b0f2ce1a5
767 changed files with 354 additions and 76 deletions
+100
View File
@@ -0,0 +1,100 @@
// WebAuthn helpers — base64url encoding compatible with Fido2NetLib
function base64urlToBuffer(base64url) {
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
const binary = atob(base64);
return Uint8Array.from(binary, c => c.charCodeAt(0)).buffer;
}
function bufferToBase64url(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (const b of bytes) binary += String.fromCharCode(b);
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
function prepareCreationOptions(options) {
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id);
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id),
}));
}
return options;
}
function prepareRequestOptions(options) {
options.challenge = base64urlToBuffer(options.challenge);
if (options.allowCredentials) {
options.allowCredentials = options.allowCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id),
}));
}
return options;
}
function serializeAttestation(credential) {
return {
id: credential.id,
rawId: bufferToBase64url(credential.rawId),
type: credential.type,
response: {
attestationObject: bufferToBase64url(credential.response.attestationObject),
clientDataJSON: bufferToBase64url(credential.response.clientDataJSON),
transports: credential.response.getTransports ? credential.response.getTransports() : [],
},
extensions: credential.getClientExtensionResults(),
};
}
function serializeAssertion(credential) {
return {
id: credential.id,
rawId: bufferToBase64url(credential.rawId),
type: credential.type,
response: {
authenticatorData: bufferToBase64url(credential.response.authenticatorData),
clientDataJSON: bufferToBase64url(credential.response.clientDataJSON),
signature: bufferToBase64url(credential.response.signature),
userHandle: credential.response.userHandle
? bufferToBase64url(credential.response.userHandle)
: null,
},
extensions: credential.getClientExtensionResults(),
};
}
window.passkeyEnroll = async function () {
const optRes = await fetch('/api/auth/register/options', { method: 'POST' });
if (!optRes.ok) throw new Error(await optRes.text());
const options = prepareCreationOptions(await optRes.json());
const credential = await navigator.credentials.create({ publicKey: options });
const completeRes = await fetch('/api/auth/register/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(serializeAttestation(credential)),
});
if (!completeRes.ok) throw new Error(await completeRes.text());
return await completeRes.json();
};
window.passkeyLogin = async function () {
const optRes = await fetch('/api/auth/login/options', { method: 'POST' });
if (!optRes.ok) throw new Error(await optRes.text());
const options = prepareRequestOptions(await optRes.json());
const credential = await navigator.credentials.get({ publicKey: options });
const completeRes = await fetch('/api/auth/login/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(serializeAssertion(credential)),
});
if (!completeRes.ok) throw new Error(await completeRes.text());
return await completeRes.json();
};