new two-factor management page

This commit is contained in:
Kyle Spearrin 2017-06-19 22:26:57 -04:00
parent 10fe79c558
commit 00e74dd2c8
9 changed files with 150 additions and 195 deletions

View File

@ -14,7 +14,7 @@ angular
recoveryCode: model.code.replace(/\s/g, '').toLowerCase() recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
}; };
$scope.submitPromise = apiService.accounts.postTwoFactorRecover(request, function () { $scope.submitPromise = apiService.twoFactor.recover(request, function () {
$analytics.eventTrack('Recovered 2FA'); $analytics.eventTrack('Recovered 2FA');
$scope.success = true; $scope.success = true;
}).$promise; }).$promise;

View File

@ -103,9 +103,6 @@
putProfile: { url: _apiUri + '/accounts/profile', method: 'POST', params: {} }, putProfile: { url: _apiUri + '/accounts/profile', method: 'POST', params: {} },
getDomains: { url: _apiUri + '/accounts/domains', method: 'GET', params: {} }, getDomains: { url: _apiUri + '/accounts/domains', method: 'GET', params: {} },
putDomains: { url: _apiUri + '/accounts/domains', method: 'POST', params: {} }, putDomains: { url: _apiUri + '/accounts/domains', method: 'POST', params: {} },
getTwoFactor: { url: _apiUri + '/accounts/two-factor', method: 'GET', params: {} },
putTwoFactor: { url: _apiUri + '/accounts/two-factor', method: 'POST', params: {} },
postTwoFactorRecover: { url: _apiUri + '/accounts/two-factor-recover', method: 'POST', params: {} },
postPasswordHint: { url: _apiUri + '/accounts/password-hint', method: 'POST', params: {} }, postPasswordHint: { url: _apiUri + '/accounts/password-hint', method: 'POST', params: {} },
putSecurityStamp: { url: _apiUri + '/accounts/security-stamp', method: 'POST', params: {} }, putSecurityStamp: { url: _apiUri + '/accounts/security-stamp', method: 'POST', params: {} },
putKeys: { url: _apiUri + '/accounts/keys', method: 'POST', params: {} }, putKeys: { url: _apiUri + '/accounts/keys', method: 'POST', params: {} },
@ -115,8 +112,13 @@
}); });
_service.twoFactor = $resource(_apiUri + '/two-factor', {}, { _service.twoFactor = $resource(_apiUri + '/two-factor', {}, {
get: { method: 'GET', params: { provider: '@provider' } }, list: { method: 'GET', params: {} },
list: { method: 'GET', params: {} } getEmail: { url: _apiUri + '/two-factor/get-email', method: 'POST', params: {} },
getAuthenticator: { url: _apiUri + '/two-factor/get-authenticator', method: 'POST', params: {} },
putEmail: { url: _apiUri + '/two-factor/email', method: 'POST', params: {} },
putAuthenticator: { url: _apiUri + '/two-factor/authenticator', method: 'POST', params: {} },
disable: { url: _apiUri + '/two-factor/disable', method: 'POST', params: {} },
recover: { url: _apiUri + '/two-factor/recover', method: 'POST', params: {} },
}); });
_service.settings = $resource(_apiUri + '/settings', {}, { _service.settings = $resource(_apiUri + '/settings', {}, {

View File

@ -105,22 +105,6 @@
}); });
}; };
$scope.twoFactor = function () {
var twoFactorModal = $uibModal.open({
animation: true,
templateUrl: 'app/settings/views/settingsTwoFactor.html',
controller: 'settingsTwoFactorController'
});
twoFactorModal.result.then(function (enabled) {
if (enabled === null) {
return;
}
$scope.model.twoFactorEnabled = enabled;
});
};
$scope.sessions = function () { $scope.sessions = function () {
$uibModal.open({ $uibModal.open({
animation: true, animation: true,

View File

@ -1,93 +0,0 @@
angular
.module('bit.settings')
.controller('settingsTwoFactorController', function ($scope, apiService, $uibModalInstance, cryptoService, authService,
$q, toastr, $analytics) {
$analytics.eventTrack('settingsTwoFactorController', { category: 'Modal' });
var _issuer = 'bitwarden',
_profile = null,
_masterPasswordHash;
authService.getUserProfile().then(function (profile) {
_profile = profile;
$scope.account = _profile.email;
$scope.enabled = function () {
return _profile.extended && _profile.extended.twoFactorEnabled;
};
});
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
$scope.authPromise = apiService.accounts.getTwoFactor({
masterPasswordHash: _masterPasswordHash,
provider: 0 /* Only authenticator provider for now. */
}, function (response) {
processResponse(response);
}).$promise;
};
function formatString(s) {
if (!s) {
return null;
}
return s.replace(/(.{4})/g, '$1 ').trim().toUpperCase();
}
function processResponse(response) {
var key = response.AuthenticatorKey;
$scope.twoFactorModel = {
enabled: response.TwoFactorEnabled,
key: formatString(key),
recovery: formatString(response.TwoFactorRecoveryCode),
qr: 'https://chart.googleapis.com/chart?chs=120x120&chld=L|0&cht=qr&chl=otpauth://totp/' +
_issuer + ':' + encodeURIComponent(_profile.email) +
'%3Fsecret=' + encodeURIComponent(key) +
'%26issuer=' + _issuer
};
}
$scope.update = function (model) {
var currentlyEnabled = $scope.twoFactorModel.enabled;
if (currentlyEnabled && !confirm('Are you sure you want to disable two-step login?')) {
return;
}
var request = {
enabled: !currentlyEnabled,
token: model.token.replace(' ', ''),
masterPasswordHash: _masterPasswordHash
};
$scope.updatePromise = apiService.accounts.putTwoFactor({}, request, function (response) {
if (response.TwoFactorEnabled) {
$analytics.eventTrack('Enabled Two-step Login');
toastr.success('Two-step login has been enabled.');
if (_profile.extended) _profile.extended.twoFactorEnabled = true;
processResponse(response);
$('#token').blur();
model.token = null;
}
else {
$analytics.eventTrack('Disabled Two-step Login');
toastr.success('Two-step login has been disabled.');
if (_profile.extended) _profile.extended.twoFactorEnabled = false;
$scope.close();
}
}).$promise;
};
$scope.print = function (printContent) {
$analytics.eventTrack('Print Recovery Code');
var w = window.open();
w.document.write('<div style="font-size: 18px; text-align: center;"><p>bitwarden two-step login recovery code:</p>' +
'<pre>' + printContent + '</pre>');
w.print();
w.close();
};
$scope.close = function () {
$uibModalInstance.close(!_profile.extended ? null : _profile.extended.twoFactorEnabled);
};
});

View File

@ -0,0 +1,88 @@
angular
.module('bit.settings')
.controller('settingsTwoStepAuthenticatorController', function ($scope, apiService, $uibModalInstance, cryptoService,
authService, $q, toastr, $analytics, constants) {
$analytics.eventTrack('settingsTwoStepAuthenticatorController', { category: 'Modal' });
var _issuer = 'bitwarden',
_profile = null,
_masterPasswordHash;
$scope.auth = function (model) {
_masterPasswordHash = cryptoService.hashPassword(model.masterPassword);
var response = null;
$scope.authPromise = apiService.twoFactor.getAuthenticator({}, {
masterPasswordHash: _masterPasswordHash
}).$promise.then(function (apiResponse) {
response = apiResponse;
return authService.getUserProfile();
}).then(function (profile) {
_profile = profile;
$scope.account = _profile.email;
processResponse(response);
});
};
function formatString(s) {
if (!s) {
return null;
}
return s.replace(/(.{4})/g, '$1 ').trim().toUpperCase();
}
function processResponse(response) {
$scope.enabled = response.Enabled;
$scope.model = {
key: formatString(response.Key),
qr: 'https://chart.googleapis.com/chart?chs=120x120&chld=L|0&cht=qr&chl=otpauth://totp/' +
_issuer + ':' + encodeURIComponent(_profile.email) +
'%3Fsecret=' + encodeURIComponent(response.Key) +
'%26issuer=' + _issuer
};
$scope.updateModel = {
token: null
};
}
$scope.submit = function (model) {
if (!model || !model.token) {
disable();
return;
}
update(model);
};
function disable() {
if (!confirm('Are you sure you want to disable the authenticator app provider?')) {
return;
}
$scope.submitPromise = apiService.twoFactor.disable({}, {
masterPasswordHash: _masterPasswordHash,
type: constants.twoFactorProvider.authenticator
}, function (response) {
$analytics.eventTrack('Disabled Two-step Authenticator');
toastr.success('Authenticator app has been disabled.');
$scope.close();
}).$promise;
}
function update(model) {
$scope.submitPromise = apiService.twoFactor.putAuthenticator({}, {
token: model.token.replace(' ', ''),
masterPasswordHash: _masterPasswordHash
}, function (response) {
$analytics.eventTrack('Enabled Two-step Authenticator');
processResponse(response);
model.token = null;
}).$promise;
}
$scope.close = function () {
$uibModalInstance.close();
};
});

View File

@ -2,7 +2,7 @@
.module('bit.settings') .module('bit.settings')
.controller('settingsTwoStepController', function ($scope, apiService, authService, toastr, $analytics, constants, .controller('settingsTwoStepController', function ($scope, apiService, authService, toastr, $analytics, constants,
$filter) { $filter, $uibModal) {
$scope.providers = [ $scope.providers = [
{ {
type: constants.twoFactorProvider.authenticator, type: constants.twoFactorProvider.authenticator,
@ -53,4 +53,21 @@
authService.getUserProfile().then(function (profile) { authService.getUserProfile().then(function (profile) {
_profile = profile; _profile = profile;
}); });
$scope.edit = function (provider) {
if (provider.type === constants.twoFactorProvider.authenticator) {
var authenticatorModal = $uibModal.open({
animation: true,
templateUrl: 'app/settings/views/settingsTwoStepAuthenticator.html',
controller: 'settingsTwoStepAuthenticatorController',
resolve: {
enabled: function () { return provider.enabled; }
}
});
authenticatorModal.result.then(function () {
});
}
};
}); });

View File

@ -85,27 +85,6 @@
</div> </div>
</form> </form>
</div> </div>
<div class="box box-default">
<div class="box-header with-border">
<h3 class="box-title">Two-step Log In</h3>
</div>
<div class="box-body">
<p>
Current Status:
<span class="label bg-green" ng-show="model.twoFactorEnabled">ENABLED</span>
<span class="label bg-gray" ng-show="!model.twoFactorEnabled">DISABLED</span>
</p>
<p>
Two-step login helps keep your account more secure by requiring a code provided by an app on your mobile device
while logging in (in addition to your master password).
</p>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default btn-flat" ng-click="twoFactor()">
Manage Two-step Log In
</button>
</div>
</div>
<div class="box box-default"> <div class="box box-default">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">Organizations</h3> <h3 class="box-title">Organizations</h3>

View File

@ -1,15 +1,13 @@
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" ng-click="close()" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="twoFactorModelLabel"><i class="fa fa-key"></i> Two-step Log In</h4> <h4 class="modal-title">
<i class="fa fa-key"></i> Two-step Log In <small>authenticator app</small>
</h4>
</div> </div>
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise" <form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
ng-if="!twoFactorModel"> ng-if="!model">
<div class="modal-body"> <div class="modal-body">
<p ng-show="enabled()"> <p>Enter your master password to modify two-step login settings.</p>
Two-step log in is already enabled on your account. To access your two-step
settings enter your master password below.
</p>
<p ng-show="!enabled()">To get started with two-step log in enter your master password below.</p>
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors"> <div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
<h4>Errors have occurred</h4> <h4>Errors have occurred</h4>
<ul> <ul>
@ -29,22 +27,22 @@
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button> <button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div> </div>
</form> </form>
<form name="updateTwoStepForm" ng-submit="updateTwoStepForm.$valid && update(updateModel)" api-form="updatePromise" <form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
ng-if="twoFactorModel"> ng-if="model">
<div class="modal-body"> <div class="modal-body">
<div ng-show="enabled()"> <div ng-if="enabled">
<div class="callout callout-success"> <div class="callout callout-success">
<h4><i class="fa fa-check-circle"></i> Enabled</h4> <h4><i class="fa fa-check-circle"></i> Enabled</h4>
<p> <p>
Two-step log in is enabled on your account. Incase you need to add it to another device, below is the QR Two-step log in is enabled on your account. Incase you need to add it to another device, below is the QR
code (or key) required by your verification app. code (or key) required by your authenticator app.
</p> </p>
</div> </div>
<p>Need a two-step verification app? Download one of the following:</p> <p>Need a two-step authenticator app? Download one of the following:</p>
</div> </div>
<div ng-show="!enabled()"> <div ng-if="!enabled">
<p>Setting up two-step verification is easy, just follow these steps:</p> <p>Setting up two-step login with an authenticator app is easy, just follow these steps:</p>
<h4>1. Download a two-step verification app</h4> <h4>1. Download a two-step authenticator app</h4>
</div> </div>
<ul class="fa-ul"> <ul class="fa-ul">
<li> <li>
@ -70,11 +68,11 @@
</li> </li>
</ul> </ul>
<p>These apps are recommended, however, other authenticator apps will also work.</p> <p>These apps are recommended, however, other authenticator apps will also work.</p>
<hr ng-show="enabled()" /> <hr ng-if="enabled" />
<h4 ng-show="!enabled()" style="margin-top: 30px;">2. Scan this QR code with your verification app</h4> <h4 ng-if="!enabled" style="margin-top: 30px;">2. Scan this QR code with your authenticator app</h4>
<div class="row"> <div class="row">
<div class="col-md-4 text-center"> <div class="col-md-4 text-center">
<p><img ng-src="{{twoFactorModel.qr}}" alt="QR" class="img-thumbnail" /></p> <p><img ng-src="{{model.qr}}" alt="QR" class="img-thumbnail" /></p>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<p> <p>
@ -82,53 +80,33 @@
following details: following details:
</p> </p>
<ul class="list-unstyled"> <ul class="list-unstyled">
<li><strong>Key:</strong> <code>{{twoFactorModel.key}}</code></li> <li><strong>Key:</strong> <code>{{model.key}}</code></li>
<li><strong>Account:</strong> {{account}}</li> <li><strong>Account:</strong> {{account}}</li>
<li><strong>Time based:</strong> Yes</li> <li><strong>Time based:</strong> Yes</li>
</ul> </ul>
</div> </div>
</div> </div>
<div ng-show="enabled()"> <div ng-if="!enabled">
<hr /> <h4 style="margin-top: 30px;">
<div class="callout callout-warning"> 3. Enter the resulting 6 digit verification code from the app
<h4><i class="fa fa-warning"></i> Recovery Code <i class="fa fa-warning"></i></h4> </h4>
<p> <div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
The recovery code allows you to access your account in the event that you lose your authenticator app. <h4>Errors have occurred</h4>
bitwarden support won't be able to assist you if you lose access to your account. We recommend <ul>
you write down or print the recovery code below and keep it in a safe place. <li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
</p> </ul>
<p><strong>Recovery Code:</strong> <code>{{twoFactorModel.recovery}}</code></p> </div>
<button type="button" class="btn btn-default" ng-click="print(twoFactorModel.recovery)"> <div class="form-group" show-errors>
<i class="fa fa-print"></i> Print <label for="token" class="sr-only">Verification Code</label>
</button> <input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token"
class="form-control" required api-field />
</div> </div>
</div> </div>
<hr ng-show="enabled()" />
<div class="callout callout-danger validation-errors" ng-show="updateTwoStepForm.$errors">
<h4>Errors have occurred</h4>
<ul>
<li ng-repeat="e in updateTwoStepForm.$errors">{{e}}</li>
</ul>
</div>
<h4 style="margin-top: 30px;">
<span ng-show="enabled()">Want to disable? </span><span ng-show="!enabled()">3. </span>
Enter the resulting verification code from the app
</h4>
<div class="form-group" show-errors>
<label for="token" class="sr-only">Verification Code</label>
<input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token"
class="form-control" required api-field />
</div>
<p ng-show="!enabled()">
NOTE: After enabling two-step log in, you will be required to enter the current code
generated by your verification app each time you log in.
</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="updateTwoStepForm.$loading"> <button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="updateTwoStepForm.$loading"></i> <i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
<span ng-show="enabled()">Disable Two-step</span> {{enabled ? 'Disable' : 'Enable'}}
<span ng-show="!enabled()">Enable Two-step</span>
</button> </button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button> <button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div> </div>

View File

@ -188,7 +188,7 @@
<script src="app/settings/settingsController.js"></script> <script src="app/settings/settingsController.js"></script>
<script src="app/settings/settingsChangePasswordController.js"></script> <script src="app/settings/settingsChangePasswordController.js"></script>
<script src="app/settings/settingsChangeEmailController.js"></script> <script src="app/settings/settingsChangeEmailController.js"></script>
<script src="app/settings/settingsTwoFactorController.js"></script> <script src="app/settings/settingsTwoStepAuthenticatorController.js"></script>
<script src="app/settings/settingsSessionsController.js"></script> <script src="app/settings/settingsSessionsController.js"></script>
<script src="app/settings/settingsDomainsController.js"></script> <script src="app/settings/settingsDomainsController.js"></script>
<script src="app/settings/settingsTwoStepController.js"></script> <script src="app/settings/settingsTwoStepController.js"></script>