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
1 change: 0 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ const translations = {
invalidRateError: 'Please enter a valid rate.',
lowRateError: 'Rate must be greater than 0.',
email: 'Please enter a valid email address.',
login: 'An error occurred while logging in. Please try again.',
},
comma: 'comma',
semicolon: 'semicolon',
Expand Down
1 change: 0 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@ const translations = {
invalidRateError: 'Por favor, introduce una tarifa válida.',
lowRateError: 'La tarifa debe ser mayor que 0.',
email: 'Por favor, introduzca una dirección de correo electrónico válida.',
login: 'Se produjo un error al iniciar sesión. Por favor intente nuevamente.',
},
comma: 'la coma',
semicolon: 'el punto y coma',
Expand Down
28 changes: 1 addition & 27 deletions src/libs/LoginUtils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {PUBLIC_DOMAINS, Str} from 'expensify-common';
import Onyx from 'react-native-onyx';
import CONFIG from '@src/CONFIG';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import * as Session from './actions/Session';
import Navigation from './Navigation/Navigation';
import {parsePhoneNumber} from './PhoneNumber';

let countryCodeByIP: number;
Expand Down Expand Up @@ -79,26 +75,4 @@ function areEmailsFromSamePrivateDomain(email1: string, email2: string): boolean
return Str.extractEmailDomain(email1).toLowerCase() === Str.extractEmailDomain(email2).toLowerCase();
}

function postSAMLLogin(body: FormData): Promise<Response | void> {
return fetch(CONFIG.EXPENSIFY.SAML_URL, {
method: CONST.NETWORK.METHOD.POST,
body,
credentials: 'omit',
}).then((response) => {
if (!response.ok) {
throw new Error('An error occurred while logging in. Please try again');
}
return response.json() as Promise<Response>;
});
}

function handleSAMLLoginError(errorMessage: string, cleanSignInData: boolean) {
if (cleanSignInData) {
Session.clearSignInData();
}

Session.setAccountError(errorMessage);
Navigation.goBack(ROUTES.HOME);
}

export {getPhoneNumberWithoutSpecialChars, appendCountryCode, isEmailPublicDomain, validateNumber, getPhoneLogin, areEmailsFromSamePrivateDomain, postSAMLLogin, handleSAMLLoginError};
export {getPhoneNumberWithoutSpecialChars, appendCountryCode, isEmailPublicDomain, validateNumber, getPhoneLogin, areEmailsFromSamePrivateDomain};
2 changes: 0 additions & 2 deletions src/pages/LogInWithShortLivedAuthTokenPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ function LogInWithShortLivedAuthTokenPage({route}: LogInWithShortLivedAuthTokenP
// For HybridApp we have separate logic to handle transitions.
if (!NativeModules.HybridAppModule && exitTo) {
Navigation.isNavigationReady().then(() => {
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
Navigation.navigate(exitTo as Route);
});
}
Expand Down
68 changes: 12 additions & 56 deletions src/pages/signin/SAMLSignInPage/index.native.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useCallback, useState} from 'react';
import {useOnyx} from 'react-native-onyx';
import WebView from 'react-native-webview';
import type {WebViewNativeEvent} from 'react-native-webview/lib/WebViewTypes';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import getPlatform from '@libs/getPlatform';
import getUAForWebView from '@libs/getUAForWebView';
import Log from '@libs/Log';
import * as LoginUtils from '@libs/LoginUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as Session from '@userActions/Session';
import CONFIG from '@src/CONFIG';
Expand All @@ -20,45 +17,15 @@ import ROUTES from '@src/ROUTES';
function SAMLSignInPage() {
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);
const samlLoginURL = `${CONFIG.EXPENSIFY.SAML_URL}?email=${credentials?.login}&referer=${CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER}&platform=${getPlatform()}`;
const [showNavigation, shouldShowNavigation] = useState(true);
const [SAMLUrl, setSAMLUrl] = useState('');
const webViewRef = useRef<WebView>(null);
const {translate} = useLocalize();

useEffect(() => {
// If we don't have a valid login to pass here, direct the user back to a clean sign in state to try again
if (!credentials?.login) {
LoginUtils.handleSAMLLoginError(translate('common.error.email'), true);
return;
}

// If we've already gotten a url back to log into the user's Identity Provider (IdP), then don't re-fetch it
if (SAMLUrl) {
return;
}

const body = new FormData();
body.append('email', credentials.login);
body.append('referer', CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER);
body.append('platform', getPlatform());
LoginUtils.postSAMLLogin(body)
.then((response) => {
if (!response || !response.url) {
LoginUtils.handleSAMLLoginError(translate('common.error.login'), false);
return;
}
setSAMLUrl(response.url);
})
.catch((error: Error) => {
LoginUtils.handleSAMLLoginError(error.message ?? translate('common.error.login'), false);
});
}, [credentials?.login, SAMLUrl, translate]);

