Red: test TOTP preference when account has both passkey and TOTP
When an account has both passkeys and TOTP enrolled, the Ente server returns passkeySessionID + twoFactorSessionIDV2 (deliberately not the V1 twoFactorSessionID field, so old clients keep using passkeys). quak only checked the V1 field, saw the passkey session, and threw 'Passkey authentication is not supported'. A CLI cannot do WebAuthn, so it must take the TOTP path via the V2 session ID.
This commit is contained in:
@@ -145,7 +145,7 @@ const buildServerFixture = async (password: string) => {
|
||||
*/
|
||||
const buildMockFetch = (
|
||||
fixture: Awaited<ReturnType<typeof buildServerFixture>>,
|
||||
opts?: { requireTOTP?: boolean },
|
||||
opts?: { requireTOTP?: boolean; requirePasskeyAndTOTP?: boolean },
|
||||
) => {
|
||||
let srpServer: SrpServer;
|
||||
const sessionID = "test-session-id";
|
||||
@@ -213,6 +213,25 @@ const buildMockFetch = (
|
||||
);
|
||||
}
|
||||
|
||||
if (opts?.requirePasskeyAndTOTP) {
|
||||
// When the account has BOTH passkeys and TOTP enabled, the
|
||||
// server sets passkeySessionID + twoFactorSessionIDV2 (not
|
||||
// twoFactorSessionID -- that's deliberate, so old clients
|
||||
// that only know the V1 field keep using the passkey flow).
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
srpM2: M2.toString("base64"),
|
||||
passkeySessionID: "passkey-session-123",
|
||||
accountsUrl: "https://accounts.ente.io",
|
||||
twoFactorSessionIDV2: "totp-session-v2-456",
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
srpM2: M2.toString("base64"),
|
||||
@@ -305,6 +324,26 @@ describe("beginLogin via SRP", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("prefers TOTP over passkey when the account has both", async () => {
|
||||
// Accounts with both passkeys and TOTP get passkeySessionID +
|
||||
// twoFactorSessionIDV2 in the verify-session response. A CLI
|
||||
// cannot perform a WebAuthn ceremony, so quak must take the TOTP
|
||||
// path using the V2 session ID. Returning { kind: "passkey" } here
|
||||
// would make such accounts unusable from the CLI even though they
|
||||
// have a perfectly good TOTP secret enrolled.
|
||||
const fixture = await buildServerFixture(TEST_PASSWORD);
|
||||
const api = new ApiClient({
|
||||
fetch: buildMockFetch(fixture, { requirePasskeyAndTOTP: true }),
|
||||
});
|
||||
|
||||
const challenge = await beginLogin(api, TEST_EMAIL, TEST_PASSWORD);
|
||||
|
||||
expect(challenge.kind).toBe("totp");
|
||||
if (challenge.kind === "totp") {
|
||||
expect(challenge.sessionID).toBe("totp-session-v2-456");
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a wrong password (SRP M1 verification fails)", async () => {
|
||||
const fixture = await buildServerFixture(TEST_PASSWORD);
|
||||
const api = new ApiClient({ fetch: buildMockFetch(fixture) });
|
||||
|
||||
Reference in New Issue
Block a user