Skip to content

Commit 3477726

Browse files
committed
docs(plugin-auth): re-measure the better-auth version attestations on 1.7.12
Every live reading that named 1.7.3 was re-measured against the installed 1.7.12 dist or by the test that pins it, and restamped with the date and the card. Facts about the 1.7.3 release itself stay as written. The internal-field-readback census is scoped to its 1.7.3 trace and flagged instead of restamped: sso's verify-domain reads the verification identifier back off the row, which the census says no flow does. The pnpm-workspace.yaml family note states the new target and what the lift moved. Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
1 parent aa31124 commit 3477726

14 files changed

Lines changed: 64 additions & 38 deletions

‎packages/plugins/plugin-auth/src/account-invitation-marker.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
* is what creates the row's credential on first set. But better-auth hands the
1212
* resulting link only to `sendResetPassword({ user, url, token }, ctx.request)`;
1313
* `requestPasswordReset` itself answers `{ status, message }` and nothing else
14-
* (installed better-auth 1.7.3, `dist/api/routes/password.mjs`). So the import
14+
* (better-auth 1.7.12, `dist/api/routes/password.mjs`, re-measured 2026-10-21
15+
* for #25096). So the import
1516
* never holds the link, and the template choice can only be made inside
1617
* `sendResetPassword` (`auth-manager.ts`). Without a marker that callback
1718
* cannot tell an administrator's invitation from a user who forgot their
@@ -65,8 +66,8 @@
6566
*
6667
* better-auth runs `sendResetPassword` through `runInBackgroundOrAwait`, which
6768
* CATCHES and logs whatever the send throws, and `requestPasswordReset` still
68-
* answers `{ status: true }` (installed 1.7.3, `dist/context/create-context.mjs`
69-
* and `dist/api/routes/password.mjs`). That is right for the public door — the
69+
* answers `{ status: true }` (better-auth 1.7.12, `dist/context/create-context.mjs`
70+
* and `dist/api/routes/password.mjs`, re-measured 2026-10-21 for #25096). That is right for the public door — the
7071
* answer must not say whether an address exists or whether its mail went out —
7172
* and it means a throw inside the send can never reach the import. No
7273
* better-auth option makes the call rethrow: `advanced.backgroundTasks.handler`

‎packages/plugins/plugin-auth/src/admin-import-users-invite-delivery-failure.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* The import invites an email row by asking better-auth for a password reset,
77
* and better-auth runs `sendResetPassword` through `runInBackgroundOrAwait`,
88
* which catches and logs whatever the send throws and still answers
9-
* `{ status: true }` (installed 1.7.3, `dist/context/create-context.mjs`). So a
9+
* `{ status: true }` (better-auth 1.7.12, `dist/context/create-context.mjs`,
10+
* re-measured 2026-10-21 for #25096). So a
1011
* throw inside the send can never reach the import. `sendResetPassword` now
1112
* settles a delivery receipt the import's own request carries
1213
* (`account-invitation-marker.ts`), and the import reads it after the request

‎packages/plugins/plugin-auth/src/auth-email-locale.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,8 @@ describe("#14641 — an invitation reads the INVITEE's own sys_user.locale", ()
867867
* recipient-keyed engine, not by asserting it about this send alone.
868868
*
869869
* Two branches, because a magic link is BOTH a sign-in for an existing account
870-
* and a sign-up for a new address (re-measured 2026-09-11 in the installed
871-
* better-auth 1.7.3, unchanged from 1.7.2: the `/sign-in/magic-link` handler
870+
* and a sign-up for a new address (re-measured 2026-10-21 for #25096 on
871+
* better-auth 1.7.12, unchanged since 1.7.2: the `/sign-in/magic-link` handler
872872
* takes `email` straight off the body, stores a verification value and calls
873873
* `sendMagicLink` — no user lookup on that path at all — while
874874
* `/magic-link/verify` reads `findUserByEmail` and, finding nobody, calls
@@ -1003,8 +1003,8 @@ describe("#15106 — the magic link reads the recipient's own sys_user.locale",
10031003
});
10041004

10051005
it('matches the address better-auth itself will resolve the link with — lowercased', async () => {
1006-
// ⚠️ Measured against the installed better-auth 1.7.3 (2026-09-11), not
1007-
// assumed, and unchanged from the 1.7.2 reading this replaces:
1006+
// ⚠️ Measured against better-auth 1.7.12 (2026-10-21, #25096), not
1007+
// assumed, and unchanged since the 1.7.2 reading:
10081008
// `signInMagicLinkBodySchema` declares `email: z.email()` with no case
10091009
// transform, so a typed `Ada@Example.com` arrives here verbatim, while
10101010
// `internalAdapter.findUserByEmail` — what `/magic-link/verify` resolves

‎packages/plugins/plugin-auth/src/auth-manager.ts‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,8 +1799,9 @@ export class AuthManager {
17991799
// The deployment's product name, from the one authority every auth
18001800
// email already reads (`getAppName()`: the `branding.workspace_name`
18011801
// override, else the configured `appName`, else 'ObjectStack').
1802-
// better-auth 1.7.3 reads this key once, into its context's `appName`
1803-
// (unset ⇒ "Better Auth"), and reads that in exactly two places: the
1802+
// better-auth 1.7.12 (re-read 2026-10-21 for #25096) reads this key once,
1803+
// into its context's `appName` (unset ⇒ "Better Auth"), and reads that
1804+
// in exactly two places: the
18041805
// issuer and label prefix of the otpauth URI that `/two-factor/enable`
18051806
// and `/two-factor/get-totp-uri` answer. No cookie name derives from it
18061807
// (the prefix is the literal "better-auth") and nothing stores it, so an
@@ -2058,8 +2059,8 @@ export class AuthManager {
20582059
);
20592060
}
20602061
// The lifetime the token in `url` really has: better-auth mints it
2061-
// with `resetPasswordTokenExpiresIn || 3600` seconds (installed
2062-
// 1.7.3 `routes/password.mjs`) — `||`, so a configured `0` is its
2062+
// with `resetPasswordTokenExpiresIn || 3600` seconds (better-auth
2063+
// 1.7.12 `routes/password.mjs`, re-measured 2026-10-21 for #25096) — `||`, so a configured `0` is its
20632064
// default too, and the mail must not say "0 minutes" for it.
20642065
const ttlSec = this.config.emailAndPassword?.resetPasswordTokenExpiresIn || 60 * 60;
20652066
// [#23343] An account an administrator created (the identity
@@ -2081,7 +2082,8 @@ export class AuthManager {
20812082
// commit 35e94c96b — the ladder's stored rung. It matters most HERE: an
20822083
// admin-initiated send (`admin-import-users.ts` calls
20832084
// `requestPasswordReset` server-side) reaches this callback with NO
2084-
// request at all (measured on 1.7.3, #23343), so without this rung
2085+
// request at all (measured on 1.7.3, #23343; unchanged on 1.7.12,
2086+
// re-read for #25096), so without this rung
20852087
// the user's mail carries only the deployment default. Best-effort;
20862088
// `undefined` leaves the #14319 ladder exactly as it was.
20872089
const storedLocale = await this.storedRecipientLocale({ id: user.id });
@@ -4723,8 +4725,8 @@ export class AuthManager {
47234725
//
47244726
// Handing better-auth no `database` makes it build its own in-memory store
47254727
// in `getBaseAdapter`, and that store is keyed by the schema KEY while
4726-
// every read resolves by `modelName`. Re-measured 2026-09-11 on the
4727-
// installed better-auth 1.7.3, unchanged from the 1.7.2 reading it replaces
4728+
// every read resolves by `modelName`. Re-measured 2026-10-21 for #25096 on
4729+
// better-auth 1.7.12, unchanged since the 1.7.2 reading
47284730
// (`db/adapter-base.mjs` builds the store by schema KEY;
47294731
// `@better-auth/memory-adapter` throws on the lookup, naming the model):
47304732
//
@@ -9698,7 +9700,8 @@ export class AuthManager {
96989700
* (plus `id`), or `undefined` when the identifier names no account.
96999701
*
97009702
* It asks exactly what better-auth's `/sign-in/email` credential check asks
9701-
* (1.7.3: `internalAdapter.findUserByEmail(email.toLowerCase())`, an
9703+
* (1.7.12, re-read 2026-10-21 for #25096:
9704+
* `internalAdapter.findUserByEmail(email.toLowerCase())`, an
97029705
* equality match of the `email` column against the lower-cased address,
97039706
* which is also the spelling `internalAdapter.createUser` stores), so the
97049707
* gate, the counter, the ledger and the credential check name one account

‎packages/plugins/plugin-auth/src/auth-schema-config.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -953,12 +953,12 @@ export const buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;
953953

954954
// NOTE: there is intentionally no scim mapping constant here, and no
955955
// `buildScimPluginSchema()`. `@better-auth/scim` hardcodes its models and exposes
956-
// NO `schema` option — still true of the installed stable `@better-auth/scim@1.7.3`
957-
// (re-read 2026-09-11: `SCIMOptions` declares no `schema` / `modelName` / `fields`
956+
// NO `schema` option — still true of the installed stable `@better-auth/scim@1.7.12`
957+
// (re-read 2026-10-21 for #25096: `SCIMOptions` declares no `schema` / `modelName` / `fields`
958958
// member at all — its six members are still connections, authentication,
959959
// managedConnections, identity, projection, compatibility; measured 2026-08-19 on
960960
// the rc, re-measured 2026-08-27 on stable 1.7.1, again 2026-08-31 on 1.7.2 for
961-
// #13940, and again on 1.7.3 with the family lift), so
961+
// #13940, again on 1.7.3 with the family lift, and on 1.7.12 with #25096), so
962962
// there is nowhere to hand one. This is no longer true
963963
// of `@better-auth/sso@1.7.1`, which now accepts one (#8224) — for scim, and for
964964
// scim alone, the ADAPTER layer is the only available route.

‎packages/plugins/plugin-auth/src/implicit-account-linking.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
*
4343
* ## Why the enforcement point is `user.validateUserInfo`
4444
*
45-
* Measured against the installed better-auth `1.7.3`
45+
* Measured against better-auth `1.7.12` (re-measured 2026-10-21 for #25096)
4646
* (`dist/oauth2/link-account.mjs`, `handleOAuthUserInfo`):
4747
*
4848
* - The library's own gate refuses an implicit link when
@@ -63,7 +63,8 @@
6363
* implicitly (`/callback/:id`, `/sign-in/social` with an id token,
6464
* one-tap, oauth-proxy, and the SSO plugin, which share the function).
6565
* - The EXPLICIT link (`/link-social` → `/callback/:id` with `link` in the
66-
* OAuth state) passes the same `action: 'link-account'`. It is told apart
66+
* OAuth state) passes the same `action: 'link-account'` (1.7.12 routes it
67+
* through `linkOAuthAccount`, in the same file). It is told apart
6768
* by the OAuth state the callback parsed (`getOAuthState().link`), which
6869
* the server writes from the session at `/link-social`; `generateState`
6970
* places `link` AFTER the client-supplied `additionalData`, so a client

‎packages/plugins/plugin-auth/src/in-process-redispatch.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
* renewing — there is no cookie to fall behind.
2828
*
2929
* better-auth reads the flag from the query on both kinds of re-dispatch
30-
* (1.7.3, measured by `in-process-session-renewal.pin.test.ts`): the
30+
* (1.7.12, re-measured 2026-10-21 for #25096 by
31+
* `in-process-session-renewal.pin.test.ts`): the
3132
* `/get-session` route declares it (`getSessionQuerySchema`, coerced), and
3233
* `getSessionFromCtx` — which `sessionMiddleware` and this plugin's own
3334
* before-hooks call — spreads the route's `ctx.query` into the read it makes,

‎packages/plugins/plugin-auth/src/internal-field-readback.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ const READBACK_FIELDS: Readonly<Record<string, readonly ReadbackColumn[]>> = {
194194
// that no generic exit — the data path, and the compliance ledger's CRUD
195195
// mirror that honours the same flag — carries it, and each is here because a
196196
// better-auth route reads it back OFF a row its storage adapter returned
197-
// (traced in the installed better-auth / @better-auth/* dist, 1.7.3):
197+
// (traced in the then-installed better-auth / @better-auth/* dist, 1.7.3;
198+
// ⚠️ [#25096] NOT re-stamped for 1.7.12 — see the `identifier` note below):
198199
//
199200
// - `sys_jwks.private_key` — the jwt plugin's key read (`findMany`, no
200201
// projection) feeds signing. MEASURED before this row existed: the flag
@@ -206,6 +207,11 @@ const READBACK_FIELDS: Readonly<Record<string, readonly ReadbackColumn[]>> = {
206207
// adapter's `consumeOne` re-attaches through this table too).
207208
// `required: true`. `identifier` is deliberately absent: no flow reads it
208209
// back off a row — it is only ever the lookup key.
210+
// ⚠️ [#25096] That census is wrong, on 1.7.3 and on 1.7.12 alike:
211+
// `@better-auth/sso`'s `/sso/verify-domain` builds the TXT record it
212+
// expects from `identifier` read back off the row, so on this store it
213+
// expects `undefined=TOKEN`. Measured and reported on #25096; nothing
214+
// here is changed by that card.
209215
// - `sys_two_factor.secret` (`required: true`) and `.backup_codes` (empty
210216
// until generated) — TOTP and backup-code verification read both off the
211217
// enrolment row.

‎packages/plugins/plugin-auth/src/invite-role-not-found-code.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
*
1313
* new APIError("BAD_REQUEST", { message: `${ROLE_NOT_FOUND}: ${unknownRoles}` })
1414
*
15-
* (`dist/plugins/organization/routes/crud-invites.mjs`, measured against the
16-
* installed better-auth `1.7.3`). Its sibling `update-member-role`
15+
* (`dist/plugins/organization/routes/crud-invites.mjs`, measured against
16+
* better-auth `1.7.12`, re-measured 2026-10-21 for #25096). Its sibling `update-member-role`
1717
* (`routes/crud-members.mjs`) throws the SAME refusal with
1818
* `code: ROLE_NOT_FOUND` beside the identical message — so one vendor
1919
* condition reached the wire coded on one door and uncoded on the other. The

‎packages/plugins/plugin-auth/src/list-user-invitations-verification.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
*
88
* ## The defect, and where it is minted
99
*
10-
* Not here: it comes out of the pinned vendor. Re-measured 2026-09-11 against
11-
* the installed better-auth `1.7.3` (unchanged from the `1.7.2` reading this
12-
* replaces), `dist/plugins/organization/routes/crud-invites.mjs`:
10+
* Not here: it comes out of the pinned vendor. Re-measured 2026-10-21 for
11+
* #25096 against better-auth `1.7.12` (unchanged since the `1.7.2` reading),
12+
* `dist/plugins/organization/routes/crud-invites.mjs`:
1313
* the three id-addressed routes ask
1414
* `shouldRequireVerifiedEmailForInvitationIdAction({ organizationOptions, … })`,
1515
* whose first line is

0 commit comments

Comments
 (0)