Red: mock server must serialize empty 2FA fields like Go does

The museum server's EmailAuthorizationResponse declares
passkeySessionID, accountsUrl, twoFactorSessionID, and
twoFactorSessionIDV2 without `omitempty`, so Go always sends them,
as "" when unset. The previous mock omitted the unset fields
entirely, which let the ?? -based dispatch pass in tests while the
real server's "" defeated it and dual-2FA logins fell through to
the unsupported passkey branch.
This commit is contained in:
2026-06-10 11:25:25 -07:00
parent 0d0dcf5987
commit 2c51074294

View File

@@ -200,11 +200,25 @@ const buildMockFetch = (
srpServer.checkM1(Buffer.from(body.srpM1, "base64"));
const M2 = srpServer.computeM2();
// IMPORTANT: the museum server's EmailAuthorizationResponse
// (server/ente/user.go) declares passkeySessionID, accountsUrl,
// twoFactorSessionID, and twoFactorSessionIDV2 WITHOUT the
// `omitempty` JSON tag. Go therefore always serializes them,
// sending "" (empty string, NOT null/absent) for any that do
// not apply. These mocks must reproduce that faithfully: a
// client that distinguishes fields with `??` instead of `||`
// passes against an omitting mock but breaks against the real
// server.
if (opts?.requireTOTP) {
return new Response(
JSON.stringify({
id: 42,
srpM2: M2.toString("base64"),
passkeySessionID: "",
accountsUrl: "",
twoFactorSessionID: "totp-session-999",
twoFactorSessionIDV2: "",
}),
{
status: 200,
@@ -218,11 +232,14 @@ const buildMockFetch = (
// server sets passkeySessionID + twoFactorSessionIDV2 (not
// twoFactorSessionID -- that's deliberate, so old clients
// that only know the V1 field keep using the passkey flow).
// The V1 field is still present on the wire as "".
return new Response(
JSON.stringify({
id: 42,
srpM2: M2.toString("base64"),
passkeySessionID: "passkey-session-123",
accountsUrl: "https://accounts.ente.io",
twoFactorSessionID: "",
twoFactorSessionIDV2: "totp-session-v2-456",
}),
{
@@ -238,6 +255,10 @@ const buildMockFetch = (
id: 42,
keyAttributes: fixture.keyAttributes,
encryptedToken: fixture.encryptedToken,
passkeySessionID: "",
accountsUrl: "",
twoFactorSessionID: "",
twoFactorSessionIDV2: "",
}),
{
status: 200,