@opensaas/stack-auth 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +122 -0
  3. package/CLAUDE.md +115 -17
  4. package/INTEGRATION_SUMMARY.md +21 -20
  5. package/README.md +82 -48
  6. package/dist/config/adopt-better-auth-tables.d.ts +107 -0
  7. package/dist/config/adopt-better-auth-tables.d.ts.map +1 -0
  8. package/dist/config/adopt-better-auth-tables.js +70 -0
  9. package/dist/config/adopt-better-auth-tables.js.map +1 -0
  10. package/dist/config/derive-auth-lists.d.ts +50 -0
  11. package/dist/config/derive-auth-lists.d.ts.map +1 -0
  12. package/dist/config/derive-auth-lists.js +274 -0
  13. package/dist/config/derive-auth-lists.js.map +1 -0
  14. package/dist/config/index.d.ts.map +1 -1
  15. package/dist/config/index.js +43 -0
  16. package/dist/config/index.js.map +1 -1
  17. package/dist/config/plugin.d.ts +1 -1
  18. package/dist/config/plugin.d.ts.map +1 -1
  19. package/dist/config/plugin.js +52 -9
  20. package/dist/config/plugin.js.map +1 -1
  21. package/dist/config/types.d.ts +130 -3
  22. package/dist/config/types.d.ts.map +1 -1
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +6 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/lists/index.d.ts +17 -11
  28. package/dist/lists/index.d.ts.map +1 -1
  29. package/dist/lists/index.js +34 -208
  30. package/dist/lists/index.js.map +1 -1
  31. package/dist/server/index.d.ts.map +1 -1
  32. package/dist/server/index.js +28 -7
  33. package/dist/server/index.js.map +1 -1
  34. package/dist/server/schema-converter.d.ts +1 -1
  35. package/dist/server/schema-converter.js +1 -1
  36. package/package.json +3 -3
  37. package/src/config/adopt-better-auth-tables.ts +146 -0
  38. package/src/config/derive-auth-lists.ts +323 -0
  39. package/src/config/index.ts +58 -0
  40. package/src/config/plugin.ts +67 -10
  41. package/src/config/types.ts +146 -3
  42. package/src/index.ts +13 -0
  43. package/src/lists/index.ts +42 -202
  44. package/src/server/index.ts +33 -10
  45. package/src/server/schema-converter.ts +1 -1
  46. package/tests/adopt-better-auth-tables.test.ts +183 -0
  47. package/tests/derive-auth-lists.test.ts +232 -0
  48. package/tests/plugin-derived-keys.test.ts +138 -0
  49. package/tests/plugin-schema-placement.test.ts +121 -0
  50. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensaas/stack-auth@0.20.1 build /home/runner/work/stack/stack/packages/auth
