Skip to content

Commit

Permalink
Merge pull request #55432 from Expensify/revert-52658-nikki-saml-use-…
Browse files Browse the repository at this point in the history
…post

Revert "Use POST for SAML login"

(cherry picked from commit ecb1e51)

(CP triggered by mountiny)
  • Loading branch information
NikkiWines authored and OSBotify committed Jan 20, 2025
1 parent 517f585 commit b00cd6b
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 117 deletions.
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);

0 comments on commit b00cd6b

Please sign in to comment.