mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-24 12:06:15 +01:00
Added two factor flow into identity login
This commit is contained in:
parent
6bb6c7074b
commit
0b63eb58ba
@ -12,9 +12,11 @@ var FolderRequest = function (folder) {
|
|||||||
this.name = folder.name ? folder.name.encryptedString : null;
|
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.email = email;
|
||||||
this.masterPasswordHash = masterPasswordHash;
|
this.masterPasswordHash = masterPasswordHash;
|
||||||
|
this.code = code;
|
||||||
|
this.provider = 'Authenticator';
|
||||||
this.device = null;
|
this.device = null;
|
||||||
if (device) {
|
if (device) {
|
||||||
this.device = new DeviceRequest(device);
|
this.device = new DeviceRequest(device);
|
||||||
@ -36,6 +38,11 @@ var TokenRequest = function (email, masterPasswordHash, device) {
|
|||||||
obj.devicePushToken = this.device.pushToken;
|
obj.devicePushToken = this.device.pushToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.code && this.provider) {
|
||||||
|
obj.twoFactorCode = this.code;
|
||||||
|
obj.twoFactorProvider = this.provider;
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -51,12 +58,6 @@ var PasswordHintRequest = function (email) {
|
|||||||
this.email = email;
|
this.email = email;
|
||||||
};
|
};
|
||||||
|
|
||||||
var TokenTwoFactorRequest = function (code) {
|
|
||||||
this.code = code;
|
|
||||||
this.provider = "Authenticator";
|
|
||||||
this.device = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var DeviceTokenRequest = function () {
|
var DeviceTokenRequest = function () {
|
||||||
this.pushToken = null;
|
this.pushToken = null;
|
||||||
};
|
};
|
||||||
|
@ -38,14 +38,6 @@ var ProfileResponse = function (response) {
|
|||||||
this.twoFactorEnabled = response.TwoFactorEnabled;
|
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) {
|
var IdentityTokenResponse = function (response) {
|
||||||
this.accessToken = response.access_token;
|
this.accessToken = response.access_token;
|
||||||
this.expiresIn = response.expires_in;
|
this.expiresIn = response.expires_in;
|
||||||
@ -59,10 +51,18 @@ var ListResponse = function (data) {
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
};
|
};
|
||||||
|
|
||||||
var ErrorResponse = function (response) {
|
var ErrorResponse = function (response, identityResponse) {
|
||||||
if (response.responseJSON) {
|
var errorModel = null;
|
||||||
this.message = response.responseJSON.Message;
|
if (identityResponse && identityResponse === true && response.responseJSON && response.responseJSON.ErrorModel) {
|
||||||
this.validationErrors = response.responseJSON.ValidationErrors;
|
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;
|
this.statusCode = response.status;
|
||||||
};
|
};
|
||||||
|
@ -32,19 +32,24 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.loginPromise = authService.logIn(model.email, model.masterPassword);
|
$scope.loginPromise = authService.logIn(model.email, model.masterPassword, null);
|
||||||
|
|
||||||
$scope.loginPromise.then(function () {
|
$scope.loginPromise.then(function (twoFactor) {
|
||||||
userService.isTwoFactorAuthenticated(function (isTwoFactorAuthenticated) {
|
if (twoFactor) {
|
||||||
if (isTwoFactorAuthenticated) {
|
$analytics.eventTrack('Logged In To Two-step');
|
||||||
$analytics.eventTrack('Logged In To Two-step');
|
$state.go('twoFactor', {
|
||||||
$state.go('twoFactor', { animation: 'in-slide-left' });
|
animation: 'in-slide-left',
|
||||||
}
|
email: model.email,
|
||||||
else {
|
masterPassword: model.masterPassword
|
||||||
$analytics.eventTrack('Logged In');
|
});
|
||||||
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true });
|
}
|
||||||
}
|
else {
|
||||||
});
|
$analytics.eventTrack('Logged In');
|
||||||
|
$state.go('tabs.vault', {
|
||||||
|
animation: 'in-slide-left',
|
||||||
|
syncOnLoad: true
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -2,12 +2,15 @@
|
|||||||
.module('bit.accounts')
|
.module('bit.accounts')
|
||||||
|
|
||||||
.controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService,
|
.controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService,
|
||||||
$analytics, i18nService) {
|
$analytics, i18nService, $stateParams) {
|
||||||
$scope.i18n = i18nService;
|
$scope.i18n = i18nService;
|
||||||
$scope.model = {};
|
$scope.model = {};
|
||||||
utilsService.initListSectionItemListeners($(document), angular);
|
utilsService.initListSectionItemListeners($(document), angular);
|
||||||
$('#code').focus();
|
$('#code').focus();
|
||||||
|
|
||||||
|
var email = $stateParams.email;
|
||||||
|
var masterPassword = $stateParams.masterPassword;
|
||||||
|
|
||||||
$scope.loginPromise = null;
|
$scope.loginPromise = null;
|
||||||
$scope.login = function (model) {
|
$scope.login = function (model) {
|
||||||
if (!model.code) {
|
if (!model.code) {
|
||||||
@ -15,7 +18,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.loginPromise = authService.logInTwoFactor(model.code);
|
$scope.loginPromise = authService.logIn(email, masterPassword, model.code);
|
||||||
$scope.loginPromise.then(function () {
|
$scope.loginPromise.then(function () {
|
||||||
$analytics.eventTrack('Logged In From Two-step');
|
$analytics.eventTrack('Logged In From Two-step');
|
||||||
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true });
|
$state.go('tabs.vault', { animation: 'in-slide-left', syncOnLoad: true });
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
controller: 'accountsLoginTwoFactorController',
|
controller: 'accountsLoginTwoFactorController',
|
||||||
templateUrl: 'app/accounts/views/accountsLoginTwoFactor.html',
|
templateUrl: 'app/accounts/views/accountsLoginTwoFactor.html',
|
||||||
data: { authorize: false },
|
data: { authorize: false },
|
||||||
params: { animation: null }
|
params: { animation: null, email: null, masterPassword: null }
|
||||||
})
|
})
|
||||||
.state('register', {
|
.state('register', {
|
||||||
url: '/register',
|
url: '/register',
|
||||||
|
@ -5,14 +5,15 @@
|
|||||||
folderService, settingsService, syncService) {
|
folderService, settingsService, syncService) {
|
||||||
var _service = {};
|
var _service = {};
|
||||||
|
|
||||||
_service.logIn = function (email, masterPassword) {
|
_service.logIn = function (email, masterPassword, twoFactorCode) {
|
||||||
email = email.toLowerCase();
|
email = email.toLowerCase();
|
||||||
var key = cryptoService.makeKey(masterPassword, email);
|
var key = cryptoService.makeKey(masterPassword, email);
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
cryptoService.hashPassword(masterPassword, key, function (hashedPassword) {
|
cryptoService.hashPassword(masterPassword, key, function (hashedPassword) {
|
||||||
var request = new TokenRequest(email, hashedPassword);
|
var request = new TokenRequest(email, hashedPassword, twoFactorCode);
|
||||||
|
|
||||||
apiService.postIdentityToken(request, function (response) {
|
apiService.postIdentityToken(request, function (response) {
|
||||||
|
// success
|
||||||
if (!response || !response.accessToken) {
|
if (!response || !response.accessToken) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -20,52 +21,24 @@
|
|||||||
tokenService.setTokens(response.accessToken, response.refreshToken, function () {
|
tokenService.setTokens(response.accessToken, response.refreshToken, function () {
|
||||||
cryptoService.setKey(key, function () {
|
cryptoService.setKey(key, function () {
|
||||||
cryptoService.setKeyHash(hashedPassword, function () {
|
cryptoService.setKeyHash(hashedPassword, function () {
|
||||||
if (tokenService.isTwoFactorScheme()) {
|
userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(), function () {
|
||||||
deferred.resolve(response);
|
chrome.runtime.sendMessage({ command: 'loggedIn' });
|
||||||
}
|
deferred.resolve(false);
|
||||||
else {
|
});
|
||||||
userService.setUserId(tokenService.getUserId(), function () {
|
|
||||||
userService.setEmail(tokenService.getEmail(), function () {
|
|
||||||
chrome.runtime.sendMessage({ command: 'loggedIn' });
|
|
||||||
deferred.resolve(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}, function () {
|
||||||
|
// two factor required
|
||||||
|
deferred.resolve(true);
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
|
// error
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
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
|
// TODO: Fix callback hell by moving to promises
|
||||||
_service.logOut = function (callback) {
|
_service.logOut = function (callback) {
|
||||||
userService.getUserId(function (userId) {
|
userService.getUserId(function (userId) {
|
||||||
@ -74,15 +47,13 @@
|
|||||||
tokenService.clearToken(function () {
|
tokenService.clearToken(function () {
|
||||||
cryptoService.clearKey(function () {
|
cryptoService.clearKey(function () {
|
||||||
cryptoService.clearKeyHash(function () {
|
cryptoService.clearKeyHash(function () {
|
||||||
userService.clearUserId(function () {
|
userService.clearUserIdAndEmail(function () {
|
||||||
userService.clearEmail(function () {
|
loginService.clear(userId, function () {
|
||||||
loginService.clear(userId, function () {
|
folderService.clear(userId, function () {
|
||||||
folderService.clear(userId, function () {
|
$rootScope.vaultLogins = null;
|
||||||
$rootScope.vaultLogins = null;
|
$rootScope.vaultFolders = null;
|
||||||
$rootScope.vaultFolders = null;
|
chrome.runtime.sendMessage({ command: 'loggedOut' });
|
||||||
chrome.runtime.sendMessage({ command: 'loggedOut' });
|
callback();
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
function ApiService(tokenService) {
|
function ApiService(tokenService) {
|
||||||
this.baseUrl = 'https://api.bitwarden.com';
|
this.baseUrl = 'http://localhost:4000';
|
||||||
this.tokenService = tokenService;
|
this.tokenService = tokenService;
|
||||||
|
|
||||||
initApiService();
|
initApiService();
|
||||||
@ -8,26 +8,7 @@ function ApiService(tokenService) {
|
|||||||
function initApiService() {
|
function initApiService() {
|
||||||
// Auth APIs
|
// Auth APIs
|
||||||
|
|
||||||
ApiService.prototype.postTokenTwoFactor = function (twoFactorTokenRequest, success, error) {
|
ApiService.prototype.postIdentityToken = function (tokenRequest, success, successWithTwoFactor, 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) {
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -40,7 +21,13 @@ function initApiService() {
|
|||||||
success(new IdentityTokenResponse(response));
|
success(new IdentityTokenResponse(response));
|
||||||
},
|
},
|
||||||
error: function (jqXHR, textStatus, errorThrown) {
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -13,16 +13,23 @@ function initUserService() {
|
|||||||
var _userId = null,
|
var _userId = null,
|
||||||
_email = null;
|
_email = null;
|
||||||
|
|
||||||
UserService.prototype.setUserId = function (userId, callback) {
|
UserService.prototype.setUserIdAndEmail = function (userId, email, callback) {
|
||||||
if (!callback || typeof callback !== 'function') {
|
if (!callback || typeof callback !== 'function') {
|
||||||
throw 'callback function required';
|
throw 'callback function required';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_email = email;
|
||||||
|
var emailObj = {};
|
||||||
|
emailObj[userEmailKey] = email;
|
||||||
|
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
var obj = {};
|
var userIdObj = {};
|
||||||
obj[userIdKey] = userId;
|
userIdObj[userIdKey] = userId;
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
callback();
|
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) {
|
UserService.prototype.getEmail = function (callback) {
|
||||||
if (!callback || typeof callback !== 'function') {
|
if (!callback || typeof callback !== 'function') {
|
||||||
throw 'callback function required';
|
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') {
|
if (!callback || typeof callback !== 'function') {
|
||||||
throw 'callback function required';
|
throw 'callback function required';
|
||||||
}
|
}
|
||||||
|
|
||||||
_email = null;
|
_userId = _email = null;
|
||||||
chrome.storage.local.remove(userEmailKey, function () {
|
chrome.storage.local.remove(userIdKey, function () {
|
||||||
callback();
|
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user