mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-29 06:06:27 -04:00
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
import { atom } from "jotai";
|
|
import authService from "../services/authService";
|
|
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
|
|
* 2. Try to login with the cached credential
|
|
* 3. If login failed, launch the SAML login window or the password login window based on the prelogin data
|
|
*/
|
|
export const connectPortalAtom = atom(
|
|
null,
|
|
async (get, set, action?: "retry-auth") => {
|
|
// Retry the SAML authentication
|
|
if (action === "retry-auth") {
|
|
set(retrySamlLoginAtom);
|
|
return;
|
|
}
|
|
|
|
const portal = get(portalAddressAtom);
|
|
if (!portal) {
|
|
set(notifyErrorAtom, "Portal is empty");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
set(statusAtom, "prelogin");
|
|
let prelogin = await portalService.prelogin(portal);
|
|
const isProcessing = get(isProcessingAtom);
|
|
if (!isProcessing) {
|
|
logger.info("Operation cancelled");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// If the portal is cached, use the cached credential
|
|
await set(loginWithCachedCredentialAtom, prelogin);
|
|
} catch {
|
|
// Otherwise, login with SAML or the password
|
|
if (prelogin.isSamlAuth) {
|
|
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);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
set(cancelConnectPortalAtom);
|
|
set(notifyErrorAtom, err);
|
|
}
|
|
}
|
|
);
|
|
|
|
connectPortalAtom.onMount = (dispatch) => {
|
|
return authService.onAuthError(() => {
|
|
dispatch("retry-auth");
|
|
});
|
|
};
|
|
|
|
export const cancelConnectPortalAtom = atom(null, (_get, set) => {
|
|
set(statusAtom, "disconnected");
|
|
});
|
|
|
|
/**
|
|
* Read the cached credential from the current portal data and login with it
|
|
*/
|
|
const loginWithCachedCredentialAtom = atom(
|
|
null,
|
|
async (get, set, prelogin: Prelogin) => {
|
|
const { cachedCredential } = get(currentPortalDataAtom);
|
|
if (!cachedCredential) {
|
|
throw new Error("No cached credential");
|
|
}
|
|
|
|
await set(loginPortalAtom, cachedCredential, prelogin);
|
|
}
|
|
);
|