Skip to content

fix(plugin-auth): pin better-auth family to 1.7.0-rc.1 — fresh 15.1.0 projects 500 on every auth endpoint#3085

Merged
os-zhuang merged 1 commit into
mainfrom
claude/brave-dijkstra-d83873
Jul 17, 2026
Merged

fix(plugin-auth): pin better-auth family to 1.7.0-rc.1 — fresh 15.1.0 projects 500 on every auth endpoint#3085
os-zhuang merged 1 commit into
mainfrom
claude/brave-dijkstra-d83873

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

P0: npx create-objectstack (15.1.0) ships with broken auth

Every fresh project returns 500 on sign-up / sign-in / get-session with Cannot set properties of undefined (setting 'modelName'), thrown from @better-auth/oauth-provider@1.6.23 during better-auth initialization (Failed to register OIDC discovery routes at startup).

Root cause: overrides don't ship with published packages

602053c pinned the better-auth family to 1.7.0-rc.1 via pnpm-workspace.yaml overrides, because GHSA-j8v8-g9cx-5qf4 (high, account/provider takeover in @better-auth/scim) is fixed only in the 1.7.0 pre-release line — npm latest is still 1.6.23 and no stable 1.7.0 exists, so waiting for stable is not an option.

That makes the whole framework CI/showcase surface green against 1.7.0-rc.1 — but pnpm overrides apply only inside this workspace. The published @objectstack/plugin-auth still declared ^1.6.23 for better-auth / @better-auth/core / @better-auth/oauth-provider / @better-auth/sso (only @better-auth/scim was pinned). npm's default resolution never selects a prerelease from a caret range, so downstream installs get scim@1.7.0-rc.1 + everything else @1.6.23 — a combination never tested here, and one that crashes at init: the 1.7 code paths import symbols that only exist in @better-auth/core 1.7.x, so the family must move together.

Fix

  • Pin all four to the exact 1.7.0-rc.1 (matching @better-auth/scim) in packages/plugins/plugin-auth/package.json. No ^ on the rc — a caret range on a prerelease is exactly how this drift shipped. Relax to ^1.7.0 the moment a stable better-auth 1.7.0 ships (tracked in the pnpm-workspace.yaml comment block).
  • Workspace lockfile unchanged by the pin (pnpm already resolved these to 1.7.0-rc.1 via the overrides; they stay as belt-and-braces).
  • Changeset: patch on @objectstack/plugin-auth (fixed group).

Prevention: override-consistency CI gate

New scripts/check-override-consistency.mjs, wired into the validate-deps workflow: for every pnpm-workspace.yaml override whose package name appears in a publishable package's dependencies / optionalDependencies / peerDependencies, the declared range must resolve to the override target under npm's default (no-prerelease) semantics — i.e. what a downstream install actually gets. Violations fail CI with:

downstream resolves "^1.6.23" WITHOUT the override; update the declared range so it resolves to 1.7.0-rc.1 (or drop the override)

