Skip to content
This repository has been archived by the owner on Oct 8, 2019. It is now read-only.

Commit

Permalink
feat(Reset2FA): form to reset two step verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Sjors committed Jan 5, 2016
1 parent d3e36b8 commit 18ba461
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 15 deletions.
1 change: 1 addition & 0 deletions app/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ head
script(src='build/js/controllers/modalNotification.controller.js')
script(src='build/js/controllers/recoverFunds.controller.js')
script(src='build/js/controllers/lostGuid.controller.js')
script(src='build/js/controllers/ResetTwoFactor.controller.js')
script(src='build/js/controllers/feedback.controller.js')
script(src='build/js/controllers/walletNavigation.controller.js')
script(src='build/js/controllers/verifyEmail.controller.js')
Expand Down
2 changes: 1 addition & 1 deletion app/partials/help.jade
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ div
h5.blue.em-300(translate="LOST_2FA")
p.em-400(translate="LOST_2FA_DESC")
.flex-1.flex-end
a.button-default.button-nowrap(translate="CONTACT_SUPPORT" href="https://blockchain.zendesk.com/anonymous_requests/new" target="_blank")
a.button-default.button-nowrap(translate="RESET_2FA" ui-sref="public.reset-two-factor")
.flex-end
a.button-muted.mtm(ui-sref="public.login-no-uid")
span(translate="GO_BACK")
80 changes: 80 additions & 0 deletions app/partials/reset-two-factor.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.pos-rel(ng-switch="currentStep")
header.flex-center.flex-between
.flex-column
h2.em-300.mtn(translate="RESET_2FA")
p(translate="RESET_2FA_HELP_1", ng-switch-when="1")
p(translate="RESET_2FA_HELP_2", ng-switch-when="1")
form.ptl.form-horizontal(name="form" autocomplete="off" novalidate)
div(ng-switch-when="1")
.form-group(ng-class="{'has-error': form.uid.$invalid && form.uid.$touched}")
label(translate="RESET_2FA_WALLET_IDENTIFIER")
input.form-control(
name="uid"
ng-model="fields.uid"
required)
p.help-block
span(translate="RESET_2FA_WALLET_IDENTIFIER_EXPLAIN")
span
a(ui-sref="public.reminder", translate="LOOK_IT_UP_HERE")
span .
.form-group(ng-class="{'has-error': form.email.$invalid && form.email.$touched}")
label(translate="RESET_2FA_REGISTERED_EMAIL")
input.form-control(
type="email"
name="email"
ng-model="fields.email"
placeholder="{{ 'OPTIONAL' | translate }}")
p.help-block(translate="RESET_2FA_REGISTERED_EMAIL_EXPLAIN")
.form-group(ng-class="{'has-error': form.newEmail.$invalid && form.newEmail.$touched}")
label(translate="RESET_2FA_NEW_EMAIL")
input.form-control(
type="email"
name="newEmail"
ng-model="fields.newEmail"
placeholder="{{ 'OPTIONAL' | translate }}")
p.help-block(translate="RESET_2FA_NEW_EMAIL_EXPLAIN")
.form-group(ng-class="{'has-error': form.secret.$invalid && form.secret.$touched}")
label(translate="RESET_2FA_SECRET_PHRASE")
input.form-control(
name="secret"
ng-model="fields.secret"
placeholder="{{ 'OPTIONAL' | translate }}")
p.help-block(translate="RESET_2FA_SECRET_PHRASE_EXPLAIN")
.form-group(ng-class="{'has-error': form.message.$invalid && form.message.$touched}")
label(translate="RESET_2FA_MESSAGE")
textarea.form-control(
name="message"
ng-model="fields.message"
placeholder="{{ 'OPTIONAL' | translate }}")
p.help-block(translate="RESET_2FA_MESSAGE_EXPLAIN")
.form-group(ng-class="{'has-error': form.captcha.$invalid && form.captcha.$touched}")
label(translate="CAPTCHA")
p(translate="CAPTCHA_EXPAIN")
img.mbl(ng-src="{{captchaSrc}}")
input.form-control(
type="text"
name="captcha"
ng-model="fields.captcha"
required)
.flex-center.flex-end.mvl(ng-switch-when="1")
img(ng-show="working" src="img/spinner.gif")
button.button-muted.mrm(
type="button"
ng-disabled="working"
ui-sref="public.help"
translate="GO_BACK")
button.button-success(
type="submit"
ng-click="resetTwoFactor()"
ng-disabled="!form.$valid || working"
translate="CONTINUE")
.flex-center.flex-justify.flex-column(ng-switch-when="2")
.level-complete.flex-center.flex-justify
i.ti-check.bright-green
h4.em-300.mtl(translate="SUCCESS")
p.em-300(translate="RESET_2FA_SUCCESS")
.flex-end
button.button-muted.mrm(
type="button"
ui-sref="public.login-no-uid"
translate="CONTINUE_TO_LOGIN")
5 changes: 3 additions & 2 deletions assets/js/controllers/app.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ angular
.module('walletApp')
.controller("AppCtrl", AppCtrl);

