diff --git a/src/js/client.js b/src/js/client.js
index be4bf24d7..2fa99606e 100644
--- a/src/js/client.js
+++ b/src/js/client.js
@@ -111,7 +111,8 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N
id: null,
username: null,
email: null,
- admin: false
+ admin: false,
+ twoFactorAuthenticationEnabled: false
};
this._config = {
apiServerOrigin: null,
@@ -220,6 +221,7 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N
this._userInfo.fallbackEmail = userInfo.fallbackEmail;
this._userInfo.displayName = userInfo.displayName;
this._userInfo.admin = !!userInfo.admin;
+ this._userInfo.twoFactorAuthenticationEnabled = userInfo.twoFactorAuthenticationEnabled;
this._userInfo.gravatar = 'https://www.gravatar.com/avatar/' + md5.createHash(userInfo.email) + '.jpg?s=24&d=mm';
this._userInfo.gravatarHuge = 'https://www.gravatar.com/avatar/' + md5.createHash(userInfo.email) + '.jpg?s=128&d=mm';
};
@@ -725,7 +727,7 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N
if (status !== 201 || typeof data !== 'object') return callback(new ClientError(status, data));
that.setToken(data.token);
- that.setUserInfo({ username: username, email: email, admin: true });
+ that.setUserInfo({ username: username, email: email, admin: true, twoFactorAuthenticationEnabled: false });
callback(null, data.activated);
}).error(defaultErrorHandler(callback));
@@ -930,6 +932,37 @@ angular.module('Application').service('Client', ['$http', '$interval', 'md5', 'N
}).error(defaultErrorHandler(callback));
};
+ Client.prototype.setTwoFactorAuthenticationSecret = function (callback) {
+ var data = {};
+
+ post('/api/v1/profile/twofactorauthentication', data).success(function(data, status) {
+ if (status !== 201) return callback(new ClientError(status, data));
+ callback(null, data);
+ }).error(defaultErrorHandler(callback));
+ };
+
+ Client.prototype.enableTwoFactorAuthentication = function (totpToken, callback) {
+ var data = {
+ totpToken: totpToken
+ };
+
+ post('/api/v1/profile/twofactorauthentication/enable', data).success(function(data, status) {
+ if (status !== 202) return callback(new ClientError(status, data));
+ callback(null);
+ }).error(defaultErrorHandler(callback));
+ };
+
+ Client.prototype.disableTwoFactorAuthentication = function (password, callback) {
+ var data = {
+ password: password
+ };
+
+ post('/api/v1/profile/twofactorauthentication/disable', data).success(function(data, status) {
+ if (status !== 202) return callback(new ClientError(status, data));
+ callback(null);
+ }).error(defaultErrorHandler(callback));
+ };
+
Client.prototype.refreshUserInfo = function (callback) {
var that = this;
diff --git a/src/views/account.html b/src/views/account.html
index 25ebf6863..593c11636 100644
--- a/src/views/account.html
+++ b/src/views/account.html
@@ -128,6 +128,41 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ twoFactorAuthentication.secret }}
+
+
+
+
+
+
+
+
+
@@ -158,6 +193,10 @@
Password recovery email |
{{ user.fallbackEmail }} |
+
+ | Two-Factor Authentication |
+ not enabled |
+
@@ -192,4 +231,4 @@
-
+
\ No newline at end of file
diff --git a/src/views/account.js b/src/views/account.js
index 8843b27df..e283eb7ee 100644
--- a/src/views/account.js
+++ b/src/views/account.js
@@ -10,6 +10,80 @@ angular.module('Application').controller('AccountController', ['$scope', 'Client
$scope.activeClients = [];
$scope.webadminClient = {};
+ $scope.twoFactorAuthentication = {
+ busy: false,
+ error: null,
+ password: '',
+ totpToken: '',
+ secret: '',
+ qrcode: '',
+
+ reset: function () {
+ $scope.twoFactorAuthentication.busy = false;
+ $scope.twoFactorAuthentication.error = null;
+ $scope.twoFactorAuthentication.password = '';
+ $scope.twoFactorAuthentication.totpToken = '';
+ $scope.twoFactorAuthentication.secret = '';
+ $scope.twoFactorAuthentication.qrcode = '';
+
+ $scope.twoFactorAuthenticationEnableForm.$setUntouched();
+ $scope.twoFactorAuthenticationEnableForm.$setPristine();
+ },
+
+ show: function () {
+ $scope.twoFactorAuthentication.reset();
+
+ if ($scope.user.twoFactorAuthenticationEnabled) {
+ $('#twoFactorAuthenticationDisableModal').modal('show');
+ } else {
+ $('#twoFactorAuthenticationEnableModal').modal('show');
+
+ Client.setTwoFactorAuthenticationSecret(function (error, result) {
+ if (error) return console.error(error);
+
+ $scope.twoFactorAuthentication.secret = result.secret;
+ $scope.twoFactorAuthentication.qrcode = result.qrcode;
+ });
+ }
+ },
+
+ enable: function() {
+ $scope.twoFactorAuthentication.busy = true;
+
+ Client.enableTwoFactorAuthentication($scope.twoFactorAuthentication.totpToken, function (error) {
+ $scope.twoFactorAuthentication.busy = false;
+
+ if (error) {
+ $scope.twoFactorAuthentication.error = error.message;
+
+ $scope.twoFactorAuthentication.totpToken = '';
+ $scope.twoFactorAuthenticationEnableForm.totpToken.$setPristine();
+ $('#twoFactorAuthenticationTotpTokenInput').focus();
+
+ return;
+ }
+
+ $('#twoFactorAuthenticationEnableModal').modal('hide');
+ });
+ },
+
+ disable: function () {
+ $scope.twoFactorAuthentication.busy = true;
+
+ Client.disableTwoFactorAuthentication($scope.twoFactorAuthentication.totpToken, function (error) {
+ $scope.twoFactorAuthentication.busy = false;
+
+ if (error) {
+ $scope.twoFactorAuthentication.error = error.message;
+ console.error(error);
+ return;
+ }
+
+ $('#twoFactorAuthenticationDisableModal').modal('hide');
+ });
+ }
+ };
+
$scope.passwordchange = {
busy: false,
error: {},
@@ -256,7 +330,7 @@ angular.module('Application').controller('AccountController', ['$scope', 'Client
});
// setup all the dialog focus handling
- ['passwordChangeModal', 'emailChangeModal', 'fallbackEmailChangeModal', 'displayNameChangeModal'].forEach(function (id) {
+ ['passwordChangeModal', 'emailChangeModal', 'fallbackEmailChangeModal', 'displayNameChangeModal', 'twoFactorAuthenticationEnableModal'].forEach(function (id) {
$('#' + id).on('shown.bs.modal', function () {
$(this).find("[autofocus]:first").focus();
});
|