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

Bug/3464 fix login bug #3492

Closed
wants to merge 13 commits into from
11 changes: 10 additions & 1 deletion app/actions/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,17 @@ export function logOut() {
};
}

export function checkedAuth() {
/**
* Temporary action to control auth flow
*
* @param {string} initialScreen - "login" or "onboarding"
* @returns - void
*/
export function checkedAuth(initialScreen) {
return {
type: 'CHECKED_AUTH',
payload: {
initialScreen,
},
};
}
2 changes: 1 addition & 1 deletion app/components/Nav/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ const App = ({ userLoggedIn }) => {

const isAuthChecked = useSelector((state) => state.user.isAuthChecked);
const dispatch = useDispatch();
const triggerCheckedAuth = () => dispatch(checkedAuth());
const triggerCheckedAuth = () => dispatch(checkedAuth('onboarding'));

const handleDeeplink = useCallback(({ error, params, uri }) => {
if (error) {
Expand Down
77 changes: 38 additions & 39 deletions app/components/Views/Login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ class Login extends PureComponent {
*/
setOnboardingWizardStep: PropTypes.func,
/**
* Boolean flag that determines if password has been set
* Temporary string that controls if componentDidMount should handle initial auth logic on mount
*/
passwordSet: PropTypes.bool,
initialScreen: PropTypes.string,
/**
* A string representing the selected address => account
*/
Expand Down Expand Up @@ -239,49 +239,40 @@ class Login extends PureComponent {
fieldRef = React.createRef();

async componentDidMount() {
const { initialScreen } = this.props;
const { KeyringController } = Engine.context;
const shouldHandleInitialAuth = initialScreen !== 'onboarding';
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);

// Lock keyring just in case
if (KeyringController.isUnlocked()) {
await KeyringController.setLocked();
}

if (!this.props.passwordSet) {
try {
await KeyringController.submitPassword('');
await SecureKeychain.resetGenericPassword();
this.props.navigation.navigate('HomeNav');
} catch (e) {
//
}
} else {
const biometryType = await SecureKeychain.getSupportedBiometryType();
if (biometryType) {
let enabled = true;
const previouslyDisabled = await AsyncStorage.getItem(BIOMETRY_CHOICE_DISABLED);
if (previouslyDisabled && previouslyDisabled === TRUE) {
enabled = false;
}
const biometryType = await SecureKeychain.getSupportedBiometryType();
if (biometryType) {
const previouslyDisabled = await AsyncStorage.getItem(BIOMETRY_CHOICE_DISABLED);
const enabled = !(previouslyDisabled && previouslyDisabled === TRUE);

this.setState({
biometryType: Device.isAndroid() ? 'biometrics' : biometryType,
biometryChoice: enabled,
biometryPreviouslyDisabled: !!previouslyDisabled,
});
this.setState({
biometryType: Device.isAndroid() ? 'biometrics' : biometryType,
biometryChoice: enabled,
biometryPreviouslyDisabled: !!previouslyDisabled,
});
if (shouldHandleInitialAuth) {
try {
if (enabled && !previouslyDisabled) {
const hasBiometricCredentials = await this.tryBiometric();
this.setState({ hasBiometricCredentials });
await this.tryBiometric();
}
} catch (e) {
console.warn(e);
}
if (!enabled) {
await this.checkIfRememberMeEnabled();
}
} else {
await this.checkIfRememberMeEnabled();
}
} else {
shouldHandleInitialAuth && (await this.checkIfRememberMeEnabled());
}

this.props.checkedAuth();
Expand Down Expand Up @@ -318,13 +309,15 @@ class Login extends PureComponent {
this.props.setOnboardingWizardStep(1);
}

// Only way to land back on Login is to log out, which clears credentials (meaning we should not show biometric button)
this.setState({ hasBiometricCredentials: false });
delete credentials.password;
this.props.logIn();
this.props.navigation.navigate('HomeNav');
}
};

onLogin = async () => {
onLogin = async (hasCredentials = false) => {
const { password } = this.state;
const { current: field } = this.fieldRef;
const locked = !passwordRequirementsMet(password);
Expand All @@ -342,7 +335,7 @@ class Login extends PureComponent {
await AsyncStorage.setItem(ENCRYPTION_LIB, ORIGINAL);
}
// If the tryBiometric has been called and they password was retrived don't set it again
if (!this.state.hasBiometricCredentials) {
if (!hasCredentials) {
if (this.state.biometryChoice && this.state.biometryType) {
await SecureKeychain.setGenericPassword(this.state.password, SecureKeychain.TYPES.BIOMETRICS);
} else if (this.state.rememberMe) {
Expand All @@ -362,7 +355,8 @@ class Login extends PureComponent {
this.props.setOnboardingWizardStep(1);
this.props.navigation.navigate('HomeNav');
}
this.setState({ loading: false, password: '' });
// Only way to land back on Login is to log out, which clears credentials (meaning we should not show biometric button)
this.setState({ loading: false, password: '', hasBiometricCredentials: false });
field.setValue('');
} catch (e) {
// Should we force people to enable passcode / biometrics?
Expand Down Expand Up @@ -394,6 +388,10 @@ class Login extends PureComponent {
}
};

triggerLogIn = () => {
this.onLogin();
};

delete = async () => {
const { KeyringController } = Engine.context;
try {
Expand Down Expand Up @@ -448,8 +446,6 @@ class Login extends PureComponent {
updateBiometryChoice = async (biometryChoice) => {
if (!biometryChoice) {
await AsyncStorage.setItem(BIOMETRY_CHOICE_DISABLED, TRUE);
// This line will disable biometrics the next time SecureKeychain.getGenericPassword is called
await SecureKeychain.resetGenericPassword();
} else {
await AsyncStorage.removeItem(BIOMETRY_CHOICE_DISABLED);
}
Expand Down Expand Up @@ -501,17 +497,20 @@ class Login extends PureComponent {
field.blur();
try {
const credentials = await SecureKeychain.getGenericPassword();
if (!credentials) return false;
if (!credentials) {
this.setState({ hasBiometricCredentials: false });
return;
}
field.blur();
this.setState({ password: credentials.password });
field.setValue(credentials.password);
field.blur();
this.onLogin();
await this.onLogin(true);
} catch (error) {
this.setState({ hasBiometricCredentials: true });
Logger.log(error);
}
field.blur();
return true;
};

render = () => (
Expand Down Expand Up @@ -601,7 +600,7 @@ class Login extends PureComponent {
value={this.state.password}
baseColor={colors.grey500}
tintColor={colors.blue}
onSubmitEditing={this.onLogin}
onSubmitEditing={this.triggerLogIn}
renderRightAccessory={() => (
<BiometryButton
onPress={this.tryBiometric}
Expand All @@ -627,7 +626,7 @@ class Login extends PureComponent {
)}

<View style={styles.ctaWrapper} testID={'log-in-button'}>
<StyledButton type={'confirm'} onPress={this.onLogin}>
<StyledButton type={'confirm'} onPress={this.triggerLogIn}>
{this.state.loading ? (
<ActivityIndicator size="small" color="white" />
) : (
Expand Down Expand Up @@ -655,15 +654,15 @@ class Login extends PureComponent {
}

const mapStateToProps = (state) => ({
passwordSet: state.user.passwordSet,
selectedAddress: state.engine.backgroundState.PreferencesController?.selectedAddress,
initialScreen: state.user.initialScreen,
});

const mapDispatchToProps = (dispatch) => ({
setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)),
logIn: () => dispatch(logIn()),
logOut: () => dispatch(logOut()),
checkedAuth: () => dispatch(checkedAuth()),
checkedAuth: () => dispatch(checkedAuth('login')),
});

export default connect(mapStateToProps, mapDispatchToProps)(Login);
2 changes: 2 additions & 0 deletions app/reducers/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const initialState = {
gasEducationCarouselSeen: false,
userLoggedIn: false,
isAuthChecked: false,
initialScreen: '',
};

const userReducer = (state = initialState, action) => {
Expand All @@ -16,6 +17,7 @@ const userReducer = (state = initialState, action) => {
return {
...state,
isAuthChecked: true,
initialScreen: action.payload.initialScreen,
};
case 'LOGIN':
return {
Expand Down
2 changes: 1 addition & 1 deletion app/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const persistTransform = createTransform(

const persistUserTransform = createTransform(
(inboundState) => {
const { isAuthChecked, ...state } = inboundState;
const { initialScreen, isAuthChecked, ...state } = inboundState;
// Reconstruct data to persist
return state;
},
Expand Down
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ SPEC CHECKSUMS:
Branch: 6a281514287f99d707615ac62c2cca69e0213df0
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cde416483dac037923206447da6e1454df403714
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
Flipper: 1bd2db48dcc31e4b167b9a33ec1df01c2ded4893
Expand All @@ -683,7 +683,7 @@ SPEC CHECKSUMS:
Flipper-RSocket: 127954abe8b162fcaf68d2134d34dc2bd7076154
FlipperKit: 651f50a42eb95c01b3e89a60996dd6aded529eeb
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
glog: 5337263514dd6f09803962437687240c5dc39aa4
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: a50d5c0160425cd4b01b852bb9578963e6d92d31
lottie-react-native: 7ca15c46249b61e3f9ffcf114cb4123e907a2156
Expand Down