fix(auth): expire stale MFA forms before accepting codes
This commit is contained in:
1 parent
298e34ef2e
commit
0c77de93b4
8 files changed
+82
-11
No files matched your search
@@ -2,12 +2,39 @@ import { expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { Window, type HTMLInputElement, type HTMLElement } from "happy-dom";
|
||||
|
||||
test("expired and legacy MFA requests are rejected on mount before entering a code", async () => {
|
||||
for (const deadline of [undefined, Date.now() - 1]) {
|
||||
const window = new Window({ url: "https://sso.example.test/mfa" });
|
||||
try {
|
||||
window.sessionStorage.setItem(
|
||||
"wrnexus.auth.mfa",
|
||||
JSON.stringify({ mfaToken: "old-challenge", mfaExpiresAt: deadline }),
|
||||
);
|
||||
window.document.body.innerHTML = `<section data-auth-mfa-challenge><form data-auth-mfa-form>
|
||||
<input name="mfaToken"><input type="radio" name="method" value="totp"><input name="code"><p data-auth-mfa-status hidden></p><button type="submit">Verify</button>
|
||||
</form><a data-auth-mfa-restart hidden href="/login">Back to sign in</a></section>`;
|
||||
window.eval(readFileSync(new URL("../assets/client/auth.js", import.meta.url), "utf8"));
|
||||
(window as any).WRNexusAuth.mount(window.document);
|
||||
expect(window.document.querySelector("button")!.disabled).toBe(true);
|
||||
expect(window.document.querySelector<HTMLElement>("[data-auth-mfa-restart]")!.hidden).toBe(
|
||||
false,
|
||||
);
|
||||
expect(window.document.querySelector("[data-auth-mfa-status]")!.textContent).toContain(
|
||||
"Sign in again",
|
||||
);
|
||||
expect(window.sessionStorage.getItem("wrnexus.auth.mfa")).toBeNull();
|
||||
} finally {
|
||||
await window.happyDOM.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("selecting delivery radios sends once, ignores stale responses, and never sends for local methods", async () => {
|
||||
const window = new Window({ url: "https://sso.example.test/mfa" });
|
||||
try {
|
||||
window.sessionStorage.setItem(
|
||||
"wrnexus.auth.mfa",
|
||||
JSON.stringify({ mfaToken: "synthetic-test-challenge" }),
|
||||
JSON.stringify({ mfaToken: "synthetic-test-challenge", mfaExpiresAt: Date.now() + 300_000 }),
|
||||
);
|
||||
window.document.body.innerHTML = `<section data-auth-mfa-challenge data-mfa-initial-method="totp"><form data-auth-mfa-form>
|
||||
<input name="mfaToken"><input name="challengeId"><input name="code">
|
||||
@@ -63,6 +90,7 @@ test("MFA radios preserve allowed methods and restore continuation without reviv
|
||||
"wrnexus.auth.mfa",
|
||||
JSON.stringify({
|
||||
mfaToken: "synthetic-test-challenge",
|
||||
mfaExpiresAt: Date.now() + 300_000,
|
||||
requires: { mfa: ["totp"] },
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user