107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import { I18nService } from "../platform/abstractions/i18n.service";
|
|
import { PlatformUtilsService } from "../platform/abstractions/platform-utils.service";
|
|
|
|
export class WebAuthnIFrame {
|
|
private iframe: HTMLIFrameElement = null;
|
|
private connectorLink: HTMLAnchorElement;
|
|
private parseFunction = this.parseMessage.bind(this);
|
|
|
|
constructor(
|
|
private win: Window,
|
|
private webVaultUrl: string,
|
|
private webAuthnNewTab: boolean,
|
|
private platformUtilsService: PlatformUtilsService,
|
|
private i18nService: I18nService,
|
|
private successCallback: Function, // eslint-disable-line
|
|
private errorCallback: Function, // eslint-disable-line
|
|
private infoCallback: Function, // eslint-disable-line
|
|
) {
|
|
this.connectorLink = win.document.createElement("a");
|
|
}
|
|
|
|
init(data: any): void {
|
|
const params = new URLSearchParams({
|
|
data: this.base64Encode(JSON.stringify(data)),
|
|
parent: encodeURIComponent(this.win.document.location.href),
|
|
btnText: encodeURIComponent(this.i18nService.t("webAuthnAuthenticate")),
|
|
v: "1",
|
|
});
|
|
|
|
if (this.webAuthnNewTab) {
|
|
// Firefox fallback which opens the webauthn page in a new tab
|
|
params.append("locale", this.i18nService.translationLocale);
|
|
this.platformUtilsService.launchUri(
|
|
`${this.webVaultUrl}/webauthn-fallback-connector.html?${params}`,
|
|
);
|
|
} else {
|
|
this.connectorLink.href = `${this.webVaultUrl}/webauthn-connector.html?${params}`;
|
|
this.iframe = this.win.document.getElementById("webauthn_iframe") as HTMLIFrameElement;
|
|
this.iframe.allow = "publickey-credentials-get " + new URL(this.webVaultUrl).origin;
|
|
this.iframe.src = this.connectorLink.href;
|
|
|
|
this.win.addEventListener("message", this.parseFunction, false);
|
|
}
|
|
}
|
|
|
|
stop() {
|
|
this.sendMessage("stop");
|
|
}
|
|
|
|
start() {
|
|
this.sendMessage("start");
|
|
}
|
|
|
|
sendMessage(message: any) {
|
|
if (!this.iframe || !this.iframe.src || !this.iframe.contentWindow) {
|
|
return;
|
|
}
|
|
|
|
this.iframe.contentWindow.postMessage(message, this.iframe.src);
|
|
}
|
|
|
|
base64Encode(str: string): string {
|
|
return btoa(
|
|
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => {
|
|
return String.fromCharCode(("0x" + p1) as any);
|
|
}),
|
|
);
|
|
}
|
|
|
|
cleanup() {
|
|
this.win.removeEventListener("message", this.parseFunction, false);
|
|
}
|
|
|
|
private parseMessage(event: MessageEvent) {
|
|
if (!this.validMessage(event)) {
|
|
return;
|
|
}
|
|
|
|
const parts: string[] = event.data.split("|");
|
|
if (parts[0] === "success" && this.successCallback) {
|
|
this.successCallback(parts[1]);
|
|
} else if (parts[0] === "error" && this.errorCallback) {
|
|
this.errorCallback(parts[1]);
|
|
} else if (parts[0] === "info" && this.infoCallback) {
|
|
this.infoCallback(parts[1]);
|
|
}
|
|
}
|
|
|
|
private validMessage(event: MessageEvent) {
|
|
if (
|
|
event.origin == null ||
|
|
event.origin === "" ||
|
|
event.origin !== (this.connectorLink as any).origin ||
|
|
event.data == null ||
|
|
typeof event.data !== "string"
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
return (
|
|
event.data.indexOf("success|") === 0 ||
|
|
event.data.indexOf("error|") === 0 ||
|
|
event.data.indexOf("info|") === 0
|
|
);
|
|
}
|
|
}
|