fix: avoid terminal cold-start paywall race
This commit is contained in:
@@ -967,8 +967,13 @@ function ScanTerminalScreen() {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
setProAccess(createEmptyAccess(false));
|
setProAccess(createEmptyAccess(false));
|
||||||
} else if (event === "TOKEN_REFRESHED" || event === "SIGNED_IN") {
|
} else if (
|
||||||
|
event === "INITIAL_SESSION" ||
|
||||||
|
event === "TOKEN_REFRESHED" ||
|
||||||
|
event === "SIGNED_IN"
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
|
if (!session?.access_token) return;
|
||||||
const payload = await loadAuthProfile(session?.access_token);
|
const payload = await loadAuthProfile(session?.access_token);
|
||||||
setProAccess((prev) => mergeAccessStateWithAuthPayload(prev, payload));
|
setProAccess((prev) => mergeAccessStateWithAuthPayload(prev, payload));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|||||||
@@ -79,4 +79,43 @@ export async function runTests() {
|
|||||||
cookieOnlyResult.user_id === "cookie-user",
|
cookieOnlyResult.user_id === "cookie-user",
|
||||||
"terminal auth bootstrap should accept an authenticated cookie profile immediately",
|
"terminal auth bootstrap should accept an authenticated cookie profile immediately",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const delayedBearerSession = deferred<{ data: { session: { access_token: string } } }>();
|
||||||
|
let coldStartSettled = false;
|
||||||
|
const coldStartResultPromise = loadTerminalAuthProfile({
|
||||||
|
hasSupabasePublicEnv: true,
|
||||||
|
getSession: () => delayedBearerSession.promise,
|
||||||
|
loadAuthProfile: (accessToken) => {
|
||||||
|
const token = String(accessToken || "");
|
||||||
|
if (!token) {
|
||||||
|
return Promise.resolve({
|
||||||
|
authenticated: true,
|
||||||
|
user_id: "cookie-user",
|
||||||
|
subscription_active: null,
|
||||||
|
degraded_auth_profile: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
authenticated: true,
|
||||||
|
user_id: "bearer-paid-user",
|
||||||
|
subscription_active: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
coldStartResultPromise.then(() => {
|
||||||
|
coldStartSettled = true;
|
||||||
|
});
|
||||||
|
await flushMicrotasks();
|
||||||
|
assert(
|
||||||
|
coldStartSettled === false,
|
||||||
|
"terminal auth bootstrap must not show a cold-start paywall from a degraded cookie profile before bearer auth has a chance to confirm Pro",
|
||||||
|
);
|
||||||
|
|
||||||
|
delayedBearerSession.resolve({ data: { session: { access_token: "paid-token" } } });
|
||||||
|
const coldStartResult = await coldStartResultPromise;
|
||||||
|
assert(
|
||||||
|
coldStartResult.user_id === "bearer-paid-user" &&
|
||||||
|
coldStartResult.subscription_active === true,
|
||||||
|
"terminal auth bootstrap should prefer the bearer-confirmed active Pro profile over a degraded cookie profile",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,4 +76,10 @@ export function runTests() {
|
|||||||
signedOutBlock.indexOf("setProAccess(createEmptyAccess(false))"),
|
signedOutBlock.indexOf("setProAccess(createEmptyAccess(false))"),
|
||||||
"terminal auth listener must re-check the current Supabase session before clearing access on SIGNED_OUT events",
|
"terminal auth listener must re-check the current Supabase session before clearing access on SIGNED_OUT events",
|
||||||
);
|
);
|
||||||
|
assert(
|
||||||
|
dashboardSource.includes('event === "INITIAL_SESSION"') &&
|
||||||
|
dashboardSource.indexOf('event === "INITIAL_SESSION"') <
|
||||||
|
dashboardSource.indexOf('event === "TOKEN_REFRESHED"'),
|
||||||
|
"terminal auth listener must hydrate access from Supabase INITIAL_SESSION events during first navigation from the landing page",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ function firstKnownProfile(cookieResult: SettledProfile, bearerResult: SettledPr
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canResolveProfileImmediately(
|
||||||
|
payload: TerminalAuthProfilePayload | null,
|
||||||
|
): payload is TerminalAuthProfilePayload {
|
||||||
|
return payload?.authenticated === true && payload.subscription_active === true;
|
||||||
|
}
|
||||||
|
|
||||||
export async function loadTerminalAuthProfile({
|
export async function loadTerminalAuthProfile({
|
||||||
getSession,
|
getSession,
|
||||||
hasSupabasePublicEnv,
|
hasSupabasePublicEnv,
|
||||||
@@ -56,7 +62,7 @@ export async function loadTerminalAuthProfile({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const resolveIfAuthenticated = (payload: TerminalAuthProfilePayload | null) => {
|
const resolveIfAuthenticated = (payload: TerminalAuthProfilePayload | null) => {
|
||||||
if (!payload?.authenticated || resolvedAuthenticated) return;
|
if (!canResolveProfileImmediately(payload) || resolvedAuthenticated) return;
|
||||||
resolvedAuthenticated = true;
|
resolvedAuthenticated = true;
|
||||||
resolveAuthenticated?.(payload);
|
resolveAuthenticated?.(payload);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user