diff --git a/gulpfile.js b/gulpfile.js index 6b31f310241..464195c39d6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -28,7 +28,8 @@ const filters = { ], safari: [ '!build/safari/**/*', - '!build/downloader/**/*' + '!build/downloader/**/*', + '!build/2fa/**/*' ], webExt: [ '!build/manifest.json' diff --git a/src/2fa/2fa.ts b/src/2fa/2fa.ts new file mode 100644 index 00000000000..bc90be29a18 --- /dev/null +++ b/src/2fa/2fa.ts @@ -0,0 +1,42 @@ +require('../scripts/duo.js'); + +document.addEventListener('DOMContentLoaded', () => { + const isSafari = (typeof safari !== 'undefined') && navigator.userAgent.indexOf(' Safari/') !== -1 && + navigator.userAgent.indexOf('Chrome') === -1; + + if (!isSafari) { + return; + } + + safari.self.addEventListener('message', (msgEvent: any) => { + init2fa(msgEvent.message); + }, false); + + function init2fa(msg: any) { + if (msg.command !== '2faPageData' || !msg.data) { + return; + } + + if (msg.data.type === 'duo') { + (window as any).Duo.init({ + host: msg.data.host, + sig_request: msg.data.signature, + submit_callback: (theForm: Document) => { + const sigElement = theForm.querySelector('input[name="sig_response"]'); + if (sigElement) { + safari.self.tab.dispatchMessage('bitwarden', { + command: '2faPageResponse', + type: 'duo', + data: { + sigValue: sigElement.nodeValue, + }, + }); + window.close(); + } + } + }); + } else { + // TODO: others like u2f? + } + } +}); diff --git a/src/2fa/index.html b/src/2fa/index.html new file mode 100644 index 00000000000..b5c78a8730b --- /dev/null +++ b/src/2fa/index.html @@ -0,0 +1,11 @@ + + + + Two-step Login + + + +
Loading...
+ + + diff --git a/src/browser/browserApi.ts b/src/browser/browserApi.ts index a82584c71dc..b6c56ee3204 100644 --- a/src/browser/browserApi.ts +++ b/src/browser/browserApi.ts @@ -228,7 +228,7 @@ class BrowserApi { } } - private static makeTabObject(tab: any): any { + static makeTabObject(tab: any): any { if (BrowserApi.isChromeApi) { return tab; } diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index 49c98d8fd33..aa9ebf8728b 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -122,6 +122,12 @@ angular u2f = null; }); + $scope.$on('2faPageResponse', (event, details) => { + if (details.type === 'duo') { + $scope.login(details.data.sigValue); + } + }); + function getDefaultProvider(twoFactorProviders) { var keys = Object.keys(twoFactorProviders); var providerType = null; @@ -154,17 +160,31 @@ angular var params; if ($scope.providerType === constants.twoFactorProvider.duo) { params = providers[constants.twoFactorProvider.duo]; - - $window.Duo.init({ - host: params.Host, - sig_request: params.Signature, - submit_callback: function (theForm) { - var sigElement = theForm.querySelector('input[name="sig_response"]'); - if (sigElement) { - $scope.login(sigElement.value); + if (platformUtilsService.isSafari()) { + var tab = BrowserApi.createNewTab(BrowserApi.getAssetUrl('2fa/index.html')); + var tabToSend = BrowserApi.makeTabObject(tab); + $timeout(() => { + BrowserApi.tabSendMessage(tabToSend, { + command: '2faPageData', + data: { + host: params.Host, + signature: params.Signature + } + }); + }, 1000); + } + else { + $window.Duo.init({ + host: params.Host, + sig_request: params.Signature, + submit_callback: function (theForm) { + var sigElement = theForm.querySelector('input[name="sig_response"]'); + if (sigElement) { + $scope.login(sigElement.value); + } } - } - }); + }); + } } else if ($scope.providerType === constants.twoFactorProvider.u2f) { params = providers[constants.twoFactorProvider.u2f]; diff --git a/src/popup/app/global/main.controller.ts b/src/popup/app/global/main.controller.ts index 51c7192ba0d..d8e511f3f1d 100644 --- a/src/popup/app/global/main.controller.ts +++ b/src/popup/app/global/main.controller.ts @@ -45,6 +45,11 @@ export class MainController implements ng.IController { tab: msg.tab, details: msg.details, }); + } else if (msg.command === '2faPageResponse') { + $scope.$broadcast('2faPageResponse', { + type: msg.type, + data: msg.data, + }); } }; diff --git a/webpack.common.js b/webpack.common.js index 28e3af9eda8..6c7080cc9ff 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -24,6 +24,7 @@ module.exports = { 'content/shortcuts': './src/content/shortcuts.js', 'notification/bar': './src/notification/bar.js', 'downloader/downloader': './src/downloader/downloader.ts', + '2fa/2fa': './src/2fa/2fa.ts', }, module: { rules: [ @@ -101,6 +102,11 @@ module.exports = { filename: 'downloader/index.html', chunks: ['downloader/downloader'] }), + new HtmlWebpackPlugin({ + template: './src/2fa/index.html', + filename: '2fa/index.html', + chunks: ['2fa/2fa'] + }), new CopyWebpackPlugin([ './src/manifest.json', { from: './src/_locales', to: '_locales' },