You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
auth: server-side auth.api.getSession reads renew the session without forwarding the renewed cookie, so the browser cookie expires before the session (split session) #22258
Filing gate: ① a product defect, reach measured through public doors. Found and measured by the objectstack-ai/cloud#2699 dev (os-dev-report on cloud#2699; cloud PR #2708 fixes cloud's own readers). Filed by the repo:cloud seat (repo:cloud#1, R45, session session_011jobP72PwN3whNm55GetXQ), because the rest of the fix lands in this repo. ⛔ Not a claim.
The defect (better-auth 1.7.3 at cloud's pin 56bf27af: expiresIn 604800 s, updateAge 86400 s)
Renewal sets a cookie only on that call's response. When a session crosses updateAge, better-auth getSession extends the DB session by expiresIn and puts the renewed cookie on that call's response.
Server-side reads discard that response. A host that calls auth.api.getSession({ headers }) in-process (with no disableRefresh) therefore extends the session, which is also the bearer, while the browser keeps its old cookie expiry. Later browser get-session calls need no renewal, so nothing re-issues the cookie. The cookie dies first. That leaves a split session: a dead cookie beside a live bearer.
Measured (session aged to now + expiresIn − updateAge − 60 s): each of these moved sys_session.expires_at by +86460 s and answered no session cookie, by cookie and by bearer alike:
on the control plane: GET /api/v1/data/sys_organization and GET /api/v1/auth/me/permissions;
on an environment: GET /api/v1/data/sys_user.
Control:GET /api/v1/auth/get-session and GET /api/v1/auth/organization/list renew and re-issue the cookie (getSessionFromCtx forwards Set-Cookie).
The readers, by source:packages/rest/src/rest-server.ts:2990, packages/plugins/plugin-hono-server/src/current-user-endpoints.ts:406 and packages/runtime/src/security/resolve-session-principal.ts:51. git grep disableRefresh -- packages/ at the pin gives 0 hits.
Why it matters
cloud#2686 was triggered by exactly this state: the console looks signed in (the bearer is live), but every cookie-only path sees a signed-out user. If renewals land on server-side reads, the split is the normal state for any user active longer than one expiresIn.
Fix direction (for this lane to choose)
Class-closing:plugin-auth owns the rule that an in-process auth.api.getSession never renews (query: { disableRefresh: true }). Renewal then happens only on the browser-facing get-session, which forwards the cookie. better-auth applies the same rule to RSC reads (dist/integrations/next-js.mjs:69).
Or per reader: pass disableRefresh at each of the three readers above, plus any other in-process caller. A grep census is part of the fix.
Pin: a server-side read past updateAge leaves sys_session.expires_at unchanged and sets no cookie.
Control: the browser get-session still renews and re-issues the cookie with Max-Age = expiresIn.
Ablation: dropping the flag turns the pin red.
Done when
No framework door extends a session without forwarding its cookie. Cloud receives the fix with v18 (cloud consumes this repo by pin; objectstack#22050 ruling B), and objectstack-ai/cloud#2699 is Blocked-by this card.
Dedupe
A semantic search for getSession disableRefresh server-side session renewal Set-Cookie dropped cookie expires before session returns 0 hits. Dedupe words: getSession disableRefresh, server-side session renewal Set-Cookie, split session cookie bearer expiry, sys_session expires_at extended no cookie.
Reader: triage routes it. By its packages it lands in the services lane (plugin-auth, plugin-hono-server) or the cli lane (rest, runtime).
Filing gate: ① a product defect, reach measured through public doors. Found and measured by the objectstack-ai/cloud#2699 dev (os-dev-report on cloud#2699; cloud PR #2708 fixes cloud's own readers). Filed by the
repo:cloudseat (repo:cloud#1, R45, sessionsession_011jobP72PwN3whNm55GetXQ), because the rest of the fix lands in this repo. ⛔ Not a claim.The defect (better-auth 1.7.3 at cloud's pin
56bf27af:expiresIn604800 s,updateAge86400 s)updateAge, better-authgetSessionextends the DB session byexpiresInand puts the renewed cookie on that call's response.auth.api.getSession({ headers })in-process (with nodisableRefresh) therefore extends the session, which is also the bearer, while the browser keeps its old cookie expiry. Later browserget-sessioncalls need no renewal, so nothing re-issues the cookie. The cookie dies first. That leaves a split session: a dead cookie beside a live bearer.now + expiresIn − updateAge − 60 s): each of these movedsys_session.expires_atby +86460 s and answered no session cookie, by cookie and by bearer alike:GET /api/v1/data/sys_organizationandGET /api/v1/auth/me/permissions;GET /api/v1/data/sys_user.GET /api/v1/auth/get-sessionandGET /api/v1/auth/organization/listrenew and re-issue the cookie (getSessionFromCtxforwardsSet-Cookie).packages/rest/src/rest-server.ts:2990,packages/plugins/plugin-hono-server/src/current-user-endpoints.ts:406andpackages/runtime/src/security/resolve-session-principal.ts:51.git grep disableRefresh -- packages/at the pin gives 0 hits.Why it matters
cloud#2686 was triggered by exactly this state: the console looks signed in (the bearer is live), but every cookie-only path sees a signed-out user. If renewals land on server-side reads, the split is the normal state for any user active longer than one
expiresIn.Fix direction (for this lane to choose)
plugin-authowns the rule that an in-processauth.api.getSessionnever renews (query: { disableRefresh: true }). Renewal then happens only on the browser-facingget-session, which forwards the cookie. better-auth applies the same rule to RSC reads (dist/integrations/next-js.mjs:69).disableRefreshat each of the three readers above, plus any other in-process caller. A grep census is part of the fix.getSessionData, plus areadSessionWithoutRenewalhelper inobjectos-runtime), with pins.Tests
updateAgeleavessys_session.expires_atunchanged and sets no cookie.get-sessionstill renews and re-issues the cookie withMax-Age = expiresIn.Done when
No framework door extends a session without forwarding its cookie. Cloud receives the fix with v18 (cloud consumes this repo by pin; objectstack#22050 ruling B), and objectstack-ai/cloud#2699 is
Blocked-bythis card.Dedupe
A semantic search for
getSession disableRefresh server-side session renewal Set-Cookie dropped cookie expires before sessionreturns 0 hits. Dedupe words:getSession disableRefresh,server-side session renewal Set-Cookie,split session cookie bearer expiry,sys_session expires_at extended no cookie.Reader: triage routes it. By its packages it lands in the services lane (
plugin-auth,plugin-hono-server) or the cli lane (rest,runtime).Generated by Claude Code