test(auth): verify expired and fresh MFA login flows end to end
This commit is contained in:
1 parent
d0e0dfeb26
commit
18f1d08bc3
1 file changed
+66
@@ -0,0 +1,66 @@
|
|||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { createContext } from "@wrnexus/core";
|
||||||
|
import { createAuthEngine } from "../src/engine.ts";
|
||||||
|
import { createAuthHttpHandlers } from "../src/http/index.ts";
|
||||||
|
import { MemoryAuthStore } from "../src/stores/memory.ts";
|
||||||
|
import { generateTotp } from "../src/totp/index.ts";
|
||||||
|
|
||||||
|
test("HTTP login creates a usable MFA challenge; expired requests require a new login", async () => {
|
||||||
|
let time = 1_720_000_000_000;
|
||||||
|
const engine = createAuthEngine({
|
||||||
|
store: new MemoryAuthStore(),
|
||||||
|
secret: "mfa-full-flow-test-secret-longer-than-thirty-two-characters",
|
||||||
|
clock: { now: () => time },
|
||||||
|
});
|
||||||
|
const user = await engine.register({ email: "flow@example.test", password: "StrongPassword123" });
|
||||||
|
const setup = await engine.beginTotp(user.user!.id);
|
||||||
|
expect(
|
||||||
|
await engine.confirmTotp(
|
||||||
|
user.user!.id,
|
||||||
|
setup.credentialId,
|
||||||
|
await generateTotp(setup.secret, { timestamp: time }),
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
const handlers = createAuthHttpHandlers({ engine });
|
||||||
|
const context = (path: string, body: unknown) => {
|
||||||
|
const req = new Request(`https://sso.example.test${path}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
return createContext(req, new URL(req.url));
|
||||||
|
};
|
||||||
|
const login = async () => {
|
||||||
|
const response = await handlers.login(
|
||||||
|
context("/api/auth/login", {
|
||||||
|
identifier: "flow@example.test",
|
||||||
|
password: "StrongPassword123",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(response.status).toBe(401);
|
||||||
|
const result = await response.json();
|
||||||
|
expect(result.code).toBe("mfa-required");
|
||||||
|
expect(result.mfaExpiresAt).toBe(time + 300_000);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const old = await login();
|
||||||
|
time += 300_001;
|
||||||
|
const expired = await handlers.completeMfa(
|
||||||
|
context("/api/auth/mfa/complete", {
|
||||||
|
mfaToken: old.mfaToken,
|
||||||
|
method: "totp",
|
||||||
|
code: await generateTotp(setup.secret, { timestamp: time }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect((await expired.json()).code).toBe("mfa-transaction-expired");
|
||||||
|
const fresh = await login();
|
||||||
|
const success = await handlers.completeMfa(
|
||||||
|
context("/api/auth/mfa/complete", {
|
||||||
|
mfaToken: fresh.mfaToken,
|
||||||
|
method: "totp",
|
||||||
|
code: await generateTotp(setup.secret, { timestamp: time }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(success.status).toBe(200);
|
||||||
|
expect(await success.json()).toMatchObject({ ok: true, user: { id: user.user!.id } });
|
||||||
|
});
|
||||||
Reference in new issue
Block a user