import { b64Decode, getQsParam } from './common'; import { buildDataString, parseWebauthnJson } from './common-webauthn'; // tslint:disable-next-line require('./webauthn.scss'); let parsed = false; let webauthnJson: any; let parentUrl: string = null; let parentOrigin: string = null; let sentSuccess = false; let locale: string = 'en'; let locales: any = {}; function parseParameters() { if (parsed) { return; } parentUrl = getQsParam('parent'); if (!parentUrl) { error('No parent.'); return; } else { parentUrl = decodeURIComponent(parentUrl); parentOrigin = new URL(parentUrl).origin; } 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 { // tslint:disable-next-line:no-console 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'); }