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

WAL-110 - Lock / Unlock components and workflows for encrypting accessing local storage private key #2527

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"react-google-recaptcha": "^2.1.0",
"react-google-recaptcha-v3-near": "^2.0.0",
"react-helmet": "^6.1.0",
"react-idle-timer": "^4.6.4",
"react-localize-redux": "^3.5.3",
"react-phone-number-input": "2.3.11",
"react-redux": "^7.2.4",
Expand Down
44 changes: 40 additions & 4 deletions packages/frontend/src/components/Routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { stringify } from 'query-string';
import React, { Component } from 'react';
import ReactDOMServer from 'react-dom/server';
import IdleTimer from 'react-idle-timer';
import { withLocalize } from 'react-localize-redux';
import { connect } from 'react-redux';
import { Redirect, Switch } from 'react-router-dom';
Expand All @@ -19,12 +20,15 @@ import * as accountActions from '../redux/actions/account';
import { handleClearAlert } from '../redux/reducers/status';
import { selectAccountSlice } from '../redux/slices/account';
import { actions as tokenFiatValueActions } from '../redux/slices/tokenFiatValues';
import ChangePassword from '../routes/ChangePassword';
import { CreateImplicitAccountWrapper } from '../routes/CreateImplicitAccountWrapper';
import { LoginWrapper } from '../routes/LoginWrapper';
import SetPassword from '../routes/SetPassword';
import { SetupLedgerNewAccountWrapper } from '../routes/SetupLedgerNewAccountWrapper';
import { SetupPassphraseNewAccountWrapper } from '../routes/SetupPassphraseNewAccountWrapper';
import { SetupRecoveryImplicitAccountWrapper } from '../routes/SetupRecoveryImplicitAccountWrapper';
import { SignWrapper } from '../routes/SignWrapper';
import UnlockWallet from '../routes/UnlockWallet';
import translations_en from '../translations/en.global.json';
import translations_pt from '../translations/pt.global.json';
import translations_ru from '../translations/ru.global.json';
Expand Down Expand Up @@ -77,16 +81,16 @@ import Navigation from './navigation/Navigation';
import { PageNotFound } from './page-not-found/PageNotFound';
import { Profile } from './profile/Profile';
import { ReceiveContainerWrapper } from './receive-money/ReceiveContainerWrapper';
import InactivityLockModal from './security/InactivityLockModal';
import { SendContainerWrapper } from './send/SendContainerWrapper';
import { StakingContainer } from './staking/StakingContainer';
import Terms from './terms/Terms';
import { Wallet } from './wallet/Wallet';

import '../index.css';

const {
fetchTokenFiatValues
} = tokenFiatValueActions;

const { fetchTokenFiatValues } = tokenFiatValueActions;

