Uh oh!
There was an error while loading. Please reload this page.
deps(auth): move the better-auth family off the 1.7.0-rc.2 prerelease onto stable ^1.7.1 - #9869
Conversation
…e ^1.7.1 line Stable 1.7.x has shipped: `npm view <pkg> dist-tags` reports latest 1.7.1 for better-auth, @better-auth/core, oauth-provider, sso, the five adapters and telemetry. The prerelease pin was the remediation for GHSA-p2fr-6hmx-4528 and GHSA-j8v8-g9cx-5qf4 (patched only in >=1.7.0-beta.4), so it could not be dropped until a stable line above that floor existed. It now does. - pnpm-workspace.yaml overrides move to `^1.7.1`, with the selector bounds reshaped to the MAJOR boundary (`pkg@<2.0.0`) so a future advisory lift moves only the target — the shape the file's own header mandates. This retires the better-auth carve-out in the drift note. - plugin-auth's direct declarations move to `^1.7.1` in lockstep (overrides do not ship with published packages; check:override-consistency holds them). - @better-auth/scim stays at 1.7.0-rc.1. Measured against the published 1.7.1 tarball, stable ships the rc.2 REWRITE — no scimProvider model, no generate-token endpoint, and all six new models present — so moving it is the ADR-0071 migration tracked by #3653, not this bump. rc.1 is above the advisory fix floor and its peer ranges accept the stable 1.7.1 core. Refs #3002
Moving to stable 1.7.1 surfaced three pieces of upstream drift that the pinned
rc.2 did not have. All three were caught by the repo's own drift gates, and all
three are mechanical parity — no behaviour is authored here.
1. THE ACCOUNT ID FIELD FLIP-FLOPPED. `1.7.0-rc.2` renamed `account.accountId`
→ `providerAccountId`; stable 1.7.0/1.7.1 renamed it BACK to `accountId`,
keeping the new required `issuer`. Measured on the installed 1.7.1:
`getAuthTables({}).account.fields` = `issuer, accountId, providerId, userId,
…` with no `providerAccountId` at all. Carrying the rc.2 spelling left
`accountId` unmapped, so the adapter asked for a column of that name and
EVERY sign-up answered 500 `Unknown field 'accountId' on object
'sys_account'`. The `account_id` column is unchanged and no data moves.
Fixed in the schema mapping, in the `createAccount` call in
admin-user-endpoints, in the two mapping pin tests, and in the client's
`/list-accounts` response type.
2. `@better-auth/oauth-provider` 1.7.1's `oauthClient` model writes three
fields the platform object did not answer for: `applicationType` (the OIDC
spelling of what rc.2 called `type` — mapped onto the EXISTING `type`
column, so no data moves), plus genuinely new `clientDiscoveryId` and
`clientCredentialsScopes`, now declared on `sys_oauth_application`. Without
these, `POST /oauth2/register` 500s at the driver.
3. Two new endpoints are mounted publicly by the catch-all:
`POST /oauth2/end-session` and `POST /oauth2/end-session/confirm` — the POST
form of OIDC RP-initiated logout, whose GET counterpart was already
ledgered. Added to BETTER_AUTH_MOUNTED_SURFACE with that rationale.
Also re-points the #5024 prerelease-pin probe at the state this leaves behind:
`@better-auth/scim` is the only prerelease pin left, so its follow-up ledger
names #3653 (the ADR-0071 migration) alone rather than a card this PR closes.
plugin-auth: 55 files, 1263 tests, all passing. tsc --noEmit clean.
Refs #3002📓 Docs Drift CheckThis PR changes 3 package(s): 18 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 1 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 24 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4bdef20df1bda7f148f07912a0c5415e0288feec && git checkout 4bdef20df1bda7f148f07912a0c5415e0288feec
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 05864fb201b5cfc16335bb80fb23828e4d4d657a 59e80dff3dee32ba0db5409657900049730710db && git checkout -B drift-repro 05864fb201b5cfc16335bb80fb23828e4d4d657a && git merge --no-ff 59e80dff3dee32ba0db5409657900049730710db
node scripts/docs-audit/affected-docs.mjs --json 05864fb201b5cfc16335bb80fb23828e4d4d657a
|
…oauth_application columns Generated output, not hand-edited: node scripts/check-i18n-bundles.mjs --write. `pnpm check:i18n` is green again (9 packages, all bundles in sync). Refs #3002
…id key
Second `internalAdapter.createAccount` call site carrying the rc.2
`providerAccountId` spelling — found by the dogfood suite, not by grep:
`showcase-demo-personas-loginable.dogfood.test.ts` failed with
phone.demo@example.com holds a credential account: expected undefined to be truthy
verify signIn failed: 401 {"code":"INVALID_EMAIL_OR_PASSWORD"}
The account row was written with no account id, so better-auth's
`findAccountByKey({ issuer, accountId })` could not see it and both demo
personas were silently un-loginable — exactly the failure mode #9308 fixture 1
exists to prevent, and exactly the shape its own comment warns about (a
misleading "User not found" pointing at the sys_user row rather than the
account). The admin persona was unaffected, which is why only the persona test
caught it.
Also adds the changeset for the family bump, the drift it absorbs, and the one
consumer-visible rename (`@objectstack/client`'s `/list-accounts` response
type), and lowers plugin-auth's TEST_DEBT ceiling 111 → 110 to the number this
branch now measures, as `check:type-check-debt` prescribes.
Verified: showcase-demo-personas-loginable dogfood 4/4 pass; the auth-path
dogfood set 57/57; app-showcase 337 tests + tsc clean.
Refs #3002…ith the flip-flop stated content/docs/permissions/authentication.mdx documented the mapping as `providerAccountId: 'account_id'` and told the reader that is "the field formerly called accountId". On stable 1.7 that is backwards, and a reader following it writes exactly the mapping that answers 500 on every sign-up. The field name moved twice inside the 1.7 line — rc.2 renamed `accountId` → `providerAccountId`, stable 1.7.0 renamed it back — so the correction carries a callout saying so rather than silently swapping the word: the next reader arriving from an rc-era note needs to know which way it went. content/docs/releases/ is left alone by rule; its v15/v17 entries are accurate records of what those releases did. Refs #3002
Base moved while this branch was in flight (55d2993 -> 86431f7). One conflict, in pnpm-lock.yaml: main carried a dev-dependency bump (@types/node 26.1.2 -> 26.2.0, eslint 10.8.0 -> 10.8.1, @typescript-eslint/parser 8.65.0 -> 8.67.0, svelte 5.56.8 -> 5.56.9, turbo 2.10.7 -> 2.10.10) while this branch carried the better-auth family move to ^1.7.1. Resolved by REGENERATION, never by hand: took origin/main's lockfile as the base, then re-derived it with `pnpm install` from the merged manifests. Both manifests (pnpm-workspace.yaml overrides, plugin-auth/package.json) merged cleanly with no conflict, so the regenerated lockfile carries both intents — verified: the family resolves to better-auth@1.7.1 with scim held at 1.7.0-rc.1, and main's five dev-dep bumps are all present. Merged with scripts/pm/os-regen-merge.sh (steps 1-3). This branch touches no `merge=os-regen` path — the i18n `*.objects.generated.ts` bundles it does touch are not routed through that driver — so step 2 was a no-op here; step 4's regeneration and gates follow in the next commit. Refs #3002
os-warren
commented
Aug 19, 2026
Merged The base moved while this branch was in flight and left the PR un-mergeable. Merged with a Merged head: One conflict,
On the generated-artifact half: this branch touches no Re-runs at
The diff is still 25 files / +437 −218, so the merge brought main in without widening this Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#3002
The restart condition this card was held on has fired. Re-verified by command, not memory:
Every pinned family member has a stable release above the
>=1.7.0-beta.4floor thatGHSA-p2fr-6hmx-4528 and GHSA-j8v8-g9cx-5qf4 are patched in, so the prerelease pin can
finally come off without putting either advisory back on the auth surface.
1. This PR contains NO data migration
Stated up front because a dependency bump is the wrong place to hide one, and because
packages/plugins/plugin-auth/src/backfill-account-issuer.tsappears in the changed-fileslist and will look like exactly that.
It is a two-line comment change. The whole diff to that file:
backfillAccountIssuer()already exists onmain— it landed with the rc.2 upgrade(#2986), not here. Its behaviour, its trigger, and the rows it touches are byte-for-byte
unchanged by this PR; only its doc comment is corrected, because it described the field under
the rc.2 name that stable renamed back. It stays in the tree because the
issuerfield isstill required on stable 1.7.1 — verified on the installed package,
getAuthTables({})stilllists
issuerfirst on the account model — so the pre-1.7 gap it closes is still real andremoving it here would break sign-in for legacy rows.
No other line added anywhere in this diff writes to storage. Verified mechanically:
returns only prose and identifier hits inside comments and a gate's self-test strings — no
call site. The two
createAccountcall sites this PR touches(
admin-user-endpoints.ts, the showcase seeder) are existing per-request / per-seed rowcreation, not sweeps: each already ran on
main, each is guarded by an existence check, andthe only change to either is the camelCase key the vendor renamed. The showcase seeder writes
example fixture data in
examples/app-showcase, not platform rows.The two new
sys_oauth_applicationcolumns are additive optional declarations — new DDLon upgrade, no rewrite of existing rows, no default backfilled over anything.
2. File surface — every file outside the claimed surface, and why
Claimed surface was
pnpm-workspace.yaml,pnpm-lock.yaml,packages/plugins/plugin-auth/**.The diff exceeds it. Each extension below is a producer-side consequence of one upstream
fact (the rc.2 → stable field rename, plus two new vendor columns), and each was forced by a
red gate or a red test — none is discretionary widening. Nothing here is a change I wanted to
make; it is where the fix has to land for the bump to work.
packages/platform-objects/src/identity/sys-oauth-application.object.tsclientDiscoveryIdandclientCredentialsScopesthrough the adapter. Without the column declarationsPOST /oauth2/register500s at the driver. The remedy location is named by the failing gate's own message: "add the field(s) to packages/platform-objects/src/identity/".packages/platform-objects/src/apps/translations/*.objects.generated.ts(×4)node scripts/check-i18n-bundles.mjs --write, forced by the two new columns above.check:i18nwas red without it.packages/platform-objects/src/identity/sys-account.object.tspackages/client/src/index.ts/list-accountsresponse type saidproviderAccountId; on stable 1.7 the route answersaccountId. Leaving it ships a published type that is factually wrong about the vendor this PR upgrades.examples/app-showcase/src/security/seed-approval-demo.tsinternalAdapter.createAccountcall site carrying the rc.2 spelling. Not cosmetic: it wrote persona accounts with no account id, and both demo personas silently stopped being able to sign in (401INVALID_EMAIL_OR_PASSWORD). Caught by the dogfood suite, not by any unit test.examples/app-showcase/src/security/demo-personas.tspackages/qa/dogfood/test/showcase-demo-personas-loginable.dogfood.test.tsscripts/check-prerelease-pin-watch.mjsscripts/check-type-check-coverage.mjspnpm check:type-check-debt --lower(plugin-auth TEST_DEBT 111 → 110), which the gate's own output prescribes when a change lowers the count.content/docs/permissions/authentication.mdxproviderAccountId: 'account_id'. On stable 1.7 that is backwards — a reader following it writes exactly the mapping that 500s every sign-up..changeset/better-auth-family-stable-1-7.mdcontent/docs/releases/is deliberately untouched per AGENTS.md; its v15/v17 entries areaccurate records of what those releases did.
The pins
pnpm-workspace.yamloverrides andplugin-auth's direct declarations both move to^1.7.1, in lockstep (check:override-consistencyholds them to it — the overrides do notship with published packages, so a downstream
npx create-objectstackresolves plugin-auth'sown ranges). The selector bounds are reshaped from
pkg@<1.7.0-rc.2to the majorboundary
pkg@<2.0.0, which is the shape the file's own header mandates: an exclusivebound sitting at the target's own version line stops covering the very versions it pinned the
day that version is itself flagged. That retires the better-auth carve-out in the drift note.
The pins are kept rather than dropped, because two of them (
oauth-provider,scim) are OSVfloors — same reasoning the file already records for
form-dataandundici: a transitivereintroduction must land on the patched line, and dropping a security pin is its own
decision, not a rider on a version bump.
@better-auth/scimstays on1.7.0-rc.1— deliberately, and that is not a shortcut.Measured against the published stable tarball rather than assumed:
Stable ships the rc.2 rewrite: no
scimProvidermodel, no generate-token endpoint, andall six replacement models present. So moving this pin is the ADR-0071 feature migration
tracked by #3653 — not the version bump this card is. Holding stays security-clean: rc.1 is
above the advisory fix floor,
pnpm audit --audit-level=highis green, and rc.1's peerranges (
better-auth/@better-auth/coreat^1.7.0-rc.1) are satisfied by the stable1.7.1 the family now resolves to.
Known skew while the hold lasts, stated rather than hidden: scim rc.1 peers
better-call@1.3.7while better-auth 1.7.1 depends onbetter-call@1.4.0, sopnpm installprints one unmet-peer warning and the tree carries a single
better-call@1.4.0. Measuredgreen across the whole plugin-auth suite including the SCIM credential-at-rest arms; it
retires with #3653.
Vendor drift the bump surfaced
Four things changed between rc.2 and stable. Every one was found by a gate or a test
failing — none by reading release notes — and each fix is mechanical parity.
1. The account id field flip-flopped, and it broke every sign-up.
1.7.0-rc.2renamedaccount.accountId→providerAccountId; stable 1.7.0/1.7.1 renamed it back toaccountId, keeping the new requiredissuer. Measured on the installed 1.7.1:No
providerAccountIdanywhere. Carrying the rc.2 spelling leftaccountIdunmapped, so theadapter asked for a column of that name:
The
account_idcolumn is unchanged and no data moves. Fixed in the schema mapping, in thecreateAccountcall inadmin-user-endpoints.ts, in the two mapping pin tests, and in theclient's
/list-accountsresponse type.2. A second
createAccountcall site, caught only by the dogfood suite. The showcasedemo-persona seeder carried the same rc.2 spelling, so both demo personas were written with
no account id and silently stopped being able to sign in:
That is precisely the failure #9308 fixture 1 exists to prevent, and precisely the shape its
own comment warns about (a misleading "User not found" pointing at the
sys_userrow ratherthan the account). The admin persona was unaffected, which is why only the persona test
caught it — no unit test would have.
3.
oauthClientgrew three fields.applicationTypeis the OIDC spelling of what rc.2called
type, so it maps onto the existingtypecolumn — no data moves.clientDiscoveryIdandclientCredentialsScopesare genuinely new and are now declared onsys_oauth_application. Without themPOST /oauth2/register500s at the driver.4. Two new public endpoints.
POST /oauth2/end-sessionandPOST /oauth2/end-session/confirm— the POST form of OIDC RP-initiated logout, whoseGETcounterpart was already ledgered. Added to
BETTER_AUTH_MOUNTED_SURFACEwith that rationale.The #5024 probe
scripts/check-prerelease-pin-watch.mjsderives its watch list from the prerelease pins, soafter this change it watches exactly one:
@better-auth/scim. Its follow-up ledger isre-pointed at #3653 alone, since #3002 is the card this PR closes.
--self-testpasses.Verification record
Every command below was run in this branch's worktree; the summaries are real output. The
dogfood row was re-run at the final head (
b679943), not at an earlier commit.npm view <pkg> dist-tags× 111.7.1islatestfor every family memberpnpm installpnpm audit --audit-level=highNo known vulnerabilities found— both GHSAs stay resolvedplugin-authsuiteplugin-authtsc --noEmitb679943showcase-demo-personas-loginable,oidc-authorization-code-flow,oidc-authorize-env-gate,admin-credential-lifecycle,auth-session-audit-trail,bearer-lane-password-change,account-oauth-tokens-not-serialized,session-token-not-serialized,admin-route-nonadmin-refusal. The earlier persona sign-in 401 is fixed and green at the final head.platform-objectssuiteapp-showcasesuite +tscpnpm check:override-consistencypnpm check:test-source-aliaspnpm check:type-source-resolutionpnpm check:type-check-coverage/--re-measurepnpm check:i18n--write)pnpm check:route-envelopepnpm check:engine-double-contractpnpm check:where-matcherpnpm check:query-options-erasurepnpm check:cross-package-test-inputsempty-state,liveness,strictness-ledger,variant-docs)changeset-fixed,no-major,empty-changeset,adr-0087-registration,objectui-changeset,changeset-gate-self-tests)doc-authoring,doc-anchors,docs-audit-scope,affected-docs)node scripts/check-osv-exemptions.mjsnode scripts/check-prerelease-pin-watch.mjs --self-testosv-scanneris not installed in this environment;pnpm audit --audit-level=highis thegate CI runs for these advisories (it is the one named in
pnpm-workspace.yaml's owncomments), and it is green.
Action item 5 confirmed against the stable release with no drift:
CustomAdapter'sconsumeOne/incrementOneare still present and still typecheck against 1.7.1's core, andsys_jwks.alg/.crvare unchanged — the JWKS and EdDSA-fallback tests pass.🤖 Generated with Claude Code