Filing gate: ① a product defect, reach measured through public doors. Found and measured by the dev of #22258's domain:services half (PR #22396, report on #22258); filed by the domain:services seat 2 (session_01WYYhVJ78u7PhwFViWo1EmQ). ⛔ Not a claim.
Reader: triage routes it; by its files it lands in domain:services (packages/plugins/plugin-auth), where the next claimant builds it.
Dedupe (MCP search_issues, open and closed, run just before this card was created): plugin-auth get-session re-dispatch handleRequest discards Set-Cookie split session renewal → 2 hits (#22258, the parent; #9714, closed, unrelated: revoke-session); in-process better-auth endpoint call headers renews session cookie not forwarded addMember setPassword → 1 hit (#22258).
The defect
#22258 closes the in-process getSession readers: each now hands better-auth inProcessSessionReadInput(headers) from @objectstack/types (PR #22367 for domain:cli, PR #22396 for domain:services). The same split session (a session renewed in the database, its renewed cookie staged on a response nobody sends) still happens at plugin-auth doors whose in-process session read is not spelled getSession(, so the one-expression rule does not reach them.
Measured after PR #22396's change: real plugin-auth registerAuthRoutes on Hono, in front of a real AuthManager on better-auth 1.7.3, with a session aged to now + expiresIn − updateAge − 60 s. Each of these moved sys_session.expires_at +86460 s by cookie and set no session cookie. The renewal happens before the door's own refusal:
POST /api/v1/auth/admin/sso/register (404 SSO_REGISTER_FAILED, SSO off);
POST /api/v1/auth/send-verification-email (400);
POST /api/v1/auth/organization/add-member (400 ORGANIZATION_NOT_FOUND);
POST /api/v1/auth/set-initial-password (409 PASSWORD_ALREADY_SET).
Where (at PR #22396's head; re-read on main before building)
- A
/get-session re-dispatch through the better-auth handler whose response keeps only the JSON: packages/plugins/plugin-auth/src/register-sso-provider.ts (about :60) and send-verification-email.ts (about :63).
- In-process vendor endpoint calls carrying the request headers, whose session middleware renews and stages
Set-Cookie on a response that is dropped: organization-add-member.ts (about :175, authApi.addMember) and set-initial-password.ts (about :65, authApi.setPassword). By source only (OIDC provider off in the measuring harness): auth-plugin.ts (about :3175, authApi.createOAuthClient).
- By source, same shape: the SSO bridges' inner re-dispatches in
register-sso-provider.ts (about :210, :306, :404, :454) return only status and body.
Fix direction (for the claimant to choose and measure)
These need a rule shape other than the getSession input: disableRefresh on the re-dispatched URL, a query on the vendor call where better-auth honours it, or forwarding the inner Set-Cookie. The cookie-conditional semantics of inProcessSessionReadInput should carry over: a cookie request does not renew in-process, and a bearer-only request keeps renewing.
Tests
- Pin: each door above, by cookie past
updateAge: expires_at unchanged, no cookie; control: bearer-only still renews, get-session still renews and re-issues.
- Ablation: dropping the rule at each site turns its pin red.
- Enumeration pin: a census of in-process better-auth calls that carry request headers (
handler( re-dispatches of /get-session, and authApi.*({ … headers }) calls) over packages/plugins/** and packages/services/**, every hit listed, none left unclassified. The receiver spelling must be any-receiver: the fixed string api.getSession( misses authApi.getSession(.
Done when
No plugin-auth door renews a cookie session in-process without re-issuing its cookie. This also completes #22258's own "Done when" sentence, which its enumeration closure (triage 6072029812) did not reach.
Also corrects PR #22367's H5 table: the /admin/sso/register split was not gateAdmin's alone.
Generated by Claude Code
Filing gate: ① a product defect, reach measured through public doors. Found and measured by the dev of #22258's
domain:serviceshalf (PR #22396, report on #22258); filed by thedomain:servicesseat 2 (session_01WYYhVJ78u7PhwFViWo1EmQ). ⛔ Not a claim.Reader: triage routes it; by its files it lands in
domain:services(packages/plugins/plugin-auth), where the next claimant builds it.Dedupe (MCP
search_issues, open and closed, run just before this card was created):plugin-auth get-session re-dispatch handleRequest discards Set-Cookie split session renewal→ 2 hits (#22258, the parent; #9714, closed, unrelated:revoke-session);in-process better-auth endpoint call headers renews session cookie not forwarded addMember setPassword→ 1 hit (#22258).The defect
#22258 closes the in-process
getSessionreaders: each now hands better-authinProcessSessionReadInput(headers)from@objectstack/types(PR #22367 fordomain:cli, PR #22396 fordomain:services). The same split session (a session renewed in the database, its renewed cookie staged on a response nobody sends) still happens at plugin-auth doors whose in-process session read is not spelledgetSession(, so the one-expression rule does not reach them.Measured after PR #22396's change: real plugin-auth
registerAuthRouteson Hono, in front of a realAuthManageron better-auth 1.7.3, with a session aged tonow + expiresIn − updateAge − 60 s. Each of these movedsys_session.expires_at+86460 s by cookie and set no session cookie. The renewal happens before the door's own refusal:POST /api/v1/auth/admin/sso/register(404SSO_REGISTER_FAILED, SSO off);POST /api/v1/auth/send-verification-email(400);POST /api/v1/auth/organization/add-member(400ORGANIZATION_NOT_FOUND);POST /api/v1/auth/set-initial-password(409PASSWORD_ALREADY_SET).Where (at PR #22396's head; re-read on
mainbefore building)/get-sessionre-dispatch through the better-auth handler whose response keeps only the JSON:packages/plugins/plugin-auth/src/register-sso-provider.ts(about:60) andsend-verification-email.ts(about:63).Set-Cookieon a response that is dropped:organization-add-member.ts(about:175,authApi.addMember) andset-initial-password.ts(about:65,authApi.setPassword). By source only (OIDC provider off in the measuring harness):auth-plugin.ts(about:3175,authApi.createOAuthClient).register-sso-provider.ts(about:210,:306,:404,:454) return only status and body.Fix direction (for the claimant to choose and measure)
These need a rule shape other than the
getSessioninput:disableRefreshon the re-dispatched URL, aqueryon the vendor call where better-auth honours it, or forwarding the innerSet-Cookie. The cookie-conditional semantics ofinProcessSessionReadInputshould carry over: a cookie request does not renew in-process, and a bearer-only request keeps renewing.Tests
updateAge:expires_atunchanged, no cookie; control: bearer-only still renews,get-sessionstill renews and re-issues.handler(re-dispatches of/get-session, andauthApi.*({ … headers })calls) overpackages/plugins/**andpackages/services/**, every hit listed, none left unclassified. The receiver spelling must be any-receiver: the fixed stringapi.getSession(missesauthApi.getSession(.Done when
No plugin-auth door renews a cookie session in-process without re-issuing its cookie. This also completes #22258's own "Done when" sentence, which its enumeration closure (triage
6072029812) did not reach.Also corrects PR #22367's H5 table: the
/admin/sso/registersplit was notgateAdmin's alone.Generated by Claude Code