Skip to content

Commit

Permalink
open an external browser when the server config for MobileExternalBro…
Browse files Browse the repository at this point in the history
…wse is set to true (#8220)
  • Loading branch information
enahum authored Sep 16, 2024
1 parent af31eed commit f04838d
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 99 deletions.
75 changes: 75 additions & 0 deletions app/screens/sso/components/auth_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {Button} from '@rneui/base';
import React from 'react';
import {Text, View} from 'react-native';

import FormattedText from '@components/formatted_text';
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

interface AuthErrorProps {
error: string;
retry: () => void;
theme: Theme;
}

const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
button: {
marginTop: 25,
},
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 AuthError = ({error, retry, theme}: AuthErrorProps) => {
const style = getStyleSheet(theme);

return (
<View style={style.infoContainer}>
<FormattedText
id='mobile.oauth.switch_to_browser.error_title'
testID='mobile.oauth.switch_to_browser.error_title'
defaultMessage='Sign in error'
style={style.infoTitle}
/>
<Text style={style.errorText}>
{`${error}.`}
</Text>
<Button
buttonStyle={[style.button, buttonBackgroundStyle(theme, 'lg', 'primary', 'default')]}
testID='mobile.oauth.try_again'
onPress={retry}
>
<FormattedText
id='mobile.oauth.try_again'
defaultMessage='Try again'
style={buttonTextStyle(theme, 'lg', 'primary', 'default')}
/>
</Button>
</View>
);
};

export default AuthError;
55 changes: 55 additions & 0 deletions app/screens/sso/components/auth_redirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import {View} from 'react-native';

import FormattedText from '@components/formatted_text';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

interface AuthRedirectProps {
theme: Theme;
}

const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
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 AuthRedirect = ({theme}: AuthRedirectProps) => {
const style = getStyleSheet(theme);

return (
<View style={style.infoContainer}>
<FormattedText
id='mobile.oauth.switch_to_browser.title'
testID='mobile.oauth.switch_to_browser.title'
defaultMessage='Redirecting...'
style={style.infoTitle}
/>
<FormattedText
id='mobile.oauth.switch_to_browser'
testID='mobile.oauth.switch_to_browser'
defaultMessage='You are being redirected to your login provider'
style={style.infoText}
/>
</View>
);
};

export default AuthRedirect;
57 changes: 57 additions & 0 deletions app/screens/sso/components/auth_success.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import {View} from 'react-native';

import FormattedText from '@components/formatted_text';
import Loading from '@components/loading';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

interface AuthSuccessProps {
theme: Theme;
}

const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
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 AuthSuccess = ({theme}: AuthSuccessProps) => {
const style = getStyleSheet(theme);

return (
<View style={style.infoContainer}>
<Loading/>
<FormattedText
id='mobile.oauth.success.title'
testID='mobile.oauth.success.title'
defaultMessage='Authentication successful'
style={style.infoTitle}
/>
<FormattedText
id='mobile.oauth.success.description'
testID='mobile.oauth.success.description'
defaultMessage='Signing in now, just a moment...'
style={style.infoText}
/>
</View>
);
};

export default AuthSuccess;
23 changes: 19 additions & 4 deletions app/screens/sso/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {getFullErrorMessage, isErrorWithUrl} from '@utils/errors';
import {logWarning} from '@utils/log';

import SSOAuthentication from './sso_authentication';
import SSOAuthenticationWithExternalBrowser from './sso_authentication_with_external_browser';

import type {LaunchProps} from '@typings/launch';
import type {AvailableScreens} from '@typings/screens/navigation';
Expand Down Expand Up @@ -155,14 +156,28 @@ const SSO = ({
theme,
};

let authentication;
if (config.MobileExternalBrowser === 'true') {
authentication = (
<SSOAuthenticationWithExternalBrowser
{...props}
serverUrl={serverUrl!}
/>
);
} else {
authentication = (
<SSOAuthentication
{...props}
serverUrl={serverUrl!}
/>
);
}

return (
<View style={styles.flex}>
<Background theme={theme}/>
<AnimatedSafeArea style={[styles.flex, transform]}>
<SSOAuthentication
{...props}
serverUrl={serverUrl!}
/>
{authentication}
</AnimatedSafeArea>
</View>
);
Expand Down
115 changes: 20 additions & 95 deletions app/screens/sso/sso_authentication.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {Button} from '@rneui/base';
import {openAuthSessionAsync} from 'expo-web-browser';
import qs from 'querystringify';
import React, {useEffect, useState} from 'react';
import {useIntl} from 'react-intl';
import {Linking, Platform, Text, View, type EventSubscription} from 'react-native';
import {Linking, Platform, StyleSheet, View, type EventSubscription} from 'react-native';
import urlParse from 'url-parse';

import FormattedText from '@components/formatted_text';
import Loading from '@components/loading';
import {Sso} from '@constants';
import NetworkManager from '@managers/network_manager';
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
import {isBetaApp} from '@utils/general';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

interface SSOWithRedirectURLProps {
import AuthError from './components/auth_error';
import AuthRedirect from './components/auth_redirect';
import AuthSuccess from './components/auth_success';

interface SSOAuthenticationProps {
doSSOLogin: (bearerToken: string, csrfToken: string) => void;
loginError: string;
loginUrl: string;
Expand All @@ -27,41 +25,16 @@ interface SSOWithRedirectURLProps {
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 style = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 24,
},
});

const SSOAuthentication = ({doSSOLogin, loginError, loginUrl, serverUrl, setLoginError, theme}: SSOWithRedirectURLProps) => {
const SSOAuthentication = ({doSSOLogin, loginError, loginUrl, serverUrl, setLoginError, theme}: SSOAuthenticationProps) => {
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) {
Expand All @@ -84,7 +57,7 @@ const SSOAuthentication = ({doSSOLogin, loginError, loginUrl, serverUrl, setLogi
};
parsedUrl.set('query', qs.stringify(query));
const url = parsedUrl.toString();
const result = await openAuthSessionAsync(url, null, {preferEphemeralSession: true});
const result = await openAuthSessionAsync(url, null, {preferEphemeralSession: true, createTask: false});
if ('url' in result && result.url) {
const resultUrl = urlParse(result.url, true);
const bearerToken = resultUrl.query?.MMAUTHTOKEN;
Expand Down Expand Up @@ -142,65 +115,17 @@ const SSOAuthentication = ({doSSOLogin, loginError, loginUrl, serverUrl, setLogi

let content;
if (loginSuccess) {
content = (
<View style={style.infoContainer}>
<Loading/>
<FormattedText
id='mobile.oauth.success.title'
testID='mobile.oauth.success.title'
defaultMessage='Authentication successful'
style={style.infoTitle}
/>
<FormattedText
id='mobile.oauth.success.description'
testID='mobile.oauth.success.description'
defaultMessage='Signing in now, just a moment...'
style={style.infoText}
/>
</View>
);
content = (<AuthSuccess theme={theme}/>);
} else if (loginError || error) {
content = (
<View style={style.infoContainer}>
<FormattedText
id='mobile.oauth.switch_to_browser.error_title'
testID='mobile.oauth.switch_to_browser.error_title'
defaultMessage='Sign in error'
style={style.infoTitle}
/>
<Text style={style.errorText}>
{`${loginError || error}.`}
</Text>
<Button
buttonStyle={[style.button, buttonBackgroundStyle(theme, 'lg', 'primary', 'default')]}
testID='mobile.oauth.try_again'
onPress={() => init()}
>
<FormattedText
id='mobile.oauth.try_again'
defaultMessage='Try again'
style={buttonTextStyle(theme, 'lg', 'primary', 'default')}
/>
</Button>
</View>
<AuthError
error={loginError || error}
retry={init}
theme={theme}
/>
);
} else {
content = (
<View style={style.infoContainer}>
<FormattedText
id='mobile.oauth.switch_to_browser.title'
testID='mobile.oauth.switch_to_browser.title'
defaultMessage='Redirecting...'
style={style.infoTitle}
/>
<FormattedText
id='mobile.oauth.switch_to_browser'
testID='mobile.oauth.switch_to_browser'
defaultMessage='You are being redirected to your login provider'
style={style.infoText}
/>
</View>
);
content = (<AuthRedirect theme={theme}/>);
}

return (
Expand Down
Loading

0 comments on commit f04838d

Please sign in to comment.