diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index c9684785ca..d3df2f32f4 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -19,7 +19,7 @@ angular customWebVaultUrl = environmentService.webVaultUrl; } - var u2f = new U2f(customWebVaultUrl, function (data) { + var u2f = new $window.U2f($window, customWebVaultUrl, function (data) { $timeout(function () { $scope.login(data); }); diff --git a/src/popup/app/app.js b/src/popup/app/app.js index 0c5c63e575..8aa66b9a22 100644 --- a/src/popup/app/app.js +++ b/src/popup/app/app.js @@ -14,11 +14,12 @@ require('angulartics-google-analytics'); require('ng-infinite-scroll'); require('../../scripts/duo.js'); -require('../../scripts/u2f.js'); require('../less/libs.less'); require('../less/popup.less'); +import U2f from '../../scripts/u2f'; + import Analytics from '../../scripts/analytics'; new Analytics(window); diff --git a/src/scripts/u2f.js b/src/scripts/u2f.js deleted file mode 100644 index 7a36537090..0000000000 --- a/src/scripts/u2f.js +++ /dev/null @@ -1,87 +0,0 @@ -window.U2f = U2f = function U2f(webVaultUrl, successCallback, errorCallback, infoCallback) { - this.success = successCallback; - this.error = errorCallback; - this.info = infoCallback; - this.iframe = null; - this.connectorLink = document.createElement('a'); - this.webVaultUrl = webVaultUrl && webVaultUrl !== '' ? webVaultUrl : 'https://vault.bitwarden.com'; -}; - -(function () { - var thisU2f = null; - - U2f.prototype.init = function (data) { - /* jshint ignore:start */ - var self = thisU2f = this; - /* jshint ignore:end */ - - self.connectorLink.href = self.webVaultUrl + '/u2f-connector.html' + - '?data=' + this.base64Encode(JSON.stringify(data)) + - '&parent=' + encodeURIComponent(document.location.href) + - '&v=1'; - - self.iframe = document.getElementById('u2f_iframe'); - self.iframe.src = self.connectorLink.href; - - window.addEventListener('message', parseMessage, false); - }; - - U2f.prototype.validMessage = function (event) { - if (!event.origin || event.origin === '' || event.origin !== this.connectorLink.origin) { - return false; - } - - return event.data.indexOf('success|') === 0 || event.data.indexOf('error|') === 0 || event.data.indexOf('info|') === 0; - }; - - U2f.prototype.stop = function () { - this.sendMessage('stop'); - }; - - U2f.prototype.start = function () { - this.sendMessage('start'); - }; - - U2f.prototype.sendMessage = function (message) { - var self = this; - if (!self.iframe || !self.iframe.src || !self.iframe.contentWindow) { - return; - } - - self.iframe.contentWindow.postMessage(message, self.iframe.src); - }; - - U2f.prototype.base64Encode = function (str) { - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { - return String.fromCharCode('0x' + p1); - })); - }; - - U2f.prototype.cleanup = function () { - window.removeEventListener('message', parseMessage, false); - }; - - function parseMessage(event) { - if (!thisU2f) { - return; - } - - if (!thisU2f.validMessage(event)) { - thisU2f.error('Invalid message.'); - return; - } - - var parts = event.data.split('|'); - if (parts[0] === 'success' && thisU2f.success) { - thisU2f.success(parts[1]); - } - else if (parts[0] === 'error' && thisU2f.error) { - thisU2f.error(parts[1]); - } - else if (parts[0] === 'info') { - if (thisU2f.info) { - thisU2f.info(parts[1]); - } - } - } -})(); diff --git a/src/scripts/u2f.ts b/src/scripts/u2f.ts new file mode 100644 index 0000000000..cbd482a029 --- /dev/null +++ b/src/scripts/u2f.ts @@ -0,0 +1,76 @@ +class U2f { + private iframe: HTMLIFrameElement = null; + private connectorLink: HTMLAnchorElement; + + constructor(private win: Window, private webVaultUrl: string, private successCallback: Function, + private errorCallback: Function, private infoCallback: Function) { + this.connectorLink = win.document.createElement('a'); + this.webVaultUrl = webVaultUrl != null && webVaultUrl !== '' ? webVaultUrl : 'https://vault.bitwarden.com'; + } + + init(data: any): void { + this.connectorLink.href = this.webVaultUrl + '/u2f-connector.html' + + '?data=' + this.base64Encode(JSON.stringify(data)) + + '&parent=' + encodeURIComponent(this.win.document.location.href) + + '&v=1'; + + this.iframe = this.win.document.getElementById('u2f_iframe') as HTMLIFrameElement; + this.iframe.src = this.connectorLink.href; + + this.win.addEventListener('message', (e) => this.parseMessage(e), 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', (e) => this.parseMessage(e), false); + } + + private parseMessage(event: any) { + if (!this.validMessage(event)) { + this.errorCallback('Invalid message.'); + 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: any) { + if (!event.origin || event.origin === '' || event.origin !== (this.connectorLink as any).origin) { + return false; + } + + return event.data.indexOf('success|') === 0 || event.data.indexOf('error|') === 0 || + event.data.indexOf('info|') === 0; + } +} + +export { U2f }; +(window as any).U2f = U2f;