diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index e14e36d44..21de094db 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -18,4 +18,6 @@
How long in milliseconds the mobile app should wait for the server to respond.
EMM Vendor or Company Name
Name of the EMM vendor or company deploying the app. Used in help text when prompting for passcodes so users are aware why the app is being protected.
+ In-App Session Auth
+ Instead of default flow from the mobile browser, enforce SSO with the WebView.
diff --git a/android/app/src/main/res/xml/app_restrictions.xml b/android/app/src/main/res/xml/app_restrictions.xml
index 27a0e9e2e..2287350b5 100644
--- a/android/app/src/main/res/xml/app_restrictions.xml
+++ b/android/app/src/main/res/xml/app_restrictions.xml
@@ -7,6 +7,12 @@
android:description="@string/inAppPinCode_description"
android:restrictionType="string"
android:defaultValue="false" />
+
{
@@ -106,6 +107,8 @@ class EMMProvider {
this.jailbreakProtection = managedConfig.jailbreakProtection === 'true';
this.vendor = managedConfig.vendor || 'Mattermost';
+ this.inAppSessionAuth = managedConfig.inAppSessionAuth === 'true';
+
const credentials = await getAppCredentials();
if (!credentials) {
this.emmServerUrl = managedConfig.serverUrl;
diff --git a/app/screens/sso/index.tsx b/app/screens/sso/index.tsx
index 5cd9ce245..99144d145 100644
--- a/app/screens/sso/index.tsx
+++ b/app/screens/sso/index.tsx
@@ -11,6 +11,7 @@ import {ViewTypes} from 'app/constants';
import tracker from 'app/utils/time_tracker';
import {scheduleExpiredNotification} from '@actions/views/session';
import {ssoLogin} from '@actions/views/user';
+import emmProvider from '@init/emm_provider';
import {DispatchFunc} from '@mm-redux/types/actions';
import {Client4} from '@mm-redux/client';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
@@ -112,18 +113,19 @@ function SSO({intl, ssoType}: SSOProps) {
theme,
};
- if (isSSOWithRedirectURLAvailable) {
+ if (!isSSOWithRedirectURLAvailable || emmProvider.inAppSessionAuth === true) {
return (
-
+
);
}
+
return (
-
+
);
}
diff --git a/app/screens/sso/sso_with_redirect_url.tsx b/app/screens/sso/sso_with_redirect_url.tsx
index c77d84130..81bc3c952 100644
--- a/app/screens/sso/sso_with_redirect_url.tsx
+++ b/app/screens/sso/sso_with_redirect_url.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React from 'react';
import {intlShape} from 'react-intl';
-import {Linking, Text, TouchableOpacity, View} from 'react-native';
+import {Linking, Platform, Text, TouchableOpacity, View} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import {SafeAreaView} from 'react-native-safe-area-context';
import urlParse from 'url-parse';
@@ -57,12 +57,23 @@ function SSOWithRedirectURL({
});
const url = parsedUrl.toString();
- const onError = () => setError(
- intl.formatMessage({
- id: 'mobile.oauth.failed_to_open_link',
- defaultMessage: 'The link failed to open. Please try again.',
- }),
- );
+ const onError = (e: Error) => {
+ let message;
+ if (e && Platform.OS === 'android' && 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 if a browser is installed in the current space.',
+ });
+ } 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);
};
@@ -151,6 +162,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
fontSize: 16,
fontWeight: '400',
lineHeight: 23,
+ textAlign: 'center',
},
infoContainer: {
alignItems: 'center',
diff --git a/app/utils/general.js b/app/utils/general.js
index 4e708cff5..b2fbce1de 100644
--- a/app/utils/general.js
+++ b/app/utils/general.js
@@ -49,6 +49,10 @@ export function alertErrorIfInvalidPermissions(result) {
}
}
+export function emptyErrorHandlingFunction(e) { // eslint-disable-line no-empty-function, @typescript-eslint/no-unused-vars
+
+}
+
export function emptyFunction() { // eslint-disable-line no-empty-function
}
diff --git a/app/utils/url.js b/app/utils/url.js
index 3917564f5..918a1d92a 100644
--- a/app/utils/url.js
+++ b/app/utils/url.js
@@ -10,7 +10,7 @@ import {Files} from '@mm-redux/constants';
import {getCurrentServerUrl} from '@init/credentials';
import {DeepLinkTypes} from '@constants';
-import {emptyFunction} from '@utils/general';
+import {emptyErrorHandlingFunction, emptyFunction} from '@utils/general';
const ytRegex = /(?:http|https):\/\/(?:www\.|m\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&?]*)/;
@@ -186,7 +186,7 @@ export async function getURLAndMatch(href, serverURL, siteURL) {
return {url, match};
}
-export function tryOpenURL(url, onError = emptyFunction, onSuccess = emptyFunction) {
+export function tryOpenURL(url, onError = emptyErrorHandlingFunction, onSuccess = emptyFunction) {
Linking.openURL(url).
then(onSuccess).
catch(onError);
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index fcd05e3dc..3f09ea1a2 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -407,6 +407,7 @@
"mobile.notification_settings.save_failed_title": "Connection issue",
"mobile.oauth.failed_to_login": "Your login attempt failed. Please try again.",
"mobile.oauth.failed_to_open_link": "The link failed to open. Please try again.",
+ "mobile.oauth.failed_to_open_link_no_browser": "The link failed to open. Please verify if a browser is installed in the current space.",
"mobile.oauth.restart_login": "Restart login",
"mobile.oauth.something_wrong": "Something went wrong",
"mobile.oauth.something_wrong.okButon": "OK",