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()
};
$scope.submitPromise = apiService.accounts.postTwoFactorRecover(request, function () {
$scope.submitPromise = apiService.twoFactor.recover(request, function () {
$analytics.eventTrack('Recovered 2FA');
$scope.success = true;
}).$promise;

View File

@ -103,9 +103,6 @@
putProfile: { url: _apiUri + '/accounts/profile', method: 'POST', params: {} },
getDomains: { url: _apiUri + '/accounts/domains', method: 'GET', 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: {} },
putSecurityStamp: { url: _apiUri + '/accounts/security-stamp', method: 'POST', params: {} },
putKeys: { url: _apiUri + '/accounts/keys', method: 'POST', params: {} },
@ -115,8 +112,13 @@
});
_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', {}, {

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 () {
$uibModal.open({
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')
.controller('settingsTwoStepController', function ($scope, apiService, authService, toastr, $analytics, constants,
$filter) {
$filter, $uibModal) {
$scope.providers = [
{
type: constants.twoFactorProvider.authenticator,
@ -53,4 +53,21 @@
authService.getUserProfile().then(function (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>
</form>
</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-header with-border">
<h3 class="box-title">Organizations</h3>

View File

@ -1,15 +1,13 @@
<div class="modal-header">
<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>
<form name="authTwoStepForm" ng-submit="authTwoStepForm.$valid && auth(authModel)" api-form="authPromise"
ng-if="!twoFactorModel">
ng-if="!model">
<div class="modal-body">
<p ng-show="enabled()">
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>
<p>Enter your master password to modify two-step login settings.</p>
<div class="callout callout-danger validation-errors" ng-show="authTwoStepForm.$errors">
<h4>Errors have occurred</h4>
<ul>
@ -29,22 +27,22 @@
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div>
</form>
<form name="updateTwoStepForm" ng-submit="updateTwoStepForm.$valid && update(updateModel)" api-form="updatePromise"
ng-if="twoFactorModel">
<form name="submitTwoStepForm" ng-submit="submitTwoStepForm.$valid && submit(updateModel)" api-form="submitPromise"
ng-if="model">
<div class="modal-body">
<div ng-show="enabled()">
<div ng-if="enabled">
<div class="callout callout-success">
<h4><i class="fa fa-check-circle"></i> Enabled</h4>
<p>
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>
</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 ng-show="!enabled()">
<p>Setting up two-step verification is easy, just follow these steps:</p>
<h4>1. Download a two-step verification app</h4>
<div ng-if="!enabled">
<p>Setting up two-step login with an authenticator app is easy, just follow these steps:</p>
<h4>1. Download a two-step authenticator app</h4>
</div>
<ul class="fa-ul">
<li>
@ -70,11 +68,11 @@
</li>
</ul>
<p>These apps are recommended, however, other authenticator apps will also work.</p>
<hr ng-show="enabled()" />
<h4 ng-show="!enabled()" style="margin-top: 30px;">2. Scan this QR code with your verification app</h4>
<hr ng-if="enabled" />
<h4 ng-if="!enabled" style="margin-top: 30px;">2. Scan this QR code with your authenticator app</h4>
<div class="row">
<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 class="col-md-8">
<p>
@ -82,53 +80,33 @@
following details:
</p>
<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>Time based:</strong> Yes</li>
</ul>
</div>
</div>
<div ng-show="enabled()">
<hr />
<div class="callout callout-warning">
<h4><i class="fa fa-warning"></i> Recovery Code <i class="fa fa-warning"></i></h4>
<p>
The recovery code allows you to access your account in the event that you lose your authenticator app.
bitwarden support won't be able to assist you if you lose access to your account. We recommend
you write down or print the recovery code below and keep it in a safe place.
</p>
<p><strong>Recovery Code:</strong> <code>{{twoFactorModel.recovery}}</code></p>
<button type="button" class="btn btn-default" ng-click="print(twoFactorModel.recovery)">
<i class="fa fa-print"></i> Print
</button>
<div ng-if="!enabled">
<h4 style="margin-top: 30px;">
3. Enter the resulting 6 digit verification code from the app
</h4>
<div class="callout callout-danger validation-errors" ng-show="submitTwoStepForm.$errors">
<h4>Errors have occurred</h4>
<ul>
<li ng-repeat="e in submitTwoStepForm.$errors">{{e}}</li>
</ul>
</div>
<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>
</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 class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="updateTwoStepForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="updateTwoStepForm.$loading"></i>
<span ng-show="enabled()">Disable Two-step</span>
<span ng-show="!enabled()">Enable Two-step</span>
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="submitTwoStepForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="submitTwoStepForm.$loading"></i>
{{enabled ? 'Disable' : 'Enable'}}
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div>

View File

@ -188,7 +188,7 @@
<script src="app/settings/settingsController.js"></script>
<script src="app/settings/settingsChangePasswordController.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/settingsDomainsController.js"></script>
<script src="app/settings/settingsTwoStepController.js"></script>