|
|
@@ -1,16 +1,15 @@
|
|
|
class WebAuthnHandler {
|
|
|
static isWebAuthnSupported() {
|
|
|
- return window.PublicKeyCredential;
|
|
|
+ return typeof PublicKeyCredential !== "undefined";
|
|
|
}
|
|
|
|
|
|
static showErrorMessage(errorMessage) {
|
|
|
- console.log("webauthn error: " + errorMessage);
|
|
|
+ console.error("WebAuthn error:", errorMessage);
|
|
|
|
|
|
const alertElement = document.getElementById("webauthn-error-alert");
|
|
|
- if (!alertElement) {
|
|
|
- return;
|
|
|
+ if (alertElement) {
|
|
|
+ alertElement.remove();
|
|
|
}
|
|
|
- alertElement.remove();
|
|
|
|
|
|
const alertTemplateElement = document.getElementById("webauthn-error");
|
|
|
if (alertTemplateElement) {
|
|
|
@@ -23,15 +22,15 @@ class WebAuthnHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async isConditionalLoginSupported() {
|
|
|
+ static async isConditionalLoginSupported() {
|
|
|
return WebAuthnHandler.isWebAuthnSupported() &&
|
|
|
window.PublicKeyCredential.isConditionalMediationAvailable &&
|
|
|
- window.PublicKeyCredential.isConditionalMediationAvailable();
|
|
|
+ await window.PublicKeyCredential.isConditionalMediationAvailable();
|
|
|
}
|
|
|
|
|
|
async conditionalLogin(abortController) {
|
|
|
- if (await this.isConditionalLoginSupported()) {
|
|
|
- this.login("", abortController);
|
|
|
+ if (await WebAuthnHandler.isConditionalLoginSupported()) {
|
|
|
+ return this.login("", abortController);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -49,7 +48,7 @@ class WebAuthnHandler {
|
|
|
async post(urlKey, username, data) {
|
|
|
let url = document.body.dataset[urlKey];
|
|
|
if (username) {
|
|
|
- url += "?username=" + username;
|
|
|
+ url += "?username=" + encodeURIComponent(username);
|
|
|
}
|
|
|
|
|
|
return sendPOSTRequest(url, data);
|
|
|
@@ -58,7 +57,7 @@ class WebAuthnHandler {
|
|
|
async get(urlKey, username) {
|
|
|
let url = document.body.dataset[urlKey];
|
|
|
if (username) {
|
|
|
- url += "?username=" + username;
|
|
|
+ url += "?username=" + encodeURIComponent(username);
|
|
|
}
|
|
|
return fetch(url);
|
|
|
}
|
|
|
@@ -83,14 +82,27 @@ class WebAuthnHandler {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const credentialCreationOptions = await registerBeginResponse.json();
|
|
|
+ let credentialCreationOptions;
|
|
|
+ try {
|
|
|
+ credentialCreationOptions = await registerBeginResponse.json();
|
|
|
+ } catch (err) {
|
|
|
+ WebAuthnHandler.showErrorMessage("Failed to parse registration options");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
credentialCreationOptions.publicKey.challenge = this.decodeBuffer(credentialCreationOptions.publicKey.challenge);
|
|
|
credentialCreationOptions.publicKey.user.id = this.decodeBuffer(credentialCreationOptions.publicKey.user.id);
|
|
|
if (Object.hasOwn(credentialCreationOptions.publicKey, 'excludeCredentials')) {
|
|
|
credentialCreationOptions.publicKey.excludeCredentials.forEach((credential) => credential.id = this.decodeBuffer(credential.id));
|
|
|
}
|
|
|
|
|
|
- const attestation = await navigator.credentials.create(credentialCreationOptions);
|
|
|
+ let attestation;
|
|
|
+ try {
|
|
|
+ attestation = await navigator.credentials.create(credentialCreationOptions);
|
|
|
+ } catch (err) {
|
|
|
+ WebAuthnHandler.showErrorMessage(err);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
let registrationFinishResponse;
|
|
|
try {
|
|
|
@@ -109,7 +121,7 @@ class WebAuthnHandler {
|
|
|
}
|
|
|
|
|
|
if (!registrationFinishResponse.ok) {
|
|
|
- throw new Error("Login failed with HTTP status code " + response.status);
|
|
|
+ throw new Error("Registration failed with HTTP status code " + registrationFinishResponse.status);
|
|
|
}
|
|
|
|
|
|
const jsonData = await registrationFinishResponse.json();
|
|
|
@@ -125,7 +137,14 @@ class WebAuthnHandler {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const credentialRequestOptions = await loginBeginResponse.json();
|
|
|
+ let credentialRequestOptions;
|
|
|
+ try {
|
|
|
+ credentialRequestOptions = await loginBeginResponse.json();
|
|
|
+ } catch (err) {
|
|
|
+ WebAuthnHandler.showErrorMessage("Failed to parse login options");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
credentialRequestOptions.publicKey.challenge = this.decodeBuffer(credentialRequestOptions.publicKey.challenge);
|
|
|
|
|
|
if (Object.hasOwn(credentialRequestOptions.publicKey, 'allowCredentials')) {
|