Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add password strength indicator #1154

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion i18n/locales/en/create-account.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
"mainnet": "My Account",
"testnet": "My Testnet Account"
},
"confirm": {
"confirm-no-password": {
"text": "You are about to create an account without password protection. Anyone that has access to your device will have access to your account funds. <1 /> <3 /> Are you sure you want to continue without setting up a password?",
"title": "Continue without password"
},
"confirm-weak-password": {
"text": "You are about to protect this account with a weak password. Anyone that has access to your device will have access to your account funds. <1 /> <3 /> Are you sure you want to continue without setting up a strong password?",
"title": "Continue without strong password"
},
"header": {
"placeholder": {
"mainnet": "New Account",
Expand Down
9 changes: 9 additions & 0 deletions i18n/locales/en/generic.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@
"timeout": "The server did not respond. Please try again."
}
},
"password-strength": {
"label": {
"0": "Very Weak",
"1": "Weak",
"2": "Fair",
"3": "Good",
"4": "Strong"
}
},
"qr-reader": {
"action": {
"cancel": "Cancel"
Expand Down
75 changes: 47 additions & 28 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
"lodash.pick": "^4.4.0",
"nanoid": "^2.1.1",
"react-hook-form": "^5.0.3",
"stellar-sdk": "^6.2.0"
"stellar-sdk": "^6.2.0",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@material-ui/core": "^4.5.1",
Expand Down Expand Up @@ -93,6 +94,7 @@
"@types/storybook__addon-actions": "^3.4.3",
"@types/storybook__react": "^3.0.9",
"@types/zen-observable": "^0.8.0",
"@types/zxcvbn": "^4.4.0",
"@typescript-eslint/parser": "^2.19.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.6",
Expand Down
32 changes: 26 additions & 6 deletions src/Account/components/AccountView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Dialog from "@material-ui/core/Dialog"
import AccountActions from "~Account/components/AccountActions"
import AccountCreationActions from "~AccountCreation/components/AccountCreationActions"
import NoPasswordConfirmation from "~AccountCreation/components/NoPasswordConfirmation"
import WeakPasswordConfirmation from "~AccountCreation/components/WeakPasswordConfirmation"
import { AccountCreation } from "~AccountCreation/types/types"
import useAccountCreation from "~AccountCreation/hooks/useAccountCreation"
import AccountHeaderCard from "~Account/components/AccountHeaderCard"
Expand Down Expand Up @@ -82,6 +83,7 @@ const AccountPageContent = React.memo(function AccountPageContent(props: Account
const { renameAccount } = React.useContext(AccountsContext)
const [accountToBackup, setAccountToBackup] = React.useState<Account | null>(null)
const [noPasswordDialogOpen, setNoPasswordDialogOpen] = React.useState(false)
const [weakPasswordDialogOpen, setWeakPasswordDialogOpen] = React.useState(false)

const showAccountCreation =
matchesRoute(router.location.pathname, routes.createAccount(props.testnet), false) ||
Expand Down Expand Up @@ -189,13 +191,24 @@ const AccountPageContent = React.memo(function AccountPageContent(props: Account
return
}

if (!accountCreation.requiresPassword && !props.testnet) {
if (accountCreation.requiresPassword && accountCreation.weakPassword) {
setWeakPasswordDialogOpen(true)
} else if (!accountCreation.requiresPassword && !props.testnet) {
setNoPasswordDialogOpen(true)
} else {
createNewAccount()
}
}, [accountCreation, createNewAccount, props.testnet, validateAccountCreation])

const closeWeakPasswordDialog = React.useCallback(() => {
setWeakPasswordDialogOpen(false)
}, [])

const confirmWeakPasswordDialog = React.useCallback(() => {
setWeakPasswordDialogOpen(false)
createNewAccount()
}, [createNewAccount])

const closeNoPasswordDialog = React.useCallback(() => {
setNoPasswordDialogOpen(false)
}, [])
Expand Down Expand Up @@ -422,11 +435,18 @@ const AccountPageContent = React.memo(function AccountPageContent(props: Account
</>
) : null}
{props.account ? null : (
<NoPasswordConfirmation
onClose={closeNoPasswordDialog}
onConfirm={confirmNoPasswordDialog}
open={noPasswordDialogOpen}
/>
<>
<NoPasswordConfirmation
onClose={closeNoPasswordDialog}
onConfirm={confirmNoPasswordDialog}
open={noPasswordDialogOpen}
/>
<WeakPasswordConfirmation
onClose={closeWeakPasswordDialog}
onConfirm={confirmWeakPasswordDialog}
open={weakPasswordDialogOpen}
/>
</>
)}
</VerticalLayout>
)
Expand Down
8 changes: 8 additions & 0 deletions src/AccountCreation/components/NewAccountSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ function NewAccountSettings(props: NewAccountSettingsProps) {
[onUpdateAccountCreation]
)

const updatePasswordStrength = React.useCallback(
(weakPassword: boolean) => {
onUpdateAccountCreation({ weakPassword })
},
[onUpdateAccountCreation]
)

return (
<List style={{ padding: isSmallScreen ? 0 : "24px 16px" }}>
{props.accountCreation.import ? (
Expand All @@ -55,6 +62,7 @@ function NewAccountSettings(props: NewAccountSettingsProps) {
onEnterPassword={updatePassword}
onRepeatPassword={updateRepeatedPassword}
onTogglePassword={togglePasswordProtection}
onPasswordStrengthChange={updatePasswordStrength}
repeatedPassword={props.accountCreation.repeatedPassword}
requiresPassword={props.accountCreation.requiresPassword}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/AccountCreation/components/NoPasswordConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ function NoPasswordConfirmation(props: NoPasswordConfirmationProps) {
}
onClose={props.onClose}
open={props.open}
title={t("create-account.confirm.title")}
title={t("create-account.confirm-no-password.title")}
>
<Trans i18nKey="create-account.confirm.text">
<Trans i18nKey="create-account.confirm-no-password.text">
You are about to create an account without password protection. Anyone that has access to your device will have
access to your account funds. <br /> <br /> Are you sure you want to continue without setting up a password?
</Trans>
Expand Down
21 changes: 20 additions & 1 deletion src/AccountCreation/components/PasswordSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,38 @@ import ListItemText from "@material-ui/core/ListItemText"
import Switch from "@material-ui/core/Switch"
import AccountSettingsItem from "~AccountSettings/components/AccountSettingsItem"
import PasswordField from "~Generic/components/PasswordField"
import ViewLoading from "~Generic/components/ViewLoading"
import withFallback from "~Generic/hocs/withFallback"

// lazy load because `zxcvbn` bundle size is big
const PasswordStrengthTextField = withFallback(
React.lazy(() => import("~Generic/components/PasswordStrengthTextField")),
<ViewLoading style={{ justifyContent: "flex-end" }} />
)

interface PasswordSettingProps {
error?: string
password: string
onEnterPassword: (password: string) => void
onRepeatPassword: (password: string) => void
onTogglePassword: () => void
onPasswordStrengthChange: (weak: boolean) => void
repeatedPassword: string
requiresPassword: boolean
}

function PasswordSetting(props: PasswordSettingProps) {
const { onPasswordStrengthChange } = props
const { t } = useTranslation()

const onScoreChange = React.useCallback(
(score: number) => {
const weak = score < 3 ? true : false
onPasswordStrengthChange(weak)
},
[onPasswordStrengthChange]
)

return (
<>
<AccountSettingsItem
Expand All @@ -39,12 +57,13 @@ function PasswordSetting(props: PasswordSettingProps) {
<Collapse in={props.requiresPassword}>
<AccountSettingsItem icon={null} subItem>
<ListItemText style={{ marginLeft: 12, marginRight: 56, marginTop: -8 }}>
<PasswordField
<PasswordStrengthTextField
error={Boolean(props.error)}
fullWidth
label={t("create-account.inputs.password.label")}
margin="normal"
onChange={event => props.onEnterPassword(event.target.value)}
onScoreChange={onScoreChange}
placeholder={t("create-account.inputs.password.placeholder")}
value={props.password}
/>
Expand Down
34 changes: 34 additions & 0 deletions src/AccountCreation/components/WeakPasswordConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react"
import { useTranslation, Trans } from "react-i18next"
import { ActionButton, ConfirmDialog } from "~Generic/components/DialogActions"

interface WeakPasswordConfirmationProps {
onClose: () => void
onConfirm: () => void
open: boolean
}

function WeakPasswordConfirmation(props: WeakPasswordConfirmationProps) {
const { t } = useTranslation()
return (
<ConfirmDialog
cancelButton={<ActionButton onClick={props.onClose}>{t("create-account.action.cancel")}</ActionButton>}
confirmButton={
<ActionButton onClick={props.onConfirm} type="primary">
{t("create-account.action.confirm")}
</ActionButton>
}
onClose={props.onClose}
open={props.open}
title={t("create-account.confirm-weak-password.title")}
>
<Trans i18nKey="create-account.confirm-weak-password.text">
You are about to protect this account with a weak password. Anyone that has access to your device will have
access to your account funds. <br /> <br /> Are you sure you want to continue without setting up a strong
password?
</Trans>
</ConfirmDialog>
)
}

export default React.memo(WeakPasswordConfirmation)
1 change: 1 addition & 0 deletions src/AccountCreation/hooks/useAccountCreation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function useAccountCreation(options: UseAccountCreationOptions) {
password: "",
repeatedPassword: "",
requiresPassword: true,
weakPassword: true,
testnet: options.testnet
}))

Expand Down
1 change: 1 addition & 0 deletions src/AccountCreation/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface AccountCreation {
requiresPassword: boolean
secretKey?: string
testnet: boolean
weakPassword: boolean
}

export interface AccountCreationErrors {
Expand Down
Loading