Merge pull request #762 from pikasTech/fix/issue753-auth-race

fix(web): bind login submit before session probe
This commit is contained in:
Lyon
2026-06-03 16:34:08 +08:00
committed by GitHub
2 changed files with 51 additions and 8 deletions
+43 -1
View File
@@ -67,6 +67,40 @@ test("login uses cloud-api auth instead of local storage fallback", async () =>
}
});
test("login submit is wired before the initial session probe resolves", async () => {
const sessionProbe = deferred();
const harness = installAuthHarness({
localSession: null,
responses: {
"/auth/session": sessionProbe.promise,
"/auth/login": {
authenticated: true,
user: { username: "admin" },
expiresAt: futureIso()
}
}
});
try {
const pendingSession = ensureWorkbenchAuth({ loginShell: harness.loginShell, appShell: harness.appShell });
await harness.waitForLoginReady();
harness.usernameInput.value = "admin";
harness.passwordInput.value = "hwlab2026";
await harness.submitLogin();
assert.deepEqual(harness.fetchCalls.map((call) => call.path), ["/auth/session", "/auth/login"]);
sessionProbe.resolve({ authenticated: false });
const session = await pendingSession;
assert.equal(session.authenticated, true);
assert.equal(harness.loginShell.hidden, true);
assert.equal(harness.appShell.hidden, false);
} finally {
harness.restore();
}
});
function installAuthHarness({ localSession, responses }) {
const previous = {
window: globalThis.window,
@@ -108,13 +142,13 @@ function installAuthHarness({ localSession, responses }) {
clearTimeout: () => {}
};
globalThis.fetch = async (path, options = {}) => {
const response = responses[path] ?? { status: 404, authenticated: false };
fetchCalls.push({
path,
method: options.method ?? "GET",
body: options.body ?? null,
credentials: options.credentials ?? null
});
const response = await (responses[path] ?? { status: 404, authenticated: false });
const status = Number(response.status ?? (response.authenticated === false && response.error ? 401 : 200));
return {
status,
@@ -153,6 +187,14 @@ function futureIso() {
return new Date(Date.now() + 60 * 60 * 1000).toISOString();
}
function deferred() {
let resolve;
const promise = new Promise((nextResolve) => {
resolve = nextResolve;
});
return { promise, resolve };
}
function elementStub() {
return {
hidden: false,
+8 -7
View File
@@ -18,13 +18,7 @@ export async function ensureWorkbenchAuth({ loginShell, appShell }) {
const config = resolveAuthConfig();
hideWorkbench(appShell);
showLogin(loginShell);
const session = await readActiveSession(config);
if (session.authenticated) {
return activateAuthenticatedWorkbench({ loginShell, appShell, session, config });
}
return new Promise((resolve) => {
const loginSession = new Promise((resolve) => {
initLoginForm({
loginShell,
appShell,
@@ -34,6 +28,13 @@ export async function ensureWorkbenchAuth({ loginShell, appShell }) {
}
});
});
const session = await readActiveSession(config);
if (session.authenticated) {
return activateAuthenticatedWorkbench({ loginShell, appShell, session, config });
}
return loginSession;
}
export function initWorkbenchLogout(button) {