Create password page (#13792)

feature/default_network_editable
VSaric 3 years ago committed by GitHub
parent 2bc7aab402
commit bea907e437
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      app/_locales/en/messages.json
  2. 201
      lavamoat/build-system/policy.json
  3. 3
      package.json
  4. 28
      ui/components/ui/form-field/form-field.js
  5. 2
      ui/helpers/constants/zendesk-url.js
  6. 53
      ui/pages/onboarding-flow/create-password/create-password.js
  7. 12
      ui/pages/onboarding-flow/create-password/index.scss
  8. 5
      yarn.lock

@ -1983,7 +1983,7 @@
"message": "“$1” was successfully added!"
},
"newPassword": {
"message": "New password (min 8 chars)"
"message": "New password (8 characters min)"
},
"newToMetaMask": {
"message": "New to MetaMask?"
@ -2277,11 +2277,18 @@
"passwordSetupDetails": {
"message": "This password will unlock your MetaMask wallet only on this device. MetaMask can not recover this password."
},
"passwordStrength": {
"message": "Password strength: $1",
"description": "Return password strength to the user when user wants to create password."
},
"passwordStrengthDescription": {
"message": "A strong password can improve the security of your wallet should your device be stolen or compromised."
},
"passwordTermsWarning": {
"message": "I understand that MetaMask cannot recover this password for me. $1"
},
"passwordsDontMatch": {
"message": "Passwords Don't Match"
"message": "Passwords don't match"
},
"pastePrivateKey": {
"message": "Enter your private key string here:",
@ -2948,6 +2955,9 @@
"storePhrase": {
"message": "Store this phrase in a password manager like 1Password."
},
"strong": {
"message": "Strong"
},
"stxAreHere": {
"message": "Smart Transactions are here!"
},
@ -3772,6 +3782,9 @@
"walletCreationSuccessTitle": {
"message": "Wallet creation successful"
},
"weak": {
"message": "Weak"
},
"web3ShimUsageNotification": {
"message": "We noticed that the current website tried to use the removed window.web3 API. If the site appears to be broken, please click $1 for more information.",
"description": "$1 is a clickable link."

@ -1052,6 +1052,16 @@
"buffer-equal": true
}
},
"are-we-there-yet": {
"builtin": {
"events.EventEmitter": true,
"util.inherits": true
},
"packages": {
"delegates": true,
"readable-stream": true
}
},
"arr-diff": {
"packages": {
"arr-flatten": true,
@ -1460,6 +1470,7 @@
"anymatch": true,
"async-each": true,
"braces": true,
"fsevents": true,
"glob-parent": true,
"inherits": true,
"is-binary-path": true,
@ -1726,6 +1737,16 @@
"through2": true
}
},
"detect-libc": {
"builtin": {
"child_process.spawnSync": true,
"fs.readdirSync": true,
"os.platform": true
},
"globals": {
"process.env": true
}
},
"detective": {
"packages": {
"acorn-node": true,
@ -2429,6 +2450,45 @@
"process.version": true
}
},
"fsevents": {
"builtin": {
"events.EventEmitter": true,
"fs.stat": true,
"path.join": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"process.nextTick": true,
"process.platform": true,
"setImmediate": true
},
"native": true,
"packages": {
"node-pre-gyp": true
}
},
"gauge": {
"builtin": {
"util.format": true
},
"globals": {
"clearInterval": true,
"process": true,
"setImmediate": true,
"setInterval": true
},
"packages": {
"aproba": true,
"console-control-strings": true,
"has-unicode": true,
"object-assign": true,
"signal-exit": true,
"string-width": true,
"strip-ansi": true,
"wide-align": true
}
},
"get-assigned-identifiers": {
"builtin": {
"assert.equal": true
@ -2807,6 +2867,16 @@
"process.argv": true
}
},
"has-unicode": {
"builtin": {
"os.type": true
},
"globals": {
"process.env.LANG": true,
"process.env.LC_ALL": true,
"process.env.LC_CTYPE": true
}
},
"has-value": {
"packages": {
"get-value": true,
@ -2978,6 +3048,11 @@
"is-plain-object": true
}
},
"is-fullwidth-code-point": {
"packages": {
"number-is-nan": true
}
},
"is-glob": {
"packages": {
"is-extglob": true
@ -3508,6 +3583,56 @@
"setTimeout": true
}
},
"node-pre-gyp": {
"builtin": {
"events.EventEmitter": true,
"fs.existsSync": true,
"fs.readFileSync": true,
"fs.renameSync": true,
"path.dirname": true,
"path.existsSync": true,
"path.join": true,
"path.resolve": true,
"url.parse": true,
"url.resolve": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"console.log": true,
"process.arch": true,
"process.cwd": true,
"process.env": true,
"process.platform": true,
"process.version.substr": true,
"process.versions": true
},
"packages": {
"detect-libc": true,
"nopt": true,
"npmlog": true,
"rimraf": true,
"semver": true
}
},
"nopt": {
"builtin": {
"path": true,
"stream.Stream": true,
"url": true
},
"globals": {
"console": true,
"process.argv": true,
"process.env.DEBUG_NOPT": true,
"process.env.NOPT_DEBUG": true,
"process.platform": true
},
"packages": {
"abbrev": true,
"osenv": true
}
},
"normalize-package-data": {
"builtin": {
"url.parse": true,
@ -3535,6 +3660,22 @@
"once": true
}
},
"npmlog": {
"builtin": {
"events.EventEmitter": true,
"util": true
},
"globals": {
"process.nextTick": true,
"process.stderr": true
},
"packages": {
"are-we-there-yet": true,
"console-control-strings": true,
"gauge": true,
"set-blocking": true
}
},
"object-copy": {
"packages": {
"copy-descriptor": true,
@ -3616,6 +3757,54 @@
"readable-stream": true
}
},
"os-homedir": {
"builtin": {
"os.homedir": true
},
"globals": {
"process.env": true,
"process.getuid": true,
"process.platform": true
}
},
"os-tmpdir": {
"globals": {
"process.env.SystemRoot": true,
"process.env.TEMP": true,
"process.env.TMP": true,
"process.env.TMPDIR": true,
"process.env.windir": true,
"process.platform": true
}
},
"osenv": {
"builtin": {
"child_process.exec": true,
"path": true
},
"globals": {
"process.env.COMPUTERNAME": true,
"process.env.ComSpec": true,
"process.env.EDITOR": true,
"process.env.HOSTNAME": true,
"process.env.PATH": true,
"process.env.PROMPT": true,
"process.env.PS1": true,
"process.env.Path": true,
"process.env.SHELL": true,
"process.env.USER": true,
"process.env.USERDOMAIN": true,
"process.env.USERNAME": true,
"process.env.VISUAL": true,
"process.env.path": true,
"process.nextTick": true,
"process.platform": true
},
"packages": {
"os-homedir": true,
"os-tmpdir": true
}
},
"p-limit": {
"packages": {
"p-try": true
@ -4325,6 +4514,12 @@
"lru-cache": true
}
},
"set-blocking": {
"globals": {
"process.stderr": true,
"process.stdout": true
}
},
"set-value": {
"packages": {
"extend-shallow": true,
@ -4588,6 +4783,7 @@
},
"string-width": {
"packages": {
"code-point-at": true,
"emoji-regex": true,
"is-fullwidth-code-point": true,
"strip-ansi": true
@ -5240,6 +5436,11 @@
"isexe": true
}
},
"wide-align": {
"packages": {
"string-width": true
}
},
"write": {
"builtin": {
"fs.createWriteStream": true,

@ -221,7 +221,8 @@
"uuid": "^8.3.2",
"valid-url": "^1.0.9",
"web3": "^0.20.7",
"web3-stream-provider": "^4.0.0"
"web3-stream-provider": "^4.0.0",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@babel/code-frame": "^7.12.13",

@ -32,6 +32,8 @@ export default function FormField({
disabled,
placeholder,
warning,
passwordStrength,
passwordStrengthText,
}) {
return (
<div
@ -122,6 +124,24 @@ export default function FormField({
{warning}
</Typography>
)}
{passwordStrength && (
<Typography
color={COLORS.BLACK}
variant={TYPOGRAPHY.H7}
className="form-field__password-strength"
>
{passwordStrength}
</Typography>
)}
{passwordStrengthText && (
<Typography
color={COLORS.UI4}
variant={TYPOGRAPHY.H8}
className="form-field__password-strength-text"
>
{passwordStrengthText}
</Typography>
)}
</label>
</div>
);
@ -192,6 +212,14 @@ FormField.propTypes = {
* Set the placeholder text for the input field
*/
placeholder: PropTypes.string,
/**
* Show password strength according to the score
*/
passwordStrength: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/**
* Show password strength description
*/
passwordStrengthText: PropTypes.string,
};
FormField.defaultProps = {

@ -9,6 +9,8 @@ const ZENDESK_URLS = {
'https://metamask.zendesk.com/hc/en-us/articles/4403988839451',
SECRET_RECOVERY_PHRASE:
'https://metamask.zendesk.com/hc/en-us/articles/360060826432-What-is-a-Secret-Recovery-Phrase-and-how-to-keep-your-crypto-wallet-secure',
PASSWORD_ARTICLE:
'https://metamask.zendesk.com/hc/en-us/articles/4404722782107',
};
export default ZENDESK_URLS;

@ -1,6 +1,7 @@
import React, { useState, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import zxcvbn from 'zxcvbn';
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
import { useI18nContext } from '../../../hooks/useI18nContext';
import Button from '../../../components/ui/button';
@ -25,6 +26,7 @@ import {
TwoStepProgressBar,
twoStepStages,
} from '../../../components/app/step-progress-bar';
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
export default function CreatePassword({
createNewAccount,
@ -35,6 +37,8 @@ export default function CreatePassword({
const [confirmPassword, setConfirmPassword] = useState('');
const [password, setPassword] = useState('');
const [passwordError, setPasswordError] = useState('');
const [passwordStrength, setPasswordStrength] = useState('');
const [passwordStrengthText, setPasswordStrengthText] = useState('');
const [confirmPasswordError, setConfirmPasswordError] = useState('');
const [termsChecked, setTermsChecked] = useState(false);
const [showPassword, setShowPassword] = useState(false);
@ -57,19 +61,51 @@ export default function CreatePassword({
return !passwordError && !confirmPasswordError;
}, [password, confirmPassword, passwordError, confirmPasswordError]);
const getPasswordStrengthLabel = (score, translation) => {
if (score >= 4) {
return {
className: 'create-password__strong',
text: translation('strong'),
description: '',
};
} else if (score === 3) {
return {
className: 'create-password__average',
text: translation('average'),
description: t('passwordStrengthDescription'),
};
}
return {
className: 'create-password__weak',
text: translation('weak'),
description: t('passwordStrengthDescription'),
};
};
const handlePasswordChange = (passwordInput) => {
let error = '';
let confirmError = '';
if (passwordInput && passwordInput.length < 8) {
error = t('passwordNotLongEnough');
}
const passwordEvaluation = zxcvbn(passwordInput);
const passwordStrengthLabel = getPasswordStrengthLabel(
passwordEvaluation.score,
t,
);
const passwordStrengthDescription = passwordStrengthLabel.description;
const passwordStrengthInput = t('passwordStrength', [
<span
key={passwordEvaluation.score}
className={passwordStrengthLabel.className}
>
{passwordStrengthLabel.text}
</span>,
]);
if (confirmPassword && passwordInput !== confirmPassword) {
confirmError = t('passwordsDontMatch');
}
setPassword(passwordInput);
setPasswordError(error);
setPasswordStrength(passwordStrengthInput);
setPasswordStrengthText(passwordStrengthDescription);
setConfirmPasswordError(confirmError);
};
@ -133,7 +169,8 @@ export default function CreatePassword({
<FormField
dataTestId="create-password-new"
autoFocus
error={passwordError}
passwordStrength={passwordStrength}
passwordStrengthText={passwordStrengthText}
onChange={handlePasswordChange}
password={!showPassword}
titleText={t('newPassword')}
@ -181,12 +218,12 @@ export default function CreatePassword({
<a
onClick={(e) => e.stopPropagation()}
key="create-password__link-text"
href="https://metamask.io/terms.html"
href={ZENDESK_URLS.PASSWORD_ARTICLE}
target="_blank"
rel="noopener noreferrer"
>
<span className="create-password__link-text">
{t('learnMore')}
{t('learnMoreUpperCase')}
</span>
</a>,
])}

@ -1,4 +1,16 @@
.create-password {
&__weak {
color: var(--error-1);
}
&__average {
color: var(--secondary-3);;
}
&__strong {
color: var(--success-3);
}
&__wrapper {
display: flex;
justify-content: center;

@ -28349,3 +28349,8 @@ zwitch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==
zxcvbn@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"
integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=

Loading…
Cancel
Save