diff --git a/src/images/loading.svg b/src/images/loading.svg new file mode 100644 index 00000000000..70763105168 --- /dev/null +++ b/src/images/loading.svg @@ -0,0 +1,6 @@ + diff --git a/src/popup/app/accounts/accountsLoginController.js b/src/popup/app/accounts/accountsLoginController.js index 6fe187c6b09..0f1284b95a7 100644 --- a/src/popup/app/accounts/accountsLoginController.js +++ b/src/popup/app/accounts/accountsLoginController.js @@ -40,7 +40,8 @@ $state.go('twoFactor', { animation: 'in-slide-left', email: model.email, - masterPassword: model.masterPassword + masterPassword: model.masterPassword, + providers: response.twoFactorProviders }); } else { diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index 2b814f9808c..3ded0d4d89e 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -2,23 +2,30 @@ .module('bit.accounts') .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, - $analytics, i18nService, $stateParams) { + $analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService) { $scope.i18n = i18nService; - $scope.model = {}; utilsService.initListSectionItemListeners($(document), angular); - $('#code').focus(); + var constants = constantsService; var email = $stateParams.email; var masterPassword = $stateParams.masterPassword; + var providers = $stateParams.providers; + + $scope.twoFactorEmail = null; + $scope.token = null; + $scope.constantsProvider = constants.twoFactorProvider; + $scope.providerType = $stateParams.provider ? $stateParams.provider : getDefaultProvider(providers); + $scope.u2fReady = false; + init(); $scope.loginPromise = null; - $scope.login = function (model) { - if (!model.code) { + $scope.login = function (token) { + if (!token) { toastr.error(i18nService.verificationCodeRequired, i18nService.errorsOccurred); return; } - $scope.loginPromise = authService.logIn(email, masterPassword, 0, model.code); + $scope.loginPromise = authService.logIn(email, masterPassword, $scope.providerType, token); $scope.loginPromise.then(function () { $analytics.eventTrack('Logged In From Two-step'); $state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true }); @@ -29,4 +36,94 @@ $analytics.eventTrack('Selected Lost 2FA App'); chrome.tabs.create({ url: 'https://help.bitwarden.com/article/lost-two-step-device/' }); }; + + $scope.sendEmail = function (doToast) { + if ($scope.providerType !== constants.twoFactorProvider.email) { + return; + } + + var key = cryptoService.makeKey(masterPassword, email); + var hash = cryptoService.hashPassword(masterPassword, key); + apiService.postTwoFactorEmail({ + email: email, + masterPasswordHash: hash + }, function () { + if (doToast) { + toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.'); + } + }, function () { + toastr.error('Could not send verification email.'); + }); + }; + + function getDefaultProvider(twoFactorProviders) { + var keys = Object.keys(twoFactorProviders); + var providerType = null; + var providerPriority = -1; + for (var i = 0; i < keys.length; i++) { + var provider = $filter('filter')(constants.twoFactorProviderInfo, { type: keys[i], active: true }); + if (provider.length && provider[0].priority > providerPriority) { + if (provider[0].type == constants.twoFactorProvider.u2f && + !utilsService.isChrome() && !utilsService.isOpera()) { + continue; + } + + providerType = provider[0].type; + providerPriority = provider[0].priority; + } + } + return parseInt(providerType); + } + + function init() { + $timeout(function () { + $('#code').focus(); + + if ($scope.providerType === constants.twoFactorProvider.duo) { + var params = providers[constants.twoFactorProvider.duo]; + + $window.Duo.init({ + host: params.Host, + sig_request: params.Signature, + submit_callback: function (theForm) { + var response = $(theForm).find('input[name="sig_response"]').val(); + $scope.login(response); + } + }); + } + else if ($scope.providerType === constants.twoFactorProvider.u2f) { + var params = providers[constants.twoFactorProvider.u2f]; + var challenges = JSON.parse(params.Challenges); + + var u2f = new U2f(function (data) { + $scope.login(data); + $scope.$apply(); + }, function (error) { + toastr.error(error, i18nService.errorsOccurred); + $scope.$apply(); + }, function (info) { + if (info === 'ready') { + $scope.u2fReady = true; + } + $scope.$apply(); + }); + + u2f.init({ + appId: challenges[0].appId, + challenge: challenges[0].challenge, + keys: [{ + version: challenges[0].version, + keyHandle: challenges[0].keyHandle + }] + }); + } + else if ($scope.providerType === constants.twoFactorProvider.email) { + var params = providers[constants.twoFactorProvider.email]; + $scope.twoFactorEmail = params.Email; + if (Object.keys(providers).length > 1) { + $scope.sendEmail(false); + } + } + }, 500); + } }); diff --git a/src/popup/app/accounts/views/accountsLoginTwoFactor.html b/src/popup/app/accounts/views/accountsLoginTwoFactor.html index f247bc29d1d..81ef904d058 100644 --- a/src/popup/app/accounts/views/accountsLoginTwoFactor.html +++ b/src/popup/app/accounts/views/accountsLoginTwoFactor.html @@ -1,4 +1,5 @@ -