+ Enter the 6 digit verification code from your authenticator app. +
++ Enter the 6 digit verification code that was emailed to {{twoFactorEmail}}. +
+diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 859bb41db3b..52b94963601 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -131,10 +131,6 @@ "message": "Verification Code", "description": "Verification Code" }, - "enterTwoStepVerCode": { - "message": "Enter your two-step verification code.", - "description": "Enter your two-step verification code." - }, "account": { "message": "Account", "description": "Account" diff --git a/src/images/two-factor/u2fkey.jpg b/src/images/two-factor/u2fkey.jpg new file mode 100644 index 00000000000..8013df0e569 Binary files /dev/null and b/src/images/two-factor/u2fkey.jpg differ diff --git a/src/images/two-factor/yubikey.jpg b/src/images/two-factor/yubikey.jpg new file mode 100644 index 00000000000..9ddf755decc Binary files /dev/null and b/src/images/two-factor/yubikey.jpg differ diff --git a/src/models/api/requestModels.js b/src/models/api/requestModels.js index 97d5801eb0f..88a362ba60e 100644 --- a/src/models/api/requestModels.js +++ b/src/models/api/requestModels.js @@ -13,11 +13,12 @@ var FolderRequest = function (folder) { this.name = folder.name ? folder.name.encryptedString : null; }; -var TokenRequest = function (email, masterPasswordHash, provider, token, device) { +var TokenRequest = function (email, masterPasswordHash, provider, token, remember, device) { this.email = email; this.masterPasswordHash = masterPasswordHash; this.token = token; this.provider = provider; + this.remember = remember || remember !== false; this.device = null; if (device) { this.device = device; @@ -42,6 +43,7 @@ var TokenRequest = function (email, masterPasswordHash, provider, token, device) if (this.token && this.provider != null && (typeof this.provider !== 'undefined')) { obj.twoFactorToken = this.token; obj.twoFactorProvider = this.provider; + obj.twoFactorRemember = this.remember ? '1' : '0'; } return obj; @@ -60,6 +62,11 @@ var PasswordHintRequest = function (email) { this.email = email; }; +var TwoFactorEmailRequest = function (email, masterPasswordHash) { + this.email = email; + this.masterPasswordHash = masterPasswordHash; +}; + var DeviceTokenRequest = function () { this.pushToken = null; }; diff --git a/src/models/api/responseModels.js b/src/models/api/responseModels.js index 76b8b9dca5e..1f64e33d8b1 100644 --- a/src/models/api/responseModels.js +++ b/src/models/api/responseModels.js @@ -72,6 +72,7 @@ var IdentityTokenResponse = function (response) { this.privateKey = response.PrivateKey; this.key = response.Key; + this.twoFactorToken = response.TwoFactorToken; }; var ListResponse = function (data) { diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index 6c98f68d59e..636b06cf214 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -1,7 +1,7 @@ angular .module('bit.accounts') - .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, + .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, SweetAlert, $analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService) { $scope.i18n = i18nService; utilsService.initListSectionItemListeners($(document), angular); @@ -11,11 +11,17 @@ var masterPassword = $stateParams.masterPassword; var providers = $stateParams.providers; + if (!email || !masterPassword || !providers) { + $state.go('login'); + return; + } + + $scope.providerType = $stateParams.provider ? $stateParams.provider : getDefaultProvider(providers); $scope.twoFactorEmail = null; $scope.token = null; $scope.constantsProvider = constants.twoFactorProvider; - $scope.providerType = $stateParams.provider ? $stateParams.provider : getDefaultProvider(providers); $scope.u2fReady = false; + $scope.remember = { checked: false }; init(); $scope.loginPromise = null; @@ -25,7 +31,12 @@ return; } - $scope.loginPromise = authService.logIn(email, masterPassword, $scope.providerType, token); + if ($scope.providerType === constants.twoFactorProvider.email || + $scope.providerType === constants.twoFactorProvider.authenticator) { + token = token.replace(' ', '') + } + + $scope.loginPromise = authService.logIn(email, masterPassword, $scope.providerType, token, $scope.remember.checked); $scope.loginPromise.then(function () { $analytics.eventTrack('Logged In From Two-step'); $state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true }); @@ -43,16 +54,15 @@ } 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.'); + cryptoService.hashPassword(masterPassword, key, function (hash) { + var request = new TwoFactorEmailRequest(email, hash); + apiService.postTwoFactorEmail(request, function () { + if (doToast) { + toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.'); + } + }, function () { + toastr.error('Could not send verification email.'); + }); }); }; @@ -131,7 +141,27 @@ else if ($scope.providerType === constants.twoFactorProvider.email) { var params = providers[constants.twoFactorProvider.email]; $scope.twoFactorEmail = params.Email; - if (Object.keys(providers).length > 1) { + + if (chrome.extension.getViews({ type: 'popup' }).length > 0) { + SweetAlert.swal({ + title: 'Two-step Login', + text: 'Clicking outside the popup window to check your email for your verification code will ' + + 'cause this popup to close. ' + + 'Do you want to open this popup in a new window so that it does not close?', + showCancelButton: true, + confirmButtonText: i18nService.yes, + cancelButtonText: i18nService.no + }, function (confirmed) { + if (confirmed) { + chrome.tabs.create({ url: '/popup/index.html#!/login' }); + return; + } + else if (Object.keys(providers).length > 1) { + $scope.sendEmail(false); + } + }); + } + else if (Object.keys(providers).length > 1) { $scope.sendEmail(false); } } diff --git a/src/popup/app/accounts/views/accountsLoginTwoFactor.html b/src/popup/app/accounts/views/accountsLoginTwoFactor.html index 3601e35df98..b34f20c8285 100644 --- a/src/popup/app/accounts/views/accountsLoginTwoFactor.html +++ b/src/popup/app/accounts/views/accountsLoginTwoFactor.html @@ -11,6 +11,14 @@
+ Enter the 6 digit verification code from your authenticator app. +
++ Enter the 6 digit verification code that was emailed to {{twoFactorEmail}}. +
++ Send verification code email again +
Use another two-step login method
@@ -61,6 +69,10 @@Insert your YubiKey into your computer's USB port, then touch its button.
+
+ @@ -96,10 +105,27 @@
+
Loading...
++ Insert your Security Key into your computer's USB port. If it has a button, touch it. +
+
+