mattermost-mobile/app/screens/sso/sso_authentication_with_external_browser.tsx
kondo 52e05fba13
MM-62411 Retrying SSO does not trigger a redirect (#8458)
* The server url is being registered as invalid.
Even if the URL is valid upon retry, it is still being registered as invalid.

* Delete unnecessary import

* Delete serverUrl

* Delete serverUrl props on test
2025-01-16 13:02:23 +01:00

162 lines
5.1 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import qs from 'querystringify';
import React, {useEffect, useState} from 'react';
import {useIntl} from 'react-intl';
import {Linking, Platform, View} from 'react-native';
import urlParse from 'url-parse';
import {Sso} from '@constants';
import {isErrorWithMessage} from '@utils/errors';
import {isBetaApp} from '@utils/general';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {tryOpenURL} from '@utils/url';
import AuthError from './components/auth_error';
import AuthRedirect from './components/auth_redirect';
import AuthSuccess from './components/auth_success';
interface SSOWithRedirectURLProps {
doSSOLogin: (bearerToken: string, csrfToken: string) => void;
loginError: string;
loginUrl: string;
setLoginError: (value: string) => void;
theme: Theme;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
button: {
marginTop: 25,
},
container: {
flex: 1,
paddingHorizontal: 24,
},
errorText: {
color: changeOpacity(theme.centerChannelColor, 0.72),
textAlign: 'center',
...typography('Body', 200, 'Regular'),
},
infoContainer: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
infoText: {
color: changeOpacity(theme.centerChannelColor, 0.72),
...typography('Body', 100, 'Regular'),
},
infoTitle: {
color: theme.centerChannelColor,
marginBottom: 4,
...typography('Heading', 700),
},
};
});
const SSOAuthenticationWithExternalBrowser = ({doSSOLogin, loginError, loginUrl, setLoginError, theme}: SSOWithRedirectURLProps) => {
const [error, setError] = useState<string>('');
const [loginSuccess, setLoginSuccess] = useState(false);
const style = getStyleSheet(theme);
const intl = useIntl();
let customUrlScheme = Sso.REDIRECT_URL_SCHEME;
if (isBetaApp) {
customUrlScheme = Sso.REDIRECT_URL_SCHEME_DEV;
}
const redirectUrl = customUrlScheme + 'callback';
const init = (resetErrors = true) => {
setLoginSuccess(false);
if (resetErrors !== false) {
setError('');
setLoginError('');
}
const parsedUrl = urlParse(loginUrl, true);
const query: Record<string, string> = {
...parsedUrl.query,
redirect_to: redirectUrl,
};
parsedUrl.set('query', qs.stringify(query));
const url = parsedUrl.toString();
const onError = (e: Error) => {
let message;
if (e && Platform.OS === 'android' && isErrorWithMessage(e) && e.message.match(/no activity found to handle intent/i)) {
message = intl.formatMessage({
id: 'mobile.oauth.failed_to_open_link_no_browser',
defaultMessage: 'The link failed to open. Please verify that a browser is installed on the device.',
});
} else {
message = intl.formatMessage({
id: 'mobile.oauth.failed_to_open_link',
defaultMessage: 'The link failed to open. Please try again.',
});
}
setError(
message,
);
};
tryOpenURL(url, onError);
};
useEffect(() => {
const onURLChange = ({url}: { url: string }) => {
if (url && url.startsWith(redirectUrl)) {
const parsedUrl = urlParse(url, true);
const bearerToken = parsedUrl.query?.MMAUTHTOKEN;
const csrfToken = parsedUrl.query?.MMCSRF;
if (bearerToken && csrfToken) {
setLoginSuccess(true);
doSSOLogin(bearerToken, csrfToken);
} else {
setError(
intl.formatMessage({
id: 'mobile.oauth.failed_to_login',
defaultMessage: 'Your login attempt failed. Please try again.',
}),
);
}
}
};
const listener = Linking.addEventListener('url', onURLChange);
const timeout = setTimeout(() => {
init(false);
}, 1000);
return () => {
listener.remove();
clearTimeout(timeout);
};
}, []);
let content;
if (loginSuccess) {
content = (<AuthSuccess theme={theme}/>);
} else if (loginError || error) {
content = (
<AuthError
error={loginError || error}
retry={init}
theme={theme}
/>
);
} else {
content = (<AuthRedirect theme={theme}/>);
}
return (
<View
style={style.container}
testID='sso.redirect_url'
>
{content}
</View>
);
};
export default SSOAuthenticationWithExternalBrowser;