const {
getAccountHelperWalletState,
Expand Down Expand Up @@ -134,7 +138,8 @@ class Routing extends Component {
super(props);

this.state = {
isInactiveAccount: null
isInactiveAccount: null,
isUserActive: true
};

this.pollTokenFiatValue = null;
Expand Down Expand Up @@ -249,6 +254,14 @@ class Routing extends Component {
this.stopPollingTokenFiatValue();
}

handleSetUserActive = () => {
return this.setState({...this.state,isUserActive: true });
}

handleSetUserIdle = () => {
return this.setState({...this.state, isUserActive: false });
}

startPollingTokenFiatValue = () => {
const { fetchTokenFiatValues } = this.props;

Expand Down Expand Up @@ -313,6 +326,14 @@ class Routing extends Component {
<Navigation isInactiveAccount={isInactiveAccount} />
<GlobalAlert />
<LedgerConfirmActionModal />
<IdleTimer
timeout={1000 * 60 * 1}
onIdle={this.handleSetUserIdle}
debounce={250}
/>
{!this.state.isUserActive &&
<InactivityLockModal isOpen={!this.state.isUserActive} onClose={this.handleSetUserActive}/>
}
{
account.requestPending !== null &&
<TwoFactorVerifyModal
Expand Down Expand Up @@ -556,6 +577,21 @@ class Routing extends Component {
exact
path='/sign'
component={SignWrapper}
/>
<PrivateRoute
exact
path='/security/unlock'
component={UnlockWallet}
/>
<PrivateRoute
exact
path='/security/set-password'
component={SetPassword}
/>
<PrivateRoute
exact
path='/security/change-password'
component={ChangePassword}
/>
{!isInactiveAccount &&
<PrivateRoute
Expand Down
66 changes: 19 additions & 47 deletions packages/frontend/src/components/accounts/CreateAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { getSearch } from 'connected-react-router';
import React, { Component } from 'react';
import { Translate } from 'react-localize-redux';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import styled from 'styled-components';

import { ACCOUNT_ID_SUFFIX, IS_MAINNET, MIN_BALANCE_TO_CREATE } from '../../config';
Expand All @@ -17,19 +16,17 @@ import { clearLocalAlert } from '../../redux/actions/status';
import { selectAccountSlice, selectActiveAccountIdIsImplicitAccount } from '../../redux/slices/account';
import { selectStatusLocalAlert, selectStatusMainLoader } from '../../redux/slices/status';
import { selectNearTokenFiatValueUSD } from '../../redux/slices/tokenFiatValues';
import isMobile from '../../utils/isMobile';
import {
ENABLE_IDENTITY_VERIFIED_ACCOUNT
} from '../../utils/wallet';
import AccountNote from '../common/AccountNote';
import { getNearAndFiatValue } from '../common/balance/helpers';
import Button from '../common/Button';
import FormButton from '../common/FormButton';
import Container from '../common/styled/Container.css';
import WhereToBuyNearModal from '../common/WhereToBuyNearModal';
import SafeTranslate from '../SafeTranslate';
import BrokenLinkIcon from '../svg/BrokenLinkIcon';
import FundNearIcon from '../svg/FundNearIcon';
import AccountFormAccountId from './AccountFormAccountId';

const StyledContainer = styled(Container)`
.input {
Expand Down Expand Up @@ -147,47 +144,38 @@ class CreateAccount extends Component {
}

handleCreateAccount = async () => {
const { accountId, fundingAmount } = this.state;
const {
fundingContract, fundingKey,
fundingAccountId,
} = this.props;
// const { accountId, fundingAmount } = this.state;
// const {
// fundingContract, fundingKey,
// fundingAccountId,
// } = this.props;

this.setState({ loader: true });
// this.setState({ loader: true });

let queryString = '';
if (fundingAccountId || fundingContract) {
const fundingOptions = fundingAccountId ? { fundingAccountId } : { fundingContract, fundingKey, fundingAmount };
queryString = `?fundingOptions=${encodeURIComponent(JSON.stringify(fundingOptions))}`;
}
// let queryString = '';
// if (fundingAccountId || fundingContract) {
// const fundingOptions = fundingAccountId ? { fundingAccountId } : { fundingContract, fundingKey, fundingAmount };
// queryString = `?fundingOptions=${encodeURIComponent(JSON.stringify(fundingOptions))}`;
// }
Mixpanel.track('CA Click create account button');
this.props.history.push(`/set-recovery/${accountId}${queryString}`);
this.props.history.push(`/set-recovery/${'accountId'}${'queryString'}`);
}

render() {
const {
loader,
accountId,
invalidNearDrop,
termsAccepted,
whereToBuy
} = this.state;

const {
localAlert,
mainLoader,
checkNewAccount,
resetAccount,
clearLocalAlert,
fundingContract,
fundingKey,
nearTokenFiatValueUSD,
locationSearch,
activeAccountIdIsImplicit
} = this.props;

const isLinkDrop = fundingContract && fundingKey;
const useLocalAlert = accountId.length > 0 ? localAlert : undefined;
const showTermsPage = IS_MAINNET && !isLinkDrop && !termsAccepted && !ENABLE_IDENTITY_VERIFIED_ACCOUNT;

if (showTermsPage) {
Expand Down Expand Up @@ -253,37 +241,21 @@ class CreateAccount extends Component {
/>
</h1>
<h2><Translate id='createAccount.pageText' /></h2>
<h4 className='small'><Translate id='createAccount.accountIdInput.title' /></h4>
<AccountFormAccountId
mainLoader={mainLoader}
handleChange={this.handleChange}
type='create'
pattern={/[^a-zA-Z0-9_-]/}
checkAvailability={checkNewAccount}
localAlert={useLocalAlert}
accountId={accountId}
clearLocalAlert={clearLocalAlert}
defaultAccountId={resetAccount && resetAccount.accountIdNotConfirmed.split('.')[0]}
autoFocus={isMobile() ? false : true}
/>
<AccountNote />
<FormButton
type='submit'
disabled={!(localAlert && localAlert.success)}
sending={loader}
<Button
onClick={this.handleCreateAccount}
data-test-id="reserveAccountIdButton"
>
<Translate id='button.reserveMyAccountId' />
</FormButton>
<Translate id='button.getStarted' />
</Button>
{!termsAccepted &&
<div className='disclaimer no-terms-page'>
<Translate id='createAccount.termsPage.disclaimer' />
</div>
}
<div className='alternatives-title'><Translate id='createAccount.alreadyHaveAnAccount' /></div>
{/* <div className='alternatives-title'><Translate id='createAccount.alreadyHaveAnAccount' /></div>
<div className='alternatives' onClick={() => { Mixpanel.track('IE Click import existing account button'); }}>
<Link to={`/recover-account${locationSearch}`}><Translate id='createAccount.recoverItHere' /></Link>
</div>
</div> */}
</form>
</StyledContainer>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const SetupSeedPhraseForm = ({
color='blue'
data-test-id="continueToSeedPhraseVerificationButton"
>
<Translate id='button.continue' />
<Translate id='button.okIveSavedItSomewhere' />
</FormButton>
</CustomDiv>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ class SetupRecoveryMethod extends Component {
trackingId='SR Click submit button'
data-test-id="submitSelectedRecoveryOption"
>
<Translate id='button.continue' />
<Translate id='button.secureMyAccount' />
</FormButton>
</form>
{isNewAccount &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ export default ({
wordIndex,
handleChangeWord,
handleStartOver,
userInputValueWrongWord
userInputValueWrongWord,
passPhrase
}) => {
console.log(passPhrase);
return (
<StyledContainer className='small-centered border'>
<form
Expand All @@ -67,9 +69,7 @@ export default ({
pattern='[a-zA-Z ]*'
className={userInputValueWrongWord ? 'problem' : ''}
/>
{userInputValueWrongWord &&
<div className='color-red'><Translate id='setupSeedPhraseVerify.inputError' /></div>
}
{ userInputValueWrongWord && <div className='color-red'><Translate id='setupSeedPhraseVerify.inputError' /></div> }
<FormButton
type='submit'
data-test-id='seedPhraseVerificationWordSubmit'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default ({ handleConfirmPassphrase }) => {
handleConfirmPassphrase({ implicitAccountId, recoveryKeyPair });
Mixpanel.track('SR-SP Verify finish');
}}
passPhrase={passPhrase}
/>
);
}
Expand Down
30 changes: 25 additions & 5 deletions packages/frontend/src/components/common/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ import PropTypes from 'prop-types';
import React from 'react';
import styled from 'styled-components';

const backgroundColorMap = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice mapping of colors! I'm curious why you decided to use this button component vs the regular FormButton? Seems it would be good if we used the same button as much as possible for consistency sake, and less components to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely. Will switch to the FormButton.

primary: '#0072CE',
secondary: '#ffffff',
destructive: '#FC5B5B'
};
const backgroundColorHoverMap = {
primary: '#007fe6',
secondary: '#cccccc',
destructive: '#FC5B5B'
};
const colorMap = {
primary: '#FFFFFF',
secondary: '#888888',
destructive: '#FFFFFF'
};
const borderColorMap = {
primary: '#0072CE',
secondary: '#cccccc',
destructive: '#FC5B5B'
};
const StyledButton = styled.button`
border-radius: 40px;
padding: 5px 32px;
Expand All @@ -15,14 +35,14 @@ const StyledButton = styled.button`
align-items: center;
justify-content: center;
transition: all 150ms ease;
background-color: ${(props) => props.theme === 'secondary' ? '#ffffff' : '#0072CE'};
border: 2px solid ${(props) => props.theme === 'secondary' ? '#cccccc' : '#0072CE'};
color: ${(props) => props.theme === 'secondary' ? '#888888' : 'white'};
background-color: ${(props) => backgroundColorMap[props.theme]};
border: 2px solid ${(props) => borderColorMap[props.theme]};
color: ${(props) => colorMap[props.theme]};

@media (min-width: 768px) {
&:enabled {
&:hover {
background-color: ${(props) => props.theme === 'secondary' ? '#cccccc' : '#007fe6'};
background-color: ${(props) => backgroundColorHoverMap[props.theme]};
color: white;
}
}
Expand Down Expand Up @@ -50,7 +70,7 @@ const Button = (props) => (

Button.propTypes = {
disabled: PropTypes.bool,
theme: PropTypes.oneOf(['primary', 'secondary']),
theme: PropTypes.oneOf(['primary', 'secondary', 'destructive']),
fullWidth: PropTypes.bool,
};

Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/components/common/modal/Modal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import ReactDom from 'react-dom';


import classNames from '../../../utils/classNames';
import isMobile from '../../../utils/isMobile';
import CloseButton from './CloseButton';
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/src/components/profile/Profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import UserIcon from '../svg/UserIcon';
import AuthorizedApp from './authorized_apps/AuthorizedApp';
import BalanceContainer from './balances/BalanceContainer';
import LockupAvailTransfer from './balances/LockupAvailTransfer';
import BrowserPassword from './browser-password/BrowserPassword';
import HardwareDevices from './hardware_devices/HardwareDevices';
import MobileSharingWrapper from './mobile_sharing/MobileSharingWrapper';
import RecoveryContainer from './Recovery/RecoveryContainer';
Expand Down Expand Up @@ -267,6 +268,12 @@ export function Profile({ match }) {
<h4><Translate id='profile.security.lessSecure'/><Tooltip translate='profile.security.lessSecureDesc' icon='icon-lg'/></h4>
<RecoveryContainer type='email' recoveryMethods={userRecoveryMethods}/>
<RecoveryContainer type='phone' recoveryMethods={userRecoveryMethods}/>
<hr/>
<div>
<h2><LockIcon/><Translate id='profile.browserPassword.ttl'/></h2>
<div className='sub-heading'><Translate id='profile.browserPassword.desc'/></div>
<BrowserPassword/>
</div>
{!account.ledgerKey &&
<>
<hr/>
Expand Down
Loading