function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout, $uibModal, $window, $translate) {
function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $cookies, $location, $timeout, $uibModal, $window, $translate) {
$scope.status = Wallet.status;
$scope.settings = Wallet.settings;
$rootScope.isMock = Wallet.isMock;
$rootScope.loginFormUID = $cookies.get("uid"); // Last entered in login form
$scope.goal = Wallet.goal;
$scope.menu = {
isCollapsed: false
Expand Down Expand Up @@ -59,7 +60,7 @@ function AppCtrl($scope, Wallet, Alerts, $state, $rootScope, $location, $timeout
};

$scope.$on('$stateChangeSuccess', (event, toState, toParams, fromState, fromParams) => {
let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve'];
let loggedOutStates = ['public', 'public.login-no-uid', 'public.login-uid', 'public.reset-two-factor', 'public.recover', 'public.reminder', 'public.signup', 'public.help', 'open', 'wallet.common.verify-email', 'wallet.common.unsubscribe', 'public.authorize-approve'];
if (loggedOutStates.every(s => toState.name !== s) && $scope.status.isLoggedIn === false) {
$state.go("public.login-no-uid");
}
Expand Down
17 changes: 6 additions & 11 deletions assets/js/controllers/login.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u
$scope.status = Wallet.status;
$scope.settings = Wallet.settings;
$scope.disableLogin = null;
$scope.status.enterkey = false;
$scope.key = $cookies.get("key");
$scope.errors = {
uid: null,
password: null,
twoFactor: null
};
$scope.uidAvailable = $cookies.get('uid') != null || $stateParams.uid;

$scope.uid = $stateParams.uid || Wallet.guid || $rootScope.loginFormUID;

$scope.uidAvailable = !!$scope.uid

$scope.user = Wallet.user;

// Browser compatibility warnings:
Expand Down Expand Up @@ -85,14 +87,6 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u
Alerts.displayWarning(translation, true);
});
}
if (Wallet.guid != null) {
$scope.uid = Wallet.guid;
} else {
$scope.uid = $stateParams.uid || $cookies.get("uid");
}
if ($scope.key != null) {
$scope.status.enterkey = true;
}
$scope.twoFactorCode = "";
$scope.busy = false;
$scope.isValid = false;
Expand Down Expand Up @@ -163,6 +157,7 @@ function LoginCtrl($scope, $rootScope, $log, $http, Wallet, Alerts, $cookies, $u
});

$scope.$watch("uid + password + twoFactor", () => {
$rootScope.loginFormUID = $scope.uid;
let isValid = null;
$scope.errors.uid = null;
$scope.errors.password = null;
Expand Down
54 changes: 54 additions & 0 deletions assets/js/controllers/resetTwoFactor.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
angular
.module('walletApp')
.controller('ResetTwoFactorCtrl', ResetTwoFactorCtrl);

function ResetTwoFactorCtrl($scope, $rootScope, $http, $translate, Wallet, Alerts) {

$scope.currentStep = 1;
$scope.fields = {
uid: $rootScope.loginFormUID,
email: '',
newEmail: '',
secret: '',
message: '',
captcha: ''
};

$scope.refreshCaptcha = () => {
let time = new Date().getTime();
$scope.captchaSrc = $rootScope.rootURL + `kaptcha.jpg?timestamp=${time}`;
$scope.fields.captcha = '';
};

$scope.resetTwoFactor = () => {
$scope.working = true;
let success = (res) => {
$scope.working = false;
$scope.currentStep = 2;
};
let error = (e) => {
$scope.working = false;
$scope.refreshCaptcha();
};

$scope.form.$setPristine();
$scope.form.$setUntouched();

Wallet.requestTwoFactorReset(
$scope.fields.uid,
$scope.fields.email,
$scope.fields.newEmail,
$scope.fields.secret,
$scope.fields.message,
$scope.fields.captcha,
success,
error)
};

// Set SID cookie by requesting headers
$http({
url: $rootScope.rootURL + 'wallet/login',
method: 'HEAD',
withCredentials: true
}).then($scope.refreshCaptcha);
}
10 changes: 10 additions & 0 deletions assets/js/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ function AppRouter($stateProvider, $urlRouterProvider) {
}
}
})
.state('public.reset-two-factor', {
url: '/reset-2fa',
views: {
alerts: commonViews.alerts,
contents: {
templateUrl: 'partials/reset-two-factor.jade',
controller: 'ResetTwoFactorCtrl'
}
}
})
.state('public.authorize-approve', {
url: '/authorize-approve/{token:.*}',
views: {
Expand Down
26 changes: 26 additions & 0 deletions assets/js/services/wallet.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,32 @@ function Wallet($http, $window, $timeout, Alerts, MyWallet, MyBlockchainApi, MyB
};
wallet.my.recoverGuid(email, captcha, success, error);
};

wallet.requestTwoFactorReset = (guid, email, new_email, secret, message, captcha, successCallback, errorCallback) => {
Alerts.clear()
let success = (message) => {
Alerts.displaySuccess(message);
successCallback();
wallet.applyIfNeeded();
};
let error = (error) => {
switch (error) {
case 'Captcha Code Incorrect':
Alerts.displayError($translate.instant('CAPTCHA_INCORRECT'), true);
break;
case 'Quota Exceeded':
Alerts.displayError($translate.instant('QUOTA_EXCEEDED'), true);
break;
default:
Alerts.displayError(error, true);
}

errorCallback();
wallet.applyIfNeeded();
};
wallet.my.requestTwoFactorReset(guid, email, new_email, secret, message, captcha, success, error);
};

wallet.create = (password, email, currency, language, success_callback) => {
let success = (uid) => {
Alerts.displaySuccess('Wallet created with identifier: ' + uid, true);
Expand Down
19 changes: 18 additions & 1 deletion locales/en-human.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,13 +475,30 @@
"LOST_2FA": "I've lost my 2FA Device",
"LOST_ID_DESC": "Email me a reminder with my Wallet ID to my verified email address",
"LOST_PWD_DESC": "Recover your funds with your 12 word recovery passphrase",
"LOST_2FA_DESC": "Contact support to help regain access to your wallet",
"RESET_2FA" : "Reset 2FA",
"REMIND_ME": "Remind Me",
"REMIND_ME_HELP": "Lost your Wallet Identifier? We'll send it to you via your verified email.",
"REMIND_SUCCESS": "You will receive an email containing your Wallet Identifier soon.",
"CAPTCHA": "Captcha",
"CAPTCHA_EXPAIN": "So that we know you're not a robot",
"CAPTCHA_INCORRECT": "The captcha you provided was incorrect, please try again",
"LOST_2FA_DESC": "Reset two step verification to regain access to your wallet",
"RESET_2FA_HELP_1" : "Are you unable to gain access to your wallet because you lost your two factor authentication (2FA) device or are unable to access your email account?",
"RESET_2FA_HELP_2" : "Please submit the form below and check your email for further instructions. 2FA reset requests are automatically approved after a certain time. The process will be quicker with more precise details provided to us. Your IP address and browser information will be recorded on submission.",
"RESET_2FA_WALLET_IDENTIFIER" : "Wallet Identifier",
"RESET_2FA_WALLET_IDENTIFIER_EXPLAIN" : "If you forgot your wallet identifier (36 characters long containing 4 dashes), please",
"RESET_2FA_REGISTERED_EMAIL" : "Registered Email",
"RESET_2FA_REGISTERED_EMAIL_EXPLAIN" : "Enter the email associated with your wallet. If you lost access to this email, please enter it regardless.",
"RESET_2FA_NEW_EMAIL" : "New Email",
"RESET_2FA_NEW_EMAIL_EXPLAIN" : "If you lost access to the email associated with your wallet, enter a new email. If the 2FA reset request is approved, this email will automatically be set as your new wallet email.",
"RESET_2FA_SECRET_PHRASE" : "Secret Phrase",
"RESET_2FA_SECRET_PHRASE_EXPLAIN" : "Enter your wallet Secret Phrase here if you have one set. If the Secret Phrase is correct, your request will be approved much quicker. If you don't know what this is, leave it blank.",
"RESET_2FA_MESSAGE" : "Message",
"RESET_2FA_MESSAGE_EXPLAIN" : "Enter a message for Blockchain.info admins to review.",
"OPTIONAL" : "Optional",
"LOOK_IT_UP_HERE" : "look it up here",
"RESET_2FA_SUCCESS" : "You will receive an email with further instructions soon.",
"NEW_EMAIL" : "Registered Email",
"QUOTA_EXCEEDED": "You have reached the maximum number of allowed attempts, please try again later",
"UNKNOWN_ERROR": "An unknown error occurred, please try again later",
"CONTINUE_TO_LOGIN": "Continue to Login",
Expand Down

0 comments on commit 18ba461

Please sign in to comment.