diff --git a/src/models/api/requestModels.js b/src/models/api/requestModels.js index c975511f63..32169105cb 100644 --- a/src/models/api/requestModels.js +++ b/src/models/api/requestModels.js @@ -12,9 +12,11 @@ var FolderRequest = function (folder) { this.name = folder.name ? folder.name.encryptedString : null; }; -var TokenRequest = function (email, masterPasswordHash, device) { +var TokenRequest = function (email, masterPasswordHash, code, device) { this.email = email; this.masterPasswordHash = masterPasswordHash; + this.code = code; + this.provider = 'Authenticator'; this.device = null; if (device) { this.device = new DeviceRequest(device); @@ -36,6 +38,11 @@ var TokenRequest = function (email, masterPasswordHash, device) { obj.devicePushToken = this.device.pushToken; } + if (this.code && this.provider) { + obj.twoFactorCode = this.code; + obj.twoFactorProvider = this.provider; + } + return obj; }; }; @@ -51,12 +58,6 @@ var PasswordHintRequest = function (email) { this.email = email; }; -var TokenTwoFactorRequest = function (code) { - this.code = code; - this.provider = "Authenticator"; - this.device = null; -}; - var DeviceTokenRequest = function () { this.pushToken = null; }; diff --git a/src/models/api/responseModels.js b/src/models/api/responseModels.js index 63c097d8ac..6ae66df740 100644 --- a/src/models/api/responseModels.js +++ b/src/models/api/responseModels.js @@ -38,14 +38,6 @@ var ProfileResponse = function (response) { this.twoFactorEnabled = response.TwoFactorEnabled; }; -var TokenResponse = function (response) { - this.token = response.Token; - - if (response.Profile) { - this.profile = new ProfileResponse(response.Profile); - } -}; - var IdentityTokenResponse = function (response) { this.accessToken = response.access_token; this.expiresIn = response.expires_in; @@ -59,10 +51,18 @@ var ListResponse = function (data) { this.data = data; }; -var ErrorResponse = function (response) { - if (response.responseJSON) { - this.message = response.responseJSON.Message; - this.validationErrors = response.responseJSON.ValidationErrors; +var ErrorResponse = function (response, identityResponse) { + var errorModel = null; + if (identityResponse && identityResponse === true && response.responseJSON && response.responseJSON.ErrorModel) { + errorModel = response.responseJSON.ErrorModel; + } + else if (response.responseJSON) { + errorModel = response.responseJSON; + } + + if (errorModel) { + this.message = errorModel.Message; + this.validationErrors = errorModel.ValidationErrors; } this.statusCode = response.status; }; diff --git a/src/popup/app/accounts/accountsLoginController.js b/src/popup/app/accounts/accountsLoginController.js index 16e341af55..e40c687322 100644 --- a/src/popup/app/accounts/accountsLoginController.js +++ b/src/popup/app/accounts/accountsLoginController.js @@ -32,19 +32,24 @@ return; } - $scope.loginPromise = authService.logIn(model.email, model.masterPassword); + $scope.loginPromise = authService.logIn(model.email, model.masterPassword, null); - $scope.loginPromise.then(function () { - userService.isTwoFactorAuthenticated(function (isTwoFactorAuthenticated) { - if (isTwoFactorAuthenticated) { - $analytics.eventTrack('Logged In To Two-step'); - $state.go('twoFactor', { animation: 'in-slide-left' }); - } - else { - $analytics.eventTrack('Logged In'); - $state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true }); - } - }); + $scope.loginPromise.then(function (twoFactor) { + if (twoFactor) { + $analytics.eventTrack('Logged In To Two-step'); + $state.go('twoFactor', { + animation: 'in-slide-left', + email: model.email, + masterPassword: model.masterPassword + }); + } + else { + $analytics.eventTrack('Logged In'); + $state.go('tabs.vault', { + animation: 'in-slide-left', + syncOnLoad: true + }); + } }); }; }); diff --git a/src/popup/app/accounts/accountsLoginTwoFactorController.js b/src/popup/app/accounts/accountsLoginTwoFactorController.js index fe8155f27e..d544f002dd 100644 --- a/src/popup/app/accounts/accountsLoginTwoFactorController.js +++ b/src/popup/app/accounts/accountsLoginTwoFactorController.js @@ -2,12 +2,15 @@ .module('bit.accounts') .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, - $analytics, i18nService) { + $analytics, i18nService, $stateParams) { $scope.i18n = i18nService; $scope.model = {}; utilsService.initListSectionItemListeners($(document), angular); $('#code').focus(); + var email = $stateParams.email; + var masterPassword = $stateParams.masterPassword; + $scope.loginPromise = null; $scope.login = function (model) { if (!model.code) { @@ -15,7 +18,7 @@ return; } - $scope.loginPromise = authService.logInTwoFactor(model.code); + $scope.loginPromise = authService.logIn(email, masterPassword, model.code); $scope.loginPromise.then(function () { $analytics.eventTrack('Logged In From Two-step'); $state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true }); diff --git a/src/popup/app/config.js b/src/popup/app/config.js index 03a700c9e6..2d912fcb46 100644 --- a/src/popup/app/config.js +++ b/src/popup/app/config.js @@ -49,7 +49,7 @@ controller: 'accountsLoginTwoFactorController', templateUrl: 'app/accounts/views/accountsLoginTwoFactor.html', data: { authorize: false }, - params: { animation: null } + params: { animation: null, email: null, masterPassword: null } }) .state('register', { url: '/register', diff --git a/src/popup/app/services/authService.js b/src/popup/app/services/authService.js index 282e6f8351..9f018596a1 100644 --- a/src/popup/app/services/authService.js +++ b/src/popup/app/services/authService.js @@ -5,14 +5,15 @@ folderService, settingsService, syncService) { var _service = {}; - _service.logIn = function (email, masterPassword) { + _service.logIn = function (email, masterPassword, twoFactorCode) { email = email.toLowerCase(); var key = cryptoService.makeKey(masterPassword, email); var deferred = $q.defer(); cryptoService.hashPassword(masterPassword, key, function (hashedPassword) { - var request = new TokenRequest(email, hashedPassword); + var request = new TokenRequest(email, hashedPassword, twoFactorCode); apiService.postIdentityToken(request, function (response) { + // success if (!response || !response.accessToken) { return; } @@ -20,52 +21,24 @@ tokenService.setTokens(response.accessToken, response.refreshToken, function () { cryptoService.setKey(key, function () { cryptoService.setKeyHash(hashedPassword, function () { - if (tokenService.isTwoFactorScheme()) { - deferred.resolve(response); - } - else { - userService.setUserId(tokenService.getUserId(), function () { - userService.setEmail(tokenService.getEmail(), function () { - chrome.runtime.sendMessage({ command: 'loggedIn' }); - deferred.resolve(response); - }); - }); - } + userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(), function () { + chrome.runtime.sendMessage({ command: 'loggedIn' }); + deferred.resolve(false); + }); }); }); }); + }, function () { + // two factor required + deferred.resolve(true); }, function (error) { + // error deferred.reject(error); }); }); return deferred.promise; }; - _service.logInTwoFactor = function (code) { - var request = new TokenTwoFactorRequest(code.replace(' ', '')); - - var deferred = $q.defer(); - apiService.postTokenTwoFactor(request, function (response) { - if (!response || !response.token) { - deferred.reject(); - return; - } - - tokenService.setToken(response.token, function () { - userService.setUserId(response.profile.id, function () { - userService.setEmail(response.profile.email, function () { - chrome.runtime.sendMessage({ command: 'loggedIn' }); - deferred.resolve(response); - }); - }); - }); - }, function (error) { - deferred.reject(error); - }); - - return deferred.promise; - }; - // TODO: Fix callback hell by moving to promises _service.logOut = function (callback) { userService.getUserId(function (userId) { @@ -74,15 +47,13 @@ tokenService.clearToken(function () { cryptoService.clearKey(function () { cryptoService.clearKeyHash(function () { - userService.clearUserId(function () { - userService.clearEmail(function () { - loginService.clear(userId, function () { - folderService.clear(userId, function () { - $rootScope.vaultLogins = null; - $rootScope.vaultFolders = null; - chrome.runtime.sendMessage({ command: 'loggedOut' }); - callback(); - }); + userService.clearUserIdAndEmail(function () { + loginService.clear(userId, function () { + folderService.clear(userId, function () { + $rootScope.vaultLogins = null; + $rootScope.vaultFolders = null; + chrome.runtime.sendMessage({ command: 'loggedOut' }); + callback(); }); }); }); diff --git a/src/services/apiService.js b/src/services/apiService.js index b534351d8e..8bdfaa7bdb 100644 --- a/src/services/apiService.js +++ b/src/services/apiService.js @@ -1,5 +1,5 @@ function ApiService(tokenService) { - this.baseUrl = 'https://api.bitwarden.com'; + this.baseUrl = 'http://localhost:4000'; this.tokenService = tokenService; initApiService(); @@ -8,26 +8,7 @@ function ApiService(tokenService) { function initApiService() { // Auth APIs - ApiService.prototype.postTokenTwoFactor = function (twoFactorTokenRequest, success, error) { - var self = this; - this.tokenService.getToken(function (token) { - $.ajax({ - type: 'POST', - url: self.baseUrl + '/auth/token/two-factor?access_token2=' + token, - data: JSON.stringify(twoFactorTokenRequest), - contentType: 'application/json; charset=utf-8', - dataType: 'json', - success: function (response) { - success(new TokenResponse(response)); - }, - error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, textStatus, errorThrown); - } - }); - }); - }; - - ApiService.prototype.postIdentityToken = function (tokenRequest, success, error) { + ApiService.prototype.postIdentityToken = function (tokenRequest, success, successWithTwoFactor, error) { var self = this; $.ajax({ @@ -40,7 +21,13 @@ function initApiService() { success(new IdentityTokenResponse(response)); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, textStatus, errorThrown); + if (jqXHR.responseJSON && jqXHR.responseJSON.TwoFactorRequired && + jqXHR.responseJSON.TwoFactorRequired === true) { + successWithTwoFactor(); + } + else { + error(new ErrorResponse(jqXHR, true)); + } } }); }; diff --git a/src/services/userService.js b/src/services/userService.js index 415141c36b..d2aa36e7ce 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -13,16 +13,23 @@ function initUserService() { var _userId = null, _email = null; - UserService.prototype.setUserId = function (userId, callback) { + UserService.prototype.setUserIdAndEmail = function (userId, email, callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; } + _email = email; + var emailObj = {}; + emailObj[userEmailKey] = email; + _userId = userId; - var obj = {}; - obj[userIdKey] = userId; - chrome.storage.local.set(obj, function () { - callback(); + var userIdObj = {}; + userIdObj[userIdKey] = userId; + + chrome.storage.local.set(userIdObj, function () { + chrome.storage.local.set(emailObj, function () { + callback(); + }); }); }; @@ -44,30 +51,6 @@ function initUserService() { }); }; - UserService.prototype.clearUserId = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - _userId = null; - chrome.storage.local.remove(userIdKey, function () { - callback(); - }); - }; - - UserService.prototype.setEmail = function (email, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - _email = email; - var obj = {}; - obj[userEmailKey] = email; - chrome.storage.local.set(obj, function () { - callback(); - }); - }; - UserService.prototype.getEmail = function (callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; @@ -86,14 +69,16 @@ function initUserService() { }); }; - UserService.prototype.clearEmail = function (callback) { + UserService.prototype.clearUserIdAndEmail = function (callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; } - _email = null; - chrome.storage.local.remove(userEmailKey, function () { - callback(); + _userId = _email = null; + chrome.storage.local.remove(userIdKey, function () { + chrome.storage.local.remove(userEmailKey, function () { + callback(); + }); }); }; @@ -114,22 +99,4 @@ function initUserService() { } }); }; - - UserService.prototype.isTwoFactorAuthenticated = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - var self = this; - self.tokenService.getToken(function (token) { - if (!token) { - callback(false); - } - else { - self.getUserId(function (userId) { - callback(userId === null); - }); - } - }); - }; };