diff --git a/src/app/services/apiService.js b/src/app/services/apiService.js index 27b7e404c1..5d2dd282ba 100644 --- a/src/app/services/apiService.js +++ b/src/app/services/apiService.js @@ -114,11 +114,13 @@ _service.twoFactor = $resource(_apiUri + '/two-factor', {}, { list: { method: 'GET', params: {} }, getEmail: { url: _apiUri + '/two-factor/get-email', method: 'POST', params: {} }, + getU2f: { url: _apiUri + '/two-factor/get-u2f', method: 'POST', params: {} }, getDuo: { url: _apiUri + '/two-factor/get-duo', method: 'POST', params: {} }, getAuthenticator: { url: _apiUri + '/two-factor/get-authenticator', method: 'POST', params: {} }, getYubi: { url: _apiUri + '/two-factor/get-yubikey', method: 'POST', params: {} }, sendEmail: { url: _apiUri + '/two-factor/send-email', method: 'POST', params: {} }, putEmail: { url: _apiUri + '/two-factor/email', method: 'POST', params: {} }, + putU2f: { url: _apiUri + '/two-factor/u2f', method: 'POST', params: {} }, putAuthenticator: { url: _apiUri + '/two-factor/authenticator', method: 'POST', params: {} }, putDuo: { url: _apiUri + '/two-factor/duo', method: 'POST', params: {} }, putYubi: { url: _apiUri + '/two-factor/yubikey', method: 'POST', params: {} }, diff --git a/src/app/settings/settingsTwoStepController.js b/src/app/settings/settingsTwoStepController.js index 620c22f219..c044ba6409 100644 --- a/src/app/settings/settingsTwoStepController.js +++ b/src/app/settings/settingsTwoStepController.js @@ -111,5 +111,19 @@ provider.enabled = enabled; }); } + else if (provider.type === constants.twoFactorProvider.u2f) { + var u2fModal = $uibModal.open({ + animation: true, + templateUrl: 'app/settings/views/settingsTwoStepU2f.html', + controller: 'settingsTwoStepU2fController', + resolve: { + enabled: function () { return provider.enabled; } + } + }); + + u2fModal.result.then(function (enabled) { + provider.enabled = enabled; + }); + } }; }); diff --git a/src/app/settings/settingsTwoStepU2fController.js b/src/app/settings/settingsTwoStepU2fController.js new file mode 100644 index 0000000000..d9bf2687b5 --- /dev/null +++ b/src/app/settings/settingsTwoStepU2fController.js @@ -0,0 +1,93 @@ +angular + .module('bit.settings') + + .controller('settingsTwoStepU2fController', function ($scope, apiService, $uibModalInstance, cryptoService, + authService, toastr, $analytics, constants, $timeout, $window) { + $analytics.eventTrack('settingsTwoStepU2fController', { category: 'Modal' }); + var _masterPasswordHash; + + $scope.deviceResponse = null; + $scope.deviceListening = false; + $scope.deviceError = false; + + $scope.auth = function (model) { + _masterPasswordHash = cryptoService.hashPassword(model.masterPassword); + + $scope.authPromise = apiService.twoFactor.getU2f({}, { + masterPasswordHash: _masterPasswordHash + }).$promise.then(function (response) { + $scope.enabled = response.Enabled; + $scope.challenge = response.Challenge; + $scope.authed = true; + return $scope.readDevice(); + }); + }; + + $scope.readDevice = function () { + $scope.deviceResponse = null; + $scope.deviceError = false; + $scope.deviceListening = true; + + $window.u2f.register($scope.challenge.AppId, [{ + version: $scope.challenge.Version, + challenge: $scope.challenge.Challenge + }], [], function (data) { + $scope.deviceListening = false; + + console.log('call back data:'); + console.log(data); + + if (data.errorCode) { + $scope.deviceError = true; + $scope.$apply(); + console.log('error: ' + data.errorCode); + return; + } + + $scope.deviceResponse = JSON.stringify(data); + $scope.$apply(); + }); + }; + + $scope.submit = function () { + if ($scope.enabled) { + disable(); + return; + } + + update(); + }; + + function disable() { + if (!confirm('Are you sure you want to disable the U2F provider?')) { + return; + } + + $scope.submitPromise = apiService.twoFactor.disable({}, { + masterPasswordHash: _masterPasswordHash, + type: constants.twoFactorProvider.u2f + }, function (response) { + $analytics.eventTrack('Disabled Two-step U2F'); + toastr.success('U2F has been disabled.'); + $scope.enabled = response.Enabled; + $scope.close(); + }).$promise; + } + + function update() { + $scope.submitPromise = apiService.twoFactor.putU2f({}, { + deviceResponse: $scope.deviceResponse, + masterPasswordHash: _masterPasswordHash + }, function (response) { + $analytics.eventTrack('Enabled Two-step U2F'); + $scope.enabled = response.Enabled; + $scope.challenge = null; + $scope.deviceResponse = null; + $scope.deviceError = false; + }).$promise; + } + + $scope.close = function () { + $uibModalInstance.close($scope.enabled); + }; + }); diff --git a/src/app/settings/views/settingsTwoStepU2f.html b/src/app/settings/views/settingsTwoStepU2f.html new file mode 100644 index 0000000000..3c57b735f7 --- /dev/null +++ b/src/app/settings/views/settingsTwoStepU2f.html @@ -0,0 +1,76 @@ + +
+ + +
+
+ + +
diff --git a/src/index.html b/src/index.html index c93b7243bf..9d45d16fc2 100644 --- a/src/index.html +++ b/src/index.html @@ -83,6 +83,7 @@ + @@ -194,6 +195,7 @@ +