2023-08-31 21:18:30 +08:00

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);
}
);