mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-28 12:45:45 +01:00
two factor page cleanup
This commit is contained in:
parent
64784d0e36
commit
1d102cad93
@ -131,10 +131,6 @@
|
|||||||
"message": "Verification Code",
|
"message": "Verification Code",
|
||||||
"description": "Verification Code"
|
"description": "Verification Code"
|
||||||
},
|
},
|
||||||
"enterTwoStepVerCode": {
|
|
||||||
"message": "Enter your two-step verification code.",
|
|
||||||
"description": "Enter your two-step verification code."
|
|
||||||
},
|
|
||||||
"account": {
|
"account": {
|
||||||
"message": "Account",
|
"message": "Account",
|
||||||
"description": "Account"
|
"description": "Account"
|
||||||
|
BIN
src/images/two-factor/u2fkey.jpg
Normal file
BIN
src/images/two-factor/u2fkey.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
BIN
src/images/two-factor/yubikey.jpg
Normal file
BIN
src/images/two-factor/yubikey.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
@ -13,11 +13,12 @@ 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, provider, token, device) {
|
var TokenRequest = function (email, masterPasswordHash, provider, token, remember, device) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.masterPasswordHash = masterPasswordHash;
|
this.masterPasswordHash = masterPasswordHash;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
this.remember = remember || remember !== false;
|
||||||
this.device = null;
|
this.device = null;
|
||||||
if (device) {
|
if (device) {
|
||||||
this.device = 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')) {
|
if (this.token && this.provider != null && (typeof this.provider !== 'undefined')) {
|
||||||
obj.twoFactorToken = this.token;
|
obj.twoFactorToken = this.token;
|
||||||
obj.twoFactorProvider = this.provider;
|
obj.twoFactorProvider = this.provider;
|
||||||
|
obj.twoFactorRemember = this.remember ? '1' : '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
@ -60,6 +62,11 @@ var PasswordHintRequest = function (email) {
|
|||||||
this.email = email;
|
this.email = email;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var TwoFactorEmailRequest = function (email, masterPasswordHash) {
|
||||||
|
this.email = email;
|
||||||
|
this.masterPasswordHash = masterPasswordHash;
|
||||||
|
};
|
||||||
|
|
||||||
var DeviceTokenRequest = function () {
|
var DeviceTokenRequest = function () {
|
||||||
this.pushToken = null;
|
this.pushToken = null;
|
||||||
};
|
};
|
||||||
|
@ -72,6 +72,7 @@ var IdentityTokenResponse = function (response) {
|
|||||||
|
|
||||||
this.privateKey = response.PrivateKey;
|
this.privateKey = response.PrivateKey;
|
||||||
this.key = response.Key;
|
this.key = response.Key;
|
||||||
|
this.twoFactorToken = response.TwoFactorToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
var ListResponse = function (data) {
|
var ListResponse = function (data) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
angular
|
angular
|
||||||
.module('bit.accounts')
|
.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) {
|
$analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService) {
|
||||||
$scope.i18n = i18nService;
|
$scope.i18n = i18nService;
|
||||||
utilsService.initListSectionItemListeners($(document), angular);
|
utilsService.initListSectionItemListeners($(document), angular);
|
||||||
@ -11,11 +11,17 @@
|
|||||||
var masterPassword = $stateParams.masterPassword;
|
var masterPassword = $stateParams.masterPassword;
|
||||||
var providers = $stateParams.providers;
|
var providers = $stateParams.providers;
|
||||||
|
|
||||||
|
if (!email || !masterPassword || !providers) {
|
||||||
|
$state.go('login');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.providerType = $stateParams.provider ? $stateParams.provider : getDefaultProvider(providers);
|
||||||
$scope.twoFactorEmail = null;
|
$scope.twoFactorEmail = null;
|
||||||
$scope.token = null;
|
$scope.token = null;
|
||||||
$scope.constantsProvider = constants.twoFactorProvider;
|
$scope.constantsProvider = constants.twoFactorProvider;
|
||||||
$scope.providerType = $stateParams.provider ? $stateParams.provider : getDefaultProvider(providers);
|
|
||||||
$scope.u2fReady = false;
|
$scope.u2fReady = false;
|
||||||
|
$scope.remember = { checked: false };
|
||||||
init();
|
init();
|
||||||
|
|
||||||
$scope.loginPromise = null;
|
$scope.loginPromise = null;
|
||||||
@ -25,7 +31,12 @@
|
|||||||
return;
|
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 () {
|
$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 });
|
||||||
@ -43,17 +54,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key = cryptoService.makeKey(masterPassword, email);
|
var key = cryptoService.makeKey(masterPassword, email);
|
||||||
var hash = cryptoService.hashPassword(masterPassword, key);
|
cryptoService.hashPassword(masterPassword, key, function (hash) {
|
||||||
apiService.postTwoFactorEmail({
|
var request = new TwoFactorEmailRequest(email, hash);
|
||||||
email: email,
|
apiService.postTwoFactorEmail(request, function () {
|
||||||
masterPasswordHash: hash
|
|
||||||
}, function () {
|
|
||||||
if (doToast) {
|
if (doToast) {
|
||||||
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
|
toastr.success('Verification email sent to ' + $scope.twoFactorEmail + '.');
|
||||||
}
|
}
|
||||||
}, function () {
|
}, function () {
|
||||||
toastr.error('Could not send verification email.');
|
toastr.error('Could not send verification email.');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.anotherMethod = function () {
|
$scope.anotherMethod = function () {
|
||||||
@ -131,7 +141,27 @@
|
|||||||
else if ($scope.providerType === constants.twoFactorProvider.email) {
|
else if ($scope.providerType === constants.twoFactorProvider.email) {
|
||||||
var params = providers[constants.twoFactorProvider.email];
|
var params = providers[constants.twoFactorProvider.email];
|
||||||
$scope.twoFactorEmail = params.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);
|
$scope.sendEmail(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,14 @@
|
|||||||
<div class="title">{{i18n.verificationCode}}</div>
|
<div class="title">{{i18n.verificationCode}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<div class="two-factor-key-page">
|
||||||
|
<p ng-if="providerType === constantsProvider.authenticator">
|
||||||
|
Enter the 6 digit verification code from your authenticator app.
|
||||||
|
</p>
|
||||||
|
<p ng-if="providerType === constantsProvider.email">
|
||||||
|
Enter the 6 digit verification code that was emailed to <b>{{twoFactorEmail}}</b>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
@ -21,14 +29,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="remember">Remember me</label>
|
<label for="remember">Remember me</label>
|
||||||
<input id="remember" name="Remember" type="checkbox" ng-model="remember">
|
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-section-footer">
|
|
||||||
{{i18n.enterTwoStepVerCode}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-accent" ng-if="providerType === constantsProvider.email">
|
||||||
|
<a href="#" stop-click ng-click="sendEmail(true)">Send verification code email again</a>
|
||||||
|
</p>
|
||||||
<p class="text-center text-accent">
|
<p class="text-center text-accent">
|
||||||
<a href="#" stop-click ng-click="anotherMethod()">Use another two-step login method</a>
|
<a href="#" stop-click ng-click="anotherMethod()">Use another two-step login method</a>
|
||||||
</p>
|
</p>
|
||||||
@ -61,6 +69,10 @@
|
|||||||
<div class="title">YubiKey</div>
|
<div class="title">YubiKey</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<div class="two-factor-key-page">
|
||||||
|
<p>Insert your YubiKey into your computer's USB port, then touch its button.</p>
|
||||||
|
<img src="../images/two-factor/yubikey.jpg" alt="" class="img-rounded img-responsive" />
|
||||||
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
@ -71,12 +83,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="remember">Remember me</label>
|
<label for="remember">Remember me</label>
|
||||||
<input id="remember" name="Remember" type="checkbox" ng-model="remember">
|
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
|
||||||
Touch the YubiKey button.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center text-accent">
|
<p class="text-center text-accent">
|
||||||
@ -96,10 +105,27 @@
|
|||||||
<div class="title">FIDO U2F</div>
|
<div class="title">FIDO U2F</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div ng-if="!u2fReady">Loading...</div>
|
<div class="two-factor-key-page">
|
||||||
<div ng-if="u2fReady">Touch button</div>
|
|
||||||
<iframe id="u2f_iframe" class="hide"></iframe>
|
<iframe id="u2f_iframe" class="hide"></iframe>
|
||||||
<p class="text-center text-accent">
|
<p ng-if="!u2fReady">Loading...</p>
|
||||||
|
<div ng-if="u2fReady">
|
||||||
|
<p>
|
||||||
|
Insert your Security Key into your computer's USB port. If it has a button, touch it.
|
||||||
|
</p>
|
||||||
|
<img src="../images/two-factor/u2fkey.jpg" alt="" class="img-rounded img-responsive" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list">
|
||||||
|
<div class="list-section">
|
||||||
|
<div class="list-section-items">
|
||||||
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
|
<label for="remember">Remember me</label>
|
||||||
|
<input id="remember" name="Remember" type="checkbox" ng-model="remember.checked">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-accent text-center">
|
||||||
<a href="#" stop-click ng-click="anotherMethod()">Use another two-step login method</a>
|
<a href="#" stop-click ng-click="anotherMethod()">Use another two-step login method</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,17 +2,30 @@
|
|||||||
.module('bit.services')
|
.module('bit.services')
|
||||||
|
|
||||||
.factory('authService', function (cryptoService, apiService, userService, tokenService, $q, $rootScope, loginService,
|
.factory('authService', function (cryptoService, apiService, userService, tokenService, $q, $rootScope, loginService,
|
||||||
folderService, settingsService, syncService, appIdService, utilsService) {
|
folderService, settingsService, syncService, appIdService, utilsService, constantsService) {
|
||||||
var _service = {};
|
var _service = {};
|
||||||
|
|
||||||
_service.logIn = function (email, masterPassword, twoFactorProvider, twoFactorToken) {
|
_service.logIn = function (email, masterPassword, twoFactorProvider, twoFactorToken, remember) {
|
||||||
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) {
|
||||||
appIdService.getAppId(function (appId) {
|
appIdService.getAppId(function (appId) {
|
||||||
|
tokenService.getTwoFactorToken(email, function (twoFactorRememberedToken) {
|
||||||
var deviceRequest = new DeviceRequest(appId, utilsService);
|
var deviceRequest = new DeviceRequest(appId, utilsService);
|
||||||
var request = new TokenRequest(email, hashedPassword, twoFactorProvider, twoFactorToken, deviceRequest);
|
var request;
|
||||||
|
|
||||||
|
if (twoFactorToken && typeof (twoFactorProvider) !== 'undefined' && twoFactorProvider !== null) {
|
||||||
|
request = new TokenRequest(email, hashedPassword, twoFactorProvider, twoFactorToken, remember,
|
||||||
|
deviceRequest);
|
||||||
|
}
|
||||||
|
else if (twoFactorRememberedToken) {
|
||||||
|
request = new TokenRequest(email, hashedPassword, constantsService.twoFactorProvider.remember,
|
||||||
|
twoFactorRememberedToken, false, deviceRequest);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
request = new TokenRequest(email, hashedPassword, null, null, false, deviceRequest);
|
||||||
|
}
|
||||||
|
|
||||||
apiService.postIdentityToken(request, function (response) {
|
apiService.postIdentityToken(request, function (response) {
|
||||||
// success
|
// success
|
||||||
@ -20,10 +33,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.twoFactorToken) {
|
||||||
|
tokenService.setTwoFactorToken(response.twoFactorToken, email, function () { });
|
||||||
|
}
|
||||||
|
|
||||||
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 () {
|
||||||
userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(), function () {
|
userService.setUserIdAndEmail(tokenService.getUserId(), tokenService.getEmail(),
|
||||||
|
function () {
|
||||||
cryptoService.setEncKey(response.key).then(function () {
|
cryptoService.setEncKey(response.key).then(function () {
|
||||||
return cryptoService.setEncPrivateKey(response.privateKey);
|
return cryptoService.setEncPrivateKey(response.privateKey);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
@ -49,6 +67,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,3 +65,13 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.two-factor-key-page {
|
||||||
|
padding: 20px 20px 0 20px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.img-responsive {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,9 @@ function initApiService() {
|
|||||||
error: function (jqXHR, textStatus, errorThrown) {
|
error: function (jqXHR, textStatus, errorThrown) {
|
||||||
if (jqXHR.responseJSON && jqXHR.responseJSON.TwoFactorProviders2 &&
|
if (jqXHR.responseJSON && jqXHR.responseJSON.TwoFactorProviders2 &&
|
||||||
Object.keys(jqXHR.responseJSON.TwoFactorProviders2).length) {
|
Object.keys(jqXHR.responseJSON.TwoFactorProviders2).length) {
|
||||||
|
self.tokenService.clearTwoFactorToken(tokenRequest.email, function () {
|
||||||
successWithTwoFactor(jqXHR.responseJSON.TwoFactorProviders2);
|
successWithTwoFactor(jqXHR.responseJSON.TwoFactorProviders2);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
error(new ErrorResponse(jqXHR, true));
|
error(new ErrorResponse(jqXHR, true));
|
||||||
@ -56,12 +58,14 @@ function initApiService() {
|
|||||||
|
|
||||||
// Two Factor APIs
|
// Two Factor APIs
|
||||||
|
|
||||||
ApiService.prototype.postTwoFactorEmail = function (success, error) {
|
ApiService.prototype.postTwoFactorEmail = function (request, success, error) {
|
||||||
var self = this;
|
var self = this;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: self.baseUrl + '/two-factor/send-email?' + token,
|
url: self.baseUrl + '/two-factor/send-email-login',
|
||||||
dataType: 'json',
|
dataType: 'text',
|
||||||
|
contentType: 'application/json; charset=utf-8',
|
||||||
|
data: JSON.stringify(request),
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
success(response);
|
success(response);
|
||||||
},
|
},
|
||||||
|
@ -102,6 +102,46 @@ function initTokenService() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TokenService.prototype.setTwoFactorToken = function (token, email, callback) {
|
||||||
|
if (!callback || typeof callback !== 'function') {
|
||||||
|
throw 'callback function required';
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = {};
|
||||||
|
obj['twoFactorToken_' + email] = token;
|
||||||
|
|
||||||
|
chrome.storage.local.set(obj, function () {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenService.prototype.getTwoFactorToken = function (email, callback) {
|
||||||
|
if (!callback || typeof callback !== 'function') {
|
||||||
|
throw 'callback function required';
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = 'twoFactorToken_' + email;
|
||||||
|
|
||||||
|
chrome.storage.local.get(prop, function (obj) {
|
||||||
|
if (obj && obj[prop]) {
|
||||||
|
callback(obj[prop]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenService.prototype.clearTwoFactorToken = function (email, callback) {
|
||||||
|
if (!callback || typeof callback !== 'function') {
|
||||||
|
throw 'callback function required';
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.local.remove('twoFactorToken_' + email, function () {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
TokenService.prototype.clearAuthBearer = function (callback) {
|
TokenService.prototype.clearAuthBearer = function (callback) {
|
||||||
if (!callback || typeof callback !== 'function') {
|
if (!callback || typeof callback !== 'function') {
|
||||||
throw 'callback function required';
|
throw 'callback function required';
|
||||||
|
Loading…
Reference in New Issue
Block a user