Repository navigation
[Web] Stale dsh-auth-* cookies: server-side self-healing (retire other authorities cookies at mint time) #12938
isheng-eqi
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
TL;DR
Every report I can find about this (#9924, #10716, #11391, #11410, #12166, #12360, #12400, #11220) describes the desktop host, and master already fixed it there in
93bef27— fix(desktop): remove legacy Host auth cookies from the default session. That commit touches onlyapps/desktop/*: it clears the Electron cookie jar.The web build (
dsh web) has no equivalent, andpackages/client/connectionstill never retires a cookie minted for a different authority. So the web side still accumulates whenever the authority changes (different port,--port 0, several profiles).This post proposes the missing half: let the server retire stale cookies itself, at mint time. The server is the only party that can name them, because the request's
Cookie:header lists every one.Why server-side retirement rather than the existing suggestions
authenticate()comparespayload.authority !== authorityand rejects on mismatch, so the name is not the only port binding — relaxing it changes the security boundaryapps/desktop-hosthardcodes--port 0maxHeaderSize(#12166)Server-side retirement needs no configuration, no naming change, no user action, and no new security assumption. It works for the web build and for any host that talks HTTP to
connection.The change (two files, both required)
1.
packages/client/connection/src/browser-auth.ts— emit expiry instructions for everydsh-auth-*cookie that is not the current authority's:and append them to
set-cookiein the mint branch ofauthorizeIndex(), plus the already-authenticated 303 and the/connect302.2.
packages/host/frontend-static/src/index.ts— thewriteHeadadapter must not stringify the array:The trap worth flagging
With only change 1 the response looks correct but does nothing: the
Cookieheader is ~531 B in a singleSet-Cookieline carrying every attribute of all four cookies. Cause:new Headers(nextHeaders)runsString()over the array value, andSet-Cookiecannot be comma-joined (its ownExpiresvalue contains a comma).packages/host/webserver'snodeHeaders()already does the right thing — it usesheaders.getSetCookie()— so once the adapter appends per value, the multi-value header survives to the wire.Diagnostic that separates the two states: read the response with a real HTTP client, not
curlpiped through a shell.resp.headers.get_all('Set-Cookie')returns 1 entry (broken adapter, ~531 B, containsMax-Age=2592000andMax-Age=0) versus 4 entries (correct).Measured on 0.2.2-alpha.2 (commit ce7073d)
Header ceiling, constructed with real-shaped cookies (~228 B each), Node default
maxHeaderSize16384:End-to-end on
dsh web:Browser-level (Playwright + Chromium, the affected profile): seed 72
dsh-auth-LEGACY*cookies for127.0.0.1:3080, then open the printed token URL → after load only 1dsh-auth-remains, the UI renders normally, console shows 0 errors.Bootstrap caveat: once the browser already carries more than 16 KB, even the cleaning response cannot be delivered — the request is rejected before the application. Start once with
node --max-http-header-size=65536 apps/cli/lib/bin.js web; the first token exchange then cleans the store, and default arguments are enough afterwards.Scope note for the web build
With the fixed default port (3080) the authority is constant, the cookie name is stable, and one row overwrites the previous one — that is why a plain
dsh webuser normally never hits this. It accumulates as soon as the port changes:--port 0, port-collision fallback, or several profiles listening on different ports. I used a fixed web port in the measurements above because it shows the ceiling and the fix without needing the desktop shell.中文摘要
截至 0.2.2-alpha.2,
dsh-auth-*cookie 按sha256(host:port)命名、Max-Age30 天、从不清理;HTTP cookie 只按 host 隔离不按端口隔离,于是同一127.0.0.1上历史累积的每一枚都会进入之后每个请求的Cookie:头,超过 Node 默认maxHeaderSize(16384 B)后服务端回 431。master 的93bef27只在apps/desktop清理 Electron cookie 库,web 侧没有对应实现。这里补的是服务端自愈:mint 新 cookie 时,读取请求的
Cookie:头,对所有不属于当前 authority 的dsh-auth-*回Max-Age=0。服务端是唯一能点出这些名字的一方。两处改动缺一不可——frontend-static的writeHead适配层用的是new Headers(nextHeaders),会先把数组String()成一条畸形头(Set-Cookie不能用逗号合并),必须逐条append。实测:68 枚 → 401、71 枚 → 431;打完补丁后,带 3 枚旧 cookie 的 token 请求回 4 条
Set-Cookie(1 新 + 3 过期),控制组只回 1 条;浏览器里预置 72 枚旧 cookie 后打开 token URL,加载完成只剩 1 枚、界面正常、console 零错误。首次应用需要一次--max-http-header-size=65536启动(否则清理指令本身也发不出去)。如果维护者认为服务端退 cookie 的方式可以接受,我可以把它整理成符合仓库规范的补丁;如果不接受,也建议至少在 web 侧补上
93bef27的等价清理。All reactions