diff --git a/src/Root.tsx b/src/Root.tsx
index 9a518fcb6b..dab1473a09 100644
--- a/src/Root.tsx
+++ b/src/Root.tsx
@@ -68,7 +68,10 @@ import AboutGroup, {
import SecurityGroup, {
SecurityGroupParamList,
} from './navigation/tabs/settings/security/SecurityGroup';
-import AuthGroup, {AuthGroupParamList} from './navigation/auth/AuthGroup';
+import AuthGroup, {
+ AuthGroupParamList,
+ AuthScreens,
+} from './navigation/auth/AuthGroup';
import BuyCryptoGroup, {
BuyCryptoGroupParamList,
} from './navigation/services/buy-crypto/BuyCryptoGroup';
@@ -332,6 +335,11 @@ export default () => {
WalletScreens.ACCOUNT_DETAILS,
WalletScreens.TRANSACTION_PROPOSAL_NOTIFICATIONS,
WalletScreens.WALLET_DETAILS,
+ WalletScreens.VERIFY_PHRASE,
+ WalletScreens.EXTENDED_PRIVATE_KEY,
+ AuthScreens.LOGIN,
+ AuthScreens.CREATE_ACCOUNT,
+ AuthScreens.SECURE_ACCOUNT,
];
const debouncedOnStateChange = useMemo(
diff --git a/src/components/form/BoxInput.tsx b/src/components/form/BoxInput.tsx
index 4bd9e07fd5..b0a39ef7bd 100644
--- a/src/components/form/BoxInput.tsx
+++ b/src/components/form/BoxInput.tsx
@@ -1,5 +1,10 @@
-import React, {useState} from 'react';
-import {KeyboardTypeOptions, TextInput, TextInputProps} from 'react-native';
+import React, {useEffect, useState} from 'react';
+import {
+ AppState,
+ KeyboardTypeOptions,
+ TextInput,
+ TextInputProps,
+} from 'react-native';
import TextInputMask, {TextInputMaskProps} from 'react-native-text-input-mask';
import styled, {css} from 'styled-components/native';
import ObfuscationHide from '../../../assets/img/obfuscation-hide.svg';
@@ -219,6 +224,15 @@ const BoxInput = React.forwardRef<
);
}
+ useEffect(() => {
+ const sub = AppState.addEventListener('change', state => {
+ if (isPassword && (state === 'inactive' || state === 'background')) {
+ props.onChangeText?.('');
+ }
+ });
+ return () => sub.remove();
+ }, [isPassword, props]);
+
return (
<>
{label ? : null}
diff --git a/src/navigation/auth/screens/CreateAccount.tsx b/src/navigation/auth/screens/CreateAccount.tsx
index 8ff2c8dd3b..01b077f741 100644
--- a/src/navigation/auth/screens/CreateAccount.tsx
+++ b/src/navigation/auth/screens/CreateAccount.tsx
@@ -15,7 +15,11 @@ import yup from '../../../lib/yup';
import {navigationRef} from '../../../Root';
import {AppActions} from '../../../store/app';
import {BitPayIdActions, BitPayIdEffects} from '../../../store/bitpay-id';
-import {useAppDispatch, useAppSelector} from '../../../utils/hooks';
+import {
+ useAppDispatch,
+ useAppSelector,
+ useSensitiveRefClear,
+} from '../../../utils/hooks';
import {AuthScreens, AuthGroupParamList} from '../AuthGroup';
import AuthFormContainer, {
AuthActionRow,
@@ -64,6 +68,7 @@ const CreateAccountScreen: React.FC = ({
const dispatch = useAppDispatch();
const [isRecaptchaVisible, setRecaptchaVisible] = useState(false);
const captchaRef = useRef(null);
+ const {clearSensitive} = useSensitiveRefClear([passwordRef]);
const schema = yup.object().shape({
givenName: yup.string().required().trim(),
@@ -168,6 +173,7 @@ const CreateAccountScreen: React.FC = ({
agreedToMarketingCommunications,
}),
);
+ clearSensitive();
},
() => {
Keyboard.dismiss();
diff --git a/src/navigation/auth/screens/Login.tsx b/src/navigation/auth/screens/Login.tsx
index f5e251df84..32d0fcd0c0 100644
--- a/src/navigation/auth/screens/Login.tsx
+++ b/src/navigation/auth/screens/Login.tsx
@@ -21,7 +21,11 @@ import {navigationRef, RootStacks} from '../../../Root';
import {AppActions} from '../../../store/app';
import {BitPayIdActions, BitPayIdEffects} from '../../../store/bitpay-id';
import {sleep} from '../../../utils/helper-methods';
-import {useAppDispatch, useAppSelector} from '../../../utils/hooks';
+import {
+ useAppDispatch,
+ useAppSelector,
+ useSensitiveRefClear,
+} from '../../../utils/hooks';
import {BitpayIdScreens} from '../../bitpay-id/BitpayIdGroup';
import {AuthScreens, AuthGroupParamList} from '../AuthGroup';
import AuthFormContainer, {
@@ -115,6 +119,8 @@ const LoginScreen: React.FC = ({navigation, route}) => {
const captchaRef = useRef(null);
const {onLoginSuccess} = route.params || {};
+ const {clearSensitive} = useSensitiveRefClear([passwordRef]);
+
useEffect(() => {
dispatch(BitPayIdEffects.startFetchSession());
}, [dispatch]);
@@ -197,6 +203,7 @@ const LoginScreen: React.FC = ({navigation, route}) => {
const onSubmit = handleSubmit(
async ({email, password}) => {
Keyboard.dismiss();
+ clearSensitive();
if (session.captchaDisabled) {
dispatch(BitPayIdEffects.startLogin({email, password}));
} else {
diff --git a/src/navigation/wallet/components/FileOrText.tsx b/src/navigation/wallet/components/FileOrText.tsx
index 4a785dedbf..3ad43fdd7c 100644
--- a/src/navigation/wallet/components/FileOrText.tsx
+++ b/src/navigation/wallet/components/FileOrText.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useEffect, useRef} from 'react';
import {
ImportTextInput,
HeaderContainer,
@@ -30,9 +30,13 @@ import {startUpdateAllWalletStatusForKey} from '../../../store/wallet/effects/st
import {updatePortfolioBalance} from '../../../store/wallet/wallet.actions';
import {useTranslation} from 'react-i18next';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
-import {ScrollView, Keyboard} from 'react-native';
+import {ScrollView, Keyboard, TextInput, AppState} from 'react-native';
import {Analytics} from '../../../store/analytics/analytics.effects';
-import {useAppDispatch, useAppSelector} from '../../../utils/hooks';
+import {
+ useAppDispatch,
+ useAppSelector,
+ useSensitiveRefClear,
+} from '../../../utils/hooks';
import {useOngoingProcess} from '../../../contexts';
const BWCProvider = BwcProvider.getInstance();
@@ -76,6 +80,8 @@ const FileOrText = () => {
const walletTermsAccepted = useAppSelector(
({WALLET}: RootState) => WALLET.walletTermsAccepted,
);
+ const plainTextRef = useRef(null);
+ const {clearSensitive} = useSensitiveRefClear([plainTextRef]);
const {
control,
@@ -157,6 +163,7 @@ const FileOrText = () => {
const onSubmit = handleSubmit(formData => {
const {text, password} = formData;
+ clearSensitive();
Keyboard.dismiss();
@@ -175,6 +182,15 @@ const FileOrText = () => {
importWallet(decryptBackupText, opts);
});
+ useEffect(() => {
+ const sub = AppState.addEventListener('change', state => {
+ if (state === 'inactive' || state === 'background') {
+ clearSensitive();
+ }
+ });
+ return () => sub.remove();
+ }, [clearSensitive]);
+
return (
{
control={control}
render={({field: {onChange, onBlur, value}}) => (
{
passphrase: undefined as string | undefined,
isMultisig: false,
});
+ const wordsRef = useRef(null);
+ const {clearSensitive} = useSensitiveRefClear([wordsRef]);
const {
control,
@@ -347,6 +353,7 @@ const RecoveryPhrase = () => {
const onSubmit = (formData: {text: string}) => {
const {text} = formData;
+ clearSensitive();
let keyOpts: Partial = {};
@@ -573,6 +580,15 @@ const RecoveryPhrase = () => {
}
}, []);
+ useEffect(() => {
+ const sub = AppState.addEventListener('change', state => {
+ if (state === 'inactive' || state === 'background') {
+ clearSensitive();
+ }
+ });
+ return () => sub.remove();
+ }, [clearSensitive]);
+
return (
{
control={control}
render={({field: {onChange, onBlur, value}}) => (
) {
+ const node: any = ref.current;
+ if (!node) return;
+
+ if (typeof node.clear === 'function') {
+ node.clear();
+ node.blur?.();
+ return;
+ }
+
+ node.setNativeProps?.({text: ''});
+ node.blur?.();
+}
+
+export function useSensitiveRefClear(
+ refs: Array>,
+) {
+ const clearSensitive = useCallback(() => {
+ refs.forEach(ref => clearInputRef(ref));
+ }, [refs]);
+ return {clearSensitive};
+}