Skip to content
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
34 changes: 34 additions & 0 deletions src/libs/SessionUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import lodashGet from 'lodash/get';

/**
* Determine if the transitioning user is logging in as a new user.
*
* @param {String} transitionURL
* @param {String} sessionEmail
* @returns {Boolean}
*/
function isLoggingInAsNewUser(transitionURL, sessionEmail) {
// The OldDot mobile app does not URL encode the parameters, but OldDot web
// does. We don't want to deploy OldDot mobile again, so as a work around we
// compare the session email to both the decoded and raw email from the transition link.
const params = new URLSearchParams(transitionURL);
const paramsEmail = params.get('email');

// If the email param matches what is stored in the session then we are
// definitely not logging in as a new user
if (paramsEmail === sessionEmail) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you confirm this handles all four cases?

  1. Unencoded new user (OldApp new user)
  2. Encoded new user (OldDot new user)
  3. Unencoded existing user (OldApp existing user)
  4. Encoded existing user (OldDot existing user)

Specifically interested in case 2? e.g. when do we return true when paramsEmail !== sessionEmail?

@neil-marcellini neil-marcellini Jun 17, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  1. Unencoded new user (OldApp new user)
    The params and session emails don't match, then the linked and session emails also don't match so we correctly return true.

  2. Encoded new user (OldDot new user)
    The params and session emails don't match so we check the linked and session emails which
    also don't match so we correctly return true.

  3. Unencoded existing user (OldApp existing user)
    The params and session emails don't match, but then the linked email matches the session email so we correctly return false.

  4. Encoded existing user (OldDot existing user)
    The params and session emails match so we correctly return false.

return false;
}

// If they do not match it might be due to encoding, so check the raw value
// Capture the un-encoded text in the email param
const emailParamRegex = /[?&]email=([^&]*)/g;
const matches = emailParamRegex.exec(transitionURL);
const linkedEmail = lodashGet(matches, 1, null);
return linkedEmail !== sessionEmail;
}

export {
// eslint-disable-next-line import/prefer-default-export
Comment thread
neil-marcellini marked this conversation as resolved.
isLoggingInAsNewUser,
};
4 changes: 2 additions & 2 deletions src/libs/actions/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as Policy from './Policy';
import NetworkConnection from '../NetworkConnection';
import Navigation from '../Navigation/Navigation';
import ROUTES from '../../ROUTES';
import * as SessionUtils from '../SessionUtils';

let currentUserAccountID;
Onyx.connect({
Expand Down Expand Up @@ -171,8 +172,7 @@ function setUpPoliciesAndNavigate(session) {
const path = new URL(url).pathname;
const params = new URLSearchParams(url);
const exitTo = params.get('exitTo');
const email = params.get('email');
const isLoggingInAsNewUser = session.email !== email;
const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(url, session.email);
const shouldCreateFreePolicy = !isLoggingInAsNewUser
&& Str.startsWith(path, Str.normalizeUrl(ROUTES.TRANSITION_FROM_OLD_DOT))
&& exitTo === ROUTES.WORKSPACE_NEW;
Expand Down
40 changes: 18 additions & 22 deletions src/pages/LogOutPreviousUserPage.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import React, {Component} from 'react';
import {Linking} from 'react-native';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import ONYXKEYS from '../ONYXKEYS';
import * as Session from '../libs/actions/Session';
import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator';
import Navigation from '../libs/Navigation/Navigation';
import * as SessionUtils from '../libs/SessionUtils';

const propTypes = {
/** The parameters needed to authenticate with a short lived token are in the URL */
route: PropTypes.shape({
/** Each parameter passed via the URL */
params: PropTypes.shape({
/** The email of the transitioning user */
email: PropTypes.string,
}),
}).isRequired,

/** The data about the current session which will be set once the user is authenticated and we return to this component as an AuthScreen */
session: PropTypes.shape({
/** The user's email for the current session */
Expand All @@ -26,20 +19,23 @@ const propTypes = {

class LogOutPreviousUserPage extends Component {
componentDidMount() {
const paramsEmail = lodashGet(this.props.route, 'params.email', null);
const sessionEmail = lodashGet(this.props.session, 'email', '');
if (paramsEmail !== sessionEmail) {
Session.signOutAndRedirectToSignIn();
return;
}
Linking.getInitialURL()
.then((transitionURL) => {
const sessionEmail = lodashGet(this.props.session, 'email', '');
const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL, sessionEmail);
if (isLoggingInAsNewUser) {
Session.signOutAndRedirectToSignIn();
return;
}

// Since we conditionally render navigators in the AppNavigator, when we
// sign out and sign back in there will be a moment where no navigator
// is rendered and the navigation state is null. We can't navigate at
// that time, so we use a promise to delay transition navigation until
// it is ready. We set the navigation ready here since we know that the
// navigator is now rendered.
Navigation.setIsNavigationReady();
// Since we conditionally render navigators in the AppNavigator, when we
// sign out and sign back in there will be a moment where no navigator
// is rendered and the navigation state is null. We can't navigate at
// that time, so we use a promise to delay transition navigation until
// it is ready. We set the navigation ready here since we know that the
// navigator is now rendered.
Navigation.setIsNavigationReady();
});
}

render() {
Expand Down