2
+ > @opensaas/stack-auth@0.22.0 build /home/runner/work/stack/stack/packages/auth
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,127 @@
1
1
  # @opensaas/stack-auth
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#509](https://github.com/OpenSaasAU/stack/pull/509) [`fdc48f8`](https://github.com/OpenSaasAU/stack/commit/fdc48f86a5a7f161bef0b512963e1511a8c8e00e) Thanks [@list({](https://github.com/list({)! - Add `adoptBetterAuthTables()` recipe for adopting an existing better-auth installation
8
+
9
+ A migrating project that already runs better-auth (its `AuthUser`/`AuthSession`/`AuthAccount`/`AuthVerification` tables live in a separate `auth` Postgres schema, and its app `User` is a different model) can now adopt those live tables without rebuilding the auth config by hand. The recipe presets the plugin-level `schema` plus each model's `modelName` (and optional column `fields` maps) to the conventions of a standard separate-schema better-auth install, so the derived Auth lists diff clean (Schema parity) against the live database — no destructive auth migration. The app's own domain `User` is left untouched; linking it to the Auth identity is the application's concern.
10
+
11
+ ```typescript
12
+ import { config } from '@opensaas/stack-core'
13
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
14
+
15
+ export default config({
16
+ db: { provider: 'postgresql', url: process.env.DATABASE_URL },
17
+ plugins: [
18
+ authPlugin({
19
+ // Defaults: AuthUser/AuthSession/AuthAccount/AuthVerification in the
20
+ // `auth` schema, pinned to your live table names (@@map) + schema (@@schema).
21
+ ...adoptBetterAuthTables(),
22
+ emailAndPassword: { enabled: true },
23
+ }),
24
+ ],
25
+ lists: {
26
+ // Your own domain User stays in `public` and is NOT touched by the plugin.
27
+ fields: { subjectId: text({ validation: { isRequired: true } }) } }),
28
+ },
29
+ })
30
+
31
+ // Customise when your live tables diverge from the defaults:
32
+ adoptBetterAuthTables({
33
+ schema: 'identity', // default: 'auth'
34
+ modelNamePrefix: 'BA', // default: 'Auth'
35
+ fields: { user: { name: 'full_name' }, session: { userId: 'user_id' } },
36
+ })
37
+ ```
38
+
39
+ - [#497](https://github.com/OpenSaasAU/stack/pull/497) [`be4181a`](https://github.com/OpenSaasAU/stack/commit/be4181ada3f2d6386052df4d4869ad150d360f89) Thanks [@{](https://github.com/{)! - Derive the auth plugin's Auth lists from the better-auth config
40
+
41
+ `authPlugin` now mirrors the better-auth config a developer writes instead of hardcoding the keys `User`/`Session`/`Account`/`Verification`. Per-model `modelName` becomes the OpenSaaS list key (and a table `@@map`), and the `fields` column map becomes per-field `@map`s. The plugin only ever adds/extends its own derived keys, so an app's separate domain `User` is never overwritten. The runtime `getUser`/`getCurrentUser` helpers now resolve the user list key from the configured user model instead of a hardcoded `'user'`.
42
+
43
+ Default behaviour (no overrides) is unchanged: the lists are still keyed `User`/`Session`/`Account`/`Verification` with the original field shapes and no `@@map`.
44
+
45
+ ```typescript
46
+ // Adopt existing better-auth tables without a destructive migration
47
+ authPlugin({
48
+ modelName: 'AuthUser', fields: { name: 'full_name' } },
49
+ session: { modelName: 'AuthSession', fields: { userId: 'user_id' } },
50
+ account: { modelName: 'AuthAccount' },
51
+ verification: { modelName: 'AuthVerification' },
52
+ })
53
+ // -> lists keyed AuthUser/AuthSession/AuthAccount/AuthVerification
54
+ // with @@map + column @map matching the live tables
55
+ ```
56
+
57
+ Lists also gain a model-level `db.map` option, which emits a `@@map("...")` on the generated Prisma model so a list key can differ from its physical table name.
58
+
59
+ - [#502](https://github.com/OpenSaasAU/stack/pull/502) [`593390c`](https://github.com/OpenSaasAU/stack/commit/593390c57d9844ca7ada8f45b340c849f1d8d647) Thanks [@{](https://github.com/{)! - Add `authPlugin` schema placement so Auth lists can adopt an existing non-`public` better-auth layout (clean-diff adoption)
60
+
61
+ The auth lists can now be placed in a non-`public` Postgres schema (e.g. `auth`) so they diff CLEAN against a separate-schema better-auth installation. A plugin-level `schema` option applies `@@schema(...)` to all generated Auth lists, with a per-list override.
62
+
63
+ ```typescript
64
+ authPlugin({
65
+ schema: 'auth', // all Auth lists get @@schema("auth")
66
+ modelName: 'AuthUser' },
67
+ session: { modelName: 'AuthSession' },
68
+ account: { modelName: 'AuthAccount' },
69
+ // per-model override: relocate one list to a different schema
70
+ verification: { modelName: 'AuthVerification', schema: 'auth_internal' },
71
+ })
72
+ ```
73
+
74
+ The plugin's `beforeGenerate` hook wires the datasource `schemas` array (always including `public`) and defaults any list without an explicit `db.schema` to `public`, producing a valid multi-schema Prisma schema. With no `schema` option the output is unchanged (greenfield default stays in `public`, no `@@schema`).
75
+
76
+ Core support added for this (mirroring the `db.map` → `@@map` work):
77
+ - List-level `db.schema` → the Prisma generator emits `@@schema("...")` on the model.
78
+ - Database-level `db.schemas` → the generator emits the datasource `schemas = [...]` array and enables the `multiSchema` preview feature.
79
+
80
+ ```typescript
81
+ // Core/generator building blocks
82
+ db: { provider: 'postgresql', schemas: ['public', 'auth'] }
83
+ AuthUser: list({ fields: { ... }, db: { map: 'AuthUser', schema: 'auth' } })
84
+ // Generates: model AuthUser { ... @@map("AuthUser") @@schema("auth") }
85
+ ```
86
+
87
+ ### Patch Changes
88
+
89
+ - [#501](https://github.com/OpenSaasAU/stack/pull/501) [`e30f6a1`](https://github.com/OpenSaasAU/stack/commit/e30f6a1ef69dc65ae68b37539fa74c3f97823cfd) Thanks [@borisno2](https://github.com/borisno2)! - Keep `createdAt`/`updatedAt` on the auth lists now that auto-timestamps are off by default
90
+
91
+ The derived auth lists (User/Session/Account/Verification) now opt into `db: { timestamps: true }`. better-auth's adapter writes those columns and the schema converter returns `null` for them assuming the generator injects them, so the opt-in keeps the generated auth models intact.
92
+
93
+ ## 0.21.0
94
+
95
+ ### Minor Changes
96
+
97
+ - [#415](https://github.com/OpenSaasAU/stack/pull/415) [`8980ff3`](https://github.com/OpenSaasAU/stack/commit/8980ff36ffb0879d8f4409740493dd940572cc9d) Thanks [@borisno2](https://github.com/borisno2)! - Curate the `@opensaas/stack-core` public surface into clearly-scoped entry points
98
+
99
+ The root entry point now exposes only the everyday consumer surface — `config`,
100
+ `list`, `getContext`, the naming helpers (`getDbKey`, `getUrlKey`,
101
+ `getListKeyFromUrl`), `ValidationError`, and the config/access types you annotate
102
+ with. Plugin and field authoring contracts move to a new `/extend` path, and the
103
+ plumbing shared with sibling packages and generated code moves to `/internal`.
104
+
105
+ ```typescript
106
+ // Everyday usage (unchanged)
107
+ import { config, list, getContext } from '@opensaas/stack-core'
108
+
109
+ // Authoring a plugin or a third-party field package
110
+ import type { Plugin, BaseFieldConfig, TypeInfo } from '@opensaas/stack-core/extend'
111
+ ```
112
+
113
+ `@opensaas/stack-core/internal` carries no semver guarantees; application code
114
+ should never import from it. `Session` stays on the root entry point because it is
115
+ the module-augmentation target.
116
+
117
+ Removed from the public surface (zero callers): the nine `*HookArgs` types and the
118
+ callerless typed-query runtime types. The other `@opensaas/*` packages and the CLI
119
+ generator are updated to import from the new paths.
120
+
121
+ ### Patch Changes
122
+
123
+ - [#414](https://github.com/OpenSaasAU/stack/pull/414) [`f03e5ac`](https://github.com/OpenSaasAU/stack/commit/f03e5ac32d5a38ef31c895b200b1a4f7a5e50c9c) Thanks [@borisno2](https://github.com/borisno2)! - Fix docs to use the canonical `authPlugin()`/`ragPlugin()` config pattern instead of the non-existent `withAuth()`/`authConfig()`/`withRAG()`/`ragConfig()` wrappers
124
+
3
125
  ## 0.20.1
4
126
 
5
127
  ## 0.20.0
package/CLAUDE.md CHANGED
@@ -13,10 +13,9 @@ Adds complete authentication to OpenSaas Stack apps with minimal configuration.
13
13
 
14
14
  ## Key Files & Exports
15
15
 
16
- ### Config (`src/config/index.ts`)
16
+ ### Config (`src/config/plugin.ts`)
17
17
 
18
- - `withAuth(config, authConfig)` - Wraps OpenSaas config, merges auth lists
19
- - `authConfig({ ... })` - Configures Better-auth plugins and session
18
+ - `authPlugin({ ... })` - Plugin added to a config's `plugins` array; merges auth lists, configures Better-auth plugins and session. This is the only configuration entry point.
20
19
 
21
20
  ### Lists (`src/lists/index.ts`)
22
21
 
@@ -53,16 +52,114 @@ Pre-built forms (client components):
53
52
 
54
53
  ### Config Merging
55
54
 
56
- `withAuth()` merges auth lists into your config:
55
+ `authPlugin()` merges auth lists into your config:
57
56
 
58
57
  ```typescript
59
- withAuth(
60
- config({ lists: { Post: list({...}) } }),
61
- authConfig({ emailAndPassword: { enabled: true } })
62
- )
58
+ config({
59
+ lists: { Post: list({...}) },
60
+ plugins: [authPlugin({ emailAndPassword: { enabled: true } })],
61
+ })
63
62
  // Result: { lists: { User, Session, Account, Verification, Post } }
64
63
  ```
65
64
 
65
+ ### Deriving Auth lists from better-auth config
66
+
67
+ The four Auth lists are **derived** from the better-auth model config the
68
+ developer writes — not hardcoded. The pure derivation lives in
69
+ `src/config/derive-auth-lists.ts` (`deriveAuthLists`), which `getAuthLists`
70
+ and the plugin's add-vs-extend logic consume:
71
+
72
+ - per-model `modelName` → list key + table `@@map`
73
+ - per-model `fields` (better-auth field → column) → field-level `@map`
74
+ - the `userId` column override → the `user` relationship foreign-key `@map`
75
+ - relationship refs between the Auth lists follow the derived keys
76
+ (e.g. `Session.user → AuthUser.sessions`)
77
+
78
+ With no `modelName`/`fields` overrides the output is unchanged
79
+ (`User`/`Session`/`Account`/`Verification`, original field shapes, no `@@map`).
80
+
81
+ ```typescript
82
+ // Adopt an existing better-auth installation (Auth lists ≠ app User)
83
+ authPlugin({
84
+ user: { modelName: 'AuthUser', fields: { name: 'full_name' } },
85
+ session: { modelName: 'AuthSession', fields: { userId: 'user_id' } },
86
+ })
87
+ // Adds AuthUser/AuthSession/... and leaves an app's own `User` untouched.
88
+ ```
89
+
90
+ Because the plugin only ever adds/extends its **derived** keys, an app's own
91
+ domain `User` (a different model from the better-auth user) is never extended
92
+ or overwritten when the user model is renamed. The runtime `getUser`/
93
+ `getCurrentUser` helpers resolve the user list's `context.db` key from the
94
+ configured user `modelName`.
95
+
96
+ ### Schema placement (relocatable Auth lists)
97
+
98
+ A plugin-level `schema` option places all generated Auth lists in a non-`public`
99
+ Postgres schema via `@@schema(...)`, so they can adopt a separate-schema
100
+ better-auth layout (e.g. an `auth` schema) and reach **Schema parity** with the
101
+ live tables. Combined with the derived keys/`@@map`/field `@map`s above, the
102
+ generated lists diff CLEAN against an existing `auth`-schema install — they are
103
+ modelled for runtime/types without producing a migration.
104
+
105
+ ```typescript
106
+ authPlugin({
107
+ schema: 'auth', // all Auth lists get @@schema("auth")
108
+ user: { modelName: 'AuthUser' },
109
+ session: { modelName: 'AuthSession' },
110
+ account: { modelName: 'AuthAccount' },
111
+ verification: { modelName: 'AuthVerification' },
112
+ // per-model override: relocate one list to a different schema
113
+ // verification: { modelName: 'AuthVerification', schema: 'auth_internal' },
114
+ })
115
+ ```
116
+
117
+ How it wires up (Postgres multi-schema):
118
+
119
+ - Each Auth list gets a list-level `db.schema` → `@@schema(...)` (per-model
120
+ `schema` override, else the plugin-level `schema`).
121
+ - The plugin's `beforeGenerate` hook adds the auth schema(s) (always plus
122
+ `public`) to the datasource `db.schemas` array and defaults any list without
123
+ an explicit `db.schema` to `public`, so the generated multi-schema Prisma
124
+ schema is valid (the generator emits `previewFeatures = ["multiSchema"]` and
125
+ `schemas = [...]`).
126
+ - With no `schema` option the Auth lists stay in `public` and no `@@schema` /
127
+ `schemas` / preview feature is emitted (greenfield default unchanged).
128
+
129
+ ### Adopting an existing better-auth install (`adoptBetterAuthTables`)
130
+
131
+ `adoptBetterAuthTables()` (`src/config/adopt-better-auth-tables.ts`) is a thin
132
+ recipe that returns the `AuthConfig` adoption knobs — the plugin-level `schema`
133
+ plus a per-model `modelName` (and optional column `fields` maps) — preset to the
134
+ conventions of a standard separate-schema better-auth install. It ties together
135
+ the keys/field derivation and schema placement so a migrator doesn't rebuild the
136
+ config by hand. Spread it into `authPlugin`:
137
+
138
+ ```typescript
139
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
140
+
141
+ authPlugin({
142
+ ...adoptBetterAuthTables(), // schema: 'auth', AuthUser/AuthSession/AuthAccount/AuthVerification
143
+ emailAndPassword: { enabled: true },
144
+ })
145
+ // Options: adoptBetterAuthTables({ schema, modelNamePrefix, fields })
146
+ ```
147
+
148
+ It is pure config (no side effects): everything it sets can also be written
149
+ directly on `authPlugin`, and spreading it before your own keys lets you
150
+ override per model. Because the derived user key is `AuthUser` (not `User`), an
151
+ app's own domain `User` is left untouched — the plugin only ever adds/extends
152
+ its derived keys. Combined with the derivation + schema placement above, the
153
+ generated Auth lists reach **Schema parity** (clean `schema:diff`) against a live
154
+ `auth`-schema install — they are modelled for runtime/types, not migrated.
155
+
156
+ **App User ≠ Auth identity.** The plugin models the **Auth identity** (the
157
+ better-auth user); it does not assume that list is the app's domain `User`.
158
+ Linking an app's `User` to the Auth identity (e.g. a `relationship({ ref:
159
+ 'AuthUser' })` the app declares) is the application's concern. See the
160
+ [Authentication guide](../../docs/content/guides/authentication.md) (“Adopting an
161
+ existing better-auth installation”) for the end-to-end migrator walkthrough.
162
+
66
163
  ### Session Provider
67
164
 
68
165
  Better-auth provides session to context via custom `prismaClientConstructor`:
@@ -78,7 +175,7 @@ const context = createContext(config, prisma, session)
78
175
  Control which User fields appear in session:
79
176
 
80
177
  ```typescript
81
- authConfig({ sessionFields: ['userId', 'email', 'name', 'role'] })
178
+ authPlugin({ sessionFields: ['userId', 'email', 'name', 'role'] })
82
179
  // Access in access control:
83
180
  access: {
84
181
  operation: {
@@ -110,7 +207,7 @@ declare module '@opensaas/stack-core' {
110
207
  **Step 2: Ensure fields match your sessionFields configuration**
111
208
 
112
209
  ```typescript
113
- authConfig({
210
+ authPlugin({
114
211
  sessionFields: ['userId', 'email', 'name', 'role'],
115
212
  extendUserList: {
116
213
  fields: {
@@ -153,7 +250,7 @@ if (context.session?.email) {
153
250
  Add custom fields to User:
154
251
 
155
252
  ```typescript
156
- authConfig({
253
+ authPlugin({
157
254
  extendUserList: {
158
255
  fields: {
159
256
  role: select({ options: [{ label: 'User', value: 'user' }] }),
@@ -196,10 +293,11 @@ export const auth = createAuth(config, rawOpensaasContext)
196
293
 
197
294
  ```typescript
198
295
  // 1. Config
199
- export default withAuth(
200
- config({ db: {...}, lists: {...} }),
201
- authConfig({ emailAndPassword: { enabled: true } })
202
- )
296
+ export default config({
297
+ db: {...},
298
+ lists: {...},
299
+ plugins: [authPlugin({ emailAndPassword: { enabled: true } })],
300
+ })
203
301
 
204
302
  // 2. Server (lib/auth.ts)
205
303
  export const auth = createAuth(config)
@@ -235,7 +333,7 @@ Post: list({
235
333
  ### OAuth Providers
236
334
 
237
335
  ```typescript
238
- authConfig({
336
+ authPlugin({
239
337
  socialProviders: {
240
338
  github: {
241
339
  clientId: process.env.GITHUB_CLIENT_ID!,
@@ -253,7 +351,7 @@ authConfig({
253
351
  Session type is inferred from `sessionFields`:
254
352
 
255
353
  ```typescript
256
- authConfig({ sessionFields: ['userId', 'email', 'role'] })
354
+ authPlugin({ sessionFields: ['userId', 'email', 'role'] })
257
355
  // session: { userId: string, email: string, role: string } | null
258
356
  ```
259
357
 
@@ -11,8 +11,7 @@ This document summarizes the complete better-auth integration for the OpenSaas S
11
11
  **Exports:**
12
12
 
13
13
  - **Main** (`@opensaas/stack-auth`):
14
- - `withAuth()` - Config wrapper that adds auth lists
15
- - `authConfig()` - Auth configuration builder
14
+ - `authPlugin()` - Plugin added to `config({ plugins: [...] })` that adds auth lists and configures Better-auth
16
15
  - `getAuthLists()` - Get all auth list definitions
17
16
 
18
17
  - **Server** (`@opensaas/stack-auth/server`):
@@ -112,20 +111,21 @@ A complete working example showing:
112
111
 
113
112
  ```typescript
114
113
  // opensaas.config.ts
115
- import { withAuth, authConfig } from '@opensaas/stack-auth'
114
+ import { config } from '@opensaas/stack-core'
115
+ import { authPlugin } from '@opensaas/stack-auth'
116
116
 
117
- export default withAuth(
118
- config({
119
- db: { provider: 'sqlite', url: 'file:./dev.db' },
120
- lists: {
121
- /* your custom lists */
122
- },
123
- }),
124
- authConfig({
125
- emailAndPassword: { enabled: true },
126
- sessionFields: ['userId', 'email', 'name'],
127
- }),
128
- )
117
+ export default config({
118
+ db: { provider: 'sqlite', url: 'file:./dev.db' },
119
+ lists: {
120
+ /* your custom lists */
121
+ },
122
+ plugins: [
123
+ authPlugin({
124
+ emailAndPassword: { enabled: true },
125
+ sessionFields: ['userId', 'email', 'name'],
126
+ }),
127
+ ],
128
+ })
129
129
  ```
130
130
 
131
131
  ### Step 2: Generate
@@ -194,7 +194,7 @@ No manual User model, no manual session handling, no manual auth routes. Everyth
194
194
  ### 2. Type-Safe Sessions
195
195
 
196
196
  ```typescript
197
- authConfig({
197
+ authPlugin({
198
198
  sessionFields: ['userId', 'email', 'name', 'role'],
199
199
  })
200
200
 
@@ -205,7 +205,7 @@ authConfig({
205
205
  ### 3. Extensible User Model
206
206
 
207
207
  ```typescript
208
- authConfig({
208
+ authPlugin({
209
209
  extendUserList: {
210
210
  fields: {
211
211
  role: select({ options: [...] }),
@@ -236,7 +236,7 @@ All forms accept custom props and callbacks:
236
236
  Configure what you need:
237
237
 
238
238
  ```typescript
239
- authConfig({
239
+ authPlugin({
240
240
  emailAndPassword: { enabled: true },
241
241
  emailVerification: { enabled: true },
242
242
  passwordReset: { enabled: true },
@@ -325,7 +325,8 @@ Potential additions:
325
325
  packages/auth/
326
326
  ├── src/
327
327
  │ ├── config/
328
- │ │ ├── index.ts # withAuth(), authConfig()
328
+ │ │ ├── index.ts # normalizeAuthConfig()
329
+ │ │ ├── plugin.ts # authPlugin()
329
330
  │ │ └── types.ts # Auth config types
330
331
  │ ├── lists/
331
332
  │ │ └── index.ts # User, Session, Account, Verification
@@ -366,7 +367,7 @@ Better-auth supports cookie caching to reduce database queries:
366
367
 
367
368
  ```typescript
368
369
  // Future enhancement
369
- authConfig({
370
+ authPlugin({
370
371
  session: {
371
372
  cookieCaching: true, // Validate at cookie level
372
373
  },
package/README.md CHANGED
@@ -23,30 +23,30 @@ pnpm add @opensaas/stack-auth better-auth
23
23
 
24
24
  ### 1. Update Your Config
25
25
 
26
- Wrap your OpenSaas config with `withAuth()`:
26
+ Add `authPlugin()` to your config's `plugins` array:
27
27
 
28
28
  ```typescript
29
29
  // opensaas.config.ts
30
30
  import { config } from '@opensaas/stack-core'
31
- import { withAuth, authConfig } from '@opensaas/stack-auth'
31
+ import { authPlugin } from '@opensaas/stack-auth'
32
32
 
33
- export default withAuth(
34
- config({
35
- db: {
36
- provider: 'sqlite',
37
- url: process.env.DATABASE_URL || 'file:./dev.db',
38
- },
39
- lists: {
40
- // Your custom lists here
41
- },
42
- }),
43
- authConfig({
44
- emailAndPassword: { enabled: true },
45
- emailVerification: { enabled: true },
46
- passwordReset: { enabled: true },
47
- sessionFields: ['userId', 'email', 'name'],
48
- }),
49
- )
33
+ export default config({
34
+ db: {
35
+ provider: 'sqlite',
36
+ url: process.env.DATABASE_URL || 'file:./dev.db',
37
+ },
38
+ lists: {
39
+ // Your custom lists here
40
+ },
41
+ plugins: [
42
+ authPlugin({
43
+ emailAndPassword: { enabled: true },
44
+ emailVerification: { enabled: true },
45
+ passwordReset: { enabled: true },
46
+ sessionFields: ['userId', 'email', 'name'],
47
+ }),
48
+ ],
49
+ })
50
50
  ```
51
51
 
52
52
  ### 2. Generate Schema and Push to Database
@@ -112,32 +112,33 @@ Sessions are now automatically available in your access control functions:
112
112
 
113
113
  ```typescript
114
114
  // opensaas.config.ts
115
- import { withAuth, authConfig } from '@opensaas/stack-auth'
116
-
117
- export default withAuth(
118
- config({
119
- lists: {
120
- Post: list({
121
- fields: { title: text(), content: text() },
122
- access: {
123
- operation: {
124
- // Session is automatically populated from better-auth
125
- create: ({ session }) => !!session,
126
- update: ({ session, item }) => {
127
- if (!session) return false
128
- return { authorId: { equals: session.userId } }
129
- },
115
+ import { config } from '@opensaas/stack-core'
116
+ import { authPlugin } from '@opensaas/stack-auth'
117
+
118
+ export default config({
119
+ lists: {
120
+ Post: list({
121
+ fields: { title: text(), content: text() },
122
+ access: {
123
+ operation: {
124
+ // Session is automatically populated from better-auth
125
+ create: ({ session }) => !!session,
126
+ update: ({ session, item }) => {
127
+ if (!session) return false
128
+ return { authorId: { equals: session.userId } }
130
129
  },
131
130
  },
132
- }),
133
- },
134
- }),
135
- authConfig({
136
- emailAndPassword: { enabled: true },
137
- // Session will contain: { userId, email, name }
138
- sessionFields: ['userId', 'email', 'name'],
139
- }),
140
- )
131
+ },
132
+ }),
133
+ },
134
+ plugins: [
135
+ authPlugin({
136
+ emailAndPassword: { enabled: true },
137
+ // Session will contain: { userId, email, name }
138
+ sessionFields: ['userId', 'email', 'name'],
139
+ }),
140
+ ],
141
+ })
141
142
  ```
142
143
 
143
144
  ## Configuration
@@ -145,7 +146,7 @@ export default withAuth(
145
146
  ### Auth Config Options
146
147
 
147
148
  ```typescript
148
- authConfig({
149
+ authPlugin({
149
150
  // Email/password authentication
150
151
  emailAndPassword: {
151
152
  enabled: true,
@@ -202,6 +203,39 @@ authConfig({
202
203
  })
203
204
  ```
204
205
 
206
+ ## Adopting an Existing better-auth Installation
207
+
208
+ Migrating a project that **already runs better-auth**? The plugin can adopt your
209
+ live tables rather than recreating them, so there's no destructive auth
210
+ migration. Use the `adoptBetterAuthTables()` recipe to preset the model/schema
211
+ knobs that match a standard separate-schema better-auth install:
212
+
213
+ ```typescript
214
+ import { authPlugin, adoptBetterAuthTables } from '@opensaas/stack-auth'
215
+
216
+ authPlugin({
217
+ // Defaults: AuthUser/AuthSession/AuthAccount/AuthVerification in the `auth`
218
+ // Postgres schema — pinned to your live table names + schema (@@map/@@schema).
219
+ ...adoptBetterAuthTables(),
220
+ emailAndPassword: { enabled: true },
221
+ })
222
+
223
+ // Customise when your live tables diverge from the defaults:
224
+ adoptBetterAuthTables({
225
+ schema: 'identity', // default: 'auth'
226
+ modelNamePrefix: 'BA', // default: 'Auth' (→ AuthUser/AuthSession/…)
227
+ fields: { user: { name: 'full_name' }, session: { userId: 'user_id' } },
228
+ })
229
+ ```
230
+
231
+ **App `User` vs the Auth identity.** The plugin models the **Auth identity** (the
232
+ better-auth user, e.g. `AuthUser`). It does not assume that list is your app's
233
+ own domain `User` — when the keys differ, your `User` is never extended or
234
+ overwritten. **Linking your domain `User` to the Auth identity is your app's
235
+ concern** (declare a `relationship({ ref: 'AuthUser' })` on your `User`). See the
236
+ [Authentication guide](https://stack.opensaas.au/docs/guides/authentication) for
237
+ the full migrator walkthrough and a Schema-parity (clean-diff) check.
238
+
205
239
  ## UI Components
206
240
 
207
241
  ### SignInForm
@@ -248,7 +282,7 @@ import { authClient } from '@/lib/auth-client'
248
282
 
249
283
  ## Auto-Generated Lists
250
284
 
251
- The following lists are automatically created when you use `withAuth()`:
285
+ The following lists are automatically created when you use `authPlugin()`:
252
286
 
253
287
  ### User
254
288
 
@@ -322,7 +356,7 @@ The following lists are automatically created when you use `withAuth()`:
322
356
  Add custom fields to the User model:
323
357
 
324
358
  ```typescript
325
- authConfig({
359
+ authPlugin({
326
360
  extendUserList: {
327
361
  fields: {
328
362
  role: select({
@@ -353,7 +387,7 @@ authConfig({
353
387
  Control which user fields are included in the session object:
354
388
 
355
389
  ```typescript
356
- authConfig({
390
+ authPlugin({
357
391
  sessionFields: ['userId', 'email', 'name', 'role'],
358
392
  })
359
393
 
@@ -434,7 +468,7 @@ See `examples/auth-demo` for a complete working example.
434
468
 
435
469
  ## How It Works
436
470
 
437
- 1. **withAuth()** merges auth lists (User, Session, Account, Verification) with your config
471
+ 1. **authPlugin()** merges auth lists (User, Session, Account, Verification) with your config
438
472
  2. **Generator** creates Prisma schema with all auth tables
439
473
  3. **Session Provider** uses better-auth to get current session
440
474
  4. **Context** includes session automatically in all access control functions