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:
@@ -200,11 +200,25 @@ const buildMockFetch = (
|
|||||||
srpServer.checkM1(Buffer.from(body.srpM1, "base64"));
|
srpServer.checkM1(Buffer.from(body.srpM1, "base64"));
|
||||||
const M2 = srpServer.computeM2();
|
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) {
|
if (opts?.requireTOTP) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
id: 42,
|
||||||
srpM2: M2.toString("base64"),
|
srpM2: M2.toString("base64"),
|
||||||
|
passkeySessionID: "",
|
||||||
|
accountsUrl: "",
|
||||||
twoFactorSessionID: "totp-session-999",
|
twoFactorSessionID: "totp-session-999",
|
||||||
|
twoFactorSessionIDV2: "",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
status: 200,
|
status: 200,
|
||||||
@@ -218,11 +232,14 @@ const buildMockFetch = (
|
|||||||
// server sets passkeySessionID + twoFactorSessionIDV2 (not
|
// server sets passkeySessionID + twoFactorSessionIDV2 (not
|
||||||
// twoFactorSessionID -- that's deliberate, so old clients
|
// twoFactorSessionID -- that's deliberate, so old clients
|
||||||
// that only know the V1 field keep using the passkey flow).
|
// that only know the V1 field keep using the passkey flow).
|
||||||
|
// The V1 field is still present on the wire as "".
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
id: 42,
|
||||||
srpM2: M2.toString("base64"),
|
srpM2: M2.toString("base64"),
|
||||||
passkeySessionID: "passkey-session-123",
|
passkeySessionID: "passkey-session-123",
|
||||||
accountsUrl: "https://accounts.ente.io",
|
accountsUrl: "https://accounts.ente.io",
|
||||||
|
twoFactorSessionID: "",
|
||||||
twoFactorSessionIDV2: "totp-session-v2-456",
|
twoFactorSessionIDV2: "totp-session-v2-456",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -238,6 +255,10 @@ const buildMockFetch = (
|
|||||||
id: 42,
|
id: 42,
|
||||||
keyAttributes: fixture.keyAttributes,
|
keyAttributes: fixture.keyAttributes,
|
||||||
encryptedToken: fixture.encryptedToken,
|
encryptedToken: fixture.encryptedToken,
|
||||||
|
passkeySessionID: "",
|
||||||
|
accountsUrl: "",
|
||||||
|
twoFactorSessionID: "",
|
||||||
|
twoFactorSessionIDV2: "",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
Reference in New Issue
Block a user