mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
refactor: auto retry the auth flow
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
import { atom } from "jotai";
|
||||
import authService from "../services/authService";
|
||||
import portalService, { Prelogin } from "../services/portalService";
|
||||
import { loginPortalAtom } from "./loginPortal";
|
||||
import portalService, {
|
||||
Prelogin,
|
||||
SamlPrelogin,
|
||||
} from "../services/portalService";
|
||||
import logger from "../utils/logger";
|
||||
import { AbnormalPortalConfigError, loginPortalAtom } from "./loginPortal";
|
||||
import { notifyErrorAtom } from "./notification";
|
||||
import { launchPasswordLoginAtom } from "./passwordLogin";
|
||||
import { currentPortalDataAtom, portalAddressAtom } from "./portal";
|
||||
import { launchSamlLoginAtom, retrySamlLoginAtom } from "./samlLogin";
|
||||
import { isProcessingAtom, statusAtom } from "./status";
|
||||
|
||||
const MAX_ATTEMPTS = 10;
|
||||
|
||||
/**
|
||||
* Connect to the portal, workflow:
|
||||
* 1. Portal prelogin to get the prelogin data
|
||||
@@ -31,10 +37,10 @@ export const connectPortalAtom = atom(
|
||||
|
||||
try {
|
||||
set(statusAtom, "prelogin");
|
||||
const prelogin = await portalService.prelogin(portal);
|
||||
let prelogin = await portalService.prelogin(portal);
|
||||
const isProcessing = get(isProcessingAtom);
|
||||
if (!isProcessing) {
|
||||
console.info("Request cancelled");
|
||||
logger.info("Operation cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -44,7 +50,31 @@ export const connectPortalAtom = atom(
|
||||
} catch {
|
||||
// Otherwise, login with SAML or the password
|
||||
if (prelogin.isSamlAuth) {
|
||||
await set(launchSamlLoginAtom, prelogin);
|
||||
let attemptCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
attemptCount++;
|
||||
logger.info(
|
||||
`(${attemptCount}/${MAX_ATTEMPTS}) Launching SAML login...`
|
||||
);
|
||||
await set(launchSamlLoginAtom, prelogin as SamlPrelogin);
|
||||
break;
|
||||
} catch (err) {
|
||||
if (attemptCount >= MAX_ATTEMPTS) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (err instanceof AbnormalPortalConfigError) {
|
||||
logger.info(
|
||||
`Got abnormal portal config: ${err.message}, retrying...`
|
||||
);
|
||||
prelogin = await portalService.prelogin(portal);
|
||||
continue;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
set(launchPasswordLoginAtom, prelogin);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { atom } from "jotai";
|
||||
import gatewayService from "../services/gatewayService";
|
||||
import logger from "../utils/logger";
|
||||
import { isProcessingAtom, statusAtom } from "./status";
|
||||
import { connectVpnAtom } from "./vpn";
|
||||
|
||||
@@ -19,6 +20,7 @@ export const loginGatewayAtom = atom(
|
||||
set(statusAtom, "gateway-login");
|
||||
let token: string;
|
||||
try {
|
||||
logger.info(`Logging in to gateway ${gateway}...`);
|
||||
token = await gatewayService.login(gateway, credential);
|
||||
} catch (err) {
|
||||
throw new Error("Failed to login to gateway");
|
||||
@@ -26,7 +28,7 @@ export const loginGatewayAtom = atom(
|
||||
|
||||
const isProcessing = get(isProcessingAtom);
|
||||
if (!isProcessing) {
|
||||
console.info("Request cancelled");
|
||||
logger.info("Operation cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -4,11 +4,23 @@ import portalService, {
|
||||
PortalCredential,
|
||||
Prelogin,
|
||||
} from "../services/portalService";
|
||||
import logger from "../utils/logger";
|
||||
import { redact } from "../utils/redact";
|
||||
import { selectedGatewayAtom } from "./gateway";
|
||||
import { loginGatewayAtom } from "./loginGateway";
|
||||
import { portalAddressAtom, updatePortalDataAtom } from "./portal";
|
||||
import { isProcessingAtom, statusAtom } from "./status";
|
||||
|
||||
/**
|
||||
* The error thrown when the portal config is abnormal, can back to normal after retrying
|
||||
*/
|
||||
export class AbnormalPortalConfigError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "AbnormalPortalConfigError";
|
||||
}
|
||||
}
|
||||
|
||||
// Indicates whether the portal config is being fetched
|
||||
// This is mainly used to show the loading indicator in the password login form
|
||||
const portalConfigLoadingAtom = atom(false);
|
||||
@@ -42,23 +54,30 @@ export const loginPortalAtom = atom(
|
||||
try {
|
||||
portalConfig = await portalService.fetchConfig(portalAddress, credential);
|
||||
configFetched?.();
|
||||
} catch (err) {
|
||||
const isProcessing = get(isProcessingAtom);
|
||||
if (!isProcessing) {
|
||||
logger.info("Operation cancelled");
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
set(portalConfigLoadingAtom, false);
|
||||
}
|
||||
|
||||
const isProcessing = get(isProcessingAtom);
|
||||
if (!isProcessing) {
|
||||
console.info("Request cancelled");
|
||||
logger.info("Operation cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
const { gateways, userAuthCookie, prelogonUserAuthCookie } = portalConfig;
|
||||
if (!gateways.length) {
|
||||
throw new Error("No gateway found");
|
||||
throw new AbnormalPortalConfigError("No gateway found");
|
||||
}
|
||||
|
||||
if (userAuthCookie === "empty" || prelogonUserAuthCookie === "empty") {
|
||||
throw new Error("Got empty user auth cookie");
|
||||
throw new AbnormalPortalConfigError("Empty user auth cookie");
|
||||
}
|
||||
|
||||
// Here, we have got the portal config successfully, refresh the cached portal data
|
||||
@@ -87,6 +106,8 @@ export const loginPortalAtom = atom(
|
||||
preferredGateway: previousSelectedGateway,
|
||||
});
|
||||
|
||||
logger.info(`Found the preferred gateway: ${name} (${redact(address)})`);
|
||||
|
||||
// Log in to the gateway
|
||||
await set(loginGatewayAtom, address, {
|
||||
user: credential.user,
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { atom } from "jotai";
|
||||
import authService, { AuthData } from "../services/authService";
|
||||
import portalService, { SamlPrelogin } from "../services/portalService";
|
||||
import logger from "../utils/logger";
|
||||
import { redact } from "../utils/redact";
|
||||
import { loginPortalAtom } from "./loginPortal";
|
||||
import { clearCookiesAtom, portalAddressAtom } from "./portal";
|
||||
import { statusAtom } from "./status";
|
||||
import { unwrap } from "./unwrap";
|
||||
|
||||
export const launchSamlLoginAtom = atom(
|
||||
null,
|
||||
@@ -38,7 +39,11 @@ export const launchSamlLoginAtom = atom(
|
||||
"prelogin-cookie": authData.prelogin_cookie,
|
||||
"portal-userauthcookie": authData.portal_userauthcookie,
|
||||
};
|
||||
|
||||
logger.info(
|
||||
`SAML login succeeded, prelogin-cookie: ${redact(
|
||||
authData.prelogin_cookie
|
||||
)}, portal-userauthcookie: ${redact(authData.portal_userauthcookie)}`
|
||||
);
|
||||
await set(loginPortalAtom, credential, prelogin);
|
||||
}
|
||||
);
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import { atom } from "jotai";
|
||||
import vpnService from "../services/vpnService";
|
||||
import logger from "../utils/logger";
|
||||
import { redact } from "../utils/redact";
|
||||
import { notifyErrorAtom } from "./notification";
|
||||
import { statusAtom } from "./status";
|
||||
|
||||
@@ -8,6 +10,11 @@ export const connectVpnAtom = atom(
|
||||
async (_get, set, vpnAddress: string, token: string) => {
|
||||
try {
|
||||
set(statusAtom, "connecting");
|
||||
logger.info(
|
||||
`Connecting to VPN ${redact(vpnAddress)} with token ${redact(
|
||||
token
|
||||
)} ...`
|
||||
);
|
||||
await vpnService.connect(vpnAddress, token);
|
||||
set(statusAtom, "connected");
|
||||
} catch (err) {
|
||||
@@ -20,6 +27,7 @@ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
export const disconnectVpnAtom = atom(null, async (get, set) => {
|
||||
try {
|
||||
set(statusAtom, "disconnecting");
|
||||
logger.info("Disconnecting from VPN...");
|
||||
await vpnService.disconnect();
|
||||
// Sleep a short time, so that the client can receive the service's disconnected event.
|
||||
await sleep(100);
|
||||
|
Reference in New Issue
Block a user