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

Check for unvalidated accounts and change in password flow #1929

Merged
merged 3 commits into from
Apr 5, 2021
Merged
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
13 changes: 13 additions & 0 deletions src/libs/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function isAuthTokenRequired(command) {
'SetPassword',
'User_SignUp',
'ResendValidateCode',
'ResetPassword',
], command);
}

Expand Down Expand Up @@ -539,6 +540,17 @@ function ResendValidateCode(parameters) {
return Network.post(commandName, parameters);
}

/**
* @param {Object} parameters
* @param {Number} parameters.email
* @returns {Promise}
*/
function ResetPassword(parameters) {
const commandName = 'ResetPassword';
requireParameters(['email'], parameters, commandName);
return Network.post(commandName, parameters);
}

/**
* @param {Object} parameters
* @param {String} parameters.password
Expand Down Expand Up @@ -642,6 +654,7 @@ export {
Report_TogglePinned,
Report_UpdateLastRead,
ResendValidateCode,
ResetPassword,
SetNameValuePair,
SetPassword,
UpdateAccount,
Expand Down
14 changes: 14 additions & 0 deletions src/libs/actions/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ function fetchAccountDetails(login) {
Onyx.merge(ONYXKEYS.ACCOUNT, {
accountExists: response.accountExists,
requiresTwoFactorAuth: response.requiresTwoFactorAuth,
validated: response.validated,
forgotPassword: false,
});

if (!response.accountExists) {
Expand Down Expand Up @@ -194,6 +196,17 @@ function resendValidationLink() {
});
}

/**
* User forgot the password so let's send them the link to reset their password
*/
function resetPassword() {
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: true, forgotPassword: true});
API.ResetPassword({email: credentials.login})
.finally(() => {
Onyx.merge(ONYXKEYS.ACCOUNT, {loading: false});
});
}

/**
* Restart the sign in process by clearing everything from Onyx
*/
Expand Down Expand Up @@ -238,5 +251,6 @@ export {
signIn,
signOut,
resendValidationLink,
resetPassword,
restartSignin,
};
21 changes: 18 additions & 3 deletions src/pages/signin/ChangeExpensifyLoginLink.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
import {restartSignin} from '../../libs/actions/Session';
import themeColors from '../../styles/themes/default';
import ONYXKEYS from '../../ONYXKEYS';

const ChangeExpensifyLoginLink = () => (
const propTypes = {
// The credentials of the logged in person
credentials: PropTypes.shape({
// The email the user logged in with
login: PropTypes.string,
}).isRequired,
};

const ChangeExpensifyLoginLink = ({credentials}) => (
<View style={[styles.mb4]}>
<TouchableOpacity
style={[styles.link]}
onPress={restartSignin}
underlayColor={themeColors.componentBG}
>
<Text style={[styles.link]}>
Change Expensify login
Not&nbsp;
{credentials.login}
</Text>
</TouchableOpacity>
</View>
);

ChangeExpensifyLoginLink.propTypes = propTypes;
ChangeExpensifyLoginLink.displayName = 'ChangeExpensifyLoginLink';

export default ChangeExpensifyLoginLink;
export default withOnyx({
credentials: {key: ONYXKEYS.CREDENTIALS},
})(ChangeExpensifyLoginLink);
13 changes: 11 additions & 2 deletions src/pages/signin/PasswordForm.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React from 'react';
import {
Text, TextInput, View,
Text, TextInput, TouchableOpacity, View,
} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import styles from '../../styles/styles';
import ButtonWithLoader from '../../components/ButtonWithLoader';
import themeColors from '../../styles/themes/default';
import {signIn} from '../../libs/actions/Session';
import {signIn, resetPassword} from '../../libs/actions/Session';
import ONYXKEYS from '../../ONYXKEYS';
import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink';

Expand Down Expand Up @@ -78,6 +78,15 @@ class PasswordForm extends React.Component {
autoFocus
/>
</View>
<TouchableOpacity
style={[styles.link, styles.mb4]}
onPress={resetPassword}
underlayColor={themeColors.componentBG}
>
<Text style={[styles.link]}>
Forgot?
</Text>
</TouchableOpacity>
{this.props.account.requiresTwoFactorAuth && (
<View style={[styles.mb4]}>
<Text style={[styles.formLabel]}>Two Factor Code</Text>
Expand Down
15 changes: 12 additions & 3 deletions src/pages/signin/ResendValidationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import _ from 'underscore';
import styles from '../../styles/styles';
import ButtonWithLoader from '../../components/ButtonWithLoader';
import {resendValidationLink} from '../../libs/actions/Session';
import {resendValidationLink, resetPassword} from '../../libs/actions/Session';
import ONYXKEYS from '../../ONYXKEYS';
import ChangeExpensifyLoginLink from './ChangeExpensifyLoginLink';

Expand All @@ -16,6 +16,9 @@ const propTypes = {
account: PropTypes.shape({
// Whether or not a sign on form is loading (being submitted)
loading: PropTypes.bool,

// Weather or not the account is validated
validated: PropTypes.bool,
}),
};

Expand Down Expand Up @@ -48,7 +51,13 @@ class ResendValidationForm extends React.Component {
formSuccess: 'Link has been re-sent',
});

resendValidationLink();
if (!this.props.account.validated) {
resendValidationLink();
console.debug('Account is unvalidated: Sending validation link.');
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason we're printing this to the console? Seems unnecessary IMO

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, because when you click "resend link" its hard to tell which flow is being called. You can check the network tab for this but its easier with two console.debug as can be seen in test steps 7 and 11. Just makes life easier when you want to debug.

} else {
resetPassword();
console.debug('Account forgot password: Sending reset password link.');
}

this.successMessageTimer = setTimeout(() => {
this.setState({formSuccess: ''});
Expand All @@ -60,7 +69,7 @@ class ResendValidationForm extends React.Component {
<View style={[styles.loginFormContainer]}>
<View>
<Text style={[styles.textP]}>
Please validate your account by clicking on the link we just sent you.
We&apos;ve sent you a magic sign in link just click on it to log in!
</Text>
</View>
<View style={[styles.mt4]}>
Expand Down
19 changes: 14 additions & 5 deletions src/pages/signin/SignInPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const propTypes = {

// Error to display when there is an account error returned
error: PropTypes.string,

// Weather or not the account is validated
validated: PropTypes.bool,

// Weather or not the account is validated
forgotPassword: PropTypes.bool,
}),

// The credentials of the person signing in
Expand Down Expand Up @@ -56,21 +62,24 @@ class SignInPage extends Component {
// - A login has not been entered yet
const showLoginForm = !this.props.credentials.login;

const validAccount = this.props.account.accountExists
&& this.props.account.validated
&& !this.props.account.forgotPassword;

// Show the password form if
// - A login has been entered
// - AND a GitHub username has been entered OR they already have access to expensify cash
// - AND an account exists already for this login
// - AND an account exists and is validated for this login
// - AND a password hasn't been entered yet
const showPasswordForm = this.props.credentials.login
&& this.props.account.accountExists
&& validAccount
&& !this.props.credentials.password;

// Show the resend validation link form if
// - A login has been entered
// - AND a GitHub username has been entered OR they already have access to this app
// - AND an account did not exist for that login
const showResendValidationLinkForm = this.props.credentials.login
&& !this.props.account.accountExists;
// - AND an account did not exist or is not validated for that login
const showResendValidationLinkForm = this.props.credentials.login && !validAccount;

return (
<>
Expand Down