From 12a6c74dccd677b01a9949c8c0f5a6af8fe4f81e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 07:55:18 +0000 Subject: [PATCH 1/4] fix(auth): upgrade better-auth to 1.7.1, verify account.issuer derives automatically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the better-auth dev dependency to 1.7.1 and moves the stale ^1.3.29 peer floor to ^1.4.0 (the release line where the index:true flags #937 depends on first shipped). Since deriveAuthLists reads better-auth's own getAuthTables() (#987/#997), the new required account.issuer column reaches the generated schema automatically — verified with a new regression test rather than assuming it. The table-level @@unique([issuer, accountId]) constraint better-auth 1.7 also declares is not emitted yet: that needs table-level db.indexes derivation (#985), which hasn't landed, so per #986's own triage note this stops short of duplicating that work. Collateral of the bump: better-auth 1.7 split the MCP plugin into a separate @better-auth/mcp package (added as an optional peer) with a new required `resource` option and a substantially redesigned OAuth table set, which needed the mcp-plugin tests and example config updated to match. Also fixes a pre-existing gap where deriveAuthLists didn't thread a static defaultValue through for number-typed fields, surfaced by the MCP plugin's expanded schema. Closes #986 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QtaxBvCjLsZcm7YiSmpteJ --- .changeset/eleven-mice-jog.md | 41 +++ CLAUDE.md | 2 +- docs/content/how-to/mcp.md | 10 +- docs/content/reference/auth.md | 11 +- examples/auth-demo/package.json | 2 +- examples/auth-demo/prisma/schema.prisma | 29 +- examples/mcp-demo/opensaas.config.ts | 9 +- examples/starter-auth/package.json | 2 +- examples/starter-auth/prisma/schema.prisma | 11 +- packages/auth/CLAUDE.md | 16 +- packages/auth/package.json | 11 +- packages/auth/src/config/derive-auth-lists.ts | 12 +- packages/auth/src/config/types.ts | 2 +- packages/auth/src/plugins/index.ts | 7 +- packages/auth/tests/auth-lists-drift.test.ts | 37 ++- .../auth/tests/generated-fk-shape.test.ts | 74 +++-- .../auth/tests/mcp-oauth-cascade-e2e.test.ts | 2 +- .../tests/plugin-table-derivation.test.ts | 58 ++-- pnpm-lock.yaml | 273 +++++++++++++----- 19 files changed, 456 insertions(+), 153 deletions(-) create mode 100644 .changeset/eleven-mice-jog.md diff --git a/.changeset/eleven-mice-jog.md b/.changeset/eleven-mice-jog.md new file mode 100644 index 00000000..2bae97a2 --- /dev/null +++ b/.changeset/eleven-mice-jog.md @@ -0,0 +1,41 @@ +--- +'@opensaas/stack-auth': minor +--- + +Bump the `better-auth` dev dependency to `1.7.1` and move the peer range off the stale `^1.3.29` floor to `^1.4.0` (the release line where better-auth's `index: true` flags — the basis for #937's index emission — first shipped; below it the generated schema silently omitted indexes better-auth itself declares). + +**New required `account.issuer` column.** better-auth 1.7 adds a required `issuer` column to its `account` model. Because `deriveAuthLists` derives the Auth lists from better-auth's own `getAuthTables()` (#987/#997), this column now appears in the generated schema automatically — no code change was needed, only verification against real generated output. Existing projects upgrading to `better-auth@^1.7` will see a **new NOT NULL column** on their `account` table and need a backfill for existing rows. Following better-auth's own `createLocalAccountIssuer`/`createOAuthAccountIssuer` helpers (`@better-auth/core/db`): + +```sql +-- Local (email/password) accounts +UPDATE "Account" SET issuer = 'local:' || "providerId" WHERE issuer IS NULL AND "providerId" = 'credential'; + +-- OAuth/social accounts without their own OIDC issuer (github, google-without-oidc, etc.) +UPDATE "Account" SET issuer = 'local:oauth:' || "providerId" WHERE issuer IS NULL AND "providerId" != 'credential'; +``` + +URL-encode `providerId` if it can contain characters outside `[A-Za-z0-9_-]`. If you configured a custom OIDC provider with its own issuer URL, use that provider's real issuer instead of the synthetic `local:oauth:` value. + +**The new `@@unique([issuer, accountId])` constraint is not yet emitted.** better-auth 1.7 also declares this composite unique index at the table level, but the stack only derives _field_-level `unique`/`index` flags today — table-level index derivation is #985, which hasn't landed. This is deliberately out of scope here (per #986's own triage note: build on #985 once it lands, don't duplicate it). When it does land, make sure your backfilled `issuer` values don't collide on `(issuer, accountId)` for any account, or the constraint will fail to apply. + +**Breaking (MCP plugin users only): `@better-auth/mcp` is now a separate package.** better-auth 1.7 split the `mcp` plugin out of `better-auth/plugins` into its own package, rebuilt on the OAuth Provider RFC 8707/9728 resource model. `@opensaas/stack-auth/plugins` now re-exports `mcp` from `@better-auth/mcp` (added as an optional peer — install it if you use MCP). The plugin also now **requires** a `resource` option: + +```typescript +import { mcp } from '@opensaas/stack-auth/plugins' + +authPlugin({ + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + // RFC 8707/9728 canonical resource identifier — required as of + // better-auth 1.7. Must match your `mcp.basePath`. HTTP is only + // accepted on loopback hosts. + resource: `${process.env.BETTER_AUTH_URL}/api/mcp`, + }), + ], +}) +``` + +better-auth 1.7's MCP plugin also declares a substantially different OAuth table set — the old `oauthApplication`/`oauthAccessToken`/`oauthConsent` three became seven tables (`oauthClient`/`oauthAccessToken`/`oauthConsent`/`oauthRefreshToken`/`oauthResource`/`oauthClientResource`/`oauthClientAssertion`). Since the derivation is schema-driven this needed no code changes, but if you have the MCP plugin enabled, running `pnpm generate` will produce a significantly different Prisma schema for these tables (new/renamed models, and `Session` gains reverse relations to the two token tables that now reference it). Review the diff and migrate your database accordingly. + +Also fixes `deriveAuthLists`'s scalar-field builder to thread a static `defaultValue` through for `number`-typed fields (`integer()`/`bigInt()`), matching the existing `string`/`boolean` behavior — surfaced by the MCP plugin's expanded schema, which now includes number fields with static defaults (e.g. `oauthResource.policyVersion`). diff --git a/CLAUDE.md b/CLAUDE.md index 669b8b65..369657d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -625,7 +625,7 @@ The stack provides Model Context Protocol server integration through `@opensaas/ 1. Enable MCP in config with `mcp: { enabled: true }` (the runtime reads `enabled`/`basePath`/`defaultTools`) 2. Core runtime derives CRUD tools for each list (query, create, update, delete) at request time -3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [mcp({ loginPage: '/sign-in' })] })`) and the `createBetterAuthMcpAdapter` session provider +3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: '' })] })`, imported from `@opensaas/stack-auth/plugins` — the plugin itself comes from the optional `@better-auth/mcp` peer since better-auth 1.7 split it out of `better-auth/plugins`) and the `createBetterAuthMcpAdapter` session provider 4. All tools respect existing access control rules 5. Custom tools can be added per-list via `mcp.customTools` (Zod or JSON Schema inputSchema); plugins can register global tools via `registerMcpTool` diff --git a/docs/content/how-to/mcp.md b/docs/content/how-to/mcp.md index 8a014f1f..09306107 100644 --- a/docs/content/how-to/mcp.md +++ b/docs/content/how-to/mcp.md @@ -45,7 +45,15 @@ export default config({ authPlugin({ emailAndPassword: { enabled: true }, // Add MCP plugin to Better Auth - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + // Canonical protected-resource identifier (RFC 8707/9728) — + // required since better-auth 1.7's MCP plugin, and must match + // `mcp.basePath` below. HTTP is only accepted on loopback hosts. + resource: `${process.env.BETTER_AUTH_URL || 'http://localhost:3000'}/api/mcp`, + }), + ], }), ], diff --git a/docs/content/reference/auth.md b/docs/content/reference/auth.md index 7dfdcd4d..9c6fb0f3 100644 --- a/docs/content/reference/auth.md +++ b/docs/content/reference/auth.md @@ -289,7 +289,12 @@ import { mcp } from '@opensaas/stack-auth/plugins' authPlugin({ betterAuthPlugins: [ - mcp({ loginPage: '/sign-in' }), + mcp({ + loginPage: '/sign-in', + // Canonical protected-resource identifier (RFC 8707/9728) — required + // since better-auth 1.7's MCP plugin. Must match `mcp.basePath` below. + resource: `${process.env.APP_URL}/api/mcp`, + }), // Add other Better Auth plugins here ], }) @@ -674,7 +679,9 @@ export default config({ plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [ + mcp({ loginPage: '/sign-in', resource: `${process.env.APP_URL}/api/mcp` }), + ], }), ], mcp: { diff --git a/examples/auth-demo/package.json b/examples/auth-demo/package.json index 95f12359..570f8eda 100644 --- a/examples/auth-demo/package.json +++ b/examples/auth-demo/package.json @@ -17,7 +17,7 @@ "@opensaas/stack-ui": "workspace:*", "@prisma/adapter-better-sqlite3": "^7.8.0", "@prisma/client": "^7.8.0", - "better-auth": "^1.6.25", + "better-auth": "^1.7.1", "next": "^16.2.10", "react": "^19.2.8", "react-dom": "^19.2.8" diff --git a/examples/auth-demo/prisma/schema.prisma b/examples/auth-demo/prisma/schema.prisma index 1cd982ad..e8274fc6 100644 --- a/examples/auth-demo/prisma/schema.prisma +++ b/examples/auth-demo/prisma/schema.prisma @@ -37,15 +37,15 @@ model User { } model Session { - id String @id @default(cuid()) - token String @unique - expiresAt DateTime? + id String @id @default(cuid()) + token String @unique + expiresAt DateTime ipAddress String? userAgent String? - userId String? @map("user") - user User? @relation(fields: [userId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt + userId String @map("userId") + user User @relation(onDelete: Cascade, fields: [userId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt @@index([userId]) } @@ -54,6 +54,7 @@ model Account { id String @id @default(cuid()) accountId String providerId String + issuer String accessToken String? refreshToken String? accessTokenExpiresAt DateTime? @@ -61,8 +62,8 @@ model Account { scope String? idToken String? password String? - userId String? @map("user") - user User? @relation(fields: [userId], references: [id]) + userId String @map("userId") + user User @relation(onDelete: Cascade, fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@ -70,10 +71,12 @@ model Account { } model Verification { - id String @id @default(cuid()) + id String @id @default(cuid()) identifier String value String - expiresAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt + expiresAt DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@index([identifier]) } diff --git a/examples/mcp-demo/opensaas.config.ts b/examples/mcp-demo/opensaas.config.ts index 9a76b070..0c5624a1 100644 --- a/examples/mcp-demo/opensaas.config.ts +++ b/examples/mcp-demo/opensaas.config.ts @@ -39,7 +39,14 @@ export default config({ plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + // RFC 8707/9728 canonical resource identifier — must match the + // `mcp.basePath` below. HTTP is accepted only on loopback hosts. + resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, + }), + ], extendUserList: { fields: { posts: relationship({ diff --git a/examples/starter-auth/package.json b/examples/starter-auth/package.json index 879467f0..58e5e4a6 100644 --- a/examples/starter-auth/package.json +++ b/examples/starter-auth/package.json @@ -20,7 +20,7 @@ "@prisma/adapter-better-sqlite3": "^7.8.0", "@prisma/client": "^7.8.0", "@tailwindcss/postcss": "^4.3.3", - "better-auth": "^1.6.25", + "better-auth": "^1.7.1", "better-sqlite3": "^12.6.2", "next": "^16.2.10", "postcss": "^8.5.26", diff --git a/examples/starter-auth/prisma/schema.prisma b/examples/starter-auth/prisma/schema.prisma index 7ccc72dc..9f8cb188 100644 --- a/examples/starter-auth/prisma/schema.prisma +++ b/examples/starter-auth/prisma/schema.prisma @@ -53,16 +53,19 @@ model Session { expiresAt DateTime ipAddress String? userAgent String? - userId String @map("user") + userId String @map("userId") user User @relation(onDelete: Cascade, fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@index([userId]) } model Account { id String @id @default(cuid()) accountId String providerId String + issuer String accessToken String? refreshToken String? accessTokenExpiresAt DateTime? @@ -70,10 +73,12 @@ model Account { scope String? idToken String? password String? - userId String @map("user") + userId String @map("userId") user User @relation(onDelete: Cascade, fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@index([userId]) } model Verification { @@ -83,4 +88,6 @@ model Verification { expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@index([identifier]) } diff --git a/packages/auth/CLAUDE.md b/packages/auth/CLAUDE.md index dff3da7d..c770b3df 100644 --- a/packages/auth/CLAUDE.md +++ b/packages/auth/CLAUDE.md @@ -49,7 +49,11 @@ Pre-built forms (client components). Each takes **server action** props (not an ### Plugins (`src/plugins/index.ts`) -- Better-auth MCP plugin for OAuth authentication with AI assistants +- `mcp` - re-exported from the optional `@better-auth/mcp` peer (better-auth + 1.7 split it out of `better-auth/plugins`) for OAuth authentication with AI + assistants. Its `resource` option (the RFC 8707/9728 canonical + protected-resource URL) is required as of that split — see the + `betterAuthPlugins` example in the root `CLAUDE.md`'s MCP section. ## Architecture Patterns @@ -69,7 +73,11 @@ config({ The four Auth lists, the conditional `RateLimit` fifth, and every table a better-auth plugin declares in its own `schema` (e.g. the `mcp` plugin's -`oauthApplication`/`oauthAccessToken`/`oauthConsent`) are all **derived** +`oauthClient`/`oauthAccessToken`/`oauthConsent`/`oauthRefreshToken`/ +`oauthResource`/`oauthClientResource`/`oauthClientAssertion` — better-auth +1.7 split the plugin into `@better-auth/mcp` and rebuilt it on the OAuth +Provider RFC 8707/9728 resource model, a wider table set than the pre-1.7 +`oauthApplication`/`oauthAccessToken`/`oauthConsent` three) are all **derived** from better-auth's own resolved table definitions, not hand-transcribed. The pure derivation lives in `src/config/derive-auth-lists.ts` (`deriveAuthLists`), which `getAuthLists` and the plugin's `init` consume. It calls `getAuthTables` @@ -108,13 +116,13 @@ lists cannot silently drift from what better-auth itself declares (issue child model's own key, with a documented override map in `derive-auth-lists.ts` for a collision or bad pluralization - a **plugin table**'s list key is PascalCased from better-auth's resolved - `modelName` (`oauthApplication` → `OauthApplication`), with `db.map` set + `modelName` (`oauthClient` → `OauthClient`), with `db.map` set back to the original whenever the case changed; its scalar/FK fields go through the exact same derivation as a base model's, including the reverse relation onto whichever list it references (base or another plugin table). A reference whose target field isn't the target's `id` (better-auth's own oidc-provider schema does this — `oauthAccessToken.clientId` references - `oauthApplication.clientId`, not its `id`) stays a plain scalar column, + `oauthClient.clientId`, not its `id`) stays a plain scalar column, since `relationship()` can only express an `id`-based FK. Plugin tables ship closed like the base models, with no `access` passthrough at all — see ADR-0034 and "Access control on Auth lists" below diff --git a/packages/auth/package.json b/packages/auth/package.json index 950009a3..e71f1865 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -57,12 +57,19 @@ "url": "https://github.com/OpenSaasAU/stack/issues" }, "peerDependencies": { + "@better-auth/mcp": "^1.7.0", "@opensaas/stack-core": "^0", - "better-auth": "^1.3.29", + "better-auth": "^1.4.0", "next": "^15.0.0 || ^16.0.0", "react": "^18.0.0 || ^19.0.0" }, + "peerDependenciesMeta": { + "@better-auth/mcp": { + "optional": true + } + }, "devDependencies": { + "@better-auth/mcp": "^1.7.1", "@opensaas/stack-cli": "workspace:*", "@opensaas/stack-core": "workspace:*", "@types/node": "^26.1.1", @@ -70,7 +77,7 @@ "@typescript/native": "npm:typescript@^7.0.2", "@vitest/coverage-v8": "^4.1.10", "@vitest/ui": "^4.1.10", - "better-auth": "^1.6.25", + "better-auth": "^1.7.1", "next": "^16.2.10", "react": "^19.2.8", "typescript": "npm:@typescript/typescript6@^6.0.2", diff --git a/packages/auth/src/config/derive-auth-lists.ts b/packages/auth/src/config/derive-auth-lists.ts index 98677f7d..5a2d3341 100644 --- a/packages/auth/src/config/derive-auth-lists.ts +++ b/packages/auth/src/config/derive-auth-lists.ts @@ -79,6 +79,11 @@ const FIELD_ORDER: Partial> = { account: [ 'accountId', 'providerId', + // `issuer` (better-auth 1.7+, issue #986) groups with accountId/providerId + // as the account's identity fields — together the table-level + // `@@unique([issuer, accountId])` better-auth declares (not yet emitted; + // blocked on #985's table-level `db.indexes` derivation). + 'issuer', 'user', 'accessToken', 'refreshToken', @@ -210,18 +215,23 @@ function buildScalarField(fieldKey: string, upstream: DBFieldAttribute): FieldCo } case 'date': return timestamp({ ...(isIndexed ? { isIndexed } : {}), db }) - case 'number': + case 'number': { + const staticDefault = + typeof upstream.defaultValue === 'function' ? undefined : upstream.defaultValue return upstream.bigint ? bigInt({ ...(isRequired ? { validation: { isRequired: true as const } } : {}), ...(isIndexed ? { isIndexed } : {}), + ...(staticDefault !== undefined ? { defaultValue: staticDefault as number } : {}), db, }) : integer({ ...(isRequired ? { validation: { isRequired: true as const } } : {}), ...(isIndexed ? { isIndexed } : {}), + ...(staticDefault !== undefined ? { defaultValue: staticDefault as number } : {}), db, }) + } default: // better-auth's `DBFieldType` also allows `json`, `string[]`/`number[]`, // and an enum array — none of which any built-in plugin (MCP, admin, diff --git a/packages/auth/src/config/types.ts b/packages/auth/src/config/types.ts index 3bc601a8..d758a44b 100644 --- a/packages/auth/src/config/types.ts +++ b/packages/auth/src/config/types.ts @@ -367,7 +367,7 @@ export type AuthConfig = { * * @example * ```typescript - * import { mcp } from 'better-auth/plugins' + * import { mcp } from '@opensaas/stack-auth/plugins' * * betterAuthPlugins: [ * mcp({ loginPage: '/sign-in' }) diff --git a/packages/auth/src/plugins/index.ts b/packages/auth/src/plugins/index.ts index 51cae2bd..e701ea06 100644 --- a/packages/auth/src/plugins/index.ts +++ b/packages/auth/src/plugins/index.ts @@ -1 +1,6 @@ -export { mcp } from 'better-auth/plugins' +/** + * better-auth 1.7 split the MCP plugin out of `better-auth/plugins` into its + * own `@better-auth/mcp` package (an optional peer of this package) — see the + * 1.7.1 upgrade note in the auth package's CHANGELOG. + */ +export { mcp } from '@better-auth/mcp' diff --git a/packages/auth/tests/auth-lists-drift.test.ts b/packages/auth/tests/auth-lists-drift.test.ts index ab0911c6..6ff193bd 100644 --- a/packages/auth/tests/auth-lists-drift.test.ts +++ b/packages/auth/tests/auth-lists-drift.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { getAuthTables } from 'better-auth/db' -import { mcp } from 'better-auth/plugins' +import { mcp } from '@better-auth/mcp' import type { BetterAuthOptions } from 'better-auth' import type { DBFieldAttribute } from 'better-auth/db' import type { FieldConfig } from '@opensaas/stack-core' @@ -38,11 +38,13 @@ type Divergence = { const FK_BRIDGE: Record> = { session: { userId: 'user' }, account: { userId: 'user' }, - // The MCP plugin's OAuth tables (issue #992) — `clientId` is deliberately - // NOT bridged here: it references oauthApplication.clientId, not its `id`, - // so it stays a plain scalar column and is compared via `compareScalarField`. - oauthApplication: { userId: 'user' }, - oauthAccessToken: { userId: 'user' }, + // The MCP plugin's OAuth tables (issue #992) — `clientId`/`resourceId` are + // deliberately NOT bridged here: they reference oauthClient.clientId / + // oauthResource.identifier, not an `id`, so they stay plain scalar columns + // and are compared via `compareScalarField`. + oauthClient: { userId: 'user' }, + oauthRefreshToken: { userId: 'user', sessionId: 'session' }, + oauthAccessToken: { userId: 'user', sessionId: 'session', refreshId: 'refresh' }, oauthConsent: { userId: 'user' }, } @@ -157,10 +159,17 @@ function compareScalarField( }) } - // Only a static, non-function default is comparable — better-auth's - // function-valued defaults (e.g. `createdAt: () => new Date()`) are applied - // at write time, not modeled as a derived field's own `defaultValue`. - if (typeof upstream.defaultValue !== 'function' && upstream.defaultValue !== undefined) { + // Only a static, non-function default on a modeled type is comparable — + // better-auth's function-valued defaults (e.g. `createdAt: () => new + // Date()`) are applied at write time, not modeled as a derived field's own + // `defaultValue`; a type this derivation doesn't model at all (`json`, + // `string[]`, ...) falls back to `text()` (see `expectedType` above), which + // can't faithfully carry that type's own default either. + if ( + expectedType !== undefined && + typeof upstream.defaultValue !== 'function' && + upstream.defaultValue !== undefined + ) { if (derived.defaultValue !== upstream.defaultValue) { divergences.push({ model, @@ -431,12 +440,16 @@ describe('derived better-auth plugin tables match better-auth’s own table defi verification: { modelName: 'Verification', fields: {} }, } - const plugin = mcp({ loginPage: '/sign-in' }) + const plugin = mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }) const upstreamTables = getAuthTables({ plugins: [plugin] }) const { keys, lists } = deriveAuthLists(defaultModels, {}, {}, [plugin]) const pluginModelToListKey: Record = { - oauthApplication: 'OauthApplication', + oauthClient: 'OauthClient', + oauthClientAssertion: 'OauthClientAssertion', + oauthClientResource: 'OauthClientResource', oauthAccessToken: 'OauthAccessToken', + oauthRefreshToken: 'OauthRefreshToken', + oauthResource: 'OauthResource', oauthConsent: 'OauthConsent', } const listKeys = { ...keys, ...pluginModelToListKey } diff --git a/packages/auth/tests/generated-fk-shape.test.ts b/packages/auth/tests/generated-fk-shape.test.ts index b023826d..4473d29f 100644 --- a/packages/auth/tests/generated-fk-shape.test.ts +++ b/packages/auth/tests/generated-fk-shape.test.ts @@ -3,7 +3,7 @@ import { config, list } from '@opensaas/stack-core' import { text, relationship } from '@opensaas/stack-core/fields' import type { OpenSaasConfig } from '@opensaas/stack-core' import type { Plugin } from '@opensaas/stack-core/extend' -import { mcp } from 'better-auth/plugins' +import { mcp } from '@better-auth/mcp' import { generatePrismaSchema } from '@opensaas/stack-cli/generator/prisma' import { authPlugin } from '../src/config/plugin.js' import { adoptBetterAuthTables } from '../src/config/adopt-better-auth-tables.js' @@ -97,6 +97,33 @@ describe('generated auth schema — Session/Account/Verification mirror better-a }) }) +describe('generated auth schema — account.issuer (better-auth 1.7, issue #986)', () => { + it('emits a required, non-nullable issuer column on Account, positioned after providerId', async () => { + const schema = await generateSchema({ + db: { provider: 'sqlite' }, + plugins: [authPlugin({ emailAndPassword: { enabled: true } })], + lists: {}, + }) + + const block = modelBlock(schema, 'Account') + expect(block).toMatch(/issuer\s+String\s/) + expect(block).not.toMatch(/issuer\s+String\?/) + + // FIELD_ORDER groups issuer with accountId/providerId — the fields that + // together form better-auth's table-level @@unique([issuer, accountId]), + // which this derivation does not yet emit (blocked on #985's table-level + // db.indexes derivation). + const providerIdLine = block.indexOf('providerId') + const issuerLine = block.indexOf('issuer') + expect(providerIdLine).toBeGreaterThan(-1) + expect(issuerLine).toBeGreaterThan(providerIdLine) + + // Not yet emitted — see the migration note in the auth package's + // CHANGELOG and issue #985. + expect(block).not.toContain('@@unique([issuer, accountId])') + }) +}) + describe('generated auth schema — adopted index names match better-auth exactly (issue #937)', () => { it('derives session_userId_idx / account_userId_idx / verification_identifier_idx under adoptBetterAuthTables()', async () => { const schema = await generateSchema({ @@ -231,13 +258,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], }), ], lists: {}, }) - for (const model of ['OauthApplication', 'OauthAccessToken', 'OauthConsent']) { + for (const model of ['OauthClient', 'OauthAccessToken', 'OauthConsent']) { const block = modelBlock(schema, model) expect(block).toContain('@relation(onDelete: Cascade, fields: [userId], references: [id])') expect(block).toContain('@map("userId")') @@ -251,24 +278,24 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], }), ], lists: {}, }) - expect(modelBlock(schema, 'OauthApplication')).toContain('@@map("oauthApplication")') + expect(modelBlock(schema, 'OauthClient')).toContain('@@map("oauthClient")') expect(modelBlock(schema, 'OauthAccessToken')).toContain('@@map("oauthAccessToken")') expect(modelBlock(schema, 'OauthConsent')).toContain('@@map("oauthConsent")') }) - it('leaves the non-PK clientId reference (oauthAccessToken/oauthConsent -> oauthApplication.clientId) as an indexed plain column, not a relation', async () => { + it('leaves the non-PK clientId reference (oauthAccessToken/oauthConsent -> oauthClient.clientId) as an indexed plain column, not a relation', async () => { const schema = await generateSchema({ db: { provider: 'sqlite' }, plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], }), ], lists: {}, @@ -277,18 +304,18 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades for (const model of ['OauthAccessToken', 'OauthConsent']) { const block = modelBlock(schema, model) expect(block).toMatch(/clientId\s+String\s/) - expect(block).not.toContain('clientId OauthApplication') + expect(block).not.toContain('clientId OauthClient') expect(block).toContain('@@index([clientId])') } }) - it('adds a reverse collection on User for every OAuth table, leaving Session/Account/Verification unchanged', async () => { + it('adds a reverse collection on User for every OAuth table referencing it, and on Session for the two token tables that also reference it, leaving Account/Verification unchanged', async () => { const withMcp = await generateSchema({ db: { provider: 'sqlite' }, plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], }), ], lists: {}, @@ -300,33 +327,42 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades }) const user = modelBlock(withMcp, 'User') - expect(user).toContain('oauthApplications') + expect(user).toContain('oauthClients') expect(user).toContain('oauthAccessTokens') + expect(user).toContain('oauthRefreshTokens') expect(user).toContain('oauthConsents') - for (const model of ['Session', 'Account', 'Verification']) { + // oauthAccessToken.sessionId / oauthRefreshToken.sessionId both reference + // session.id (unlike better-auth's pre-1.7 MCP schema, which had no + // session-scoped OAuth tables at all) — Session gains reverse collections + // for both, Account/Verification stay untouched by the MCP plugin. + const session = modelBlock(withMcp, 'Session') + expect(session).toContain('oauthAccessTokens') + expect(session).toContain('oauthRefreshTokens') + + for (const model of ['Account', 'Verification']) { expect(modelBlock(withMcp, model)).toBe(modelBlock(withoutMcp, model)) } }) - it('generates a required, non-nullable userId FK on OauthConsent (required upstream), and an optional one on OauthApplication (optional upstream)', async () => { + it('generates a required, non-nullable userId FK on OauthRefreshToken (required upstream), and an optional one on OauthClient (optional upstream)', async () => { const schema = await generateSchema({ db: { provider: 'sqlite' }, plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], }), ], lists: {}, }) - const consent = modelBlock(schema, 'OauthConsent') - expect(consent).toMatch(/userId\s+String\s/) - expect(consent).not.toMatch(/userId\s+String\?/) + const refreshToken = modelBlock(schema, 'OauthRefreshToken') + expect(refreshToken).toMatch(/userId\s+String\s/) + expect(refreshToken).not.toMatch(/userId\s+String\?/) - const application = modelBlock(schema, 'OauthApplication') - expect(application).toMatch(/userId\s+String\?/) + const client = modelBlock(schema, 'OauthClient') + expect(client).toMatch(/userId\s+String\?/) }) }) diff --git a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts index 81c60e71..a77ac0f9 100644 --- a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts +++ b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts @@ -59,7 +59,7 @@ export default config({ plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in' })], + betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'http://localhost:3000/api/mcp' })], // Not modelled by AuthConfig — passed through verbatim so /delete-user // deletes immediately instead of requiring an email-verification // round trip (see packages/auth/CLAUDE.md, "betterAuthOptions"). diff --git a/packages/auth/tests/plugin-table-derivation.test.ts b/packages/auth/tests/plugin-table-derivation.test.ts index 3630e634..d75a474e 100644 --- a/packages/auth/tests/plugin-table-derivation.test.ts +++ b/packages/auth/tests/plugin-table-derivation.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from 'vitest' -import { mcp } from 'better-auth/plugins' +import { mcp } from '@better-auth/mcp' import { deriveAuthLists } from '../src/config/derive-auth-lists.js' import type { NormalizedAuthModels } from '../src/config/types.js' @@ -22,36 +22,46 @@ const defaultModels: NormalizedAuthModels = { describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('derives every MCP OAuth table under a PascalCase key, mapped back to its camelCase physical table', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) expect(Object.keys(lists).sort()).toEqual([ 'Account', 'OauthAccessToken', - 'OauthApplication', + 'OauthClient', + 'OauthClientAssertion', + 'OauthClientResource', 'OauthConsent', + 'OauthRefreshToken', + 'OauthResource', 'Session', 'User', 'Verification', ]) - expect(lists.OauthApplication.db?.map).toBe('oauthApplication') + expect(lists.OauthClient.db?.map).toBe('oauthClient') expect(lists.OauthAccessToken.db?.map).toBe('oauthAccessToken') expect(lists.OauthConsent.db?.map).toBe('oauthConsent') }) it('generates a real relationship (not a bare column) for a PK-targeting reference (userId -> user.id)', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) - const userField = lists.OauthApplication.fields.user + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) + const userField = lists.OauthClient.fields.user expect(userField.type).toBe('relationship') - expect(userField.ref).toBe('User.oauthApplications') + expect(userField.ref).toBe('User.oauthClients') expect(userField.db?.foreignKey).toEqual({ map: 'userId' }) - // oauthApplication.userId is optional upstream (required: false). + // oauthClient.userId is optional upstream (required: false). expect(userField.db?.isNullable).toBe(true) }) it('carries the referential action from `onDelete: cascade` through to the generated relation', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) - const userField = lists.OauthApplication.fields.user + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) + const userField = lists.OauthClient.fields.user const extend = userField.db?.extendPrismaSchema const relationLine = extend ? extend({ relationLine: '@relation(fields: [x], references: [id])' }).relationLine @@ -85,8 +95,10 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { expect(relationLine).toContain('onDelete: Restrict') }) - it('leaves a non-PK-targeting reference as a plain scalar column (clientId -> oauthApplication.clientId)', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) + it('leaves a non-PK-targeting reference as a plain scalar column (clientId -> oauthClient.clientId)', () => { + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) const clientId = lists.OauthAccessToken.fields.clientId expect(clientId.type).toBe('text') @@ -97,11 +109,13 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { }) it('adds a reverse relation on the target base list for every plugin-table reference to it, with no name collisions', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) - expect(lists.User.fields.oauthApplications).toMatchObject({ + expect(lists.User.fields.oauthClients).toMatchObject({ type: 'relationship', - ref: 'OauthApplication.user', + ref: 'OauthClient.user', many: true, }) expect(lists.User.fields.oauthAccessTokens).toMatchObject({ @@ -136,9 +150,11 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { }) it('ships plugin-table lists closed — no access control', () => { - const { lists } = deriveAuthLists(defaultModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) + const { lists } = deriveAuthLists(defaultModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) - expect(lists.OauthApplication.access).toBeUndefined() + expect(lists.OauthClient.access).toBeUndefined() expect(lists.OauthAccessToken.access).toBeUndefined() expect(lists.OauthConsent.access).toBeUndefined() }) @@ -244,9 +260,11 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { account: { modelName: 'AuthAccount', fields: {} }, verification: { modelName: 'AuthVerification', fields: {} }, } - const { lists } = deriveAuthLists(remappedModels, {}, {}, [mcp({ loginPage: '/sign-in' })]) + const { lists } = deriveAuthLists(remappedModels, {}, {}, [ + mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + ]) - expect(lists.OauthApplication.fields.user.ref).toBe('AuthUser.oauthApplications') - expect(lists.AuthUser.fields.oauthApplications).toBeDefined() + expect(lists.OauthClient.fields.user.ref).toBe('AuthUser.oauthClients') + expect(lists.AuthUser.fields.oauthClients).toBeDefined() }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fb675a2..2e4e15de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,8 +151,8 @@ importers: specifier: ^7.8.0 version: 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) better-auth: - specifier: ^1.6.25 - version: 1.6.25(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + specifier: ^1.7.1 + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) next: specifier: ^16.2.10 version: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -754,8 +754,8 @@ importers: specifier: ^4.3.3 version: 4.3.3 better-auth: - specifier: ^1.6.25 - version: 1.6.25(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + specifier: ^1.7.1 + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) better-sqlite3: specifier: ^12.6.2 version: 12.6.2 @@ -863,6 +863,9 @@ importers: packages/auth: devDependencies: + '@better-auth/mcp': + specifier: ^1.7.1 + version: 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10))(better-call@1.4.0(zod@4.4.3)) '@opensaas/stack-cli': specifier: workspace:* version: link:../cli @@ -885,8 +888,8 @@ importers: specifier: ^4.1.10 version: 4.1.10(vitest@4.1.10) better-auth: - specifier: ^1.6.25 - version: 1.6.25(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + specifier: ^1.7.1 + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) next: specifier: ^16.2.10 version: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1599,14 +1602,14 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@better-auth/core@1.6.25': - resolution: {integrity: sha512-lMTlhtwyK4NpY9kPF+2rQCRKYpg136d3gM2xl8esxT1PjJx5Nh5YwZvxcYCIjDuO759sx6TCloJTuwcZGG6ZBw==} + '@better-auth/core@1.7.1': + resolution: {integrity: sha512-eZ9lqcnVLMZ3QtUByRo4VZqkB1ESyRddd9NfWjBdDPgh+jcwLScoIUAqhtHLR8zaSUJZah8OLGlkzObyPdUH7A==} peerDependencies: '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@cloudflare/workers-types': '>=4' '@opentelemetry/api': ^1.9.0 - better-call: 1.3.7 + better-call: 1.4.0 jose: ^6.1.0 kysely: ^0.28.5 || ^0.29.0 nanostores: ^1.0.1 @@ -1616,46 +1619,62 @@ packages: '@opentelemetry/api': optional: true - '@better-auth/drizzle-adapter@1.6.25': - resolution: {integrity: sha512-ru/DeKjFPQUVeKkxF/ScazmPqIY7lwfkAV5Yt4j24wmn1Y8vFwoiPRnHgXUeZqBs10+nubaRwEqLF39CP6EhRw==} + '@better-auth/drizzle-adapter@1.7.1': + resolution: {integrity: sha512-qlqNyg5V9bXHSP68/vtlsiZayhR4hgvEGiS/E3SIj8bCpWWFGmyQkxJbQCqpBmC7vT30wE/kNtJMHIgnV3rkiw==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 - drizzle-orm: ^0.45.2 + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 peerDependenciesMeta: drizzle-orm: optional: true - '@better-auth/kysely-adapter@1.6.25': - resolution: {integrity: sha512-zxiePhtN1YClS1irKYPVwWfN6kYp+QoYlz1hdQUOj8hXyo2aE/ny4RNAb6v332b0+U6Vu88EhYITRPdmvCo6uA==} + '@better-auth/kysely-adapter@1.7.1': + resolution: {integrity: sha512-yWCpE1cZpMUj37nD6JFDK+GDR8zS37L5WI73il3qbU9TXtWsxUQKc/5c3IHsHizWQsmcQI8uv2pAFKxsRDa+AQ==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 kysely: ^0.28.17 || ^0.29.0 peerDependenciesMeta: kysely: optional: true - '@better-auth/memory-adapter@1.6.25': - resolution: {integrity: sha512-GhEzTumc8yfTz+OZ6pMg06BA49xob49x1bX+1mEl/FStDJoSF+6mTfI5M2ytFxaiN89336/aUjkW8u+qRyLexw==} + '@better-auth/mcp@1.7.1': + resolution: {integrity: sha512-uBDWaaDPYQ2Q4QaesQVhZkaTSXrnFvW6Za0mr8lYTL2TvnKkzEWT0decyQGONwhswLLAlMGLKG4M2JeIATgGGA==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 + better-auth: ^1.7.1 + better-call: 1.4.0 + + '@better-auth/memory-adapter@1.7.1': + resolution: {integrity: sha512-6NX1yv88DeqdoG7owYFqKwlrDGaIPhsC52JUGrUgeGVKyOq8a/6hHlHsqG1C2FwT23SHQiKVYCEAG9N6aH5OvQ==} + peerDependencies: + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.6.25': - resolution: {integrity: sha512-ZtMmjcOdXR2Ziqx5y8ptTOaNpe0snNfALbBUPXJsgeyeRkDJDYzyLZ8MpuvNBTNllNeIFDbiXWAK5k+pEBZrUQ==} + '@better-auth/mongo-adapter@1.7.1': + resolution: {integrity: sha512-9ILTcNqhG37QK//qR4UhYLyKzNqq6w6zVTf5KX6xkiTjNcV7Oh1yS31lkIJEVTqRNcy9AoV6FZMW6Bbsm8IMDA==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: optional: true - '@better-auth/prisma-adapter@1.6.25': - resolution: {integrity: sha512-ym7B6Iqcry+/4aQnYpFwqP/GBIiXvjrm/5B6+0qmx8mkTY/apHFTpHuGzUYYNf4vPTtzF3eYY2+s2GOsomKaRg==} + '@better-auth/oauth-provider@1.7.1': + resolution: {integrity: sha512-VWIw7ti6rodlbbdSbn0mts/TZcBWUj6YaoIpREmv70eoGmWTa6MPWEbGuUdADQe3Vy4YqysIbmQA6qgRqfLTaw==} + peerDependencies: + '@better-auth/core': ^1.7.1 + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.1 + better-auth: ^1.7.1 + better-call: 1.4.0 + + '@better-auth/prisma-adapter@1.7.1': + resolution: {integrity: sha512-ZiUcafQ85InAofcUjyGgCPjKLfQjXr9SvDmMjuFUW8oEbreA6C6GaFAEA77VuV2doZUQlzvQQ4gCoTmS28W92A==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -1665,16 +1684,19 @@ packages: prisma: optional: true - '@better-auth/telemetry@1.6.25': - resolution: {integrity: sha512-2ZfC9lp7tU6Jw/q2Lz/bKfQqGMdMwc/IQDTYdBhvtGi24qInYVnhp2ZCW57hHM9j+fq1ULOtxgg6M3T1LEaihw==} + '@better-auth/telemetry@1.7.1': + resolution: {integrity: sha512-kLKjMfFlTbyt49DGeI9okHAsn0MtBZcMoQYKaEdgR0H3BHzqqyzePcQz/hxAmRgjB4p/6inise3zJwhX0sgXrQ==} peerDependencies: - '@better-auth/core': ^1.6.25 + '@better-auth/core': ^1.7.1 '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@better-auth/utils@0.4.2': resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} + '@better-auth/utils@0.5.0': + resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} + '@better-fetch/fetch@1.3.1': resolution: {integrity: sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==} @@ -4647,8 +4669,8 @@ packages: resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==} hasBin: true - better-auth@1.6.25: - resolution: {integrity: sha512-fvoq+oCO+FF5fpP3XfU7znRyGFpHB77UG2EyxsKNy+Cak7Q5pELu+auvvDveQbWQxcoKugZ7jYQQPFQLpUTGOw==} + better-auth@1.7.1: + resolution: {integrity: sha512-g8WlTQijxXWJjPVZfFu1+EJg9cwwHrKDmIkcYMzx8CzYA+tDxl6NI7qQbKkbgw5UtHILsT5VH+RMzFzwnVJqAg==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -4656,8 +4678,8 @@ packages: '@tanstack/react-start': ^1.0.0 '@tanstack/solid-start': ^1.0.0 better-sqlite3: ^12.0.0 - drizzle-kit: '>=0.31.4' - drizzle-orm: ^0.45.2 + drizzle-kit: '>=0.31.4 || >=1.0.0-beta.1' + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 mongodb: ^6.0.0 || ^7.0.0 mysql2: ^3.0.0 next: ^14.0.0 || ^15.0.0 || ^16.0.0 @@ -4709,8 +4731,8 @@ packages: vue: optional: true - better-call@1.3.7: - resolution: {integrity: sha512-Al51/hjp2SSp6CRTa3F2ptcx4yQVS1xWKoY6jcVXqNYOap6mHFP2jUBn5EwIL4iIed1/Sq4hlQ+Umm6EflZG+w==} + better-call@1.4.0: + resolution: {integrity: sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA==} peerDependencies: zod: ^4.0.0 peerDependenciesMeta: @@ -6928,8 +6950,8 @@ packages: rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} - rou3@0.7.12: - resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} + rou3@0.9.2: + resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} @@ -8160,13 +8182,27 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2)': + '@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2)': dependencies: '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@opentelemetry/semantic-conventions': 1.43.0 '@standard-schema/spec': 1.1.0 - better-call: 1.3.7(zod@4.4.3) + better-call: 1.4.0(zod@4.4.3) + jose: 6.2.8 + kysely: 0.29.4 + nanostores: 1.4.2 + zod: 4.4.3 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + + '@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2)': + dependencies: + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.1 + '@opentelemetry/semantic-conventions': 1.43.0 + '@standard-schema/spec': 1.1.0 + better-call: 1.4.0(zod@4.4.3) jose: 6.2.8 kysely: 0.29.4 nanostores: 1.4.2 @@ -8174,41 +8210,100 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@better-auth/drizzle-adapter@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': + '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + + '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 - '@better-auth/kysely-adapter@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': + '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + optionalDependencies: + kysely: 0.29.4 + + '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 optionalDependencies: kysely: 0.29.4 - '@better-auth/memory-adapter@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': + '@better-auth/mcp@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10))(better-call@1.4.0(zod@4.4.3))': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/oauth-provider': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10))(better-call@1.4.0(zod@4.4.3)) + better-auth: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + better-call: 1.4.0(zod@4.4.3) + jose: 6.2.8 + transitivePeerDependencies: + - '@better-auth/utils' + - '@better-fetch/fetch' + + '@better-auth/memory-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0)': + '@better-auth/memory-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0)': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + optionalDependencies: + mongodb: 7.1.0 + + '@better-auth/mongo-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0)': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 optionalDependencies: mongodb: 7.1.0 - '@better-auth/prisma-adapter@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': + '@better-auth/oauth-provider@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10))(better-call@1.4.0(zod@4.4.3))': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.1 + better-auth: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + better-call: 1.4.0(zod@4.4.3) + jose: 6.2.8 + zod: 4.4.3 + + '@better-auth/prisma-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + optionalDependencies: + '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + + '@better-auth/prisma-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 optionalDependencies: '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@better-auth/telemetry@1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.1 + + '@better-auth/telemetry@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 @@ -8216,6 +8311,10 @@ snapshots: dependencies: '@noble/hashes': 2.2.0 + '@better-auth/utils@0.5.0': + dependencies: + '@noble/hashes': 2.2.0 + '@better-fetch/fetch@1.3.1': {} '@blazediff/core@1.9.1': {} @@ -10914,20 +11013,20 @@ snapshots: bcryptjs@3.0.3: {} - better-auth@1.6.25(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): + better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): dependencies: - '@better-auth/core': 1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/drizzle-adapter': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/kysely-adapter': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4) - '@better-auth/memory-adapter': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0) - '@better-auth/prisma-adapter': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - '@better-auth/telemetry': 1.6.25(@better-auth/core@1.6.25(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.3.7(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) + '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4) + '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0) + '@better-auth/prisma-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 '@noble/ciphers': 2.2.0 '@noble/hashes': 2.2.0 - better-call: 1.3.7(zod@4.4.3) + better-call: 1.4.0(zod@4.4.3) defu: 6.1.7 jose: 6.2.8 kysely: 0.29.4 @@ -10948,11 +11047,45 @@ snapshots: - '@cloudflare/workers-types' - '@opentelemetry/api' - better-call@1.3.7(zod@4.4.3): + better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): dependencies: + '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) + '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4) + '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0) + '@better-auth/prisma-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) '@better-auth/utils': 0.4.2 '@better-fetch/fetch': 1.3.1 - rou3: 0.7.12 + '@noble/ciphers': 2.2.0 + '@noble/hashes': 2.2.0 + better-call: 1.4.0(zod@4.4.3) + defu: 6.1.7 + jose: 6.2.8 + kysely: 0.29.4 + nanostores: 1.4.2 + zod: 4.4.3 + optionalDependencies: + '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + better-sqlite3: 12.6.2 + mongodb: 7.1.0 + mysql2: 3.15.3 + next: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + pg: 8.22.0 + prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.11)(yaml@2.9.0)) + transitivePeerDependencies: + - '@cloudflare/workers-types' + - '@opentelemetry/api' + + better-call@1.4.0(zod@4.4.3): + dependencies: + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.1 + rou3: 0.9.2 set-cookie-parser: 3.1.2 optionalDependencies: zod: 4.4.3 @@ -11608,8 +11741,8 @@ snapshots: '@next/eslint-plugin-next': 16.2.10 eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)) @@ -11635,7 +11768,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -11646,22 +11779,22 @@ snapshots: tinyglobby: 0.2.17 unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.14.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)) eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -11672,7 +11805,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.5(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0)) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -13424,7 +13557,7 @@ snapshots: rope-sequence@1.3.4: {} - rou3@0.7.12: {} + rou3@0.9.2: {} router@2.2.0: dependencies: From 1e107f96aebc97fba8321d866c3bab67b43ffaac Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 08:06:34 +0000 Subject: [PATCH 2/4] fix(auth): derive plugin-table asymmetric timestamps, require jwt() alongside mcp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI caught two real bugs in the earlier commit that only surface with a live better-auth 1.7 instance: 1. deriveAuthLists silently dropped createdAt/updatedAt for any plugin table that doesn't declare both symmetrically (several of the MCP plugin's new OAuth tables declare only createdAt). The column vanished entirely rather than falling back to an ordinary field, so the first real write supplying it crashed with a Prisma "Unknown argument" error — which is exactly what happened when better-auth's own OAuth Provider seeds an oauthResource row during betterAuth() init. Fixed by only relying on the list-level db.timestamps auto-columns when a model declares both fields; otherwise the field derives normally like any other column. 2. better-auth 1.7's mcp() is built on the OAuth Provider, which issues JWT-based access tokens and now hard-requires better-auth's own jwt() plugin registered alongside it (throws BetterAuthError: jwt_config otherwise). Added it to the e2e test fixture, examples/mcp-demo, and the three places docs showed betterAuthPlugins: [mcp(...)]. Also rewrote the e2e test's OAuth row assertions for the new table/field shapes (oauthApplication -> oauthClient, no more combined access+refresh token row) and made its baseURL explicit, which the OAuth Provider now needs to resolve at init and which this test's in-process createAuth() doesn't get from the CLI subprocess's BETTER_AUTH_URL. Verified locally: full auth unit suite plus both e2e suites (RUN_MCP_OAUTH_CASCADE_E2E=1, RUN_RATE_LIMIT_E2E=1) green, full monorepo build/lint/format clean. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QtaxBvCjLsZcm7YiSmpteJ --- .changeset/eleven-mice-jog.md | 10 +++- CLAUDE.md | 2 +- docs/content/how-to/mcp.md | 5 ++ docs/content/reference/auth.md | 8 +++ examples/mcp-demo/opensaas.config.ts | 5 ++ packages/auth/CLAUDE.md | 6 +- packages/auth/src/config/derive-auth-lists.ts | 39 ++++++++++++- .../auth/tests/mcp-oauth-cascade-e2e.test.ts | 56 +++++++++++++------ 8 files changed, 109 insertions(+), 22 deletions(-) diff --git a/.changeset/eleven-mice-jog.md b/.changeset/eleven-mice-jog.md index 2bae97a2..967f231f 100644 --- a/.changeset/eleven-mice-jog.md +++ b/.changeset/eleven-mice-jog.md @@ -22,9 +22,14 @@ URL-encode `providerId` if it can contain characters outside `[A-Za-z0-9_-]`. If ```typescript import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' authPlugin({ betterAuthPlugins: [ + // The OAuth Provider mcp() is built on issues JWT-based access tokens + // and requires better-auth's own jwt() plugin registered alongside it — + // omitting it throws `BetterAuthError: jwt_config` at init. + jwt(), mcp({ loginPage: '/sign-in', // RFC 8707/9728 canonical resource identifier — required as of @@ -38,4 +43,7 @@ authPlugin({ better-auth 1.7's MCP plugin also declares a substantially different OAuth table set — the old `oauthApplication`/`oauthAccessToken`/`oauthConsent` three became seven tables (`oauthClient`/`oauthAccessToken`/`oauthConsent`/`oauthRefreshToken`/`oauthResource`/`oauthClientResource`/`oauthClientAssertion`). Since the derivation is schema-driven this needed no code changes, but if you have the MCP plugin enabled, running `pnpm generate` will produce a significantly different Prisma schema for these tables (new/renamed models, and `Session` gains reverse relations to the two token tables that now reference it). Review the diff and migrate your database accordingly. -Also fixes `deriveAuthLists`'s scalar-field builder to thread a static `defaultValue` through for `number`-typed fields (`integer()`/`bigInt()`), matching the existing `string`/`boolean` behavior — surfaced by the MCP plugin's expanded schema, which now includes number fields with static defaults (e.g. `oauthResource.policyVersion`). +Also fixes two `deriveAuthLists` gaps surfaced by the MCP plugin's expanded schema: + +- the scalar-field builder now threads a static `defaultValue` through for `number`-typed fields (`integer()`/`bigInt()`), matching the existing `string`/`boolean` behavior (e.g. `oauthResource.policyVersion`, which defaults to `1`) +- a plugin table that declares only one of `createdAt`/`updatedAt` upstream (several of the new OAuth tables declare `createdAt` alone) is no longer silently dropped — it derives as an ordinary required column instead. Previously any model with an asymmetric timestamp pair had the field skipped entirely with no replacement, which crashed the first real write that supplied it (better-auth's own OAuth Provider does this at `betterAuth()` init time, seeding an `oauthResource` row) diff --git a/CLAUDE.md b/CLAUDE.md index 369657d5..6b764c44 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -625,7 +625,7 @@ The stack provides Model Context Protocol server integration through `@opensaas/ 1. Enable MCP in config with `mcp: { enabled: true }` (the runtime reads `enabled`/`basePath`/`defaultTools`) 2. Core runtime derives CRUD tools for each list (query, create, update, delete) at request time -3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: '' })] })`, imported from `@opensaas/stack-auth/plugins` — the plugin itself comes from the optional `@better-auth/mcp` peer since better-auth 1.7 split it out of `better-auth/plugins`) and the `createBetterAuthMcpAdapter` session provider +3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [jwt(), mcp({ loginPage: '/sign-in', resource: '' })] })`, imported from `@opensaas/stack-auth/plugins` — the plugin itself comes from the optional `@better-auth/mcp` peer since better-auth 1.7 split it out of `better-auth/plugins`, and requires `jwt()` from `better-auth/plugins` registered alongside it) and the `createBetterAuthMcpAdapter` session provider 4. All tools respect existing access control rules 5. Custom tools can be added per-list via `mcp.customTools` (Zod or JSON Schema inputSchema); plugins can register global tools via `registerMcpTool` diff --git a/docs/content/how-to/mcp.md b/docs/content/how-to/mcp.md index 09306107..af8e1362 100644 --- a/docs/content/how-to/mcp.md +++ b/docs/content/how-to/mcp.md @@ -39,6 +39,7 @@ In your `opensaas.config.ts`, configure the auth plugin with the MCP plugin: import { config, list } from '@opensaas/stack-core' import { authPlugin } from '@opensaas/stack-auth' import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' export default config({ plugins: [ @@ -46,6 +47,10 @@ export default config({ emailAndPassword: { enabled: true }, // Add MCP plugin to Better Auth betterAuthPlugins: [ + // better-auth 1.7's mcp() is built on the OAuth Provider, which + // issues JWT-based access tokens and requires better-auth's own + // jwt() plugin registered alongside it. + jwt(), mcp({ loginPage: '/sign-in', // Canonical protected-resource identifier (RFC 8707/9728) — diff --git a/docs/content/reference/auth.md b/docs/content/reference/auth.md index 9c6fb0f3..aa716e18 100644 --- a/docs/content/reference/auth.md +++ b/docs/content/reference/auth.md @@ -286,9 +286,14 @@ Add Better Auth plugins for additional functionality: ```typescript import { authPlugin } from '@opensaas/stack-auth' import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' authPlugin({ betterAuthPlugins: [ + // better-auth 1.7's mcp() is built on the OAuth Provider, which issues + // JWT-based access tokens and requires better-auth's own jwt() plugin + // registered alongside it. + jwt(), mcp({ loginPage: '/sign-in', // Canonical protected-resource identifier (RFC 8707/9728) — required @@ -674,12 +679,15 @@ To enable Model Context Protocol support with Better Auth authentication: ```typescript import { authPlugin } from '@opensaas/stack-auth' import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' export default config({ plugins: [ authPlugin({ emailAndPassword: { enabled: true }, betterAuthPlugins: [ + // better-auth 1.7's mcp() requires the jwt() plugin alongside it. + jwt(), mcp({ loginPage: '/sign-in', resource: `${process.env.APP_URL}/api/mcp` }), ], }), diff --git a/examples/mcp-demo/opensaas.config.ts b/examples/mcp-demo/opensaas.config.ts index 0c5624a1..9f5e66fe 100644 --- a/examples/mcp-demo/opensaas.config.ts +++ b/examples/mcp-demo/opensaas.config.ts @@ -2,6 +2,7 @@ import { config, list } from '@opensaas/stack-core' import { text, relationship, select, timestamp } from '@opensaas/stack-core/fields' import { authPlugin } from '@opensaas/stack-auth' import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' import type { AccessControl } from '@opensaas/stack-core' import { z } from 'zod' import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3' @@ -40,6 +41,10 @@ export default config({ authPlugin({ emailAndPassword: { enabled: true }, betterAuthPlugins: [ + // better-auth 1.7's mcp() is built on the OAuth Provider, which + // issues JWT-based access tokens and requires better-auth's own + // jwt() plugin registered alongside it. + jwt(), mcp({ loginPage: '/sign-in', // RFC 8707/9728 canonical resource identifier — must match the diff --git a/packages/auth/CLAUDE.md b/packages/auth/CLAUDE.md index c770b3df..c235aea7 100644 --- a/packages/auth/CLAUDE.md +++ b/packages/auth/CLAUDE.md @@ -52,7 +52,11 @@ Pre-built forms (client components). Each takes **server action** props (not an - `mcp` - re-exported from the optional `@better-auth/mcp` peer (better-auth 1.7 split it out of `better-auth/plugins`) for OAuth authentication with AI assistants. Its `resource` option (the RFC 8707/9728 canonical - protected-resource URL) is required as of that split — see the + protected-resource URL) is required as of that split, and it now needs + better-auth's own `jwt()` plugin (from `better-auth/plugins`, unaffected by + the `@better-auth/mcp` split) registered alongside it — the OAuth Provider + `mcp()` is built on issues JWT-based access tokens and throws + `BetterAuthError: jwt_config` at init without it. See the `betterAuthPlugins` example in the root `CLAUDE.md`'s MCP section. ## Architecture Patterns diff --git a/packages/auth/src/config/derive-auth-lists.ts b/packages/auth/src/config/derive-auth-lists.ts index 5a2d3341..2d0b145d 100644 --- a/packages/auth/src/config/derive-auth-lists.ts +++ b/packages/auth/src/config/derive-auth-lists.ts @@ -100,6 +100,24 @@ const FIELD_ORDER: Partial> = { /** Carried via list-level `db.timestamps` (see `listDb`) rather than as ordinary derived fields. */ const TIMESTAMP_FIELDS = new Set(['createdAt', 'updatedAt']) +/** + * Whether a model declares BOTH `createdAt` and `updatedAt` upstream — the + * only shape `db.timestamps: true` can express, since it always emits both + * columns together (`resolveListTimestamps` in the Prisma generator). The + * four base models always satisfy this. A better-auth plugin table is not + * guaranteed to: the MCP plugin's OAuth tables (better-auth 1.7, issue #992) + * include several with only `createdAt` (e.g. `oauthAccessToken`, + * `oauthRefreshToken`, `oauthClientResource`) — those fall through to the + * general field-derivation loop below instead, so `createdAt` alone is still + * derived as an ordinary scalar column rather than silently dropped (which + * previously crashed a real write the moment better-auth's own adapter tried + * to set it — see `oauthResource`'s seeded row in the MCP OAuth cascade e2e + * test). + */ +function hasSymmetricTimestamps(upstreamFields: Record): boolean { + return 'createdAt' in upstreamFields && 'updatedAt' in upstreamFields +} + /** * The reverse relation field name a foreign key implies on its target model * (e.g. `User.sessions`) has no source in better-auth's metadata at all — @@ -438,8 +456,16 @@ export function deriveAuthLists( for (const modelKey of Object.keys(tables)) { const upstreamFields = tables[modelKey].fields + // Base models always carry `db.timestamps: true` below regardless of + // this check; a plugin table only skips createdAt/updatedAt here when it + // declares both (so db.timestamps: true, set further down, can stand in + // for them) — otherwise they fall through and derive as ordinary scalar + // columns instead of being silently dropped. + const skipTimestampFields = + (BASE_MODEL_KEYS as readonly string[]).includes(modelKey) || + hasSymmetricTimestamps(upstreamFields) for (const [fieldKey, upstream] of Object.entries(upstreamFields)) { - if (TIMESTAMP_FIELDS.has(fieldKey)) continue + if (TIMESTAMP_FIELDS.has(fieldKey) && skipTimestampFields) continue if (upstream.references) { const targetModelKey = upstream.references.model @@ -567,9 +593,18 @@ export function deriveAuthLists( if (!listKey) continue // unreachable: buildModelRegistry registers every table key const physicalName = tables[modelKey].modelName + const timestamps = hasSymmetricTimestamps(tables[modelKey].fields) + const mapNeeded = listKey !== physicalName lists[listKey] = list({ fields: assembleFields(modelKey), - ...(listKey !== physicalName ? { db: { map: physicalName } } : {}), + ...(timestamps || mapNeeded + ? { + db: { + ...(timestamps ? { timestamps: true as const } : {}), + ...(mapNeeded ? { map: physicalName } : {}), + }, + } + : {}), }) } diff --git a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts index a77ac0f9..fa7f6ca9 100644 --- a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts +++ b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts @@ -53,17 +53,34 @@ function configSource(): string { return `import { config } from '@opensaas/stack-core' import { authPlugin } from '@opensaas/stack-auth' import { mcp } from '@opensaas/stack-auth/plugins' +import { jwt } from 'better-auth/plugins' import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3' export default config({ plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'http://localhost:3000/api/mcp' })], + // better-auth 1.7's mcp() is built on the OAuth Provider, which issues + // JWT-based access tokens and requires better-auth's own jwt() plugin + // to be registered alongside it (throws BetterAuthError: jwt_config + // otherwise) — see @better-auth/mcp's own usage example. + betterAuthPlugins: [ + jwt(), + mcp({ loginPage: '/sign-in', resource: 'http://localhost:3000/api/mcp' }), + ], // Not modelled by AuthConfig — passed through verbatim so /delete-user // deletes immediately instead of requiring an email-verification // round trip (see packages/auth/CLAUDE.md, "betterAuthOptions"). - betterAuthOptions: { user: { deleteUser: { enabled: true } } }, + // baseURL is explicit rather than relying on the BETTER_AUTH_URL env + // var: better-auth 1.7's OAuth Provider needs a resolvable URL to build + // its own endpoint/issuer URLs during init, and this test constructs + // \`auth\` in-process via createAuth() (not through the CLI subprocess + // that does see BETTER_AUTH_URL), so the auto-detected origin would be + // undefined here. + betterAuthOptions: { + baseURL: 'http://localhost:3000', + user: { deleteUser: { enabled: true } }, + }, }), ], db: { @@ -142,7 +159,7 @@ async function cleanupProject(dir: string): Promise { describe.skipIf(!prerequisitesPresent)( 'MCP plugin OAuth tables cascade on user deletion — live end-to-end (issue #992)', () => { - it('deleting a user via better-auth’s own /delete-user removes their OAuth applications, access tokens and consents; another user’s rows survive', async () => { + it('deleting a user via better-auth’s own /delete-user removes their OAuth clients, access tokens and consents; another user’s rows survive', async () => { const dir = await setupProject() try { const { auth, context } = await createAuthInstanceForProject(dir) @@ -170,37 +187,44 @@ describe.skipIf(!prerequisitesPresent)( // OAuth rows are created through the raw Prisma client — these lists // ship closed (ADR-0013), and this is the same path better-auth's own - // OAuth flows write through in production. + // OAuth flows write through in production. better-auth 1.7's OAuth + // Provider (issue #986) splits what the pre-1.7 MCP plugin modelled + // as a single "application" with embedded access/refresh tokens into + // oauthClient (the registered client) plus a standalone + // oauthAccessToken (no more combined access+refresh token row). for (const [suffix, userId] of [ ['deleted', deletedUserId], ['survivor', survivorUserId], ]) { - await prisma.oauthApplication.create({ + await prisma.oauthClient.create({ data: { name: `App ${suffix}`, clientId: `client-${suffix}`, - redirectUrls: 'http://localhost/callback', - type: 'web', + redirectUris: 'http://localhost/callback', userId, }, }) await prisma.oauthAccessToken.create({ data: { - accessToken: `access-${suffix}`, - refreshToken: `refresh-${suffix}`, - accessTokenExpiresAt: new Date(Date.now() + 3_600_000), - refreshTokenExpiresAt: new Date(Date.now() + 7_200_000), + token: `access-${suffix}`, clientId: `client-${suffix}`, scopes: 'openid', + expiresAt: new Date(Date.now() + 3_600_000), + // oauthAccessToken declares createdAt but not updatedAt + // upstream, so it derives as an ordinary required column with + // no DB-level default (see hasSymmetricTimestamps in + // derive-auth-lists.ts) — better-auth's own adapter always + // supplies it explicitly, so this raw-Prisma seed must too. + createdAt: new Date(), userId, }, }) await prisma.oauthConsent.create({ - data: { clientId: `client-${suffix}`, scopes: 'openid', consentGiven: true, userId }, + data: { clientId: `client-${suffix}`, scopes: 'openid', userId }, }) } - expect(await prisma.oauthApplication.count()).toBe(2) + expect(await prisma.oauthClient.count()).toBe(2) expect(await prisma.oauthAccessToken.count()).toBe(2) expect(await prisma.oauthConsent.count()).toBe(2) @@ -214,9 +238,7 @@ describe.skipIf(!prerequisitesPresent)( // No orphans: every row belonging to the deleted user is gone via the // database cascade, not just the user row itself. - expect( - await prisma.oauthApplication.findFirst({ where: { userId: deletedUserId } }), - ).toBeNull() + expect(await prisma.oauthClient.findFirst({ where: { userId: deletedUserId } })).toBeNull() expect( await prisma.oauthAccessToken.findFirst({ where: { userId: deletedUserId } }), ).toBeNull() @@ -225,7 +247,7 @@ describe.skipIf(!prerequisitesPresent)( // The other user's rows are untouched — the cascade is scoped to the // deleted user's own foreign key, not a wholesale table wipe. expect(await prisma.user.findUnique({ where: { id: survivorUserId } })).not.toBeNull() - expect(await prisma.oauthApplication.count()).toBe(1) + expect(await prisma.oauthClient.count()).toBe(1) expect(await prisma.oauthAccessToken.count()).toBe(1) expect(await prisma.oauthConsent.count()).toBe(1) From 0f45e386c65893eb6cf33c7a3680a0e9fff5fa2b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 08:08:38 +0000 Subject: [PATCH 3/4] docs(auth): fix undocumented env var and MySQL/table-name gaps in review Addresses automated review feedback on PR #1015: - docs/content/reference/auth.md's MCP snippets used an undocumented APP_URL env var (would resolve to undefined/api/mcp); switched to NEXT_PUBLIC_APP_URL with the localhost fallback, matching this file's own existing baseURL convention. - The changeset's account.issuer backfill SQL used || for concatenation, which is logical OR on MySQL by default and would silently write 0/1 instead of the intended string. Added a MySQL-safe CONCAT variant alongside the PostgreSQL/SQLite one, and a note to substitute the project's actual (possibly renamed/schema-qualified) account table name. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QtaxBvCjLsZcm7YiSmpteJ --- .changeset/eleven-mice-jog.md | 10 +++++++--- docs/content/reference/auth.md | 7 +++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.changeset/eleven-mice-jog.md b/.changeset/eleven-mice-jog.md index 967f231f..29def51b 100644 --- a/.changeset/eleven-mice-jog.md +++ b/.changeset/eleven-mice-jog.md @@ -7,13 +7,17 @@ Bump the `better-auth` dev dependency to `1.7.1` and move the peer range off the **New required `account.issuer` column.** better-auth 1.7 adds a required `issuer` column to its `account` model. Because `deriveAuthLists` derives the Auth lists from better-auth's own `getAuthTables()` (#987/#997), this column now appears in the generated schema automatically — no code change was needed, only verification against real generated output. Existing projects upgrading to `better-auth@^1.7` will see a **new NOT NULL column** on their `account` table and need a backfill for existing rows. Following better-auth's own `createLocalAccountIssuer`/`createOAuthAccountIssuer` helpers (`@better-auth/core/db`): ```sql --- Local (email/password) accounts +-- PostgreSQL / SQLite (|| is string concatenation on both) UPDATE "Account" SET issuer = 'local:' || "providerId" WHERE issuer IS NULL AND "providerId" = 'credential'; - --- OAuth/social accounts without their own OIDC issuer (github, google-without-oidc, etc.) UPDATE "Account" SET issuer = 'local:oauth:' || "providerId" WHERE issuer IS NULL AND "providerId" != 'credential'; + +-- MySQL (|| is logical OR by default, NOT concatenation — use CONCAT instead) +UPDATE `Account` SET issuer = CONCAT('local:', providerId) WHERE issuer IS NULL AND providerId = 'credential'; +UPDATE `Account` SET issuer = CONCAT('local:oauth:', providerId) WHERE issuer IS NULL AND providerId != 'credential'; ``` +`"Account"`/`` `Account` `` above is the stack's own greenfield default table name — substitute your project's actual (and, on Postgres, schema-qualified) table name if you renamed it via `authPlugin({ account: { tableName } })` or adopted an existing install with `adoptBetterAuthTables({ useBetterAuthTableNames: true })` (physical table `account`, commonly under a non-`public` schema). + URL-encode `providerId` if it can contain characters outside `[A-Za-z0-9_-]`. If you configured a custom OIDC provider with its own issuer URL, use that provider's real issuer instead of the synthetic `local:oauth:` value. **The new `@@unique([issuer, accountId])` constraint is not yet emitted.** better-auth 1.7 also declares this composite unique index at the table level, but the stack only derives _field_-level `unique`/`index` flags today — table-level index derivation is #985, which hasn't landed. This is deliberately out of scope here (per #986's own triage note: build on #985 once it lands, don't duplicate it). When it does land, make sure your backfilled `issuer` values don't collide on `(issuer, accountId)` for any account, or the constraint will fail to apply. diff --git a/docs/content/reference/auth.md b/docs/content/reference/auth.md index aa716e18..b6ed4038 100644 --- a/docs/content/reference/auth.md +++ b/docs/content/reference/auth.md @@ -298,7 +298,7 @@ authPlugin({ loginPage: '/sign-in', // Canonical protected-resource identifier (RFC 8707/9728) — required // since better-auth 1.7's MCP plugin. Must match `mcp.basePath` below. - resource: `${process.env.APP_URL}/api/mcp`, + resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, }), // Add other Better Auth plugins here ], @@ -688,7 +688,10 @@ export default config({ betterAuthPlugins: [ // better-auth 1.7's mcp() requires the jwt() plugin alongside it. jwt(), - mcp({ loginPage: '/sign-in', resource: `${process.env.APP_URL}/api/mcp` }), + mcp({ + loginPage: '/sign-in', + resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, + }), ], }), ], From a5234d91df8fb8a2a0214100576f3d757834a938 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 08:30:43 +0000 Subject: [PATCH 4/4] =?UTF-8?q?fix(auth):=20resolve=20mcp-demo=20build=20f?= =?UTF-8?q?ailure=20=E2=80=94=20missing=20dep,=20dupe=20@better-auth/core,?= =?UTF-8?q?=20missing=20consentPage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The full turbo test run (which builds every example, unlike the fast local `pnpm build`/`pnpm test` I'd been checking) surfaced three more real problems in examples/mcp-demo, all collateral of the better-auth 1.7 MCP redesign: 1. mcp-demo's opensaas.config.ts imports `jwt` from 'better-auth/plugins' directly, but the package never declared `better-auth` as its own dependency (relying on incidental hoisting, which Next.js/Turbopack's strict per-package module resolution doesn't honor). Added it. 2. better-auth 1.7.1's own published packages disagree on their `@better-auth/utils` dependency (better-auth pins exactly 0.4.2, better-call - shared by @better-auth/core/oauth-provider/mcp - wants ^0.5.0), so pnpm resolved two physical @better-auth/core instances. jwt() and mcp() ended up typed against different instances, so betterAuthPlugins: [jwt(), mcp(...)] failed to type-check with a structural BetterAuthPlugin mismatch even though both were configured correctly. Added a root pnpm.overrides pin forcing one instance. 3. better-auth 1.7's mcp() also requires a `consentPage` option (the page where a user approves/denies an MCP client's requested scopes), alongside the resource option already added. Missing it is a type error, not a runtime throw like the missing jwt()/resource cases, so it only surfaced once (1) and (2) were fixed and the build actually reached type-checking. Added it everywhere resource was added. Verified with the actual commands CI runs rather than the narrower ones I'd been using locally: `pnpm turbo run build` and `pnpm turbo run test` across all 24 workspace packages/examples, both green (32/32 tasks). Also re-ran the full auth unit suite and both e2e suites. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QtaxBvCjLsZcm7YiSmpteJ --- .changeset/eleven-mice-jog.md | 5 + CLAUDE.md | 2 +- docs/content/how-to/mcp.md | 3 + docs/content/reference/auth.md | 4 + examples/mcp-demo/opensaas.config.ts | 3 + examples/mcp-demo/package.json | 1 + package.json | 5 + packages/auth/CLAUDE.md | 11 +- packages/auth/src/config/types.ts | 4 +- packages/auth/tests/auth-lists-drift.test.ts | 6 +- .../auth/tests/generated-fk-shape.test.ts | 40 ++++- .../auth/tests/mcp-oauth-cascade-e2e.test.ts | 2 +- .../tests/plugin-table-derivation.test.ts | 14 +- pnpm-lock.yaml | 159 ++++-------------- 14 files changed, 115 insertions(+), 144 deletions(-) diff --git a/.changeset/eleven-mice-jog.md b/.changeset/eleven-mice-jog.md index 29def51b..5d4f30b6 100644 --- a/.changeset/eleven-mice-jog.md +++ b/.changeset/eleven-mice-jog.md @@ -36,6 +36,9 @@ authPlugin({ jwt(), mcp({ loginPage: '/sign-in', + // The page where a user approves/denies an MCP client's requested + // scopes — also required as of better-auth 1.7. + consentPage: '/consent', // RFC 8707/9728 canonical resource identifier — required as of // better-auth 1.7. Must match your `mcp.basePath`. HTTP is only // accepted on loopback hosts. @@ -47,6 +50,8 @@ authPlugin({ better-auth 1.7's MCP plugin also declares a substantially different OAuth table set — the old `oauthApplication`/`oauthAccessToken`/`oauthConsent` three became seven tables (`oauthClient`/`oauthAccessToken`/`oauthConsent`/`oauthRefreshToken`/`oauthResource`/`oauthClientResource`/`oauthClientAssertion`). Since the derivation is schema-driven this needed no code changes, but if you have the MCP plugin enabled, running `pnpm generate` will produce a significantly different Prisma schema for these tables (new/renamed models, and `Session` gains reverse relations to the two token tables that now reference it). Review the diff and migrate your database accordingly. +**Workspace-wide: pins `@better-auth/utils` to `0.5.0` via a root `pnpm.overrides`.** better-auth 1.7.1's own published packages disagree on this transitive dependency — `better-auth` pins it at exactly `0.4.2` while `better-call` (used by `@better-auth/core`, `@better-auth/oauth-provider`, and `@better-auth/mcp`) requires `^0.5.0` — so pnpm resolves two separate physical instances of `@better-auth/core` depending on which peer chain a given package sits in. That split is invisible at runtime but breaks TypeScript: `jwt()` (from `better-auth/plugins`) and `mcp()` (from `@better-auth/mcp`) end up typed against different `@better-auth/core` instances, so `betterAuthPlugins: [jwt(), mcp(...)]` fails to type-check with a `BetterAuthPlugin` structural-mismatch error even though both plugins are otherwise correctly configured. The override forces one instance workspace-wide. If you hit the same error in your own app, add the equivalent override to your own `package.json`. + Also fixes two `deriveAuthLists` gaps surfaced by the MCP plugin's expanded schema: - the scalar-field builder now threads a static `defaultValue` through for `number`-typed fields (`integer()`/`bigInt()`), matching the existing `string`/`boolean` behavior (e.g. `oauthResource.policyVersion`, which defaults to `1`) diff --git a/CLAUDE.md b/CLAUDE.md index 6b764c44..664d7c2e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -625,7 +625,7 @@ The stack provides Model Context Protocol server integration through `@opensaas/ 1. Enable MCP in config with `mcp: { enabled: true }` (the runtime reads `enabled`/`basePath`/`defaultTools`) 2. Core runtime derives CRUD tools for each list (query, create, update, delete) at request time -3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [jwt(), mcp({ loginPage: '/sign-in', resource: '' })] })`, imported from `@opensaas/stack-auth/plugins` — the plugin itself comes from the optional `@better-auth/mcp` peer since better-auth 1.7 split it out of `better-auth/plugins`, and requires `jwt()` from `better-auth/plugins` registered alongside it) and the `createBetterAuthMcpAdapter` session provider +3. OAuth with AI assistants is wired through Better-auth's `mcp` plugin (`authPlugin({ betterAuthPlugins: [jwt(), mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: '' })] })`, imported from `@opensaas/stack-auth/plugins` — the plugin itself comes from the optional `@better-auth/mcp` peer since better-auth 1.7 split it out of `better-auth/plugins`, and requires `jwt()` from `better-auth/plugins` registered alongside it) and the `createBetterAuthMcpAdapter` session provider 4. All tools respect existing access control rules 5. Custom tools can be added per-list via `mcp.customTools` (Zod or JSON Schema inputSchema); plugins can register global tools via `registerMcpTool` diff --git a/docs/content/how-to/mcp.md b/docs/content/how-to/mcp.md index af8e1362..095d2f00 100644 --- a/docs/content/how-to/mcp.md +++ b/docs/content/how-to/mcp.md @@ -53,6 +53,9 @@ export default config({ jwt(), mcp({ loginPage: '/sign-in', + // The page where a user approves/denies an MCP client's requested + // scopes — also required since better-auth 1.7's MCP plugin. + consentPage: '/consent', // Canonical protected-resource identifier (RFC 8707/9728) — // required since better-auth 1.7's MCP plugin, and must match // `mcp.basePath` below. HTTP is only accepted on loopback hosts. diff --git a/docs/content/reference/auth.md b/docs/content/reference/auth.md index b6ed4038..03900c6a 100644 --- a/docs/content/reference/auth.md +++ b/docs/content/reference/auth.md @@ -296,6 +296,9 @@ authPlugin({ jwt(), mcp({ loginPage: '/sign-in', + // The page where a user approves/denies an MCP client's requested + // scopes — also required since better-auth 1.7's MCP plugin. + consentPage: '/consent', // Canonical protected-resource identifier (RFC 8707/9728) — required // since better-auth 1.7's MCP plugin. Must match `mcp.basePath` below. resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, @@ -690,6 +693,7 @@ export default config({ jwt(), mcp({ loginPage: '/sign-in', + consentPage: '/consent', resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, }), ], diff --git a/examples/mcp-demo/opensaas.config.ts b/examples/mcp-demo/opensaas.config.ts index 9f5e66fe..2c7f5876 100644 --- a/examples/mcp-demo/opensaas.config.ts +++ b/examples/mcp-demo/opensaas.config.ts @@ -47,6 +47,9 @@ export default config({ jwt(), mcp({ loginPage: '/sign-in', + // Not implemented in this demo — a real project needs a page here + // for users to approve/deny an MCP client's requested scopes. + consentPage: '/consent', // RFC 8707/9728 canonical resource identifier — must match the // `mcp.basePath` below. HTTP is accepted only on loopback hosts. resource: `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/mcp`, diff --git a/examples/mcp-demo/package.json b/examples/mcp-demo/package.json index aa3852b5..0a3acb70 100644 --- a/examples/mcp-demo/package.json +++ b/examples/mcp-demo/package.json @@ -17,6 +17,7 @@ "@opensaas/stack-ui": "workspace:*", "@prisma/adapter-better-sqlite3": "^7.8.0", "@prisma/client": "^7.8.0", + "better-auth": "^1.7.1", "next": "^16.2.10", "react": "^19.2.8", "react-dom": "^19.2.8", diff --git a/package.json b/package.json index adde3788..c8a8a348 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,11 @@ ], "author": "", "license": "MIT", + "pnpm": { + "overrides": { + "@better-auth/utils": "0.5.0" + } + }, "devDependencies": { "@changesets/changelog-github": "^0.7.0", "@changesets/cli": "^3.0.0", diff --git a/packages/auth/CLAUDE.md b/packages/auth/CLAUDE.md index c235aea7..3f48c0d8 100644 --- a/packages/auth/CLAUDE.md +++ b/packages/auth/CLAUDE.md @@ -52,11 +52,12 @@ Pre-built forms (client components). Each takes **server action** props (not an - `mcp` - re-exported from the optional `@better-auth/mcp` peer (better-auth 1.7 split it out of `better-auth/plugins`) for OAuth authentication with AI assistants. Its `resource` option (the RFC 8707/9728 canonical - protected-resource URL) is required as of that split, and it now needs - better-auth's own `jwt()` plugin (from `better-auth/plugins`, unaffected by - the `@better-auth/mcp` split) registered alongside it — the OAuth Provider - `mcp()` is built on issues JWT-based access tokens and throws - `BetterAuthError: jwt_config` at init without it. See the + protected-resource URL) and `consentPage` (where a user approves/denies an + MCP client's requested scopes) are both required as of that split, and it + now needs better-auth's own `jwt()` plugin (from `better-auth/plugins`, + unaffected by the `@better-auth/mcp` split) registered alongside it — the + OAuth Provider `mcp()` is built on issues JWT-based access tokens and + throws `BetterAuthError: jwt_config` at init without it. See the `betterAuthPlugins` example in the root `CLAUDE.md`'s MCP section. ## Architecture Patterns diff --git a/packages/auth/src/config/types.ts b/packages/auth/src/config/types.ts index d758a44b..a0c86b43 100644 --- a/packages/auth/src/config/types.ts +++ b/packages/auth/src/config/types.ts @@ -368,9 +368,11 @@ export type AuthConfig = { * @example * ```typescript * import { mcp } from '@opensaas/stack-auth/plugins' + * import { jwt } from 'better-auth/plugins' * * betterAuthPlugins: [ - * mcp({ loginPage: '/sign-in' }) + * jwt(), // required alongside mcp() since better-auth 1.7 + * mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://your-app.com/api/mcp' }) * ] * ``` */ diff --git a/packages/auth/tests/auth-lists-drift.test.ts b/packages/auth/tests/auth-lists-drift.test.ts index 6ff193bd..15eda2f9 100644 --- a/packages/auth/tests/auth-lists-drift.test.ts +++ b/packages/auth/tests/auth-lists-drift.test.ts @@ -440,7 +440,11 @@ describe('derived better-auth plugin tables match better-auth’s own table defi verification: { modelName: 'Verification', fields: {} }, } - const plugin = mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }) + const plugin = mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }) const upstreamTables = getAuthTables({ plugins: [plugin] }) const { keys, lists } = deriveAuthLists(defaultModels, {}, {}, [plugin]) const pluginModelToListKey: Record = { diff --git a/packages/auth/tests/generated-fk-shape.test.ts b/packages/auth/tests/generated-fk-shape.test.ts index 4473d29f..5b184ac5 100644 --- a/packages/auth/tests/generated-fk-shape.test.ts +++ b/packages/auth/tests/generated-fk-shape.test.ts @@ -258,7 +258,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }), + ], }), ], lists: {}, @@ -278,7 +284,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }), + ], }), ], lists: {}, @@ -295,7 +307,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }), + ], }), ], lists: {}, @@ -315,7 +333,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }), + ], }), ], lists: {}, @@ -351,7 +375,13 @@ describe('generated MCP plugin OAuth schema gets real foreign keys and cascades plugins: [ authPlugin({ emailAndPassword: { enabled: true }, - betterAuthPlugins: [mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' })], + betterAuthPlugins: [ + mcp({ + loginPage: '/sign-in', + consentPage: '/consent', + resource: 'https://example.com/mcp', + }), + ], }), ], lists: {}, diff --git a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts index fa7f6ca9..b6603679 100644 --- a/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts +++ b/packages/auth/tests/mcp-oauth-cascade-e2e.test.ts @@ -66,7 +66,7 @@ export default config({ // otherwise) — see @better-auth/mcp's own usage example. betterAuthPlugins: [ jwt(), - mcp({ loginPage: '/sign-in', resource: 'http://localhost:3000/api/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'http://localhost:3000/api/mcp' }), ], // Not modelled by AuthConfig — passed through verbatim so /delete-user // deletes immediately instead of requiring an email-verification diff --git a/packages/auth/tests/plugin-table-derivation.test.ts b/packages/auth/tests/plugin-table-derivation.test.ts index d75a474e..6807d845 100644 --- a/packages/auth/tests/plugin-table-derivation.test.ts +++ b/packages/auth/tests/plugin-table-derivation.test.ts @@ -23,7 +23,7 @@ const defaultModels: NormalizedAuthModels = { describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('derives every MCP OAuth table under a PascalCase key, mapped back to its camelCase physical table', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) expect(Object.keys(lists).sort()).toEqual([ @@ -46,7 +46,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('generates a real relationship (not a bare column) for a PK-targeting reference (userId -> user.id)', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) const userField = lists.OauthClient.fields.user @@ -59,7 +59,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('carries the referential action from `onDelete: cascade` through to the generated relation', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) const userField = lists.OauthClient.fields.user const extend = userField.db?.extendPrismaSchema @@ -97,7 +97,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('leaves a non-PK-targeting reference as a plain scalar column (clientId -> oauthClient.clientId)', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) const clientId = lists.OauthAccessToken.fields.clientId @@ -110,7 +110,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('adds a reverse relation on the target base list for every plugin-table reference to it, with no name collisions', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) expect(lists.User.fields.oauthClients).toMatchObject({ @@ -151,7 +151,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { it('ships plugin-table lists closed — no access control', () => { const { lists } = deriveAuthLists(defaultModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) expect(lists.OauthClient.access).toBeUndefined() @@ -261,7 +261,7 @@ describe('deriveAuthLists — better-auth plugin tables (issue #992)', () => { verification: { modelName: 'AuthVerification', fields: {} }, } const { lists } = deriveAuthLists(remappedModels, {}, {}, [ - mcp({ loginPage: '/sign-in', resource: 'https://example.com/mcp' }), + mcp({ loginPage: '/sign-in', consentPage: '/consent', resource: 'https://example.com/mcp' }), ]) expect(lists.OauthClient.fields.user.ref).toBe('AuthUser.oauthClients') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e4e15de..1b078589 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@better-auth/utils': 0.5.0 + importers: .: @@ -152,7 +155,7 @@ importers: version: 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) better-auth: specifier: ^1.7.1 - version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) next: specifier: ^16.2.10 version: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -477,6 +480,9 @@ importers: '@prisma/client': specifier: ^7.8.0 version: 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + better-auth: + specifier: ^1.7.1 + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) next: specifier: ^16.2.10 version: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -755,7 +761,7 @@ importers: version: 4.3.3 better-auth: specifier: ^1.7.1 - version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) + version: 1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) better-sqlite3: specifier: ^12.6.2 version: 12.6.2 @@ -1605,7 +1611,7 @@ packages: '@better-auth/core@1.7.1': resolution: {integrity: sha512-eZ9lqcnVLMZ3QtUByRo4VZqkB1ESyRddd9NfWjBdDPgh+jcwLScoIUAqhtHLR8zaSUJZah8OLGlkzObyPdUH7A==} peerDependencies: - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.1 '@cloudflare/workers-types': '>=4' '@opentelemetry/api': ^1.9.0 @@ -1623,7 +1629,7 @@ packages: resolution: {integrity: sha512-qlqNyg5V9bXHSP68/vtlsiZayhR4hgvEGiS/E3SIj8bCpWWFGmyQkxJbQCqpBmC7vT30wE/kNtJMHIgnV3rkiw==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 peerDependenciesMeta: drizzle-orm: @@ -1633,7 +1639,7 @@ packages: resolution: {integrity: sha512-yWCpE1cZpMUj37nD6JFDK+GDR8zS37L5WI73il3qbU9TXtWsxUQKc/5c3IHsHizWQsmcQI8uv2pAFKxsRDa+AQ==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 kysely: ^0.28.17 || ^0.29.0 peerDependenciesMeta: kysely: @@ -1650,13 +1656,13 @@ packages: resolution: {integrity: sha512-6NX1yv88DeqdoG7owYFqKwlrDGaIPhsC52JUGrUgeGVKyOq8a/6hHlHsqG1C2FwT23SHQiKVYCEAG9N6aH5OvQ==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@better-auth/mongo-adapter@1.7.1': resolution: {integrity: sha512-9ILTcNqhG37QK//qR4UhYLyKzNqq6w6zVTf5KX6xkiTjNcV7Oh1yS31lkIJEVTqRNcy9AoV6FZMW6Bbsm8IMDA==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: @@ -1666,7 +1672,7 @@ packages: resolution: {integrity: sha512-VWIw7ti6rodlbbdSbn0mts/TZcBWUj6YaoIpREmv70eoGmWTa6MPWEbGuUdADQe3Vy4YqysIbmQA6qgRqfLTaw==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.1 better-auth: ^1.7.1 better-call: 1.4.0 @@ -1675,7 +1681,7 @@ packages: resolution: {integrity: sha512-ZiUcafQ85InAofcUjyGgCPjKLfQjXr9SvDmMjuFUW8oEbreA6C6GaFAEA77VuV2doZUQlzvQQ4gCoTmS28W92A==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: @@ -1688,12 +1694,9 @@ packages: resolution: {integrity: sha512-kLKjMfFlTbyt49DGeI9okHAsn0MtBZcMoQYKaEdgR0H3BHzqqyzePcQz/hxAmRgjB4p/6inise3zJwhX0sgXrQ==} peerDependencies: '@better-auth/core': ^1.7.1 - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.1 - '@better-auth/utils@0.4.2': - resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} - '@better-auth/utils@0.5.0': resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} @@ -8182,20 +8185,6 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2)': - dependencies: - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@opentelemetry/semantic-conventions': 1.43.0 - '@standard-schema/spec': 1.1.0 - better-call: 1.4.0(zod@4.4.3) - jose: 6.2.8 - kysely: 0.29.4 - nanostores: 1.4.2 - zod: 4.4.3 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2)': dependencies: '@better-auth/utils': 0.5.0 @@ -8210,27 +8199,15 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - - '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': + '@better-auth/drizzle-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - - '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - optionalDependencies: - kysely: 0.29.4 + '@better-auth/utils': 0.5.0 - '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4)': + '@better-auth/kysely-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(kysely@0.29.4)': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 optionalDependencies: kysely: 0.29.4 @@ -8245,27 +8222,15 @@ snapshots: - '@better-auth/utils' - '@better-fetch/fetch' - '@better-auth/memory-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - - '@better-auth/memory-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)': + '@better-auth/memory-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - - '@better-auth/mongo-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0)': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - optionalDependencies: - mongodb: 7.1.0 + '@better-auth/utils': 0.5.0 - '@better-auth/mongo-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0)': + '@better-auth/mongo-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(mongodb@7.1.0)': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 optionalDependencies: mongodb: 7.1.0 @@ -8279,38 +8244,20 @@ snapshots: jose: 6.2.8 zod: 4.4.3 - '@better-auth/prisma-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - optionalDependencies: - '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - - '@better-auth/prisma-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': + '@better-auth/prisma-adapter@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 optionalDependencies: '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@better-auth/telemetry@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - - '@better-auth/telemetry@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)': dependencies: '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/utils': 0.4.2 + '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.1 - '@better-auth/utils@0.4.2': - dependencies: - '@noble/hashes': 2.2.0 - '@better-auth/utils@0.5.0': dependencies: '@noble/hashes': 2.2.0 @@ -11013,50 +10960,16 @@ snapshots: bcryptjs@3.0.3: {} - better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): - dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4) - '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0) - '@better-auth/prisma-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@noble/ciphers': 2.2.0 - '@noble/hashes': 2.2.0 - better-call: 1.4.0(zod@4.4.3) - defu: 6.1.7 - jose: 6.2.8 - kysely: 0.29.4 - nanostores: 1.4.2 - zod: 4.4.3 - optionalDependencies: - '@prisma/client': 7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - better-sqlite3: 12.6.2 - mongodb: 7.1.0 - mysql2: 3.15.3 - next: 16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - pg: 8.22.0 - prisma: 7.9.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.11)(yaml@2.9.0)) - transitivePeerDependencies: - - '@cloudflare/workers-types' - - '@opentelemetry/api' - better-auth@1.7.1(@opentelemetry/api@1.9.1)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(better-sqlite3@12.6.2)(mongodb@7.1.0)(mysql2@3.15.3)(next@16.2.10(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(pg@8.22.0)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): dependencies: - '@better-auth/core': 1.7.1(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) - '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(kysely@0.29.4) - '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(mongodb@7.1.0) - '@better-auth/prisma-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2) + '@better-auth/drizzle-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0) + '@better-auth/kysely-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(kysely@0.29.4) + '@better-auth/memory-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0) + '@better-auth/mongo-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(mongodb@7.1.0) + '@better-auth/prisma-adapter': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@prisma/client@7.8.0(@typescript/typescript6@6.0.2)(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)))(prisma@7.9.0(@types/react@19.2.14)(@typescript/typescript6@6.0.2)(better-sqlite3@12.6.2)(magicast@0.5.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) + '@better-auth/telemetry': 1.7.1(@better-auth/core@1.7.1(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.4.3))(jose@6.2.8)(kysely@0.29.4)(nanostores@1.4.2))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1) + '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.1 '@noble/ciphers': 2.2.0 '@noble/hashes': 2.2.0