From e4490e0af5c76e2ab4cf960e7b2faf823df8438e Mon Sep 17 00:00:00 2001
From: Salah Benmoussati <51402489+sbenmoussati@users.noreply.github.com>
Date: Mon, 3 Apr 2023 16:36:25 +0200
Subject: [PATCH] SDA-4120 Proxy authentication on Browser login flow (#1826)
---
src/app/main-api-handler.ts | 118 +++++++++++++++++++------
src/renderer/components/basic-auth.tsx | 2 +-
src/renderer/styles/basic-auth.less | 47 ++++++----
3 files changed, 123 insertions(+), 44 deletions(-)
diff --git a/src/app/main-api-handler.ts b/src/app/main-api-handler.ts
index dfdecd8c..a4e14db0 100644
--- a/src/app/main-api-handler.ts
+++ b/src/app/main-api-handler.ts
@@ -72,6 +72,24 @@ const broadcastMessage = (method, data) => {
const getBrowserLoginUrl = (pod: string) =>
`${pod}/login/sso/initsso?RelayState=${pod}/client-bff/device-login/index.html?callbackScheme=symphony&action=login`;
const AUTH_STATUS_PATH = '/login/checkauth?type=user';
+interface IProxyDetails {
+ username: string;
+ password: string;
+ hostname: string;
+ retries: number;
+}
+const proxyDetails: IProxyDetails = {
+ username: '',
+ password: '',
+ hostname: '',
+ retries: 0,
+};
+
+let loginUrl = '';
+let formattedPodUrl = '';
+let credentialsPromise;
+const credentialsPromiseRefHolder: { [key: string]: any } = {};
+
/**
* Handle API related ipc messages from renderers. Only messages from windows
* we have created are allowed.
@@ -368,7 +386,8 @@ ipcMain.on(
}
break;
case apiCmds.unmaximizeMainWindow:
- const mainWindow = windowHandler.getMainWindow();
+ const mainWindow =
+ windowHandler.getMainWindow() as ICustomBrowserWindow;
if (mainWindow && windowExists(mainWindow)) {
if (mainWindow.isFullScreen()) {
mainWindow.setFullScreen(false);
@@ -402,38 +421,14 @@ ipcMain.on(
? userConfigURL
: globalConfigURL;
const { subdomain, domain, tld } = whitelistHandler.parseDomain(podUrl);
- const formattedPodUrl = `https://${subdomain}.${domain}${tld}`;
- const loginUrl = getBrowserLoginUrl(formattedPodUrl);
+ formattedPodUrl = `https://${subdomain}.${domain}${tld}`;
+ loginUrl = getBrowserLoginUrl(formattedPodUrl);
logger.info(
'main-api-handler:',
'check if sso is enabled for the pod',
formattedPodUrl,
);
- const response = await fetch(`${formattedPodUrl}${AUTH_STATUS_PATH}`);
- const authResponse = (await response.json()) as IAuthResponse;
- logger.info('main-api-handler:', 'check auth response', authResponse);
- if (
- arg.isBrowserLoginEnabled &&
- authResponse.authenticationType === 'sso'
- ) {
- logger.info(
- 'main-api-handler:',
- 'browser login is enabled - logging in',
- loginUrl,
- );
- await shell.openExternal(loginUrl);
- } else {
- logger.info(
- 'main-api-handler:',
- 'browser login is not enabled - loading main window with',
- formattedPodUrl,
- );
- const mainWebContents = windowHandler.getMainWebContents();
- if (mainWebContents && !mainWebContents.isDestroyed()) {
- windowHandler.setMainWindowOrigin(formattedPodUrl);
- mainWebContents.loadURL(formattedPodUrl);
- }
- }
+ loadPodUrl();
break;
case apiCmds.setBroadcastMessage:
if (swiftSearchInstance) {
@@ -650,3 +645,70 @@ const logApiCallParams = (arg: any) => {
break;
}
};
+
+const loadPodUrl = (proxyLogin = false) => {
+ logger.info('loading pod URL. Proxy: ', proxyLogin);
+ let onLogin = {};
+ if (proxyLogin) {
+ onLogin = {
+ async onLogin(authInfo) {
+ // this 'authInfo' is the one received by the 'login' event. See https://www.electronjs.org/docs/latest/api/client-request#event-login
+ proxyDetails.hostname = authInfo.host || authInfo.realm;
+ await credentialsPromise;
+ return Promise.resolve({
+ username: proxyDetails.username,
+ password: proxyDetails.password,
+ });
+ },
+ };
+ }
+ fetch(`${formattedPodUrl}${AUTH_STATUS_PATH}`, onLogin)
+ .then(async (response) => {
+ const authResponse = (await response.json()) as IAuthResponse;
+ logger.info('main-api-handler:', 'check auth response', authResponse);
+ if (authResponse.authenticationType === 'sso') {
+ logger.info(
+ 'main-api-handler:',
+ 'browser login is enabled - logging in',
+ loginUrl,
+ );
+ await shell.openExternal(loginUrl);
+ } else {
+ logger.info(
+ 'main-api-handler:',
+ 'browser login is not enabled - loading main window with',
+ formattedPodUrl,
+ );
+ const mainWebContents = windowHandler.getMainWebContents();
+ if (mainWebContents && !mainWebContents.isDestroyed()) {
+ windowHandler.setMainWindowOrigin(formattedPodUrl);
+ mainWebContents.loadURL(formattedPodUrl);
+ }
+ }
+ })
+ .catch(async (error) => {
+ if (
+ (error.type === 'proxy' && error.code === 'PROXY_AUTH_FAILED') ||
+ (error.code === 'ERR_TOO_MANY_RETRIES' && proxyLogin)
+ ) {
+ credentialsPromise = new Promise((res, _rej) => {
+ credentialsPromiseRefHolder.resolutionCallback = res;
+ });
+ const welcomeWindow =
+ windowHandler.getMainWindow() as ICustomBrowserWindow;
+ windowHandler.createBasicAuthWindow(
+ welcomeWindow,
+ proxyDetails.hostname,
+ proxyDetails.retries === 0,
+ undefined,
+ (username, password) => {
+ proxyDetails.username = username;
+ proxyDetails.password = password;
+ credentialsPromiseRefHolder.resolutionCallback(true);
+ loadPodUrl(true);
+ },
+ );
+ proxyDetails.retries += 1;
+ }
+ });
+};
diff --git a/src/renderer/components/basic-auth.tsx b/src/renderer/components/basic-auth.tsx
index 4ae5ed9e..41955a6f 100644
--- a/src/renderer/components/basic-auth.tsx
+++ b/src/renderer/components/basic-auth.tsx
@@ -62,7 +62,7 @@ export default class BasicAuth extends React.Component<{}, IState> {
BASIC_AUTH_NAMESPACE,
)()}
- {hostname}
+ {hostname && {hostname}}
{i18n.t('Invalid user name/password', BASIC_AUTH_NAMESPACE)()}
diff --git a/src/renderer/styles/basic-auth.less b/src/renderer/styles/basic-auth.less
index 9d30413a..addf36d1 100644
--- a/src/renderer/styles/basic-auth.less
+++ b/src/renderer/styles/basic-auth.less
@@ -1,4 +1,4 @@
-@import "theme";
+@import 'theme';
@color-red: red;
@@ -10,16 +10,22 @@ html {
body {
margin: 0;
- height: 100%;
+ height: calc(100% - 40px);
+ margin: 20px;
font-size: 14px;
}
+.components-window-root {
+ height: 100%;
+}
+
.container {
display: flex;
flex-direction: column;
font-family: @font-family;
text-align: center;
- padding: 20px;
+ height: 100%;
+ justify-content: space-evenly;
}
.container:lang(ja-JP) {
@@ -27,12 +33,11 @@ body {
}
span {
- padding-top: 10px;
text-align: start;
}
.hostname {
- font-size: .9em;
+ font-size: 0.9em;
}
.credentials-error {
@@ -44,28 +49,40 @@ span {
display: block;
}
-form {
- padding-top: 15px;
-}
-
table {
border-spacing: 5px;
+ display: flex;
+ tbody {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ tr {
+ display: flex;
+ justify-content: space-between;
+ text-align: left;
+ td:last-child {
+ margin-left: 8px;
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
+ }
+ }
+ }
}
input {
- width: 200px;
+ width: calc(100% - 8px);
height: 20px;
display: inline-block;
-}
-
-button {
- width: 80px;
+ &:lang(fr-FR) {
+ max-width: 180px;
+ }
}
.footer {
display: flex;
text-align: center;
- padding-top: 25px;
+ padding-top: 20px;
}
.button-container {