From 64ff7bc178e42c14f193a77114b2ca939e15f7c8 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Tue, 8 Sep 2020 18:08:58 -0400 Subject: [PATCH 1/6] feat(VerifyEmailScreen): Add link to resend verification email. --- lib/components/user/user-account-screen.js | 5 +- lib/components/user/verify-email-screen.js | 83 ++++++++++++++++------ 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index 0c77fa85d..27090adaf 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -88,14 +88,14 @@ class UserAccountScreen extends Component { // TODO: Update title bar during componentDidMount. render () { - const { auth, loggedInUser } = this.props + const { auth, config, loggedInUser } = this.props const { userData } = this.state let formContents if (isNewUser(loggedInUser)) { if (!auth.user.email_verified) { // Check and prompt for email verification first to avoid extra user wait. - formContents = + formContents = } else { // New users are shown "wizard" (step-by-step) mode // (includes when a "new" user clicks 'My Account' from the account menu in the nav bar). @@ -134,6 +134,7 @@ class UserAccountScreen extends Component { const mapStateToProps = (state, ownProps) => { return { + config: state.otp.config, loggedInUser: state.user.loggedInUser } } diff --git a/lib/components/user/verify-email-screen.js b/lib/components/user/verify-email-screen.js index cf212a9c3..b3c4a9177 100644 --- a/lib/components/user/verify-email-screen.js +++ b/lib/components/user/verify-email-screen.js @@ -1,7 +1,12 @@ -import React from 'react' +import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import styled from 'styled-components' -const _handleClick = () => window.location.reload() +import { secureFetch } from '../../util/middleware' + +const DivSpacer = styled.div` + margin-top: ${props => props.space || 2}em; +` /** * This component contains the prompt for the user to verify their email address. @@ -10,25 +15,59 @@ const _handleClick = () => window.location.reload() * (One way to make sure the parent page fetches the latest email verification status * is to simply reload the page.) */ -const VerifyEmailScreen = () => ( -
-

Verify your email address

-

- Please check your email inbox and follow the link in the message - to verify your email address before finishing your account setup. -

-

- Once you're verified, click the button below to continue. -

- - -
-) +class VerifyEmailScreen extends Component { + resendVerificationEmail = () => { + const { auth, middlewareConfig } = this.props + const { accessToken } = auth + if (!accessToken) { + console.warn('No access token found.') + return + } + const { apiBaseUrl, apiKey } = middlewareConfig + + secureFetch(`${apiBaseUrl}/api/secure/user/verification-email`, accessToken, apiKey) + .then(json => window.alert('Verification email resent!')) + // TODO: check status of the request. + } + + _handleClick = () => window.location.reload() + + render () { + const { middlewareConfig } = this.props + return ( +
+

Verify your email address

+ + Please check your email inbox and follow the link in the message + to verify your email address before finishing your account setup. + +

+ Once you're verified, click the button below to continue. +

+ + + + + {middlewareConfig && ( + + + + )} +
+ ) + } +} export default VerifyEmailScreen From 199136e0f8c986ac7a2537ec8cd6e98412f4c188 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:22:17 -0500 Subject: [PATCH 2/6] refactor(VerifyEmailScreen): Create action for resending verif. email, remove old code. --- lib/actions/user.js | 19 ++++++++ lib/components/user/user-account-screen.js | 4 +- lib/components/user/verify-email-screen.js | 51 +++++++++++----------- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index aa8e8d095..10a4ef981 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -152,6 +152,25 @@ export function createOrUpdateUser (userData, silentOnSuccess = false) { } } +/** + * Requests the verification email for the new user to be resent. + */ +export function resendVerificationEmail (auth0) { + return async function (dispatch, getState) { + const { apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) + const accessToken = await auth0.getAccessTokenSilently() + + // TODO: add any throttling. + const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/verification-email` + const { status } = await secureFetch(requestUrl, accessToken, apiKey) + + // TODO: improve the UI feedback messages for this. + if (status === 'success') { + alert('The email verification message has been resent.') + } + } +} + /** * Fetches the saved/monitored trips for a user. * We use the accessToken to fetch the data regardless of diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index f1b0b4676..81721259e 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -134,7 +134,6 @@ class UserAccountScreen extends Component { render () { const { auth0, - config, loggedInUser, phoneFormatOptions, requestPhoneVerificationSms, @@ -167,7 +166,7 @@ class UserAccountScreen extends Component { if (this.state.isNewUser) { if (!auth0.user.email_verified) { // Check and prompt for email verification first to avoid extra user wait. - formContents = + formContents = } else { // New users are shown "wizard" (step-by-step) mode // (includes when a "new" user clicks "My Account" from the account menu in the nav bar). @@ -209,7 +208,6 @@ class UserAccountScreen extends Component { const mapStateToProps = (state, ownProps) => { return { - config: state.otp.config, loggedInUser: state.user.loggedInUser, phoneFormatOptions: state.otp.config.phoneFormatOptions } diff --git a/lib/components/user/verify-email-screen.js b/lib/components/user/verify-email-screen.js index b3c4a9177..efc5eaef0 100644 --- a/lib/components/user/verify-email-screen.js +++ b/lib/components/user/verify-email-screen.js @@ -1,8 +1,9 @@ import React, { Component } from 'react' import { Button } from 'react-bootstrap' +import { connect } from 'react-redux' import styled from 'styled-components' -import { secureFetch } from '../../util/middleware' +import * as userActions from '../../actions/user' const DivSpacer = styled.div` margin-top: ${props => props.space || 2}em; @@ -16,24 +17,14 @@ const DivSpacer = styled.div` * is to simply reload the page.) */ class VerifyEmailScreen extends Component { - resendVerificationEmail = () => { - const { auth, middlewareConfig } = this.props - const { accessToken } = auth - if (!accessToken) { - console.warn('No access token found.') - return - } - const { apiBaseUrl, apiKey } = middlewareConfig - - secureFetch(`${apiBaseUrl}/api/secure/user/verification-email`, accessToken, apiKey) - .then(json => window.alert('Verification email resent!')) - // TODO: check status of the request. + _handleResendVerificationEmail = () => { + const { auth0, resendVerificationEmail } = this.props + resendVerificationEmail(auth0) } _handleClick = () => window.location.reload() render () { - const { middlewareConfig } = this.props return (

Verify your email address

@@ -54,20 +45,28 @@ class VerifyEmailScreen extends Component { - {middlewareConfig && ( - - - - )} + + +
) } } -export default VerifyEmailScreen +// connect to the redux store + +const mapStateToProps = () => { + return {} +} + +const mapDispatchToProps = { + resendVerificationEmail: userActions.resendVerificationEmail +} + +export default connect(mapStateToProps, mapDispatchToProps)(VerifyEmailScreen) From 7152d277edd003953ccc3fb8d5921ebfd9f93b67 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 6 Nov 2020 08:45:33 -0500 Subject: [PATCH 3/6] refactor(VerifyEmailScreen): Rename handler. --- lib/components/user/verify-email-screen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/user/verify-email-screen.js b/lib/components/user/verify-email-screen.js index efc5eaef0..ba153f4c8 100644 --- a/lib/components/user/verify-email-screen.js +++ b/lib/components/user/verify-email-screen.js @@ -22,7 +22,7 @@ class VerifyEmailScreen extends Component { resendVerificationEmail(auth0) } - _handleClick = () => window.location.reload() + _handleEmailVerified = () => window.location.reload() render () { return ( @@ -39,7 +39,7 @@ class VerifyEmailScreen extends Component { From 9381350db12d60d05fa80192aac6a88412df7642 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Mon, 9 Nov 2020 17:59:46 -0500 Subject: [PATCH 4/6] refactor(VerifyEmailScreen): Tweak formatting per PR comment. --- lib/components/user/verify-email-screen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/user/verify-email-screen.js b/lib/components/user/verify-email-screen.js index ba153f4c8..d0e056725 100644 --- a/lib/components/user/verify-email-screen.js +++ b/lib/components/user/verify-email-screen.js @@ -28,10 +28,10 @@ class VerifyEmailScreen extends Component { return (

