diff --git a/webapp/channels/src/components/login/login.tsx b/webapp/channels/src/components/login/login.tsx
index e4a11ae4be..b8d736edf4 100644
--- a/webapp/channels/src/components/login/login.tsx
+++ b/webapp/channels/src/components/login/login.tsx
@@ -48,6 +48,7 @@ import PasswordInput from 'components/widgets/inputs/password_input/password_inp
import Constants from 'utils/constants';
import DesktopApp from 'utils/desktop_api';
+import {isEmbedded} from 'utils/embed';
import {t} from 'utils/i18n';
import {showNotification} from 'utils/notifications';
import {isDesktopApp} from 'utils/user_agent';
@@ -164,7 +165,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
icon: ,
label: GitLabButtonText || formatMessage({id: 'login.gitlab', defaultMessage: 'GitLab'}),
style: {color: GitLabButtonColor, borderColor: GitLabButtonColor},
- onClick: desktopExternalAuth(url),
+ onClick: handleExternalAuth(url, 'gitlab'),
});
}
@@ -175,7 +176,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
url,
icon: ,
label: formatMessage({id: 'login.google', defaultMessage: 'Google'}),
- onClick: desktopExternalAuth(url),
+ onClick: handleExternalAuth(url, 'google'),
});
}
@@ -186,7 +187,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
url,
icon: ,
label: formatMessage({id: 'login.office365', defaultMessage: 'Entra ID'}),
- onClick: desktopExternalAuth(url),
+ onClick: handleExternalAuth(url, 'office365'),
});
}
@@ -198,7 +199,7 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
icon: ,
label: OpenIdButtonText || formatMessage({id: 'login.openid', defaultMessage: 'Open ID'}),
style: {color: OpenIdButtonColor, borderColor: OpenIdButtonColor},
- onClick: desktopExternalAuth(url),
+ onClick: handleExternalAuth(url, 'openid'),
});
}
@@ -209,21 +210,68 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
url,
icon: ,
label: SamlLoginButtonText || formatMessage({id: 'login.saml', defaultMessage: 'SAML'}),
- onClick: desktopExternalAuth(url),
+ onClick: handleExternalAuth(url, 'saml'),
});
}
return externalLoginOptions;
};
- const desktopExternalAuth = (href: string) => {
+ const handleExternalAuth = (href: string, provider: string) => {
return (event: React.MouseEvent) => {
+ // If the user is running the desktop app, we need to redirect them to the desktop login page
if (isDesktopApp()) {
event.preventDefault();
setDesktopLoginLink(href);
history.push(`/login/desktop${search}`);
}
+
+ // If the user is running the app in an embedded view, we need send the parent window a message
+ // to continue the login process if the parent frame answers a message to confirm that is going to
+ // take care for the authentication process.
+ if (isEmbedded()) {
+ event.preventDefault();
+
+ // Create a promise that will resolve if the parent window responds
+ const messagePromise = new Promise((resolve) => {
+ // Set up a one-time event listener for the response
+ const messageHandler = (event: MessageEvent) => {
+ // Right now we are embedding from a plugin so let's just check the origin is the same as this server origin.
+ if (event.origin !== window.location.origin) {
+ return;
+ }
+
+ if (event.data && event.data.type === 'mattermost_external_auth_login' && event.data.ack === true) {
+ window.removeEventListener('message', messageHandler);
+ resolve(true);
+ }
+ };
+
+ window.addEventListener('message', messageHandler);
+
+ // Wait for at least one second for a response from the parent.
+ setTimeout(() => {
+ window.removeEventListener('message', messageHandler);
+ resolve(false);
+ }, 1000);
+ });
+
+ // Notify the parent
+ window.parent.postMessage({
+ type: 'mattermost_external_auth_login',
+ provider,
+ href,
+ }, window.location.origin);
+
+ // Wait for response or timeout, following with the usual authentication flow
+ messagePromise.then((received) => {
+ if (!received) {
+ // If the parent didn't respond, navigate to the href directly
+ history.push(href);
+ }
+ });
+ }
};
};
diff --git a/webapp/channels/src/utils/embed.tsx b/webapp/channels/src/utils/embed.tsx
new file mode 100644
index 0000000000..c9c37d7517
--- /dev/null
+++ b/webapp/channels/src/utils/embed.tsx
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+/**
+ * Checks if the application is running embedded in another platform
+ * This is determined by checking if window.self is not equal to window.parent.
+ * If accessing window.parent throws an error, it is assumed the app is in a cross-origin iframe.
+ * @returns True if the app is running embedded
+ */
+export function isEmbedded(): boolean {
+ try {
+ return window.self !== window.parent;
+ } catch (e) {
+ // If accessing window.parent throws an error, we're in a cross-origin iframe
+ return true;
+ }
+}