fix(auth): a tab acts only as the account it was built for, and the review's other findings
A tab names the account it was claimed for on every request, beacon and upload (X-Siftlode-Expect-Account); current_user and /auth/logout refuse a mismatch with 401 account_changed and keep the session, so a tab left showing the account signed in before reloads instead of writing to the new one. /auth/login clears a connect marker left by an abandoned connect, and a link completes only on a session that still holds. The prefs merge reads the row under a lock. A tab's sessionStorage records its own owner. A page the account loses is given up, address bar and remembered page included. The panel layouts are preferences; the storage contract also refuses a key handed to the Storage API or to usePref outside its kind. A refused sign-in as someone else and a failed sign-out say so. The chromium-only e2e specs are in the gate register, the member walk waits for its requests, and devices.spec checks the save itself.
This commit is contained in:
@@ -8,6 +8,7 @@ import { formatBytes, formatDecimal } from "../format";
|
||||
import { reportError } from "../errorDialog";
|
||||
import { logRequest, QUIET_HEADER, REQUEST_ID_HEADER, ROUTE_HEADER, send } from "../http";
|
||||
import { currentDebugSession, newRequestId } from "../log";
|
||||
import { claimedAccount } from "../storage";
|
||||
|
||||
/** The active UI language, as a bare code the server understands ("en"/"hu" — never "en-GB").
|
||||
* Lives here so the request builders can stamp it themselves: a signal that has to be passed in by
|
||||
@@ -270,13 +271,24 @@ export const setupHeaders = (token: string) => ({
|
||||
// The debug session this tab is recording: the server tags its own lines of the request with
|
||||
// it, and logs them at the session's level. Ignored by the server unless it is the caller's own.
|
||||
const DEBUG_SESSION_HEADER = "X-Debug-Session";
|
||||
// The account this tab was built for. The cookie alone says who is asking, and a sign-in as someone
|
||||
// else in another tab replaces it under this one too: the server refuses (401) a request whose tab
|
||||
// shows another account than the one signed in, and the 401 reloads the tab as the one who is. It
|
||||
// never picks an account — a player checkpoint or a pref save from a tab still showing the previous
|
||||
// account would otherwise land on the new one.
|
||||
const EXPECTED_ACCOUNT_HEADER = "X-Siftlode-Expect-Account";
|
||||
|
||||
/** What this request carries besides the session cookie (which alone says who is asking) — the
|
||||
* debug session it records into — as the headers every request path sends (`req`, `beacon`,
|
||||
* `uploadReq`): one definition, so a path cannot quietly go out without them. */
|
||||
* account the tab was built for, the debug session it records into — as the headers every request
|
||||
* path sends (`req`, `beacon`, `uploadReq`): one definition, so a path cannot quietly go out
|
||||
* without them. */
|
||||
export function identityHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {};
|
||||
const account = claimedAccount();
|
||||
if (account) headers[EXPECTED_ACCOUNT_HEADER] = account;
|
||||
const session = currentDebugSession();
|
||||
return session ? { [DEBUG_SESSION_HEADER]: session } : {};
|
||||
if (session) headers[DEBUG_SESSION_HEADER] = session;
|
||||
return headers;
|
||||
}
|
||||
|
||||
// Fire-and-forget POST that survives page unload (keepalive) — for every write a closing page must
|
||||
@@ -320,8 +332,8 @@ export async function req<T = unknown>(
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
// Per-tab identity override (only setup calls and raw uploads pass their own headers:
|
||||
// setup is pre-auth, and an upload spreads `identityHeaders()` into its own).
|
||||
// Who the tab is (only setup calls and raw uploads pass their own headers: setup is
|
||||
// pre-auth, and an upload spreads `identityHeaders()` into its own).
|
||||
...identityHeaders(),
|
||||
},
|
||||
...opts,
|
||||
|
||||
Reference in New Issue
Block a user