Declarations the override can never rewrite (range doesn't intersect the override's pkg@selector scope, e.g. create-objectstack's tar ^7.5.20 vs tar@>=2.0.0 <7.5.11) are skipped — workspace and downstream already agree there. Negative-tested: reverting one pin to ^1.6.23 fails the check; current tree passes. Needs semver (root devDependency; the step runs after pnpm install).

Verification (packed artifact, not workspace)

Two fresh create-objectstack@15.1.0 projects, side by side; the fixed one resolves @objectstack/plugin-auth to the pnpm pack tarball of this branch:

broken-app (npm 15.1.0 as-is) fixed-app (this branch's tarball)
lockfile family resolution 1.6.23 + scim 1.7.0-rc.1 mix all 1.7.0-rc.1 (adapters + telemetry follow)
startup log Failed to register OIDC discovery routes (modelName TypeError) clean — zero occurrences
POST /api/v1/auth/sign-up/email 500 modelName 200 (token + user)
POST /api/v1/auth/sign-in/email 500 modelName 200 (session token)
GET /api/v1/auth/get-session 500 modelName 200 (user + session)

Refs: GHSA-j8v8-g9cx-5qf4 · 602053c · aead168 (validate-deps pipeline)

🤖 Generated with Claude Code

…ed manifest

Every fresh `npx create-objectstack` (15.1.0) project shipped with auth
returning 500 on sign-up / sign-in / get-session: "Cannot set properties
of undefined (setting 'modelName')", thrown from
@better-auth/oauth-provider@1.6.23 during better-auth init.

The workspace forces the whole better-auth family to 1.7.0-rc.1 via
pnpm-workspace.yaml overrides (GHSA-j8v8-g9cx-5qf4 is only fixed in the
1.7.0 pre-release line, see 602053c), so every CI job and showcase here
ran a graph downstream installs can never reproduce: overrides do not
ship with published packages, and plugin-auth still declared ^1.6.23 for
better-auth / @better-auth/core / @better-auth/oauth-provider /
@better-auth/sso. Downstream resolved those to 1.6.23 (still the npm
`latest`), and the 1.7 scim + 1.6.23 rest mix crashes at startup.

- Pin all four to the exact 1.7.0-rc.1 (same as @better-auth/scim) — the
  only combination this repo actually builds and tests. No `^` on the rc:
  npm's default resolution never selects a prerelease from a caret range,
  which is precisely how this drift shipped. Relax to ^1.7.0 once a
  stable better-auth 1.7.0 exists.
- Workspace lockfile is unchanged by the pin (pnpm already resolved these
  to 1.7.0-rc.1 via the overrides); the overrides stay as belt-and-braces.
- Add scripts/check-override-consistency.mjs + a validate-deps step: for
  every pnpm-workspace override whose package appears in a publishable
  package's dependencies/optionalDependencies/peerDependencies, the
  declared range must resolve to the override target under npm's default
  (no-prerelease) semantics, so tested-vs-published drift fails CI with a
  "overrides do not ship with published packages" hint instead of
  shipping silently. Needs semver, added as a root devDependency.

Verified against the packed artifact: `pnpm pack` of this plugin-auth,
installed into a fresh create-objectstack@15.1.0 project via a pnpm
override — lockfile resolves the whole family (adapters + telemetry
included) to 1.7.0-rc.1, startup log has zero "Failed to register OIDC
discovery routes", and sign-up / sign-in / get-session all return 200. A
control project without the override reproduces the three 500s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 17, 2026 4:34am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tooling size/m labels Jul 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth.

9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/sso.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit 01ba3b3 into main Jul 17, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/brave-dijkstra-d83873 branch July 17, 2026 04:39
os-zhuang added a commit that referenced this pull request Jul 17, 2026
…rom core auth (#3087)

Follow-up to #3085. In the 15.1.0 incident, @better-auth/oauth-provider
threw "Cannot set properties of undefined (setting 'modelName')" during
plugin construction, and because the better-auth instance is built
lazily per request, that single optional federation feature turned EVERY
auth endpoint (sign-up / sign-in / get-session) into a 500 — a blast
radius wildly out of proportion to the feature.

AuthManager.buildPluginList now constructs plugins in two explicit tiers:

- OPTIONAL (organization, admin, phoneNumber, magicLink, genericOAuth,
  jwt+oauthProvider as one atomic unit, sso, scim, deviceAuthorization):
  built through addOptionalPlugin(), which on failure skips the feature
  with one loud actionable console.error, records it in
  getDegradedAuthFeatures(), and leaves core email/password + session
  auth up. The feature's endpoints 404 until the operator fixes the
  underlying error and restarts.

- CORE (bearer, twoFactor, haveIBeenPwned, customSession): still fail
  hard. Skipping these would silently weaken auth — 2FA-enrolled
  accounts signing in on password alone, opted-in breached-password
  rejection vanishing, the ADR-0069 authGate (password expiry, enforced
  MFA) and positions[] disappearing from sessions. Better a hard 500
  than fail-open security.

The OIDC discovery mount in auth-plugin.ts consults the degraded set and
skips advertising /.well-known documents for an IdP whose endpoints did
not come up (previously the "Failed to register OIDC discovery routes"
path), logging why instead of sending external clients into 404s.

Regression tests run the REAL better-auth pipeline (no betterAuth mock,
a minimal in-memory IDataEngine behind the genuine ObjectQL adapter)
with the exact incident TypeError injected into oauthProvider: sign-up
and get-session return 200, the degraded feature is reported, the
console.error fires; a throwing CORE plugin (bearer) still rejects; the
degraded set resets on a healthy rebuild. Full plugin-auth suite: 457
tests pass.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 17, 2026
…r would (#3100)

The 15.1.0 incident (#3091): workspace pnpm overrides kept every in-repo CI
job on better-auth 1.7.0-rc.1, but overrides do not ship with published
packages, so every fresh `npx create-objectstack` project resolved an
untested dependency mix that 500'd all auth endpoints. #3085 added the static
gate (override ↔ declaration consistency); this adds the dynamic gate —
install the exact bits a user would get and drive the first-run flow.

scripts/publish-smoke.sh, two modes sharing one probe set:
- pack (default): pnpm-pack all 67 publishable packages (pack applies the
  same manifest rewrites as publish), scaffold a fresh project outside the
  workspace with the repo-built create-objectstack, pin @objectstack/* to
  the tarballs via the project's OWN pnpm-workspace.yaml overrides (nothing
  inherited from the repo), install, and smoke. A lockfile assertion proves
  no @objectstack/* leaked to the registry.
- registry: npx create-objectstack@latest + npm install against the real
  registry — catches ^-range ecosystem drift breaking published versions.

Probes: anonymous get-session 200 → sign-up 200 → sign-in 200 → session
carries the user → REST CRUD round (201/200/200/200) on the scaffolded
object as the seeded dev admin → zero error/fatal log lines (ANSI-stripped;
catches the #3091 'Failed to register OIDC discovery routes' signature).

.github/workflows/publish-smoke.yml:
- workflow_run after each Release run — the changesets release PR is pushed
  with GITHUB_TOKEN, whose events never trigger on:push/pull_request (the
  release PR has no Actions checks today), so the smoke runs after Release
  completes, checks out the release-branch head SHA iff an open release PR
  exists, and reports the verdict back as a commit status on that SHA so it
  is visible on the release PR. Deliberately not in normal PR CI (too slow).
- weekly registry canary (Mon 04:47 UTC) that opens/refreshes an issue on
  failure.

Verified locally: pack mode green on the current tree; a negative run with
plugin-auth's declarations reverted to the 15.1.0 mix (^1.6.23 + scim rc.1)
goes red via the log scan — core sign-in stays up thanks to the #3087
isolation, and the smoke still catches the broken optional-plugin init.
Registry mode green against published 15.1.1. better-sqlite3 stays an
optionalDependency, so a failed native build falls back to WASM sqlite
(#2229) instead of blocking the smoke.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant