1
0
mirror of https://github.com/bitwarden/web synced 2025-12-26 13:13:24 +00:00
Files
web/src/connectors/webauthn.ts
2021-08-26 18:14:18 +02:00

172 lines
3.8 KiB
TypeScript

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 btnText: string = null;
let parentUrl: string = null;
let parentOrigin: string = null;
let callbackUri: string = null;
let stopWebAuthn = false;
let sentSuccess = false;
let obj: any = null;
document.addEventListener('DOMContentLoaded', () => {
init();
parseParameters();
if (btnText) {
const button = document.getElementById('webauthn-button');
button.innerText = decodeURI(btnText);
button.onclick = executeWebAuthn;
}
});
function init() {
start();
onMessage();
info('ready');
}
function parseParameters() {
if (parsed) {
return;
}
parentUrl = getQsParam('parent');
if (!parentUrl) {
error('No parent.');
return;
} else {
parentUrl = decodeURIComponent(parentUrl);
parentOrigin = new URL(parentUrl).origin;
}
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);
btnText = getQsParam('btnText');
}
function parseParametersV2() {
let dataObj: { data: any, btnText: string; callbackUri?: string } = null;
try {
dataObj = JSON.parse(b64Decode(getQsParam('data')));
}
catch (e) {
error('Cannot parse data.');
return;
}
callbackUri = dataObj.callbackUri;
webauthnJson = dataObj.data;
btnText = dataObj.btnText;
}
function start() {
sentSuccess = false;
if (!('credentials' in navigator)) {
error('WebAuthn is not supported in this browser.');
return;
}
parseParameters();
if (!webauthnJson) {
error('No data.');
return;
}
try {
obj = parseWebauthnJson(webauthnJson);
}
catch (e) {
error('Cannot parse webauthn data.');
return;
}
stopWebAuthn = false;
if (callbackUri != null || (navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1)) {
// Safari and mobile chrome blocks non-user initiated WebAuthn requests.
} else {
executeWebAuthn();
}
}
function executeWebAuthn() {
if (stopWebAuthn) {
return;
}
navigator.credentials.get({ publicKey: obj })
.then(success)
.catch(err => error('WebAuth Error: ' + err));
}
function onMessage() {
window.addEventListener('message', event => {
if (!event.origin || event.origin === '' || event.origin !== parentOrigin) {
return;
}
if (event.data === 'stop') {
stopWebAuthn = true;
}
else if (event.data === 'start' && stopWebAuthn) {
start();
}
}, false);
}
function error(message: string) {
if (callbackUri) {
document.location.replace(callbackUri + '?error=' + encodeURIComponent(message));
} else {
parent.postMessage('error|' + message, parentUrl);
}
}
function success(assertedCredential: PublicKeyCredential) {
if (sentSuccess) {
return;
}
const dataString = buildDataString(assertedCredential);
if (callbackUri) {
document.location.replace(callbackUri + '?data=' + encodeURIComponent(dataString));
} else {
parent.postMessage('success|' + dataString, parentUrl);
}
sentSuccess = true;
}
function info(message: string) {
if (callbackUri) {
return;
}
parent.postMessage('info|' + message, parentUrl);
}