Verify your email address

- +

Please check your email inbox and follow the link in the message to verify your email address before finishing your account setup. - +

Once you're verified, click the button below to continue.

From b9b0ba35ab20c854ca8c40b9229c79ab2b85f092 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Wed, 11 Nov 2020 13:33:19 -0500 Subject: [PATCH 5/6] refactor(actions/user): Add separate action to fetch auth0 token. --- lib/actions/user.js | 98 +++++++++---------- .../user/with-logged-in-user-support.js | 25 ++++- 2 files changed, 70 insertions(+), 53 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index 10a4ef981..2b33449fd 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -49,65 +49,63 @@ function getMiddlewareVariables (state) { } } +/** + * Attempts to fetch the auth0 access token and store it in the redux state, under state.user. + * @param auth0 The auth0 context used to obtain the access token for subsequent middleware fetches. + */ +export function fetchAuth0Token (auth0) { + return async function (dispatch, getState) { + try { + const accessToken = await auth0.getAccessTokenSilently() + + dispatch(setCurrentUser({ accessToken })) + } catch (error) { + // TODO: improve UI if there is an errror. + alert('Error obtaining an authorization token.') + } + } +} + /** * Attempts to fetch user preferences (or set initial values if the user is being created) * into the redux state, under state.user. - * @param auth0 If provided, the auth0 login object used to initially obtain the auth0 access token - * for subsequent middleware fetches (and also to initialize new users from auth0 email and id). - * If absent, state.user.accessToken will be used for fetches. + * @param auth0 If provided, the auth0 login object used to initialize the default user object (with email and auth0 id). */ export function fetchOrInitializeUser (auth0) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/fromtoken` - // Get the Auth0 access token. If one is in state.user, use it, - // otherwise if auth0 is provided, fetch it. - let token - if (accessToken) { - token = accessToken - } else if (auth0) { - try { - token = await auth0.getAccessTokenSilently() - } catch (error) { - // TODO: improve UI if there is an errror. - alert('Error obtaining an authorization token.') - } - } - - // Once accessToken is available, proceed to fetch or initialize loggedInUser. - if (token) { - const { data: user, status } = await secureFetch(requestUrl, token, apiKey) - - // Beware! On AWS API gateway, if a user is not found in the middleware - // (e.g. they just created their Auth0 password but have not completed the account setup form yet), - // the call above will return, for example: - // { - // status: 'success', - // data: { - // "result": "ERR", - // "message": "No user with id=000000 found.", - // "code": 404, - // "detail": null - // } - // } - // - // The same call to a middleware instance that is not behind an API gateway - // will return: - // { - // status: 'error', - // message: 'Error get-ing user...' - // } - // TODO: Improve AWS response. - - const isNewAccount = status === 'error' || (user && user.result === 'ERR') - const userData = isNewAccount ? createNewUser(auth0.user) : user - dispatch(setCurrentUser({ accessToken: token, user: userData })) - - // Also load monitored trips for existing users. - if (!isNewAccount) { - dispatch(fetchUserMonitoredTrips()) - } + const { data: user, status } = await secureFetch(requestUrl, accessToken, apiKey) + + // Beware! On AWS API gateway, if a user is not found in the middleware + // (e.g. they just created their Auth0 password but have not completed the account setup form yet), + // the call above will return, for example: + // { + // status: 'success', + // data: { + // "result": "ERR", + // "message": "No user with id=000000 found.", + // "code": 404, + // "detail": null + // } + // } + // + // The same call to a middleware instance that is not behind an API gateway + // will return: + // { + // status: 'error', + // message: 'Error get-ing user...' + // } + // TODO: Improve AWS response. + + const isNewAccount = status === 'error' || (user && user.result === 'ERR') + const userData = isNewAccount ? createNewUser(auth0.user) : user + dispatch(setCurrentUser({ accessToken, user: userData })) + + // Also load monitored trips for existing users. + if (!isNewAccount) { + dispatch(fetchUserMonitoredTrips()) } } } diff --git a/lib/components/user/with-logged-in-user-support.js b/lib/components/user/with-logged-in-user-support.js index 951c3f743..17b1c5bcf 100644 --- a/lib/components/user/with-logged-in-user-support.js +++ b/lib/components/user/with-logged-in-user-support.js @@ -49,10 +49,26 @@ class UserLoaderScreen extends Component { return auth0 && auth0.isAuthenticated && !loggedInUser } + /** + * Determines whether an auth0 token should be fetched. + * @returns true if the logged-in user has passed Auth0 authentication + * and state.user.accessToken has not been set; false otherwise. + */ + acccessTokenIsUnfetched = () => { + const { accessToken, auth0 } = this.props + return auth0 && auth0.isAuthenticated && !accessToken + } + componentDidUpdate () { - const { auth0, fetchOrInitializeUser } = this.props + const { + auth0, + fetchAuth0Token, + fetchOrInitializeUser + } = this.props - if (this.loggedInUserIsUnfetched()) { + if (this.acccessTokenIsUnfetched()) { + fetchAuth0Token(auth0) + } else if (this.loggedInUserIsUnfetched()) { fetchOrInitializeUser(auth0) } } @@ -77,12 +93,15 @@ class UserLoaderScreen extends Component { // connect to the redux store const mapStateToProps = (state, ownProps) => { + const { accessToken, loggedInUser } = state.user return { - loggedInUser: state.user.loggedInUser + accessToken, + loggedInUser } } const mapDispatchToProps = { + fetchAuth0Token: userActions.fetchAuth0Token, fetchOrInitializeUser: userActions.fetchOrInitializeUser } From 77a1a2c0f422a76331c75fbaea09579946a51692 Mon Sep 17 00:00:00 2001 From: binh-dam-ibigroup <56846598+binh-dam-ibigroup@users.noreply.github.com> Date: Fri, 13 Nov 2020 18:53:48 -0500 Subject: [PATCH 6/6] refactor(actions/user): Address PR comments Remove auth0 as arg for resendVerificationEmail; change arg of fetchOrInitializeUser to auth0User; cleanup. --- lib/actions/user.js | 11 +++++------ lib/components/user/user-account-screen.js | 8 ++------ lib/components/user/verify-email-screen.js | 8 ++------ lib/components/user/with-logged-in-user-support.js | 2 +- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/actions/user.js b/lib/actions/user.js index 2b33449fd..af53e96d0 100644 --- a/lib/actions/user.js +++ b/lib/actions/user.js @@ -69,9 +69,9 @@ export function fetchAuth0Token (auth0) { /** * Attempts to fetch user preferences (or set initial values if the user is being created) * into the redux state, under state.user. - * @param auth0 If provided, the auth0 login object used to initialize the default user object (with email and auth0 id). + * @param auth0User If provided, the auth0.user object used to initialize the default user object (with email and auth0 id). */ -export function fetchOrInitializeUser (auth0) { +export function fetchOrInitializeUser (auth0User) { return async function (dispatch, getState) { const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/fromtoken` @@ -100,7 +100,7 @@ export function fetchOrInitializeUser (auth0) { // TODO: Improve AWS response. const isNewAccount = status === 'error' || (user && user.result === 'ERR') - const userData = isNewAccount ? createNewUser(auth0.user) : user + const userData = isNewAccount ? createNewUser(auth0User) : user dispatch(setCurrentUser({ accessToken, user: userData })) // Also load monitored trips for existing users. @@ -153,10 +153,9 @@ export function createOrUpdateUser (userData, silentOnSuccess = false) { /** * Requests the verification email for the new user to be resent. */ -export function resendVerificationEmail (auth0) { +export function resendVerificationEmail () { return async function (dispatch, getState) { - const { apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) - const accessToken = await auth0.getAccessTokenSilently() + const { accessToken, apiBaseUrl, apiKey } = getMiddlewareVariables(getState()) // TODO: add any throttling. const requestUrl = `${apiBaseUrl}${API_OTPUSER_PATH}/verification-email` diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index 81721259e..0143e8508 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -74,11 +74,7 @@ class UserAccountScreen extends Component { // Capture whether user is a new user at this stage, and retain that value as long as this screen is active. // Reminder: When a new user progresses through the account steps, // isNewUser(loggedInUser) will change to false as the database gets updated. - isNewUser: isNewUser(props.loggedInUser), - - // Last number and last time we requested a code for (to avoid repeat SMS and not waste SMS quota). - lastPhoneNumberRequested: null, - lastPhoneRequestTime: null + isNewUser: isNewUser(props.loggedInUser) } } @@ -166,7 +162,7 @@ class UserAccountScreen extends Component { if (this.state.isNewUser) { if (!auth0.user.email_verified) { // Check and prompt for email verification first to avoid extra user wait. - formContents = + formContents = } else { // New users are shown "wizard" (step-by-step) mode // (includes when a "new" user clicks "My Account" from the account menu in the nav bar). diff --git a/lib/components/user/verify-email-screen.js b/lib/components/user/verify-email-screen.js index d0e056725..3664f5cdc 100644 --- a/lib/components/user/verify-email-screen.js +++ b/lib/components/user/verify-email-screen.js @@ -17,14 +17,10 @@ const DivSpacer = styled.div` * is to simply reload the page.) */ class VerifyEmailScreen extends Component { - _handleResendVerificationEmail = () => { - const { auth0, resendVerificationEmail } = this.props - resendVerificationEmail(auth0) - } - _handleEmailVerified = () => window.location.reload() render () { + const { resendVerificationEmail } = this.props return (

Verify your email address

@@ -48,7 +44,7 @@ class VerifyEmailScreen extends Component {