Last active
September 24, 2024 18:12
-
-
Save Zmetser/a25cbcea15560664906dbfad555c6b3a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const registerURL = "https://level2.d13s.com/m/webauthn/register"; | |
const authURL = "https://level2.d13s.com/m/webauthn/authenticate"; | |
const appToken = "D-lnChRTnKj9eMhmqiIhHEFoo/ER3G8d1Ke4oezHpq1MA"; | |
async function register() { | |
const response = (await getRegisterChallenge()).request; | |
console.log("register response", response); | |
const credential = await createCredential(response); | |
console.log("credential", credential); | |
const finishResponse = await finishRegistration(response, credential); | |
console.log("finish response", await finishResponse.json()); | |
} | |
async function getRegisterChallenge() { | |
const response = await fetch(registerURL, { | |
method: "POST", | |
headers: { | |
Authorization: `Bearer ${appToken}`, | |
}, | |
}); | |
return await response.json(); | |
} | |
async function createCredential(response) { | |
const { publicKeyCredentialCreationOptions: credentialOptions } = response; | |
const request = { | |
publicKey: { | |
rp: { id: credentialOptions.rp.id, name: credentialOptions.rp.name }, | |
user: { | |
id: base64urlToBuffer(credentialOptions.user.id), | |
name: credentialOptions.user.name, | |
displayName: credentialOptions.user.displayName, | |
}, | |
challenge: base64urlToBuffer(credentialOptions.challenge), | |
pubKeyCredParams: credentialOptions.pubKeyCredParams, | |
}, | |
}; | |
console.log("our create request", request); | |
return navigator.credentials.create(request); | |
} | |
async function finishRegistration(response, credential) { | |
const body = { | |
requestId: response.requestId, | |
credential: { | |
id: credential.id, | |
rawId: bufferToBase64url(credential.rawId), | |
type: credential.type, | |
clientExtensionResults: {}, | |
response: { | |
attestationObject: bufferToBase64url( | |
credential.response.attestationObject | |
), | |
clientDataJSON: bufferToBase64url(credential.response.clientDataJSON), | |
}, | |
}, | |
sessionToken: response.sessionToken || null, | |
}; | |
console.log("our response", body); | |
return await fetch(registerURL + "/finish", { | |
method: "POST", | |
body: JSON.stringify(body), | |
headers: { | |
Authorization: `Bearer ${appToken}`, | |
}, | |
}); | |
} | |
async function auth() { | |
const response = (await getAuthChallenge()).request; | |
console.log("auth response", response); | |
const credential = await authCredential(response); | |
console.log("credential", credential); | |
const finishResponse = await finishAuth(response, credential); | |
console.log("finish response", await finishResponse.json()); | |
} | |
async function getAuthChallenge() { | |
const response = await fetch(authURL, { | |
method: "POST", | |
}); | |
return await response.json(); | |
} | |
async function authCredential(response) { | |
const { publicKeyCredentialRequestOptions } = response; | |
const request = { | |
publicKey: { | |
challenge: base64urlToBuffer(publicKeyCredentialRequestOptions.challenge), | |
rpid: publicKeyCredentialRequestOptions.rpId, | |
}, | |
}; | |
console.log("our get request", request); | |
return navigator.credentials.get(request); | |
} | |
async function finishAuth(response, credential) { | |
const body = { | |
requestId: response.requestId, | |
credential: { | |
id: credential.id, | |
rawId: bufferToBase64url(credential.rawId), | |
type: credential.type, | |
response: { | |
authenticatorData: bufferToBase64url( | |
credential.response.authenticatorData | |
), | |
signature: bufferToBase64url(credential.response.signature), | |
clientDataJSON: bufferToBase64url(credential.response.clientDataJSON), | |
userHandle: bufferToBase64url(credential.response.userHandle), | |
}, | |
clientExtensionResults: {}, | |
}, | |
}; | |
console.log("our response", body); | |
return await fetch(authURL + "/finish", { | |
method: "POST", | |
body: JSON.stringify(body), | |
}); | |
} | |
function base64urlToBuffer(baseurl64String) { | |
const padding = "==".slice(0, (4 - (baseurl64String.length % 4)) % 4); | |
const base64String = | |
baseurl64String.replace(/-/g, "+").replace(/_/g, "/") + padding; | |
const str = atob(base64String); | |
const buffer = new ArrayBuffer(str.length); | |
const byteView = new Uint8Array(buffer); | |
for (let i = 0; i < str.length; i++) { | |
byteView[i] = str.charCodeAt(i); | |
} | |
return buffer; | |
} | |
function bufferToBase64url(buffer) { | |
const byteView = new Uint8Array(buffer); | |
let str = ""; | |
for (const charCode of byteView) { | |
str += String.fromCharCode(charCode); | |
} | |
const base64String = btoa(str); | |
const base64urlString = base64String | |
.replace(/\+/g, "-") | |
.replace(/\//g, "_") | |
.replace(/=/g, ""); | |
return base64urlString; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment