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

Commit

Permalink
feat(Browser): improve chekcs and display browser error message in form
Browse files Browse the repository at this point in the history
Moved browser detection to a directive. The message no longer
disappears after a few seconds. It's displayed right inside the login /
signup / recovery form.

You can test this by using an old browser or by increasing the required
version.
  • Loading branch information
Sjors committed Jun 7, 2016
1 parent ae219f6 commit 48d8b36
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 121 deletions.
8 changes: 5 additions & 3 deletions app/partials/login.jade
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ div
p.em-300(translate="LOGIN_BELOW")
//GUID & Password fields
form.ptl.form-horizontal.clearfix(role="form" autocomplete="off" novalidate)
browser-detection(result="browser")
.form-group(ng-class="{'has-error': errors.uid}")
label.em-500(translate="UID", for="UID_input")
div
input#UID_input.form-control(type="text", ng-model="uid", ng-disabled="busy || disableLogin" name="UID_input" focus-when="!uidAvailable")
input#UID_input.form-control(type="text", ng-model="uid", ng-disabled="busy || browser.disabled" name="UID_input" focus-when="!uidAvailable")

span.help-block(ng-show="errors.uid" ui-sref="public.reminder" translate="{{ errors.uid }}")
span.help-block.info.hidden-xs(translate="FIND_GUID_EXPLAIN")
.form-group(ng-class="{'has-error': errors.password}")
label.em-500(translate="PASSWORD", for="pass_input")
div
input#pass_input.form-control(type="password", name="pass_input" ng-model="password", ng-disabled="busy || disableLogin" focus-when="uidAvailable")
input#pass_input.form-control(type="password", name="pass_input" ng-model="password", ng-disabled="busy || browser.disabled" focus-when="uidAvailable")
span.help-block {{ errors.password }}
.form-group(ng-show="settings.needs2FA", ng-class="{'has-error': errors.twoFactor || settings.twoFactorMethod == 3}")
label.em-500
Expand All @@ -36,7 +38,7 @@ div
img(ng-show="resending" src="img/spinner.gif")
a(ng-click="resend()", ng-show="!resending", translate="RESEND")
.mtl.flex-center.flex-end
button.button-primary(ui-ladda="busy", ng-click="login()", data-style="expand-left", ladda-translate="SIGNIN", ng-disabled="!isValid")
button.button-primary(ui-ladda="busy", ng-click="login()", data-style="expand-left", ladda-translate="SIGNIN", ng-disabled="!isValid || browser.disabled")
//Wallet Recovery
.ptl.flex-row.flex-between
a.em-300(href="{{rootURL}}wallet-legacy/login" translate="LOGIN_TO_LEGACY")
Expand Down
5 changes: 3 additions & 2 deletions app/partials/recover-funds.jade
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
p(translate="RECOVER_BTC_LOST")
form.ptl.form-horizontal(name="passphraseForm" autocomplete="off" ng-switch-when="1" novalidate)
div
browser-detection(result="browser")
.security-red.mbl.em-400.flex-center
i.ti-hand-stop.mrm.h3.mvn.hidden-xs
span(translate="RECOVER_FUNDS_WARNING")
Expand All @@ -19,7 +20,7 @@
ng-keyup="getMnemonicLength()"
ng-model="fields.mnemonic"
is-valid="isValidMnemonic(fields.mnemonic)"
ng-disabled="disableSignup"
ng-disabled="browser.disabled"
required)
.flex-center.flex-end
img(ng-show="working" src="img/spinner.gif")
Expand All @@ -31,7 +32,7 @@
button.button-primary(
type="submit"
ng-click="nextStep()"
ng-disabled="!passphraseForm.$valid || disableSignup"
ng-disabled="!passphraseForm.$valid || browser.disabled"
translate="CONTINUE")
form.ptl.form-horizontal(name="recoveryForm" ng-switch-when="2")
div
Expand Down
11 changes: 6 additions & 5 deletions app/partials/signup.jade
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ div
autocomplete="off"
novalidate)
.form-group(ng-class="{'has-error': signupForm.email.$invalid && signupForm.email.$touched}")
browser-detection(result="browser")
label.col-sm-4.control-label(translate="EMAIL")
.col-sm-8
input.form-control(
name="email"
type="email"
ng-model="fields.email"
required
ng-disabled="disableSignup"
ng-disabled="browser.disabled"
autofocus)
span.help-block
div(ng-show="signupForm.email.$touched")
Expand All @@ -32,7 +33,7 @@ div
min-entropy="25"
ng-change="fields.confirmation = ''"
focus-when="fields.email.length"
ng-disabled="disableSignup"
ng-disabled="browser.disabled"
required)
password-entropy.help-block(password="fields.password")
span.help-block
Expand All @@ -47,7 +48,7 @@ div
type="password"
ng-model="fields.confirmation"
is-valid="fields.confirmation == fields.password"
ng-disabled="disableSignup"
ng-disabled="browser.disabled"
required)
span.help-block
div(ng-show="signupForm.confirmation.$touched")
Expand All @@ -58,15 +59,15 @@ div
name="agreement"
type="checkbox"
ng-model="fields.acceptedAgreement"
ng-disabled="disableSignup"
ng-disabled="browser.disabled"
required)
label.em-300.col-sm-8.accept-label.right-align
| I have read and agree to the
a(href="https://blockchain.info/Resources/TermsofServicePolicy.pdf", translate="TERMS_OF_SERVICE", target="_blank")
.flex-center.flex-end.mbl.prl
button.button-primary(
type="submit"
ng-disabled="signupForm.$invalid || disableSignup"
ng-disabled="signupForm.$invalid || browser.disabled"
translate="CONTINUE"
ng-show="!working")
img(ng-show="working" src="img/spinner.gif")
2 changes: 1 addition & 1 deletion assets/js/controllers/login.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function LoginCtrl ($scope, $rootScope, $location, $log, $http, Wallet, WalletNe

$scope.user = Wallet.user;

$scope.disableLogin = !Wallet.checkBrowserVersion();
$scope.browser = {disabled: true};

$scope.twoFactorCode = '';
$scope.busy = false;
Expand Down
2 changes: 1 addition & 1 deletion assets/js/controllers/recoverFunds.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function RecoverFundsCtrl ($scope, $rootScope, $state, $timeout, $translate, $co
bip39phrase: ''
};

$scope.disableSignup = !Wallet.checkBrowserVersion();
$scope.browser = {disabled: true};

$scope.performImport = () => {
$scope.working = true;
Expand Down
2 changes: 1 addition & 1 deletion assets/js/controllers/signup.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function SignupCtrl ($scope, $state, $cookies, $filter, $translate, $uibModal, W
$scope.alerts = Alerts.alerts;
$scope.status = Wallet.status;

$scope.disableSignup = !Wallet.checkBrowserVersion();
$scope.browser = {disabled: true};

$scope.$watch('status.isLoggedIn', newValue => {
if (newValue) {
Expand Down
141 changes: 141 additions & 0 deletions assets/js/directives/browserDetection.directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

angular
.module('walletApp')
.directive('browserDetection', directive);

// Browser compatibility warnings:
// * Secure random number generator: https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues
// * AngularJS support (?)
// * No support before Safari 6 and equivalents based on experience

const browsers = {
'ie': {
name: 'Internet Explorer',
requiredVersion: 11
},
'chrome': {
name: 'Chrome',
requiredVersion: 30 // Roughly the same as Webkit 537.85.17
},
'firefox': {
name: 'Firefox',
requiredVersion: 21
},
'opera': {
name: 'Opera',
requiredVersion: 20 // First Chromium (33) based release
}
};

directive.$inject = ['$q', '$translate', 'MyWallet'];

function directive ($q, $translate, MyWallet) {
const directive = {
restrict: 'E',
replace: true,
scope: {
result: '=' // Should initially be {disabled: true}
},
template: `
<div class="browser-detection">
<p class="text-warning" ng-show="result.warn">{{ result.msg }}</p>
<p class="text-danger" ng-show="result.disabled">{{ result.msg }}</p>
</div>
`,
link: link
};
return directive;

function link (scope, elem, attrs) {
// Unknown browser, check if it can handle the math required:
const checkUnknownBrowser = (defer) => {
if (!MyWallet.browserCheckFast()) {
$translate('UNSUITABLE_BROWSER').then((translation) => {
defer.reject({msg: translation});
});
} else {
$translate('UNKNOWN_BROWSER').then((translation) => {
defer.resolve({warn: true, msg: translation});
});
}
};

scope.getUserAgent = () => navigator.userAgent;

const checkBrowserVersion = () => {
let defer = $q.defer();

let info = browserDetection();
let matchingBrowser = browsers[info.browser.toLowerCase()];
if (matchingBrowser) { // One of the known browsers listed above
let requiredVersion = matchingBrowser.requiredVersion;
if (info.version < requiredVersion) {
$translate('MINIMUM_BROWSER', {browser: matchingBrowser.name, userVersion: info.version, requiredVersion: requiredVersion}).then((translation) => {
defer.reject({msg: translation});
});
} else if (info.browser === 'ie') {
$translate('WARN_AGAINST_IE').then((translation) => {
defer.resolve({warn: true, msg: translation});
});
} else {
defer.resolve({});
}
} else if (info.webkit) {
// 537.36 is the version referenced by modern Chrome, slightly lower
// might still be acceptable.
let minimumWebkitVersion = '537.36';
// Patch field may be undefined:
let userVersion = [
info.webkit.major,
info.webkit.minor,
info.webkit.patch
].filter((x) => typeof (x) === 'number').join('.');
if (compareVersions(userVersion, minimumWebkitVersion) === -1) {
// Webkit version too old.
if (info.browser === 'safari') {
let safariVersion = 'version';
const safari = scope.getUserAgent().match(/version\/(\d+\.\d+)/i);
if (safari && safari.length === 2) {
safariVersion = safari[1];
}

// The last patch version of Safari 5 (5.1.4 - webkit 534.54.16) did
// work when last checked, but 5.1.2. does not. We increased the
// minimum Safari 6.1 to be on the safe side.
$translate('MINIMUM_BROWSER', {browser: 'Safari', userVersion: safariVersion, requiredVersion: '6.1'}).then((translation) => {
defer.reject({msg: translation});
});
} else {
$translate('MINIMUM_BROWSER', {browser: 'Webkit', userVersion: userVersion, requiredVersion: minimumWebkitVersion}).then((translation) => {
defer.reject({msg: translation});
});
}
} else {
if (info.browser === 'safari') {
defer.resolve({});
} else {
// Unknown webkit browser:
checkUnknownBrowser(defer);
}
}
} else {
// Unknown non-webkit browser:
checkUnknownBrowser(defer);
}
return defer.promise;
};

scope.performCheck = () => {
checkBrowserVersion().then((res) => {
scope.result.disabled = false;
scope.result.warn = res.warn;
scope.result.msg = res.msg;
}).catch((res) => {
scope.result.disabled = true;
scope.result.msg = res.msg;
});
};

scope.performCheck();
}
}
62 changes: 0 additions & 62 deletions assets/js/services/wallet.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -1159,68 +1159,6 @@ function Wallet ($http, $window, $timeout, $location, Alerts, MyWallet, MyBlockc
wallet.my.wallet.encrypt(password, success, error, encrypting, syncing);
};

// Browser compatibility warnings:
// * Secure random number generator: https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues
// * AngularJS support (?)
// * No support before Safari 6 and equivalents based on experience

const browsers = {
'ie': {
browser: 'Internet Explorer',
requiredVersion: 11
},
'chrome': {
browser: 'Chrome',
requiredVersion: 30 // Roughly the same as Webkit 537.85.17
},
'firefox': {
browser: 'Firefox',
requiredVersion: 21
},
'opera': {
browser: 'Opera',
requiredVersion: 20 // First Chromium (33) based release
}
};
wallet.checkBrowserVersion = () => {
let info = browserDetection();
let matchingBrowser = browsers[info.browser.toLowerCase()];
if (matchingBrowser) { // One of the known browsers listed above
let requiredVersion = matchingBrowser.requiredVersion;
if (info.version < requiredVersion) {
$translate('MINIMUM_BROWSER', {browser: info.browser, userVersion: info.version, requiredVersion: requiredVersion}).then(Alerts.displayError);
return false;
} else if (info.browser === 'ie') {
Alerts.displayWarning('WARN_AGAINST_IE');
return true;
} else {
return true;
}
} else if (info.webkit) {
let minimumWebkitVersion = '537.85.17';
// Patch field may be undefined:
let userVersion = [
info.webkit.major,
info.webkit.minor,
info.webkit.patch
].filter((x) => typeof (x) === 'number').join('.');
if (compareVersions(userVersion, minimumWebkitVersion) === -1) {
// Webkit version too old.
if (info.browser === 'safari') {
$translate('MINIMUM_BROWSER', {browser: 'Safari', userVersion: 'version', requiredVersion: '6.2.8'}).then(Alerts.displayError);
} else {
$translate('MINIMUM_BROWSER', {browser: 'Webkit', userVersion: info.version, requiredVersion: minimumWebkitVersion}).then(Alerts.displayError);
}
return false;
} else {
return true;
}
} else {
Alerts.displayWarning('UNKNOWN_BROWSER');
return true;
}
};

// Testing: only works on mock MyWallet

wallet.refresh = () => {
Expand Down
1 change: 1 addition & 0 deletions locales/en-human.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
"WARN_AGAINST_IE" : "We recommend that you login using a more secure browser like Chrome or Safari.",
"MINIMUM_BROWSER" : "{{ browser }} {{ userVersion }} is not supported. Please install {{ browser }} {{ requiredVersion }} or newer.",
"UNKNOWN_BROWSER" : "Be careful when using a browser other than Chrome, Firefox or Safari. Make sure it supports secure random number generation and offers strong protection against cross site scripting attacks or you may lose your bitcoins.",
"UNSUITABLE_BROWSER" : "Your browser is not supported",
"SPONSORS" : "Sponsored Links",
"BITCOIN_CURRENCY" : "Bitcoin Unit",
"BITCOIN_CURRENCY_EXPLAIN": "Adjust the precision you would prefer bitcoin values to be displayed in.",
Expand Down
Loading

0 comments on commit 48d8b36

Please sign in to comment.