bitwarden-estensione-browser/apps/web/src/connectors/webauthn-fallback.ts

165 lines
3.7 KiB
TypeScript

import { b64Decode, getQsParam } from "./common";
import { buildDataString, parseWebauthnJson } from "./common-webauthn";
require("./webauthn.scss");
let parsed = false;
let webauthnJson: any;
let parentUrl: string = null;
let sentSuccess = false;
let locale = "en";
let locales: any = {};
function parseParameters() {
if (parsed) {
return;
}
parentUrl = getQsParam("parent");
if (!parentUrl) {
error("No parent.");
return;
} else {
parentUrl = decodeURIComponent(parentUrl);
}
locale = getQsParam("locale").replace("-", "_");
const version = getQsParam("v");
if (version === "1") {
parseParametersV1();
} else {
parseParametersV2();
}
parsed = true;
}
function parseParametersV1() {
const data = getQsParam("data");
if (!data) {
error("No data.");
return;
}
webauthnJson = b64Decode(data);
}
function parseParametersV2() {
let dataObj: { data: any; btnText: string } = null;
try {
dataObj = JSON.parse(b64Decode(getQsParam("data")));
} catch (e) {
error("Cannot parse data.");
return;
}
webauthnJson = dataObj.data;
}
document.addEventListener("DOMContentLoaded", async () => {
parseParameters();
try {
locales = await loadLocales(locale);
} catch {
// eslint-disable-next-line
console.error("Failed to load the locale", locale);
locales = await loadLocales("en");
}
document.getElementById("msg").innerText = translate("webAuthnFallbackMsg");
document.getElementById("remember-label").innerText = translate("rememberMe");
const button = document.getElementById("webauthn-button");
button.innerText = translate("webAuthnAuthenticate");
button.onclick = start;
document.getElementById("spinner").classList.add("d-none");
const content = document.getElementById("content");
content.classList.add("d-block");
content.classList.remove("d-none");
});
async function loadLocales(newLocale: string) {
const filePath = `locales/${newLocale}/messages.json?cache=${process.env.CACHE_TAG}`;
const localesResult = await fetch(filePath);
return await localesResult.json();
}
function translate(id: string) {
return locales[id]?.message || "";
}
function start() {
if (sentSuccess) {
return;
}
if (!("credentials" in navigator)) {
error(translate("webAuthnNotSupported"));
return;
}
parseParameters();
if (!webauthnJson) {
error("No data.");
return;
}
let json: any;
try {
json = parseWebauthnJson(webauthnJson);
} catch (e) {
error("Cannot parse data.");
return;
}
initWebAuthn(json);
}
async function initWebAuthn(obj: any) {
try {
const assertedCredential = (await navigator.credentials.get({
publicKey: obj,
})) as PublicKeyCredential;
if (sentSuccess) {
return;
}
const dataString = buildDataString(assertedCredential);
const remember = (document.getElementById("remember") as HTMLInputElement).checked;
window.postMessage({ command: "webAuthnResult", data: dataString, remember: remember }, "*");
sentSuccess = true;
success(translate("webAuthnSuccess"));
} catch (err) {
error(err);
}
}
function error(message: string) {
const el = document.getElementById("msg");
resetMsgBox(el);
el.textContent = message;
el.classList.add("alert");
el.classList.add("alert-danger");
}
function success(message: string) {
(document.getElementById("webauthn-button") as HTMLButtonElement).disabled = true;
const el = document.getElementById("msg");
resetMsgBox(el);
el.textContent = message;
el.classList.add("alert");
el.classList.add("alert-success");
}
function resetMsgBox(el: HTMLElement) {
el.classList.remove("alert");
el.classList.remove("alert-danger");
el.classList.remove("alert-success");
}