Files
Félix Malfait 39535a8248 test(e2e): cookie-authenticate direct backend calls, and clear the token pair on switch (#24982)
> **Scope changed.** #24983 was merged into this branch, so this PR now
carries both changes: the e2e helper refactor **and** the token-pair
clear. It is no longer the e2e-only PR its original description covered.
Still stacked on #24981.

## 1. e2e: authenticate direct backend calls by session cookie

`getAccessAuthToken` read the token pair out of `localStorage` to build
a `Bearer` header. `login.setup.ts` saves storage state straight after
sign-in with no reload, so the pair is there today — but
`CookieSessionBootEffect` switches the client onto the session cookie on
the first authenticated page load, and once part 2 clears the pair the
helper throws `"No auth token pair found in local storage"`.

`page.request` shares the browser context's cookie jar, so these calls
now carry the session cookie the app itself uses.
`CookieSessionCsrfMiddleware` fails closed on a missing `Origin` for a
cookie-authenticated write and `page.request` sends none, so the shared
helper sets it from `FRONTEND_BASE_URL`, which the server already
resolves as an allowed credentialed origin.

## 2. front: clear the token pair once the client switches to cookie
auth

Closes core-team-issues#2756.

#23755 kept the pair as a dormant fallback because a server with
`AUTH_COOKIE_SESSIONS_ENABLED=false` ignores the session cookie. That
flag no longer exists — #24257 removed it and every branch behind it, so
no deployed configuration can turn cookie sessions off. The fallback's
only remaining effect was keeping a 60-day refresh token in
`localStorage`, which is the XSS-exfiltration surface cookie sessions
exist to close.

`switchToCookieAuth` clears the pair again, and the fallback it fed goes
with it: `canFallBackFromCookieAuth`, the cookie-auth branch in
`handleTokenRenewal`, and the `isMissingCredentialGraphQLError` branch
from #24275 — which was gated on a retained refresh token and so could
never fire once the pair is gone. `isMissingCredentialGraphQLError.ts`
and `setIsCookieAuthActive.ts` lose their only consumers.

Also fixes a race the review caught: `attemptTokenRenewal` published
through `onTokenPairChange` unconditionally while `ensureTokenRenewed`
guards the same write behind a refresh-token snapshot. A renewal in
flight when the boot effect probes could resolve after the clear and put
a fresh refresh token back for the rest of the session, since the boot
effect only probes while cookie auth is inactive. It now takes the same
snapshot. Regression test included.

Two things deliberately kept:

- **The auth link still suppresses `Bearer` while cookie auth is
active.** A pair exists between sign-in and the boot effect's switch,
and across the impersonation exchange; sending it would take precedence
over the session cookie and bypass the CSRF origin check.
- **`FORBIDDEN` stays in `AUTH_REFUSAL_CODES`.** A new bundle talking to
a server predating #24981 still gets `FORBIDDEN` on the probe.

## Sequencing

Recovery from a session cookie the server does not accept now rests
entirely on the server reporting `UNAUTHENTICATED`. `WorkspaceAuthGuard`
has done so since #24276; `UserAuthGuard` does as of #24981, which is
this PR's base. **Without #24981 in place first, part 2 reopens the
blank-app wedge for `UserAuthGuard` resolvers, `currentUser` among
them.** The stacked base enforces the order.

## What this does not fix

The pair is not removed for a freshly signed-in tab: sign-in still
stores one and navigates client-side, and the boot effect has already
spent its single probe on the sign-in page. That window closes only when
the web token-pair machinery is deleted outright —
core-team-issues#2747, gated on the 60-day refresh window.

## CI

Both CI failures on this stack were confirmed transient by re-run on
identical commits: `server-integration-test (9)` (one runner's database
had not migrated; 15/16 shards green) and `front-sb-test (3, pages)`
(lost iframe, zero tests run; 11/12 shards green). Both passed on
attempt 2 with no code change.
2026-08-28 15:41:18 +02:00
..