@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +122 -0
- package/CLAUDE.md +115 -17
- package/INTEGRATION_SUMMARY.md +21 -20
- package/README.md +82 -48
- package/dist/config/adopt-better-auth-tables.d.ts +107 -0
- package/dist/config/adopt-better-auth-tables.d.ts.map +1 -0
- package/dist/config/adopt-better-auth-tables.js +70 -0
- package/dist/config/adopt-better-auth-tables.js.map +1 -0
- package/dist/config/derive-auth-lists.d.ts +50 -0
- package/dist/config/derive-auth-lists.d.ts.map +1 -0
- package/dist/config/derive-auth-lists.js +274 -0
- package/dist/config/derive-auth-lists.js.map +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +43 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/plugin.d.ts +1 -1
- package/dist/config/plugin.d.ts.map +1 -1
- package/dist/config/plugin.js +52 -9
- package/dist/config/plugin.js.map +1 -1
- package/dist/config/types.d.ts +130 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/lists/index.d.ts +17 -11
- package/dist/lists/index.d.ts.map +1 -1
- package/dist/lists/index.js +34 -208
- package/dist/lists/index.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +28 -7
- package/dist/server/index.js.map +1 -1
- package/dist/server/schema-converter.d.ts +1 -1
- package/dist/server/schema-converter.js +1 -1
- package/package.json +3 -3
- package/src/config/adopt-better-auth-tables.ts +146 -0
- package/src/config/derive-auth-lists.ts +323 -0
- package/src/config/index.ts +58 -0
- package/src/config/plugin.ts +67 -10
- package/src/config/types.ts +146 -3
- package/src/index.ts +13 -0
- package/src/lists/index.ts +42 -202
- package/src/server/index.ts +33 -10
- package/src/server/schema-converter.ts +1 -1
- package/tests/adopt-better-auth-tables.test.ts +183 -0
- package/tests/derive-auth-lists.test.ts +232 -0
- package/tests/plugin-derived-keys.test.ts +138 -0
- package/tests/plugin-schema-placement.test.ts +121 -0
- package/tsconfig.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
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/
|
|
16
|
+
### Config (`src/config/plugin.ts`)
|
|
17
17
|
|
|
18
|
-
- `
|
|
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
|
-
`
|
|
55
|
+
`authPlugin()` merges auth lists into your config:
|
|
57
56
|
|
|
58
57
|
```typescript
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
+
authPlugin({ sessionFields: ['userId', 'email', 'role'] })
|
|
257
355
|
// session: { userId: string, email: string, role: string } | null
|
|
258
356
|
```
|
|
259
357
|
|
package/INTEGRATION_SUMMARY.md
CHANGED
|
@@ -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
|
-
- `
|
|
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 {
|
|
114
|
+
import { config } from '@opensaas/stack-core'
|
|
115
|
+
import { authPlugin } from '@opensaas/stack-auth'
|
|
116
116
|
|
|
117
|
-
export default
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
lists
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 #
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
31
|
+
import { authPlugin } from '@opensaas/stack-auth'
|
|
32
32
|
|
|
33
|
-
export default
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
lists
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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 {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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. **
|
|
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
|