Repository navigation
Commit 4f4c4ed
Fixes #22258
Clause-②: no
## What this changes
This is the `domain:services` half of the split-session card.
better-auth's `getSession` renews a session older than `updateAge`: it
moves `sys_session.expires_at` to `now + expiresIn` and stages the
renewed cookie on that call's own response. The nine in-process readers
below answered with their own response, so the renewal landed in the
database and its cookie was discarded, leaving a live bearer beside a
dying cookie.
Each reader now hands better-auth `inProcessSessionReadInput(headers)`
from `@objectstack/types`, the rule PR #22367 landed (`a45d5d8ab7`):
- A request carrying a session cookie reads with `query.disableRefresh`,
so the session renews only through `GET /api/v1/auth/get-session`, which
re-issues the cookie.
- A bearer-only request reads exactly as before, renewal included.
Each change is a one-expression substitution. The headers pass through
untouched, so every reader resolves the same session it did before.
- `@objectstack/plugin-webhooks` gains a workspace dependency on
`@objectstack/types`, per the triage ruling (`6072029812`). There is no
cycle: `@objectstack/types` depends only on `@objectstack/spec`, and
`@objectstack/core` already pulled `types` in transitively.
`pnpm-lock.yaml` was regenerated by `pnpm install` (+3 lines).
- `check:test-source-alias` then required the matching anchored alias in
`packages/plugins/plugin-webhooks/vitest.config.ts`. That registry is
shrink-only, so the gate's own remedy is the alias, and that line is in
this PR.
- No `packages/types`, `rest`, `runtime`, `plugin-hono-server`,
`cloud-connection` or `packages/spec` edit.
## Enumeration pin: the census over `packages/services/**` and
`packages/plugins/**`, non-test sources
Measured at the merge base `117d34de` and at this head `c09841ccf`.
Excluded: `*.test.ts`, `__tests__/**`, `*.testkit.ts`, `*.md`.
| reader (line at head) | spelling | before (`117d34de`) | after
(`c09841ccf`) |
|---|---|---|---|
| plugin-auth `auth-plugin.ts:2465` (toggle-disabled gate) |
`authApi.getSession(` | `{ headers: c.req.raw.headers }` |
`inProcessSessionReadInput(c.req.raw.headers)` |
| plugin-auth `auth-plugin.ts:2528` (`gateAdmin`) | `(authApi as
any).getSession(` | `{ headers: c.req.raw.headers }` |
`inProcessSessionReadInput(c.req.raw.headers)` |
| plugin-auth `auth-plugin.ts:2595` (unlock-user gate) |
`authApi.getSession(` | `{ headers: c.req.raw.headers }` |
`inProcessSessionReadInput(c.req.raw.headers)` |
| plugin-auth `auth-plugin.ts:2913` (has-permission branch) | `(authApi
as any).getSession(` | `{ headers: c.req.raw.headers }` |
`inProcessSessionReadInput(c.req.raw.headers)` |
| plugin-webhooks `webhook-outbox-plugin.ts:483` | `api.getSession(` |
`{ headers: c.req.raw.headers }` |
`inProcessSessionReadInput(c.req.raw.headers)` |
| service-storage `storage-service-plugin.ts:844` | `api.getSession(` |
`{ headers }` | `inProcessSessionReadInput(headers)` |
| plugin-sharing `sharing-plugin.ts:941` | `api?.getSession?.(` | `{
headers: h }` | `inProcessSessionReadInput(h)` |
| service-settings `settings-service-plugin.ts:300` |
`api?.getSession?.(` | `{ headers: h }` | `inProcessSessionReadInput(h)`
|
| service-datasource `admin-routes.ts:212` | `api?.getSession?.(` | `{
headers }` | `inProcessSessionReadInput(headers)` |
| plugin-hono-server `current-user-endpoints.ts:412` | `api.getSession(`
| already the helper (PR #22367) | unchanged |
- **Counted per spelling, at `c09841ccf`:**
- The fixed string `api.getSession(` catches 3 calls (webhooks, storage,
hono) plus 2 doc comments (`anonymous-session-refusal.ts:63` and
`platform-admin-gate.ts:90`).
- The fixed string `api?.getSession?.(` catches 3 calls (sharing,
settings, datasource).
- The any-receiver pattern `[)a-zA-Z_]\??\.getSession\??\.?\(` catches
all 10 calls above, the same 2 doc comments, and 5 hits that are not
better-auth reads: `store.getSession(` ×4 in
`service-storage/src/storage-routes.ts` and `this.getSession(` in
`metadata-store.ts`, both the upload-session store.
- **None left.** At `c09841ccf`, the pattern `getSession\??\.?\(\{
*headers` over the same scope matches only the doc comment at
`platform-admin-gate.ts:90`. No call passes a bare `{ headers }`.
- **The triage spelling misses plugin-auth.** As a fixed string,
`api.getSession(` is case-sensitive. It matches neither
`authApi.getSession(` (capital `A`) nor `(authApi as any).getSession(`,
so it finds none of the four plugin-auth readers. The table rests on the
any-receiver pattern.
## Pins, and the ablation of each
- **plugin-auth: real better-auth.**
`src/in-process-session-renewal.pin.test.ts` runs the installed
better-auth, with `expiresIn` and `updateAge` read off the live
instance. It uses a real `AuthManager` and the plugin's real
`registerAuthRoutes` on a Hono app (the
`admin-remove-user-gate-ordering` harness).
- The session is aged to `now + expiresIn − updateAge − 60 s`, and
`sys_session` is read back after every request.
- Precondition: a bare in-process `getSession` renews.
- Control: `GET /get-session` renews and re-issues the cookie with
`Max-Age = expiresIn`.
- **The other five: input pins** through each package's real door. They
assert what the reader hands better-auth: `query: { disableRefresh: true
}` for a cookie, and for cookie plus bearer; no `query` at all for
bearer-only. What that input does against real better-auth is pinned by
the plugin-auth file above and by
`packages/runtime/src/in-process-session-renewal.pin.test.ts`.
- **Ablation**, run at `c09841ccf` through
`scripts/ablation-replace.mjs` in wrap mode (literal anchor that must
hit exactly once, on-disk counts and blob hashes, restore proven against
HEAD). Each leg put the old `{ headers }` call back for one reader and
ran that reader's pin. Every mutated reader resolves from `src/` in its
suite (relative imports), so no `dist/` leg applies.
| reader | pin and door | by cookie | bearer-only control | ablation:
failing output | restored |
|---|---|---|---|---|---|
| plugin-auth `:2465` | real better-auth, `POST
/admin/oauth2/toggle-disabled` | 0 s, no cookie | renews to `now +
expiresIn`, no cookie | 1 failed, 20 passed: exactly "toggle-disabled —
by cookie" (`the session renewed (+86460 s) but its cookie was not
re-issued`) | blob `32c004a7d80f` == HEAD |
| plugin-auth `:2528` `gateAdmin` | real better-auth, `POST
/admin/set-user-manager` | 0 s, no cookie | renews, no cookie | 1
failed, 20 passed: exactly "set-user-manager (gateAdmin) — by cookie"
(+86460 s) | `32c004a7d80f` == HEAD |
| plugin-auth `:2595` | real better-auth, `POST /admin/unlock-user` | 0
s, no cookie | renews, no cookie | 1 failed, 20 passed: exactly
"unlock-user — by cookie" (+86460 s) | `32c004a7d80f` == HEAD |
| plugin-auth `:2913` | real better-auth, `POST /admin/has-permission` |
0 s, no cookie | renews, no cookie | 1 failed, 20 passed: exactly
"has-permission — by cookie" (+86460 s) | `32c004a7d80f` == HEAD |
| plugin-webhooks `:483` | input, `POST /api/v1/webhooks/redeliver` |
`disableRefresh` | no query | 2 failed, 1 passed: the cookie and
cookie-plus-bearer cases (`expected undefined to deeply equal {
disableRefresh: true }`) | `60dc0b99b985` == HEAD |
| service-storage `:844` | input, `GET
/api/v1/storage/upload/chunked/:uploadId/progress` via
`mountStorageRoutes` | `disableRefresh` | no query | 2 failed, 1 passed:
the two cookie cases | `b85d1c3f6fe1` == HEAD |
| plugin-sharing `:941` | input, `GET /api/v1/share-links` (real plugin
boot) | `disableRefresh` | no query | 2 failed, 1 passed: the two cookie
cases | `026ac1febf6e` == HEAD |
| service-settings `:300` | input, the routes' `contextFromRequest`
(real plugin boot, pass-through capture) | `disableRefresh` | no query |
2 failed, 1 passed: the two cookie cases | `e9af2764acea` == HEAD |
| service-datasource `:212` | input, `GET /api/v1/datasources/drivers`
(real registrar on Hono) | `disableRefresh` | no query | 2 failed, 1
passed: the two cookie cases | `0868657715d2` == HEAD |
Each plugin-auth leg reddened exactly the one door it ablated, so each
door reaches exactly one reader. The bearer control stayed green in
every leg.
## Verification at `c09841ccf` (this branch merged with `origin/main`
`191543456`)
- **Build:** `turbo run build --filter=!@objectstack/docs`: 72/72 tasks,
exit 0.
- **`pnpm --filter PKG test`, all exit 0:**
- plugin-auth: 133 files, 2693 passed, 10 skipped;
- plugin-webhooks: 16 files, 168 passed;
- service-storage: 47 files, 776 passed;
- plugin-sharing: 41 files, 1005 passed;
- service-settings: 42 files, 755 passed;
- service-datasource: 42 files, 763 passed.
- **`pnpm --filter PKG typecheck`:** exit 0 for all six. Each new pin
file is listed by a program the typecheck script runs (`tsc
--listFilesOnly`): `tsconfig.json`, or `tsconfig.test.json` through
`check:test-typecheck`.
- **Gates:** `node scripts/pm/dispatch-gates.mjs --commands` derived 82
commands for this diff (the pre-derived 76, plus
`check:engine-double-contract`, `check:objectql-double-limit`,
`check:query-options-erasure`, `check:type-check-coverage`,
`check:type-check-debt` and `check:where-matcher`). All 82 exit 0 at
`c09841ccf`. `--ran` with the recorded exit codes prints: `Run
reconciliation — 82 derived, 82 run, 0 NOT-MEASURED, 0 UNRUN.`
- **Lint, narrowed and declared** (full `pnpm lint` is CI's run):
- The population, read from eslint's own config: the 13 changed `.ts`
files. The other three changed files (`.changeset/*.md`, `package.json`,
`pnpm-lock.yaml`) answer "File ignored because no matching configuration
was supplied".
- `eslint --no-inline-config --format json` over the 13: 13 files
linted, 0 errors, 0 warnings.
- Invariance: the config sets no `parserOptions.project` and no
type-aware rule, so this diff cannot move the verdict on an untouched
file.
## Acceptance notes
1. **Residue measured, out of this census.** Four plugin-auth doors
still split a cookie session after this change, through in-process reads
that do not spell `getSession(`.
- Measured on the same real-better-auth harness: each moved `expires_at`
+86460 s by cookie and set no cookie.
- `POST /api/v1/auth/admin/sso/register`: the `/get-session` re-dispatch
through the better-auth handler in `register-sso-provider.ts:60`, behind
`gateAdmin`.
- `POST /api/v1/auth/send-verification-email`: the same re-dispatch in
`send-verification-email.ts:63`.
- `POST /api/v1/auth/organization/add-member`: `authApi.addMember({ ...,
headers })` in `organization-add-member.ts:175`; the vendor's session
read inside renews.
- `POST /api/v1/auth/set-initial-password`: `authApi.setPassword({ ...,
headers })` in `set-initial-password.ts:65`.
- Same mechanism by source, not measured: `authApi.createOAuthClient({
..., headers })` at `auth-plugin.ts:3175`, and the SSO bridges' inner
re-dispatches (`register-sso-provider.ts:210, 306, 404, 454`). Each
bridge returns only status and body, so a vendor `Set-Cookie` there is
discarded.
- These need a rule shape for a handler re-dispatch or a vendor endpoint
call, not the one-expression `getSession` input, so they are reported to
the seat rather than changed here.
2. **A correction to PR #22367's H5 table.** H5 attributed the `POST
/admin/sso/register` split to `gateAdmin` alone. With `gateAdmin`
converted, that door still splits through the re-dispatch in note 1. The
`gateAdmin` pin therefore uses `POST /admin/set-user-manager`, which
reads the session once.
3. **The cli half's pending changeset, amended in `8d9dcbb4`** (seat's
edit of this note, after patch round 1).
`.changeset/22258-in-process-session-read-no-renewal-behind-cookie.md`
ended by saying the services-lane readers "still renew a cookie session
without re-issuing its cookie", which this PR makes false in the same
release. Under the seat's ruling A (#22258, ACCEPT `6073337286`), its
last paragraph now reads: "The same rule is applied to the in-process
`auth.api.getSession` readers in … by their own changeset." No other
sentence and no frontmatter changed. `Check Changeset` is red by design
(the DELIBERATE CORRECTION class); the at-tier record `6073440660` on
`8d9dcbb4` confirms it: do not restore the old sentence.
4. **Outside the planned surface: one line.**
`packages/plugins/plugin-webhooks/vitest.config.ts` gains the anchored
`@objectstack/types` alias that `check:test-source-alias` dictates for
the new dependency.
5. **Imported fixture.** The plugin-auth pin imports
`createMemoryEngine` from `impersonation-bearer-rotation.test.ts`, as
twenty sibling files do, so that file's ten cases also run inside this
pin (21 = 11 + 10). Reusing the pinned double adds no new engine double
to the ledger.
6. **A doc comment left as is.** `platform-admin-gate.ts:90` still says
the gate's `session` is what `auth.api.getSession({ headers })`
returned. It describes the result's shape, which is unchanged.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WYYhVJ78u7PhwFViWo1EmQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: os-elon-musk <elon-musk@objectstack.ai>
1 parent 440bed6 commit 4f4c4ed
17 files changed
Lines changed: 874 additions & 12 deletions
File tree
- .changeset
- packages
- plugins
- plugin-auth/src
- plugin-sharing/src
- plugin-webhooks
- src
- services
- service-datasource/src
- __tests__
- service-settings/src
- service-storage/src
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
2461 | 2462 | | |
2462 | 2463 | | |
2463 | 2464 | | |
2464 | | - | |
| 2465 | + | |
2465 | 2466 | | |
2466 | 2467 | | |
2467 | 2468 | | |
| |||
2524 | 2525 | | |
2525 | 2526 | | |
2526 | 2527 | | |
2527 | | - | |
| 2528 | + | |
2528 | 2529 | | |
2529 | 2530 | | |
2530 | 2531 | | |
| |||
2591 | 2592 | | |
2592 | 2593 | | |
2593 | 2594 | | |
2594 | | - | |
| 2595 | + | |
2595 | 2596 | | |
2596 | 2597 | | |
2597 | 2598 | | |
| |||
2909 | 2910 | | |
2910 | 2911 | | |
2911 | 2912 | | |
2912 | | - | |
| 2913 | + | |
2913 | 2914 | | |
2914 | 2915 | | |
2915 | 2916 | | |
| |||
Lines changed: 276 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
0 commit comments