/**
* Handles in-app navigation once we get a response back from Expensify
*/
const handleNavigationStateChange = useCallback(
({url}: WebViewNativeEvent) => {
Log.info('SAMLSignInPage - Handling SAML navigation change');
// If we've gotten a callback then remove the option to navigate back to the sign-in page
if (url.includes('loginCallback')) {
shouldShowNavigation(false);
Expand All @@ -75,12 +42,7 @@ function SAMLSignInPage() {
if (searchParams.has('error')) {
Session.clearSignInData();
Session.setAccountError(searchParams.get('error') ?? '');

Navigation.isNavigationReady().then(() => {
// We must call goBack() to remove the /transition route from history
Navigation.goBack();
Navigation.navigate(ROUTES.HOME);
});
Navigation.navigate(ROUTES.HOME);
}
},
[credentials?.login, shouldShowNavigation, account?.isLoading],
Expand All @@ -104,20 +66,14 @@ function SAMLSignInPage() {
/>
)}
<FullPageOfflineBlockingView>
{!SAMLUrl ? (
<SAMLLoadingIndicator />
) : (
<WebView
ref={webViewRef}
originWhitelist={['https://*']}
source={{uri: SAMLUrl}}
userAgent={getUAForWebView()}
incognito // 'incognito' prop required for Android, issue here https://github.com/react-native-webview/react-native-webview/issues/1352
startInLoadingState
renderLoading={() => <SAMLLoadingIndicator />}
onNavigationStateChange={handleNavigationStateChange}
/>
)}
<WebView
originWhitelist={['https://*']}
source={{uri: samlLoginURL}}
incognito // 'incognito' prop required for Android, issue here https://github.com/react-native-webview/react-native-webview/issues/1352
startInLoadingState
renderLoading={() => <SAMLLoadingIndicator />}
onNavigationStateChange={handleNavigationStateChange}
/>
</FullPageOfflineBlockingView>
</ScreenWrapper>
);
Expand Down
39 changes: 9 additions & 30 deletions src/pages/signin/SAMLSignInPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
import React, {useEffect} from 'react';
import {useOnyx} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import SAMLLoadingIndicator from '@components/SAMLLoadingIndicator';
import useLocalize from '@hooks/useLocalize';
import * as LoginUtils from '@libs/LoginUtils';
import CONFIG from '@src/CONFIG';
import ONYXKEYS from '@src/ONYXKEYS';
import type {SAMLSignInPageOnyxProps, SAMLSignInPageProps} from './types';

function SAMLSignInPage() {
const {translate} = useLocalize();
const [credentials] = useOnyx(ONYXKEYS.CREDENTIALS);

function SAMLSignInPage({credentials}: SAMLSignInPageProps) {
useEffect(() => {
// If we don't have a valid login to pass here, direct the user back to a clean sign in state to try again
if (!credentials?.login) {
LoginUtils.handleSAMLLoginError(translate('common.error.email'), true);
return;
}

const body = new FormData();
body.append('email', credentials.login);
body.append('referer', CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER);

LoginUtils.postSAMLLogin(body)
.then((response) => {
if (!response || !response.url) {
LoginUtils.handleSAMLLoginError(translate('common.error.login'), false);
return;
}
window.location.replace(response.url);
})
.catch((error: Error) => {
LoginUtils.handleSAMLLoginError(error.message ?? translate('common.error.login'), false);
});
}, [credentials?.login, translate]);
window.location.replace(`${CONFIG.EXPENSIFY.SAML_URL}?email=${credentials?.login}&referer=${CONFIG.EXPENSIFY.EXPENSIFY_CASH_REFERER}`);
}, [credentials?.login]);

return <SAMLLoadingIndicator />;
}

SAMLSignInPage.displayName = 'SAMLSignInPage';

export default SAMLSignInPage;
export default withOnyx<SAMLSignInPageProps, SAMLSignInPageOnyxProps>({
account: {key: ONYXKEYS.ACCOUNT},
credentials: {key: ONYXKEYS.CREDENTIALS},
})(SAMLSignInPage);