Fix SSO and clear cookies (#4422)
* Fix SSO and clear cookies * Fix unit tests * Removed unnecessary argument Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com> Co-authored-by: Miguel Alatzar <migbot@users.noreply.github.com>
This commit is contained in:
parent
e0d2fba243
commit
023109e250
9 changed files with 104 additions and 111 deletions
|
|
@ -5,6 +5,7 @@ import {Keyboard, Platform} from 'react-native';
|
|||
import {Navigation} from 'react-native-navigation';
|
||||
import merge from 'deepmerge';
|
||||
|
||||
import {Preferences} from '@mm-redux/constants';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
|
|
@ -83,7 +84,7 @@ export function resetToChannel(passProps = {}) {
|
|||
}
|
||||
|
||||
export function resetToSelectServer(allowOtherServers) {
|
||||
const theme = getThemeFromState();
|
||||
const theme = Preferences.THEMES.default;
|
||||
|
||||
Navigation.setRoot({
|
||||
root: {
|
||||
|
|
|
|||
|
|
@ -157,33 +157,35 @@ class GlobalEventHandler {
|
|||
emmProvider.handleManagedConfig(true);
|
||||
};
|
||||
|
||||
onLogout = async () => {
|
||||
Store.redux.dispatch(closeWebSocket(false));
|
||||
Store.redux.dispatch(setServerVersion(''));
|
||||
|
||||
if (analytics) {
|
||||
await analytics.reset();
|
||||
}
|
||||
|
||||
removeAppCredentials();
|
||||
deleteFileCache();
|
||||
resetMomentLocale();
|
||||
|
||||
// TODO: Handle when multi-server support is added
|
||||
clearCookiesAndWebData = async () => {
|
||||
try {
|
||||
await CookieManager.clearAll(Platform.OS === 'ios');
|
||||
} catch (error) {
|
||||
// Nothing to clear
|
||||
}
|
||||
PushNotifications.clearNotifications();
|
||||
const cacheDir = RNFetchBlob.fs.dirs.CacheDir;
|
||||
const mainPath = cacheDir.split('/').slice(0, -1).join('/');
|
||||
|
||||
mattermostBucket.removePreference('cert');
|
||||
mattermostBucket.removePreference('emm');
|
||||
if (Platform.OS === 'ios') {
|
||||
mattermostBucket.removeFile('entities');
|
||||
} else {
|
||||
switch (Platform.OS) {
|
||||
case 'ios': {
|
||||
const mainPath = RNFetchBlob.fs.dirs.DocumentDir.split('/').slice(0, -1).join('/');
|
||||
const libraryDir = `${mainPath}/Library`;
|
||||
const cookiesDir = `${libraryDir}/Cookies`;
|
||||
const cookies = await RNFetchBlob.fs.exists(cookiesDir);
|
||||
const webkitDir = `${libraryDir}/WebKit`;
|
||||
const webkit = await RNFetchBlob.fs.exists(webkitDir);
|
||||
|
||||
if (cookies) {
|
||||
RNFetchBlob.fs.unlink(cookiesDir);
|
||||
}
|
||||
|
||||
if (webkit) {
|
||||
RNFetchBlob.fs.unlink(webkitDir);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'android': {
|
||||
const cacheDir = RNFetchBlob.fs.dirs.CacheDir;
|
||||
const mainPath = cacheDir.split('/').slice(0, -1).join('/');
|
||||
const cookies = await RNFetchBlob.fs.exists(`${mainPath}/app_webview/Cookies`);
|
||||
const cookiesJ = await RNFetchBlob.fs.exists(`${mainPath}/app_webview/Cookies-journal`);
|
||||
if (cookies) {
|
||||
|
|
@ -193,7 +195,31 @@ class GlobalEventHandler {
|
|||
if (cookiesJ) {
|
||||
RNFetchBlob.fs.unlink(`${mainPath}/app_webview/Cookies-journal`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onLogout = async () => {
|
||||
Store.redux.dispatch(closeWebSocket(false));
|
||||
Store.redux.dispatch(setServerVersion(''));
|
||||
|
||||
if (analytics) {
|
||||
await analytics.reset();
|
||||
}
|
||||
|
||||
mattermostBucket.removePreference('cert');
|
||||
mattermostBucket.removePreference('emm');
|
||||
if (Platform.OS === 'ios') {
|
||||
mattermostBucket.removeFile('entities');
|
||||
}
|
||||
|
||||
removeAppCredentials();
|
||||
deleteFileCache();
|
||||
resetMomentLocale();
|
||||
|
||||
await this.clearCookiesAndWebData();
|
||||
PushNotifications.clearNotifications();
|
||||
|
||||
if (this.launchApp) {
|
||||
this.launchApp();
|
||||
|
|
|
|||
|
|
@ -7,13 +7,11 @@ import {intlShape} from 'react-intl';
|
|||
import {
|
||||
Dimensions,
|
||||
Image,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import Button from 'react-native-button';
|
||||
import CookieManager from '@react-native-community/cookies';
|
||||
|
||||
import {goToScreen} from '@actions/navigation';
|
||||
import LocalConfig from '@assets/config';
|
||||
|
|
@ -23,6 +21,7 @@ import FormattedText from '@components/formatted_text';
|
|||
import StatusBar from '@components/status_bar';
|
||||
import {paddingHorizontal as padding} from '@components/safe_area_view/iphone_x_spacing';
|
||||
import {ViewTypes} from '@constants';
|
||||
import globalEventHandler from '@init/global_event_handler';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
|
|
@ -50,11 +49,8 @@ export default class LoginOptions extends PureComponent {
|
|||
const {intl} = this.context;
|
||||
const screen = 'Login';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'});
|
||||
try {
|
||||
await CookieManager.clearAll(Platform.OS === 'ios');
|
||||
} catch (error) {
|
||||
// Nothing to clear
|
||||
}
|
||||
|
||||
globalEventHandler.clearCookiesAndWebData();
|
||||
goToScreen(screen, title);
|
||||
});
|
||||
|
||||
|
|
@ -62,11 +58,8 @@ export default class LoginOptions extends PureComponent {
|
|||
const {intl} = this.context;
|
||||
const screen = 'SSO';
|
||||
const title = intl.formatMessage({id: 'mobile.routes.sso', defaultMessage: 'Single Sign-On'});
|
||||
try {
|
||||
await CookieManager.clearAll(Platform.OS === 'ios');
|
||||
} catch (error) {
|
||||
// Nothing to clear
|
||||
}
|
||||
|
||||
globalEventHandler.clearCookiesAndWebData();
|
||||
goToScreen(screen, title, {ssoType});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const HEADERS = {
|
|||
'X-Mobile-App': 'mattermost',
|
||||
};
|
||||
|
||||
const postMessageJS = "window.ReactNativeWebView.postMessage(document.body.innerText, '*');";
|
||||
const postMessageJS = 'window.ReactNativeWebView.postMessage(document.body.innerText);';
|
||||
|
||||
// Used to make sure that OneLogin forms scale appropriately on both platforms.
|
||||
const oneLoginFormScalingJS = `
|
||||
|
|
@ -104,6 +104,44 @@ class SSO extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.cookiesTimeout);
|
||||
}
|
||||
|
||||
extractCookie = (parsedUrl) => {
|
||||
CookieManager.get(parsedUrl.origin, true).then((res) => {
|
||||
const mmtoken = res.MMAUTHTOKEN;
|
||||
const token = typeof mmtoken === 'object' ? mmtoken.value : mmtoken;
|
||||
|
||||
if (token) {
|
||||
clearTimeout(this.cookiesTimeout);
|
||||
this.setState({renderWebView: false});
|
||||
const {
|
||||
ssoLogin,
|
||||
} = this.props.actions;
|
||||
|
||||
Client4.setToken(token);
|
||||
if (this.props.serverUrl !== parsedUrl.origin) {
|
||||
const original = urlParse(this.props.serverUrl);
|
||||
|
||||
// Check whether we need to set a sub-path
|
||||
parsedUrl.set('pathname', original.pathname || '');
|
||||
Client4.setUrl(parsedUrl.href);
|
||||
}
|
||||
ssoLogin(token).then((result) => {
|
||||
if (result.error) {
|
||||
this.onLoadEndError(result.error);
|
||||
return;
|
||||
}
|
||||
this.goToChannel();
|
||||
});
|
||||
} else if (this.webView && !this.state.error) {
|
||||
this.webView.injectJavaScript(postMessageJS);
|
||||
this.cookiesTimeout = setTimeout(this.extractCookie.bind(null, parsedUrl), 250);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
goToChannel = () => {
|
||||
tracker.initialLoad = Date.now();
|
||||
|
||||
|
|
@ -122,6 +160,7 @@ class SSO extends PureComponent {
|
|||
status_code: statusCode,
|
||||
} = response;
|
||||
if (id && message && statusCode !== 200) {
|
||||
clearTimeout(this.cookiesTimeout);
|
||||
this.setState({error: message});
|
||||
}
|
||||
}
|
||||
|
|
@ -158,35 +197,7 @@ class SSO extends PureComponent {
|
|||
}
|
||||
|
||||
if (isLastRedirect) {
|
||||
CookieManager.get(parsed.origin, this.useWebkit).then((res) => {
|
||||
const mmtoken = res.MMAUTHTOKEN;
|
||||
const token = typeof mmtoken === 'object' ? mmtoken.value : mmtoken;
|
||||
|
||||
if (token) {
|
||||
this.setState({renderWebView: false});
|
||||
const {
|
||||
ssoLogin,
|
||||
} = this.props.actions;
|
||||
|
||||
Client4.setToken(token);
|
||||
if (this.props.serverUrl !== parsed.origin) {
|
||||
const original = urlParse(this.props.serverUrl);
|
||||
|
||||
// Check whether we need to set a sub-path
|
||||
parsed.set('pathname', original.pathname || '');
|
||||
Client4.setUrl(parsed.href);
|
||||
}
|
||||
ssoLogin(token).then((result) => {
|
||||
if (result.error) {
|
||||
this.onLoadEndError(result.error);
|
||||
return;
|
||||
}
|
||||
this.goToChannel();
|
||||
});
|
||||
} else if (this.webView && !this.state.error) {
|
||||
this.webView.injectJavaScript(postMessageJS);
|
||||
}
|
||||
});
|
||||
this.extractCookie(parsed);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -235,9 +246,10 @@ class SSO extends PureComponent {
|
|||
onShouldStartLoadWithRequest={() => true}
|
||||
injectedJavaScript={jsCode}
|
||||
onLoadEnd={this.onLoadEnd}
|
||||
onMessage={messagingEnabled ? this.onMessage : null}
|
||||
onMessage={(messagingEnabled || Platform.OS === 'android') ? this.onMessage : null}
|
||||
sharedCookiesEnabled={Platform.OS === 'android'}
|
||||
cacheEnabled={false}
|
||||
useSharedProcessPool={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ PODS:
|
|||
- React-jsinspector (0.62.2)
|
||||
- react-native-cameraroll (1.6.2):
|
||||
- React
|
||||
- react-native-cookies (2.0.9):
|
||||
- react-native-cookies (3.0.0):
|
||||
- React
|
||||
- react-native-document-picker (3.4.0):
|
||||
- React
|
||||
|
|
@ -597,7 +597,7 @@ SPEC CHECKSUMS:
|
|||
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
|
||||
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
|
||||
react-native-cameraroll: 94bec91c68b94ac946c61b497b594bb38692c41b
|
||||
react-native-cookies: bc1f91a504ee091f1998a1b6c7ff4b7a9ed5b80e
|
||||
react-native-cookies: 6fe6a70d3b2fd8e81ddcd94d60a87727010d9b2e
|
||||
react-native-document-picker: d694111879537cec2c258a1dcd2243d9df746824
|
||||
react-native-hw-keyboard-event: b517cefb8d5c659a38049c582de85ff43337dc53
|
||||
react-native-image-picker: 668e72d0277dc8c12ae90e835507c1eddd2e4f85
|
||||
|
|
|
|||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -5223,9 +5223,9 @@
|
|||
}
|
||||
},
|
||||
"@react-native-community/cookies": {
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cookies/-/cookies-2.0.9.tgz",
|
||||
"integrity": "sha512-gZvnLQjkpZX//4/CIOX4lOFdfOT2gQi/Ov1t+K6D2KZ0GyqY1CQ9xe0F1NsRSIGciTUkxOPTeway/LRWgwHsxQ==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cookies/-/cookies-3.0.0.tgz",
|
||||
"integrity": "sha512-ms/CsrBlHffTyTRhXsKSmSK6WlWScG8wklfSmkr4WeeA0j0AQ1sknTg8YNsK0bMr6Ij1uDuvX9t96vZrFGpYqQ==",
|
||||
"requires": {
|
||||
"invariant": "^2.2.4"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"@babel/runtime": "7.9.6",
|
||||
"@react-native-community/async-storage": "1.10.1",
|
||||
"@react-native-community/cameraroll": "1.6.2",
|
||||
"@react-native-community/cookies": "2.0.9",
|
||||
"@react-native-community/cookies": "3.0.0",
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-native-community/netinfo": "5.8.1",
|
||||
"@react-navigation/native": "5.3.2",
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
diff --git a/node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m b/node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m
|
||||
index 9cdc74d..5477405 100644
|
||||
--- a/node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m
|
||||
+++ b/node_modules/@react-native-community/cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m
|
||||
@@ -139,7 +139,8 @@ + (BOOL)requiresMainQueueSetup
|
||||
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
|
||||
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
|
||||
for (NSHTTPCookie *cookie in allCookies) {
|
||||
- if ([topLevelDomain containsString:cookie.domain]) {
|
||||
+ NSString *domainWithDot = [NSString stringWithFormat:@".%@", cookie.domain];
|
||||
+ if([cookie.domain containsString:topLevelDomain] || [domainWithDot containsString:topLevelDomain]) {
|
||||
[cookies setObject:[self createCookieData:cookie] forKey:cookie.name];
|
||||
}
|
||||
}
|
||||
@@ -185,6 +186,8 @@ + (BOOL)requiresMainQueueSetup
|
||||
}
|
||||
resolve(nil);
|
||||
}
|
||||
+
|
||||
+ [self deleteBinaryCookiesAndWebsiteData];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(clearByName:(NSString *) name
|
||||
@@ -244,4 +247,15 @@ -(NSDictionary *)createCookieData:(NSHTTPCookie *)cookie
|
||||
return cookieData;
|
||||
}
|
||||
|
||||
+-(void) deleteBinaryCookiesAndWebsiteData {
|
||||
+ NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
|
||||
+ NSString *cookiesPath = [libraryPath stringByAppendingString:@"/Cookies/Cookies.binarycookies"];
|
||||
+ NSString *dataPath = [libraryPath stringByAppendingString:@"/WebKit/WebsiteData"];
|
||||
+
|
||||
+ NSError *error;
|
||||
+ NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
+ [fileManager removeItemAtPath:cookiesPath error:&error];
|
||||
+ [fileManager removeItemAtPath:dataPath error:&error];
|
||||
+}
|
||||
+
|
||||
@end
|
||||
|
|
@ -122,7 +122,7 @@ jest.mock('react-native-device-info', () => {
|
|||
jest.mock('rn-fetch-blob', () => ({
|
||||
fs: {
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
DocumentDir: '/data/com.mattermost.beta/documents',
|
||||
CacheDir: '/data/com.mattermost.beta/cache',
|
||||
},
|
||||
exists: jest.fn(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue