865 lines
30 KiB
JavaScript
865 lines
30 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
let AboutWebauthnService = null;
|
|
|
|
var AboutWebauthnManagerJS = {
|
|
_topic: "about-webauthn-prompt",
|
|
_initialized: false,
|
|
_l10n: null,
|
|
_bio_l10n: null,
|
|
_curr_data: null,
|
|
_current_tab: "",
|
|
_previous_tab: "",
|
|
|
|
init() {
|
|
if (this._initialized) {
|
|
return;
|
|
}
|
|
this._l10n = new Localization(["toolkit/about/aboutWebauthn.ftl"], true);
|
|
this._bio_l10n = new Map();
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpGood",
|
|
"about-webauthn-ctap2-enroll-feedback-good"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooHigh",
|
|
"about-webauthn-ctap2-enroll-feedback-too-high"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooLow",
|
|
"about-webauthn-ctap2-enroll-feedback-too-low"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooLeft",
|
|
"about-webauthn-ctap2-enroll-feedback-too-left"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooRight",
|
|
"about-webauthn-ctap2-enroll-feedback-too-right"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooFast",
|
|
"about-webauthn-ctap2-enroll-feedback-too-fast"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooSlow",
|
|
"about-webauthn-ctap2-enroll-feedback-too-slow"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpPoorQuality",
|
|
"about-webauthn-ctap2-enroll-feedback-poor-quality"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooSkewed",
|
|
"about-webauthn-ctap2-enroll-feedback-too-skewed"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpTooShort",
|
|
"about-webauthn-ctap2-enroll-feedback-too-short"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpMergeFailure",
|
|
"about-webauthn-ctap2-enroll-feedback-merge-failure"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackFpExists",
|
|
"about-webauthn-ctap2-enroll-feedback-exists"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackNoUserActivity",
|
|
"about-webauthn-ctap2-enroll-feedback-no-user-activity"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackNoUserPresenceTransition",
|
|
"about-webauthn-ctap2-enroll-feedback-no-user-presence-transition"
|
|
);
|
|
this._bio_l10n.set(
|
|
"Ctap2EnrollFeedbackOther",
|
|
"about-webauthn-ctap2-enroll-feedback-other"
|
|
);
|
|
|
|
Services.obs.addObserver(this, this._topic);
|
|
this._initialized = true;
|
|
reset_page();
|
|
},
|
|
|
|
uninit() {
|
|
Services.obs.removeObserver(this, this._topic);
|
|
this._initialized = false;
|
|
this._l10n = null;
|
|
this._current_tab = "";
|
|
this._previous_tab = "";
|
|
},
|
|
|
|
observe(aSubject, aTopic, aData) {
|
|
let data = JSON.parse(aData);
|
|
|
|
// We have token
|
|
if (data.type == "selected-device") {
|
|
this._curr_data = data.auth_info;
|
|
this.show_ui_based_on_authenticator_info(data);
|
|
fake_click_event_for_id("info-tab-button");
|
|
} else if (data.type == "select-device") {
|
|
set_info_text("about-webauthn-text-select-device");
|
|
} else if (data.type == "pin-required") {
|
|
open_pin_required_tab();
|
|
} else if (data.type == "pin-invalid") {
|
|
let retries = data.retries ? data.retries : 0;
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-invalid-error",
|
|
JSON.stringify({ retriesLeft: retries })
|
|
);
|
|
open_pin_required_tab();
|
|
} else if (data.type == "bio-enrollment-update") {
|
|
if (data.result.EnrollmentList) {
|
|
show_results_banner("success", "about-webauthn-results-success");
|
|
this.show_enrollment_list(data.result.EnrollmentList);
|
|
bio_enrollment_in_progress(false);
|
|
} else if (data.result.DeleteSuccess || data.result.AddSuccess) {
|
|
show_results_banner("success", "about-webauthn-results-success");
|
|
clear_bio_enrollment_samples();
|
|
// Update AuthenticatorInfo
|
|
this._curr_data = data.result.DeleteSuccess ?? data.result.AddSuccess;
|
|
fake_click_event_for_id("bio-enrollments-tab-button");
|
|
bio_enrollment_in_progress(false);
|
|
// If we still have some enrollments to show, update the list
|
|
// otherwise, remove it.
|
|
if (
|
|
this._curr_data.options.bioEnroll === true ||
|
|
this._curr_data.options.userVerificationMgmtPreview === true
|
|
) {
|
|
list_bio_enrollments();
|
|
} else {
|
|
// Hide the list, because it's empty
|
|
document.getElementById("bio-enrollment-list-subsection").hidden =
|
|
true;
|
|
}
|
|
} else if (data.result.UpdateSuccess) {
|
|
fake_click_event_for_id("bio-enrollments-tab-button");
|
|
list_bio_enrollments();
|
|
} else if (data.result.SampleStatus) {
|
|
show_add_bio_enrollment_section();
|
|
let up = document.getElementById("enrollment-update");
|
|
let sample_update = document.createElement("div");
|
|
let new_line = document.createElement("label");
|
|
new_line.classList.add("sample");
|
|
new_line.setAttribute(
|
|
"data-l10n-id",
|
|
this._bio_l10n.get(data.result.SampleStatus[0])
|
|
);
|
|
sample_update.appendChild(new_line);
|
|
let samples_needed_line = document.createElement("label");
|
|
samples_needed_line.setAttribute(
|
|
"data-l10n-id",
|
|
"about-webauthn-samples-still-needed"
|
|
);
|
|
samples_needed_line.setAttribute(
|
|
"data-l10n-args",
|
|
JSON.stringify({ repeatCount: data.result.SampleStatus[1] })
|
|
);
|
|
sample_update.classList.add("bio-enrollment-sample");
|
|
sample_update.appendChild(samples_needed_line);
|
|
up.appendChild(sample_update);
|
|
}
|
|
} else if (data.type == "credential-management-update") {
|
|
credential_management_in_progress(false);
|
|
if (data.result.CredentialList) {
|
|
show_results_banner("success", "about-webauthn-results-success");
|
|
this.show_credential_list(data.result.CredentialList.credential_list);
|
|
} else {
|
|
// DeleteSuccess or UpdateSuccess
|
|
show_results_banner("success", "about-webauthn-results-success");
|
|
list_credentials();
|
|
}
|
|
} else if (data.type == "listen-success") {
|
|
reset_page();
|
|
// Show results
|
|
show_results_banner("success", "about-webauthn-results-success");
|
|
this._reset_in_progress = "";
|
|
AboutWebauthnService.listen();
|
|
} else if (data.type == "listen-error") {
|
|
reset_page();
|
|
|
|
if (!data.error) {
|
|
show_results_banner("error", "about-webauthn-results-general-error");
|
|
} else if (data.error.type == "pin-auth-blocked") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-auth-blocked-error"
|
|
);
|
|
} else if (data.error.type == "pin-not-set") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-not-set-error"
|
|
);
|
|
} else if (data.error.type == "device-blocked") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-blocked-error"
|
|
);
|
|
} else if (data.error.type == "pin-is-too-short") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-too-short-error"
|
|
);
|
|
} else if (data.error.type == "pin-is-too-long") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-too-long-error"
|
|
);
|
|
} else if (data.error.type == "pin-invalid") {
|
|
let retries = data.error.retries
|
|
? JSON.stringify({ retriesLeft: data.error.retries })
|
|
: null;
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-pin-invalid-error",
|
|
retries
|
|
);
|
|
} else if (data.error.type == "cancel") {
|
|
show_results_banner(
|
|
"error",
|
|
"about-webauthn-results-cancelled-by-user-error"
|
|
);
|
|
} else {
|
|
show_results_banner("error", "about-webauthn-results-general-error");
|
|
}
|
|
AboutWebauthnService.listen();
|
|
}
|
|
},
|
|
|
|
show_authenticator_options(options, element, l10n_base) {
|
|
let table = document.getElementById(element);
|
|
var empty_table = document.createElement("table");
|
|
empty_table.id = element;
|
|
table.parentNode.replaceChild(empty_table, table);
|
|
table = document.getElementById(element);
|
|
for (let key in options) {
|
|
if (key == "options") {
|
|
continue;
|
|
}
|
|
// Create an empty <tr> element and add it to the 1st position of the table:
|
|
var row = table.insertRow(0);
|
|
|
|
// Insert new cells (<td> elements) at the 1st and 2nd position of the "new" <tr> element:
|
|
var cell1 = row.insertCell(0);
|
|
var cell2 = row.insertCell(1);
|
|
|
|
// Add some text to the new cells:
|
|
let key_text = this._l10n.formatValueSync(
|
|
l10n_base + "-" + key.toLowerCase().replace(/_/g, "-")
|
|
);
|
|
var key_node = document.createTextNode(key_text);
|
|
cell1.appendChild(key_node);
|
|
var raw_value = JSON.stringify(options[key]);
|
|
var value = raw_value;
|
|
if (["true", "false", "null"].includes(raw_value)) {
|
|
value = this._l10n.formatValueSync(l10n_base + "-" + raw_value);
|
|
}
|
|
var value_node = document.createTextNode(value);
|
|
cell2.appendChild(value_node);
|
|
}
|
|
},
|
|
|
|
show_ui_based_on_authenticator_info(data) {
|
|
// Hide the "Please plug in a token"-message
|
|
document.getElementById("info-text-div").hidden = true;
|
|
// Show options, based on what the token supports
|
|
if (data.auth_info) {
|
|
document.getElementById("ctap2-token-info").style.display = "flex";
|
|
this.show_authenticator_options(
|
|
data.auth_info.options,
|
|
"authenticator-options",
|
|
"about-webauthn-auth-option"
|
|
);
|
|
this.show_authenticator_options(
|
|
data.auth_info,
|
|
"authenticator-info",
|
|
"about-webauthn-auth-info"
|
|
);
|
|
// Check if token supports PINs
|
|
if (data.auth_info.options.clientPin != null) {
|
|
document.getElementById("pin-tab-button").style.display = "flex";
|
|
if (data.auth_info.options.clientPin === true) {
|
|
// It has a Pin set
|
|
document.getElementById("change-pin-button").style.display = "block";
|
|
document.getElementById("set-pin-button").style.display = "none";
|
|
document.getElementById("current-pin-div").hidden = false;
|
|
} else {
|
|
// It does not have a Pin set yet
|
|
document.getElementById("change-pin-button").style.display = "none";
|
|
document.getElementById("set-pin-button").style.display = "block";
|
|
document.getElementById("current-pin-div").hidden = true;
|
|
}
|
|
} else {
|
|
document.getElementById("pin-tab-button").style.display = "none";
|
|
}
|
|
|
|
if (
|
|
data.auth_info.options.credMgmt ||
|
|
data.auth_info.options.credentialMgmtPreview
|
|
) {
|
|
document.getElementById("credentials-tab-button").style.display =
|
|
"flex";
|
|
} else {
|
|
document.getElementById("credentials-tab-button").style.display =
|
|
"none";
|
|
}
|
|
|
|
if (
|
|
data.auth_info.options.bioEnroll != null ||
|
|
data.auth_info.options.userVerificationMgmtPreview != null
|
|
) {
|
|
document.getElementById("bio-enrollments-tab-button").style.display =
|
|
"flex";
|
|
} else {
|
|
document.getElementById("bio-enrollments-tab-button").style.display =
|
|
"none";
|
|
}
|
|
} else {
|
|
// Currently auth-rs doesn't send this, because it filters out ctap2-devices.
|
|
// U2F / CTAP1 tokens can't be managed
|
|
set_info_text("about-webauthn-text-non-ctap2-device");
|
|
}
|
|
},
|
|
|
|
show_credential_list(credential_list) {
|
|
// We may have temporarily hidden the tab when asking the user for a PIN
|
|
// so we have to show it again.
|
|
fake_click_event_for_id("credentials-tab-button");
|
|
document.getElementById("credential-list-subsection").hidden = false;
|
|
let table = document.getElementById("credential-list");
|
|
var empty_table = document.createElement("table");
|
|
empty_table.id = "credential-list";
|
|
table.parentNode.replaceChild(empty_table, table);
|
|
if (!credential_list.length) {
|
|
document.getElementById("credential-list-empty-label").hidden = false;
|
|
return;
|
|
}
|
|
document.getElementById("credential-list-empty-label").hidden = true;
|
|
table = document.getElementById("credential-list");
|
|
credential_list.forEach(rp => {
|
|
// Add some text to the new cells:
|
|
let key_text = rp.rp.id;
|
|
rp.credentials.forEach(cred => {
|
|
let value_text = cred.user.name;
|
|
// Create an empty <tr> element and add it to the 1st position of the table:
|
|
var row = table.insertRow(0);
|
|
var key_node = document.createTextNode(key_text);
|
|
var value_node = document.createTextNode(value_text);
|
|
row.insertCell(0).appendChild(key_node);
|
|
row.insertCell(1).appendChild(value_node);
|
|
var delete_button = document.createElement("button");
|
|
delete_button.classList.add("delete-button");
|
|
delete_button.classList.add("credentials-button");
|
|
let garbage_icon = document.createElement("img");
|
|
garbage_icon.setAttribute(
|
|
"src",
|
|
"chrome://global/skin/icons/delete.svg"
|
|
);
|
|
garbage_icon.classList.add("delete-icon");
|
|
delete_button.appendChild(garbage_icon);
|
|
let delete_text = document.createElement("span");
|
|
delete_text.setAttribute(
|
|
"data-l10n-id",
|
|
"about-webauthn-delete-button"
|
|
);
|
|
delete_button.appendChild(delete_text);
|
|
delete_button.addEventListener("click", function () {
|
|
let context = document.getElementById("confirmation-context");
|
|
context.textContent = key_text + " - " + value_text;
|
|
credential_management_in_progress(true);
|
|
let cmd = {
|
|
CredentialManagement: { DeleteCredential: cred.credential_id },
|
|
};
|
|
context.setAttribute("data-ctap-command", JSON.stringify(cmd));
|
|
open_delete_confirmation_tab();
|
|
});
|
|
row.insertCell(2).appendChild(delete_button);
|
|
});
|
|
});
|
|
},
|
|
|
|
show_enrollment_list(enrollment_list) {
|
|
// We may have temporarily hidden the tab when asking the user for a PIN
|
|
// so we have to show it again.
|
|
fake_click_event_for_id("bio-enrollments-tab-button");
|
|
document.getElementById("bio-enrollment-list-subsection").hidden = false;
|
|
let table = document.getElementById("bio-enrollment-list");
|
|
var empty_table = document.createElement("table");
|
|
empty_table.id = "bio-enrollment-list";
|
|
table.parentNode.replaceChild(empty_table, table);
|
|
if (!enrollment_list.length) {
|
|
document.getElementById("bio-enrollment-list-empty-label").hidden = false;
|
|
return;
|
|
}
|
|
document.getElementById("bio-enrollment-list-empty-label").hidden = true;
|
|
table = document.getElementById("bio-enrollment-list");
|
|
enrollment_list.forEach(enrollment => {
|
|
let key_text = enrollment.template_friendly_name ?? "<unnamed>";
|
|
var row = table.insertRow(0);
|
|
var key_node = document.createTextNode(key_text);
|
|
row.insertCell(0).appendChild(key_node);
|
|
var delete_button = document.createElement("button");
|
|
delete_button.classList.add("delete-button");
|
|
delete_button.classList.add("bio-enrollment-button");
|
|
let garbage_icon = document.createElement("img");
|
|
garbage_icon.setAttribute("src", "chrome://global/skin/icons/delete.svg");
|
|
garbage_icon.classList.add("delete-icon");
|
|
delete_button.appendChild(garbage_icon);
|
|
let delete_text = document.createElement("span");
|
|
delete_text.setAttribute("data-l10n-id", "about-webauthn-delete-button");
|
|
delete_button.appendChild(delete_text);
|
|
delete_button.addEventListener("click", function () {
|
|
let context = document.getElementById("confirmation-context");
|
|
context.textContent = key_text;
|
|
bio_enrollment_in_progress(true);
|
|
let cmd = {
|
|
BioEnrollment: { DeleteEnrollment: enrollment.template_id },
|
|
};
|
|
context.setAttribute("data-ctap-command", JSON.stringify(cmd));
|
|
open_delete_confirmation_tab();
|
|
});
|
|
row.insertCell(1).appendChild(delete_button);
|
|
});
|
|
},
|
|
};
|
|
|
|
function set_info_text(l10nId) {
|
|
document.getElementById("info-text-div").hidden = false;
|
|
let field = document.getElementById("info-text-field");
|
|
field.setAttribute("data-l10n-id", l10nId);
|
|
document.getElementById("ctap2-token-info").style.display = "none";
|
|
}
|
|
|
|
function show_results_banner(result, l10n, l10n_args) {
|
|
let ctap_result = document.getElementById("ctap-listen-result");
|
|
ctap_result.setAttribute("data-l10n-id", l10n);
|
|
ctap_result.classList.add(result);
|
|
if (l10n_args) {
|
|
ctap_result.setAttribute("data-l10n-args", l10n_args);
|
|
}
|
|
}
|
|
|
|
function hide_results_banner() {
|
|
let res_banner = document.getElementById("ctap-listen-result");
|
|
let res_div = document.getElementById("ctap-listen-div");
|
|
let empty_banner = document.createElement("label");
|
|
empty_banner.id = "ctap-listen-result";
|
|
res_div.replaceChild(empty_banner, res_banner);
|
|
}
|
|
|
|
function operation_in_progress(name, in_progress) {
|
|
let buttons = Array.from(document.getElementsByClassName(name));
|
|
buttons.forEach(button => {
|
|
button.disabled = in_progress;
|
|
});
|
|
}
|
|
|
|
function credential_management_in_progress(in_progress) {
|
|
operation_in_progress("credentials-button", in_progress);
|
|
}
|
|
|
|
function bio_enrollment_in_progress(in_progress) {
|
|
operation_in_progress("bio-enrollment-button", in_progress);
|
|
}
|
|
|
|
function clear_bio_enrollment_samples() {
|
|
// Remove all previous status updates
|
|
let up = document.getElementById("enrollment-update");
|
|
while (up.firstChild) {
|
|
up.removeChild(up.lastChild);
|
|
}
|
|
document.getElementById("enrollment-name").value = "";
|
|
}
|
|
|
|
function fake_click_event_for_id(id) {
|
|
// Not using document.getElementById(id).click();
|
|
// here, because we have to add additional data, so we don't
|
|
// hide the results-div here, if there is any. 'Normal' clicking
|
|
// by the user will hide it.
|
|
const evt = new CustomEvent("click", {
|
|
detail: { skip_results_clearing: true },
|
|
});
|
|
document.getElementById(id).dispatchEvent(evt);
|
|
}
|
|
|
|
function reset_page() {
|
|
// Hide everything that needs a device to know if it should be displayed
|
|
document.getElementById("ctap2-token-info").style.display = "none";
|
|
Array.from(document.getElementsByClassName("optional-category")).forEach(
|
|
div => {
|
|
div.style.display = "none";
|
|
}
|
|
);
|
|
|
|
// Only display the "please connect a device" - text
|
|
set_info_text("about-webauthn-text-connect-device");
|
|
|
|
// Clear results and input fields
|
|
hide_results_banner();
|
|
var divs = Array.from(document.getElementsByTagName("input"));
|
|
divs.forEach(div => {
|
|
div.value = "";
|
|
});
|
|
|
|
sidebar_set_disabled(false);
|
|
|
|
// ListCredentials
|
|
credential_management_in_progress(false);
|
|
document.getElementById("credential-list-subsection").hidden = true;
|
|
|
|
// BioEnrollment
|
|
clear_bio_enrollment_samples();
|
|
document.getElementById("bio-enrollment-list-subsection").hidden = true;
|
|
bio_enrollment_in_progress(false);
|
|
|
|
AboutWebauthnManagerJS._previous_tab = "";
|
|
AboutWebauthnManagerJS._current_tab = "";
|
|
|
|
// Not using `document.getElementById("info-tab-button").click();`
|
|
// here, because if we were focused on a category-button that got removed (e.g.
|
|
// when unplugging the device), we have to reset the ARIA-related attributes
|
|
// first, before we can click on the button, otherwise the a11y-tests
|
|
// will complain. So we fake the click again.
|
|
const evt = {
|
|
detail: {},
|
|
currentTarget: document.getElementById("info-tab-button"),
|
|
};
|
|
open_info_tab(evt);
|
|
}
|
|
|
|
function sidebar_set_disabled(disabled) {
|
|
var cats = Array.from(document.getElementsByClassName("category"));
|
|
cats.forEach(cat => {
|
|
if (disabled) {
|
|
cat.classList.add("disabled-category");
|
|
} else {
|
|
cat.classList.remove("disabled-category");
|
|
}
|
|
});
|
|
}
|
|
|
|
function check_pin_repeat_is_correct() {
|
|
let pin = document.getElementById("new-pin");
|
|
let pin_repeat = document.getElementById("new-pin-repeat");
|
|
let has_current_pin = !document.getElementById("current-pin-div").hidden;
|
|
let current_pin = document.getElementById("current-pin");
|
|
let can_enable_button =
|
|
pin.value != null && pin.value != "" && pin.value == pin_repeat.value;
|
|
if (has_current_pin && !current_pin.value) {
|
|
can_enable_button = false;
|
|
}
|
|
if (!can_enable_button) {
|
|
pin.classList.add("different");
|
|
pin_repeat.classList.add("different");
|
|
document.getElementById("set-pin-button").disabled = true;
|
|
document.getElementById("change-pin-button").disabled = true;
|
|
return false;
|
|
}
|
|
pin.classList.remove("different");
|
|
pin_repeat.classList.remove("different");
|
|
document.getElementById("set-pin-button").disabled = false;
|
|
document.getElementById("change-pin-button").disabled = false;
|
|
return true;
|
|
}
|
|
|
|
function send_pin() {
|
|
close_temporary_overlay_tab();
|
|
let pin = document.getElementById("pin-required").value;
|
|
AboutWebauthnService.pinCallback(0, pin);
|
|
}
|
|
|
|
function set_pin() {
|
|
let pin = document.getElementById("new-pin").value;
|
|
let cmd = { SetPIN: pin };
|
|
AboutWebauthnService.runCommand(JSON.stringify(cmd));
|
|
}
|
|
|
|
function change_pin() {
|
|
let curr_pin = document.getElementById("current-pin").value;
|
|
let new_pin = document.getElementById("new-pin").value;
|
|
let cmd = { ChangePIN: [curr_pin, new_pin] };
|
|
AboutWebauthnService.runCommand(JSON.stringify(cmd));
|
|
}
|
|
|
|
function list_credentials() {
|
|
credential_management_in_progress(true);
|
|
let cmd = { CredentialManagement: "GetCredentials" };
|
|
AboutWebauthnService.runCommand(JSON.stringify(cmd));
|
|
}
|
|
|
|
function list_bio_enrollments() {
|
|
bio_enrollment_in_progress(true);
|
|
let cmd = { BioEnrollment: "GetEnrollments" };
|
|
AboutWebauthnService.runCommand(JSON.stringify(cmd));
|
|
}
|
|
|
|
function show_add_bio_enrollment_section() {
|
|
const evt = new CustomEvent("click", {
|
|
detail: { temporary_overlay: true },
|
|
});
|
|
open_tab(evt, "add-bio-enrollment-section");
|
|
document.getElementById("enrollment-name").focus();
|
|
}
|
|
|
|
function start_bio_enrollment() {
|
|
bio_enrollment_in_progress(true);
|
|
let name = document.getElementById("enrollment-name").value;
|
|
if (!name) {
|
|
name = null; // Empty means "Not set"
|
|
}
|
|
let cmd = { BioEnrollment: { StartNewEnrollment: name } };
|
|
AboutWebauthnService.runCommand(JSON.stringify(cmd));
|
|
}
|
|
|
|
function cancel_transaction() {
|
|
credential_management_in_progress(false);
|
|
bio_enrollment_in_progress(false);
|
|
AboutWebauthnService.cancel(0);
|
|
}
|
|
|
|
function confirm_deletion() {
|
|
let context = document.getElementById("confirmation-context");
|
|
let cmd = context.getAttribute("data-ctap-command");
|
|
AboutWebauthnService.runCommand(cmd);
|
|
}
|
|
|
|
function cancel_confirmation() {
|
|
credential_management_in_progress(false);
|
|
bio_enrollment_in_progress(false);
|
|
close_temporary_overlay_tab();
|
|
}
|
|
|
|
async function onLoad() {
|
|
document.getElementById("set-pin-button").addEventListener("click", set_pin);
|
|
document
|
|
.getElementById("change-pin-button")
|
|
.addEventListener("click", change_pin);
|
|
document
|
|
.getElementById("list-credentials-button")
|
|
.addEventListener("click", list_credentials);
|
|
document
|
|
.getElementById("list-bio-enrollments-button")
|
|
.addEventListener("click", list_bio_enrollments);
|
|
document
|
|
.getElementById("add-bio-enrollment-button")
|
|
.addEventListener("click", show_add_bio_enrollment_section);
|
|
document
|
|
.getElementById("start-enrollment-button")
|
|
.addEventListener("click", start_bio_enrollment);
|
|
document
|
|
.getElementById("new-pin")
|
|
.addEventListener("input", check_pin_repeat_is_correct);
|
|
document
|
|
.getElementById("new-pin-repeat")
|
|
.addEventListener("input", check_pin_repeat_is_correct);
|
|
document
|
|
.getElementById("current-pin")
|
|
.addEventListener("input", check_pin_repeat_is_correct);
|
|
let info_button = document.getElementById("info-tab-button");
|
|
info_button.addEventListener("click", open_info_tab);
|
|
info_button.addEventListener("keydown", handle_keydowns);
|
|
let pin_button = document.getElementById("pin-tab-button");
|
|
pin_button.addEventListener("click", open_pin_tab);
|
|
pin_button.addEventListener("keydown", handle_keydowns);
|
|
let credentials_button = document.getElementById("credentials-tab-button");
|
|
credentials_button.addEventListener("click", open_credentials_tab);
|
|
credentials_button.addEventListener("keydown", handle_keydowns);
|
|
let bio_enrollments_button = document.getElementById(
|
|
"bio-enrollments-tab-button"
|
|
);
|
|
bio_enrollments_button.addEventListener("click", open_bio_enrollments_tab);
|
|
bio_enrollments_button.addEventListener("keydown", handle_keydowns);
|
|
document
|
|
.getElementById("send-pin-button")
|
|
.addEventListener("click", send_pin);
|
|
document
|
|
.getElementById("cancel-send-pin-button")
|
|
.addEventListener("click", cancel_transaction);
|
|
document
|
|
.getElementById("cancel-enrollment-button")
|
|
.addEventListener("click", cancel_transaction);
|
|
document
|
|
.getElementById("cancel-confirmation-button")
|
|
.addEventListener("click", cancel_confirmation);
|
|
document
|
|
.getElementById("confirm-deletion-button")
|
|
.addEventListener("click", confirm_deletion);
|
|
AboutWebauthnManagerJS.init();
|
|
try {
|
|
AboutWebauthnService.listen();
|
|
} catch (ex) {
|
|
set_info_text("about-webauthn-text-not-available");
|
|
AboutWebauthnManagerJS.uninit();
|
|
}
|
|
}
|
|
|
|
function handle_keydowns(event) {
|
|
let index;
|
|
let event_was_handled = true;
|
|
let tabs = Array.from(document.getElementsByClassName("category"));
|
|
if (tabs.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
switch (event.key) {
|
|
case "ArrowLeft":
|
|
case "ArrowUp":
|
|
if (event.currentTarget === tabs[0]) {
|
|
event.currentTarget.focus();
|
|
} else {
|
|
index = tabs.indexOf(event.currentTarget);
|
|
tabs[index - 1].focus();
|
|
}
|
|
break;
|
|
|
|
case "ArrowRight":
|
|
case "ArrowDown":
|
|
if (event.currentTarget === tabs[tabs.length - 1]) {
|
|
event.currentTarget.focus();
|
|
} else {
|
|
index = tabs.indexOf(event.currentTarget);
|
|
tabs[index + 1].focus();
|
|
}
|
|
break;
|
|
|
|
case "Home":
|
|
tabs[0].focus();
|
|
break;
|
|
|
|
case "End":
|
|
tabs[tabs.length - 1].focus();
|
|
break;
|
|
|
|
case "Enter":
|
|
case " ":
|
|
event.currentTarget.click();
|
|
break;
|
|
|
|
default:
|
|
event_was_handled = false;
|
|
break;
|
|
}
|
|
|
|
if (event_was_handled) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
function open_tab(evt, tabName) {
|
|
var tabcontent, tablinks;
|
|
// Hide all others
|
|
tabcontent = Array.from(document.getElementsByClassName("tabcontent"));
|
|
tabcontent.forEach(tab => {
|
|
tab.style.display = "none";
|
|
});
|
|
// Display the one we selected
|
|
document.getElementById(tabName).style.display = "block";
|
|
|
|
// If this is a temporary overlay, like pin-required, we don't
|
|
// touch the sidebar and which button is selected.
|
|
if (!evt.detail.temporary_overlay) {
|
|
tablinks = Array.from(document.getElementsByClassName("category"));
|
|
tablinks.forEach(tablink => {
|
|
tablink.removeAttribute("selected");
|
|
tablink.setAttribute("aria-selected", "false");
|
|
tablink.setAttribute("tabindex", "-1");
|
|
tablink.disabled = false;
|
|
});
|
|
evt.currentTarget.setAttribute("selected", "true");
|
|
evt.currentTarget.setAttribute("tabindex", "0");
|
|
evt.currentTarget.setAttribute("aria-selected", "true");
|
|
}
|
|
|
|
if (!evt.detail.skip_results_clearing) {
|
|
hide_results_banner();
|
|
}
|
|
sidebar_set_disabled(false);
|
|
AboutWebauthnManagerJS._previous_tab = AboutWebauthnManagerJS._current_tab;
|
|
AboutWebauthnManagerJS._current_tab = tabName;
|
|
}
|
|
|
|
function open_info_tab(evt) {
|
|
open_tab(evt, "token-info-section");
|
|
}
|
|
function open_pin_tab(evt) {
|
|
open_tab(evt, "set-change-pin-section");
|
|
}
|
|
function open_credentials_tab(evt) {
|
|
open_tab(evt, "credential-management-section");
|
|
}
|
|
function open_bio_enrollments_tab(evt) {
|
|
// We can only list, if there are any registered already
|
|
if (
|
|
AboutWebauthnManagerJS._curr_data.options.bioEnroll === true ||
|
|
AboutWebauthnManagerJS._curr_data.options.userVerificationMgmtPreview ===
|
|
true
|
|
) {
|
|
document.getElementById("list-bio-enrollments-button").style.display =
|
|
"inline-block";
|
|
} else {
|
|
document.getElementById("list-bio-enrollments-button").style.display =
|
|
"none";
|
|
}
|
|
open_tab(evt, "bio-enrollment-section");
|
|
}
|
|
function open_reset_tab(evt) {
|
|
open_tab(evt, "reset-token-section");
|
|
}
|
|
function open_pin_required_tab() {
|
|
// Remove any old value we might have had
|
|
document.getElementById("pin-required").value = "";
|
|
const evt = new CustomEvent("click", {
|
|
detail: {
|
|
temporary_overlay: true,
|
|
skip_results_clearing: true, // We might be called multiple times, if PIN was invalid
|
|
},
|
|
});
|
|
open_tab(evt, "pin-required-section");
|
|
document.getElementById("pin-required").focus();
|
|
// This is a temporary overlay, so we don't want the
|
|
// user to click away from it, unless via the Cancel-button.
|
|
sidebar_set_disabled(true);
|
|
}
|
|
function close_temporary_overlay_tab() {
|
|
const evt = new CustomEvent("click", {
|
|
detail: { temporary_overlay: true },
|
|
});
|
|
open_tab(evt, AboutWebauthnManagerJS._previous_tab);
|
|
sidebar_set_disabled(false);
|
|
}
|
|
function open_delete_confirmation_tab() {
|
|
const evt = new CustomEvent("click", {
|
|
detail: {
|
|
temporary_overlay: true,
|
|
},
|
|
});
|
|
open_tab(evt, "confirm-deletion-section");
|
|
// This is a temporary overlay, so we don't want the
|
|
// user to click away from it, unless via the Cancel-button.
|
|
sidebar_set_disabled(true);
|
|
}
|
|
|
|
try {
|
|
AboutWebauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
|
|
Ci.nsIWebAuthnService
|
|
);
|
|
document.addEventListener("DOMContentLoaded", onLoad);
|
|
window.addEventListener("beforeunload", () => {
|
|
AboutWebauthnManagerJS.uninit();
|
|
if (AboutWebauthnService) {
|
|
AboutWebauthnService.cancel(0);
|
|
}
|
|
});
|
|
} catch (ex) {
|
|
// Do nothing if we fail to create a singleton instance,
|
|
// showing the default no-module message.
|
|
console.error(ex);
|
|
}
|