@opensaas/stack-auth 0.21.0 → 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 +90 -0
- package/CLAUDE.md +98 -0
- package/README.md +33 -0
- 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.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/package.json +2 -2
- 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 +66 -9
- 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 +31 -9
- 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
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure `better-auth config → Auth lists` derivation.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally free of side effects and plugin/runtime
|
|
5
|
+
* concerns: given the resolved better-auth model config (per-model `modelName`
|
|
6
|
+
* and `fields` column maps) plus any custom User fields, it produces the four
|
|
7
|
+
* OpenSaaS Auth lists (user/session/account/verification) with:
|
|
8
|
+
*
|
|
9
|
+
* - list keys taken from each model's `modelName`
|
|
10
|
+
* - a table `@@map` (list-level `db.map`) when the key differs from the
|
|
11
|
+
* default better-auth model name
|
|
12
|
+
* - field-level `@map` (`db.map`) for any better-auth field → column override
|
|
13
|
+
* - relationship refs between the auth lists wired to the *derived* keys
|
|
14
|
+
* (e.g. `Session.user → AuthUser.sessions`)
|
|
15
|
+
*
|
|
16
|
+
* When the developer supplies no `modelName`/`fields` overrides, the output is
|
|
17
|
+
* byte-for-byte the historical default set keyed `User`/`Session`/`Account`/
|
|
18
|
+
* `Verification` with the original field shapes — see the unit tests.
|
|
19
|
+
*
|
|
20
|
+
* `getAuthLists`/`convertBetterAuthSchema` (and the runtime user-key
|
|
21
|
+
* resolution) consume this module so derivation lives in exactly one place.
|
|
22
|
+
*/
|
|
23
|
+
import { list } from '@opensaas/stack-core';
|
|
24
|
+
import { text, timestamp, checkbox, relationship } from '@opensaas/stack-core/fields';
|
|
25
|
+
/**
|
|
26
|
+
* Default better-auth model names — used to decide whether a `@@map` is needed
|
|
27
|
+
* (only when the configured `modelName` differs from the default).
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_MODEL_NAMES = {
|
|
30
|
+
user: 'User',
|
|
31
|
+
session: 'Session',
|
|
32
|
+
account: 'Account',
|
|
33
|
+
verification: 'Verification',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Build the list-level `db` config (`timestamps` + `@@map` + `@@schema`) for a
|
|
37
|
+
* derived list.
|
|
38
|
+
*
|
|
39
|
+
* Always opts the list into auto-timestamps (`timestamps: true`). better-auth's
|
|
40
|
+
* adapter writes `createdAt`/`updatedAt` on every auth row and the schema
|
|
41
|
+
* converter returns `null` for those columns (it assumes the generator injects
|
|
42
|
+
* them). Now that auto-timestamps are OFF by default (ADR-0004), each derived
|
|
43
|
+
* Auth list must opt back in so the generated models keep those columns and
|
|
44
|
+
* better-auth keeps working.
|
|
45
|
+
*
|
|
46
|
+
* When the developer renames the model (e.g. `modelName: 'AuthUser'`), we also
|
|
47
|
+
* pin the physical table name to that model name via `@@map("AuthUser")` so the
|
|
48
|
+
* generated list adopts the developer's live table exactly. When a `schema` is
|
|
49
|
+
* configured (plugin-level or per-model), the list is placed in that Postgres
|
|
50
|
+
* schema via `@@schema(...)`.
|
|
51
|
+
*
|
|
52
|
+
* With no `modelName`/`schema` overrides we emit only `timestamps: true`,
|
|
53
|
+
* leaving the default `User`/`Session`/... table/schema output unchanged.
|
|
54
|
+
*/
|
|
55
|
+
function listDb(model, defaultModelName) {
|
|
56
|
+
const map = model.modelName !== defaultModelName ? model.modelName : undefined;
|
|
57
|
+
const schema = model.schema;
|
|
58
|
+
return {
|
|
59
|
+
timestamps: true,
|
|
60
|
+
...(map !== undefined ? { map } : {}),
|
|
61
|
+
...(schema !== undefined ? { schema } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the `db.map` (`@map` column override) for a better-auth field, or
|
|
66
|
+
* `undefined` when there is no override (so default output is unchanged).
|
|
67
|
+
*
|
|
68
|
+
* The field builders capture `options.db` in a closure when generating Prisma
|
|
69
|
+
* types, so the column map MUST be passed through the builder's `db` option
|
|
70
|
+
* rather than patched onto the returned field object.
|
|
71
|
+
*/
|
|
72
|
+
function fieldDb(fieldName, fields) {
|
|
73
|
+
const column = fields[fieldName];
|
|
74
|
+
return column ? { map: column } : undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the foreign-key `db` config for a `user` relationship, honouring a
|
|
78
|
+
* `userId` column override from the better-auth `fields` map.
|
|
79
|
+
*/
|
|
80
|
+
function userForeignKeyDb(fields) {
|
|
81
|
+
const column = fields.userId;
|
|
82
|
+
return column ? { foreignKey: { map: column } } : undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create the Auth user list, applying derived field column maps + table map and
|
|
86
|
+
* wiring the session/account relationships to the derived keys.
|
|
87
|
+
*/
|
|
88
|
+
function createUserList(model, keys, userConfig) {
|
|
89
|
+
const f = model.fields;
|
|
90
|
+
return list({
|
|
91
|
+
fields: {
|
|
92
|
+
name: text({ validation: { isRequired: true }, db: fieldDb('name', f) }),
|
|
93
|
+
email: text({
|
|
94
|
+
validation: { isRequired: true },
|
|
95
|
+
isIndexed: 'unique',
|
|
96
|
+
db: fieldDb('email', f),
|
|
97
|
+
}),
|
|
98
|
+
emailVerified: checkbox({ defaultValue: false, db: fieldDb('emailVerified', f) }),
|
|
99
|
+
image: text({ db: fieldDb('image', f) }),
|
|
100
|
+
// Relationships to the other auth lists — refs follow the derived keys.
|
|
101
|
+
sessions: relationship({ ref: `${keys.session}.user`, many: true }),
|
|
102
|
+
accounts: relationship({ ref: `${keys.account}.user`, many: true }),
|
|
103
|
+
// Custom fields from user config
|
|
104
|
+
...(userConfig.fields || {}),
|
|
105
|
+
},
|
|
106
|
+
db: listDb(model, DEFAULT_MODEL_NAMES.user),
|
|
107
|
+
access: userConfig.access || {
|
|
108
|
+
operation: {
|
|
109
|
+
query: () => true,
|
|
110
|
+
create: () => true,
|
|
111
|
+
update: ({ session, item }) => {
|
|
112
|
+
if (!session)
|
|
113
|
+
return false;
|
|
114
|
+
const userId = session.userId;
|
|
115
|
+
const itemId = item?.id;
|
|
116
|
+
return userId === itemId;
|
|
117
|
+
},
|
|
118
|
+
delete: ({ session, item }) => {
|
|
119
|
+
if (!session)
|
|
120
|
+
return false;
|
|
121
|
+
const userId = session.userId;
|
|
122
|
+
const itemId = item?.id;
|
|
123
|
+
return userId === itemId;
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
hooks: userConfig.hooks,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create the Auth session list.
|
|
132
|
+
*/
|
|
133
|
+
function createSessionList(model, keys) {
|
|
134
|
+
const f = model.fields;
|
|
135
|
+
return list({
|
|
136
|
+
fields: {
|
|
137
|
+
token: text({
|
|
138
|
+
validation: { isRequired: true },
|
|
139
|
+
isIndexed: 'unique',
|
|
140
|
+
db: fieldDb('token', f),
|
|
141
|
+
}),
|
|
142
|
+
expiresAt: timestamp({ db: fieldDb('expiresAt', f) }),
|
|
143
|
+
ipAddress: text({ db: fieldDb('ipAddress', f) }),
|
|
144
|
+
userAgent: text({ db: fieldDb('userAgent', f) }),
|
|
145
|
+
user: relationship({
|
|
146
|
+
ref: `${keys.user}.sessions`,
|
|
147
|
+
db: userForeignKeyDb(f),
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
db: listDb(model, DEFAULT_MODEL_NAMES.session),
|
|
151
|
+
access: {
|
|
152
|
+
operation: {
|
|
153
|
+
query: ({ session }) => {
|
|
154
|
+
if (!session)
|
|
155
|
+
return false;
|
|
156
|
+
const userId = session.userId;
|
|
157
|
+
if (!userId)
|
|
158
|
+
return false;
|
|
159
|
+
return {
|
|
160
|
+
user: { id: { equals: userId } },
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
create: () => true,
|
|
164
|
+
update: () => false,
|
|
165
|
+
delete: ({ session, item }) => {
|
|
166
|
+
if (!session)
|
|
167
|
+
return false;
|
|
168
|
+
const userId = session.userId;
|
|
169
|
+
const itemUserId = item?.user?.id;
|
|
170
|
+
return userId === itemUserId;
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create the Auth account list.
|
|
178
|
+
*/
|
|
179
|
+
function createAccountList(model, keys) {
|
|
180
|
+
const f = model.fields;
|
|
181
|
+
return list({
|
|
182
|
+
fields: {
|
|
183
|
+
accountId: text({ validation: { isRequired: true }, db: fieldDb('accountId', f) }),
|
|
184
|
+
providerId: text({ validation: { isRequired: true }, db: fieldDb('providerId', f) }),
|
|
185
|
+
user: relationship({
|
|
186
|
+
ref: `${keys.user}.accounts`,
|
|
187
|
+
db: userForeignKeyDb(f),
|
|
188
|
+
}),
|
|
189
|
+
accessToken: text({ db: fieldDb('accessToken', f) }),
|
|
190
|
+
refreshToken: text({ db: fieldDb('refreshToken', f) }),
|
|
191
|
+
accessTokenExpiresAt: timestamp({ db: fieldDb('accessTokenExpiresAt', f) }),
|
|
192
|
+
refreshTokenExpiresAt: timestamp({ db: fieldDb('refreshTokenExpiresAt', f) }),
|
|
193
|
+
scope: text({ db: fieldDb('scope', f) }),
|
|
194
|
+
idToken: text({ db: fieldDb('idToken', f) }),
|
|
195
|
+
password: text({ db: fieldDb('password', f) }),
|
|
196
|
+
},
|
|
197
|
+
db: listDb(model, DEFAULT_MODEL_NAMES.account),
|
|
198
|
+
access: {
|
|
199
|
+
operation: {
|
|
200
|
+
query: ({ session }) => {
|
|
201
|
+
if (!session)
|
|
202
|
+
return false;
|
|
203
|
+
const userId = session.userId;
|
|
204
|
+
if (!userId)
|
|
205
|
+
return false;
|
|
206
|
+
return {
|
|
207
|
+
user: { id: { equals: userId } },
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
create: () => true,
|
|
211
|
+
update: ({ session, item }) => {
|
|
212
|
+
if (!session)
|
|
213
|
+
return false;
|
|
214
|
+
const userId = session.userId;
|
|
215
|
+
const itemUserId = item?.user?.id;
|
|
216
|
+
return userId === itemUserId;
|
|
217
|
+
},
|
|
218
|
+
delete: ({ session, item }) => {
|
|
219
|
+
if (!session)
|
|
220
|
+
return false;
|
|
221
|
+
const userId = session.userId;
|
|
222
|
+
const itemUserId = item?.user?.id;
|
|
223
|
+
return userId === itemUserId;
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Create the Auth verification list.
|
|
231
|
+
*/
|
|
232
|
+
function createVerificationList(model) {
|
|
233
|
+
const f = model.fields;
|
|
234
|
+
return list({
|
|
235
|
+
fields: {
|
|
236
|
+
identifier: text({ validation: { isRequired: true }, db: fieldDb('identifier', f) }),
|
|
237
|
+
value: text({ validation: { isRequired: true }, db: fieldDb('value', f) }),
|
|
238
|
+
expiresAt: timestamp({ db: fieldDb('expiresAt', f) }),
|
|
239
|
+
},
|
|
240
|
+
db: listDb(model, DEFAULT_MODEL_NAMES.verification),
|
|
241
|
+
access: {
|
|
242
|
+
operation: {
|
|
243
|
+
query: () => false,
|
|
244
|
+
create: () => true,
|
|
245
|
+
update: () => false,
|
|
246
|
+
delete: () => true,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Derive the OpenSaaS Auth lists from the resolved better-auth model config.
|
|
253
|
+
*
|
|
254
|
+
* @param models - Resolved better-auth per-model config (modelName + field column maps)
|
|
255
|
+
* @param userConfig - Extra User-list fields/access/hooks supplied via `extendUserList`
|
|
256
|
+
* @returns The derived list keys and the four Auth list configs keyed by those keys
|
|
257
|
+
*/
|
|
258
|
+
export function deriveAuthLists(models, userConfig = {}) {
|
|
259
|
+
const keys = {
|
|
260
|
+
user: models.user.modelName,
|
|
261
|
+
session: models.session.modelName,
|
|
262
|
+
account: models.account.modelName,
|
|
263
|
+
verification: models.verification.modelName,
|
|
264
|
+
};
|
|
265
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ListConfig must accept any TypeInfo
|
|
266
|
+
const lists = {
|
|
267
|
+
[keys.user]: createUserList(models.user, keys, userConfig),
|
|
268
|
+
[keys.session]: createSessionList(models.session, keys),
|
|
269
|
+
[keys.account]: createAccountList(models.account, keys),
|
|
270
|
+
[keys.verification]: createVerificationList(models.verification),
|
|
271
|
+
};
|
|
272
|
+
return { keys, lists };
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=derive-auth-lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-auth-lists.js","sourceRoot":"","sources":["../../src/config/derive-auth-lists.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EAEV,oBAAoB,EAMrB,MAAM,YAAY,CAAA;AAmDnB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,oBAAoB,CA+D5E;AAED,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAA;AAChD,cAAc,YAAY,CAAA"}
|
package/dist/config/index.js
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default better-auth model names. Used when the developer does not override
|
|
3
|
+
* `modelName`, preserving the historical `User`/`Session`/`Account`/`Verification`
|
|
4
|
+
* keys exactly.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_MODEL_NAMES = {
|
|
7
|
+
user: 'User',
|
|
8
|
+
session: 'Session',
|
|
9
|
+
account: 'Account',
|
|
10
|
+
verification: 'Verification',
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a single better-auth model config block into its normalized form,
|
|
14
|
+
* falling back to the better-auth default model name and an empty column map.
|
|
15
|
+
* The model's Postgres schema is the per-model `schema` override when present,
|
|
16
|
+
* otherwise the plugin-level `schema` default (or `undefined` for `public`).
|
|
17
|
+
*/
|
|
18
|
+
function normalizeModelConfig(config, defaultModelName, defaultSchema) {
|
|
19
|
+
return {
|
|
20
|
+
modelName: config?.modelName || defaultModelName,
|
|
21
|
+
fields: config?.fields || {},
|
|
22
|
+
schema: config?.schema ?? defaultSchema,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the better-auth model config for all four auth models.
|
|
27
|
+
* `defaultSchema` is the plugin-level `schema` applied to every model unless a
|
|
28
|
+
* per-model `schema` override is given.
|
|
29
|
+
*/
|
|
30
|
+
function normalizeAuthModels(config) {
|
|
31
|
+
const defaultSchema = config.schema;
|
|
32
|
+
return {
|
|
33
|
+
user: normalizeModelConfig(config.user, DEFAULT_MODEL_NAMES.user, defaultSchema),
|
|
34
|
+
session: normalizeModelConfig(config.session, DEFAULT_MODEL_NAMES.session, defaultSchema),
|
|
35
|
+
account: normalizeModelConfig(config.account, DEFAULT_MODEL_NAMES.account, defaultSchema),
|
|
36
|
+
verification: normalizeModelConfig(config.verification, DEFAULT_MODEL_NAMES.verification, defaultSchema),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
1
39
|
/**
|
|
2
40
|
* Normalize auth configuration with defaults
|
|
3
41
|
*/
|
|
@@ -32,12 +70,17 @@ export function normalizeAuthConfig(config) {
|
|
|
32
70
|
};
|
|
33
71
|
// Session fields defaults
|
|
34
72
|
const sessionFields = config.sessionFields || ['userId', 'email', 'name'];
|
|
73
|
+
// Resolve better-auth per-model config (modelName + field column maps).
|
|
74
|
+
// Defaults preserve the historical User/Session/Account/Verification keys.
|
|
75
|
+
const models = normalizeAuthModels(config);
|
|
35
76
|
return {
|
|
36
77
|
emailAndPassword,
|
|
37
78
|
emailVerification,
|
|
38
79
|
passwordReset,
|
|
39
80
|
socialProviders: config.socialProviders || {},
|
|
40
81
|
session,
|
|
82
|
+
models,
|
|
83
|
+
schema: config.schema,
|
|
41
84
|
sessionFields,
|
|
42
85
|
extendUserList: config.extendUserList || {},
|
|
43
86
|
sendEmail: config.sendEmail ||
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/config/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/config/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAA;AAKlE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAqJrD"}
|
package/dist/config/plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getDbKey } from '@opensaas/stack-core';
|
|
1
2
|
import { normalizeAuthConfig } from './index.js';
|
|
2
3
|
import { getAuthLists } from '../lists/index.js';
|
|
3
4
|
import { convertBetterAuthSchema } from '../server/schema-converter.js';
|
|
@@ -32,8 +33,12 @@ export function authPlugin(config) {
|
|
|
32
33
|
typeName: 'AuthRuntimeServices',
|
|
33
34
|
},
|
|
34
35
|
init: async (context) => {
|
|
35
|
-
//
|
|
36
|
-
|
|
36
|
+
// Derive the auth lists from the better-auth model config (modelName +
|
|
37
|
+
// field column maps). With no overrides this yields the historical
|
|
38
|
+
// User/Session/Account/Verification keys; with overrides (e.g.
|
|
39
|
+
// user.modelName: 'AuthUser') the lists are keyed and column-mapped to
|
|
40
|
+
// match the developer's live better-auth tables.
|
|
41
|
+
const authLists = getAuthLists(normalized.extendUserList, normalized.models);
|
|
37
42
|
// Extract additional lists from Better Auth plugins
|
|
38
43
|
for (const plugin of normalized.betterAuthPlugins) {
|
|
39
44
|
if (plugin && typeof plugin === 'object' && 'schema' in plugin) {
|
|
@@ -58,10 +63,18 @@ export function authPlugin(config) {
|
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
|
-
// Add all auth lists
|
|
66
|
+
// Add all auth lists.
|
|
67
|
+
//
|
|
68
|
+
// The plugin only ever touches its OWN derived keys. When a developer
|
|
69
|
+
// renames the auth user model (e.g. user.modelName: 'AuthUser'), the
|
|
70
|
+
// derived key is 'AuthUser' and an app's separate 'User' list is left
|
|
71
|
+
// untouched — the plugin never extends/overwrites a list it didn't
|
|
72
|
+
// derive. Extending only kicks in when an existing list shares the
|
|
73
|
+
// derived key (e.g. the default 'User'), which is the intended
|
|
74
|
+
// "merge auth fields into my User" behaviour.
|
|
62
75
|
for (const [listName, listConfig] of Object.entries(authLists)) {
|
|
63
76
|
if (context.config.lists[listName]) {
|
|
64
|
-
//
|
|
77
|
+
// A list already exists under this derived key — merge auth fields in.
|
|
65
78
|
context.extendList(listName, {
|
|
66
79
|
fields: listConfig.fields,
|
|
67
80
|
hooks: listConfig.hooks,
|
|
@@ -78,7 +91,40 @@ export function authPlugin(config) {
|
|
|
78
91
|
// Access at runtime via: config._pluginData.auth
|
|
79
92
|
context.setPluginData('auth', normalized);
|
|
80
93
|
},
|
|
94
|
+
beforeGenerate: (generationConfig) => {
|
|
95
|
+
// Collect every schema the Auth lists are placed in (per-model schema,
|
|
96
|
+
// else the plugin-level schema). When none is configured the Auth lists
|
|
97
|
+
// stay in the default `public` schema and we leave the config untouched —
|
|
98
|
+
// the greenfield default Prisma schema is unchanged (no `schemas`, no
|
|
99
|
+
// `previewFeatures`, no `@@schema`).
|
|
100
|
+
const authSchemas = Array.from(new Set(Object.values(normalized.models)
|
|
101
|
+
.map((model) => model.schema)
|
|
102
|
+
.filter((schema) => Boolean(schema))));
|
|
103
|
+
if (authSchemas.length === 0) {
|
|
104
|
+
return generationConfig;
|
|
105
|
+
}
|
|
106
|
+
// Multi-schema Prisma requires the datasource to list every schema in use
|
|
107
|
+
// AND every model to carry an `@@schema`. Merge the auth schema(s) into the
|
|
108
|
+
// datasource `schemas` array (always including `public` for the app's own
|
|
109
|
+
// lists), and default any list without an explicit `db.schema` to `public`
|
|
110
|
+
// so the generated multi-schema schema is coherent and valid.
|
|
111
|
+
const schemas = Array.from(new Set(['public', ...(generationConfig.db.schemas ?? []), ...authSchemas]));
|
|
112
|
+
const lists = Object.fromEntries(Object.entries(generationConfig.lists).map(([listKey, listConfig]) => {
|
|
113
|
+
if (listConfig.db?.schema) {
|
|
114
|
+
return [listKey, listConfig];
|
|
115
|
+
}
|
|
116
|
+
return [listKey, { ...listConfig, db: { ...listConfig.db, schema: 'public' } }];
|
|
117
|
+
}));
|
|
118
|
+
return {
|
|
119
|
+
...generationConfig,
|
|
120
|
+
db: { ...generationConfig.db, schemas },
|
|
121
|
+
lists,
|
|
122
|
+
};
|
|
123
|
+
},
|
|
81
124
|
runtime: (context) => {
|
|
125
|
+
// Resolve the user list's context.db key from the configured user model.
|
|
126
|
+
// context.db is keyed camelCase, so 'User' -> 'user', 'AuthUser' -> 'authUser'.
|
|
127
|
+
const userDbKey = getDbKey(normalized.models.user.modelName);
|
|
82
128
|
// Provide auth-related utilities at runtime
|
|
83
129
|
return {
|
|
84
130
|
/**
|
|
@@ -86,9 +132,7 @@ export function authPlugin(config) {
|
|
|
86
132
|
* Uses the access-controlled context to fetch user data
|
|
87
133
|
*/
|
|
88
134
|
getUser: async (userId) => {
|
|
89
|
-
|
|
90
|
-
const userListKey = 'user'; // TODO: Make this configurable based on list name
|
|
91
|
-
return await context.db[userListKey].findUnique({
|
|
135
|
+
return await context.db[userDbKey].findUnique({
|
|
92
136
|
where: { id: userId },
|
|
93
137
|
});
|
|
94
138
|
},
|
|
@@ -100,8 +144,7 @@ export function authPlugin(config) {
|
|
|
100
144
|
if (!context.session?.userId) {
|
|
101
145
|
return null;
|
|
102
146
|
}
|
|
103
|
-
|
|
104
|
-
return await context.db[userListKey].findUnique({
|
|
147
|
+
return await context.db[userDbKey].findUnique({
|
|
105
148
|
where: { id: context.session.userId },
|
|
106
149
|
});
|
|
107
150
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/config/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/config/plugin.ts"],"names":[],"mappings":"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"}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -75,6 +75,54 @@ export type SessionConfig = {
|
|
|
75
75
|
*/
|
|
76
76
|
updateAge?: boolean;
|
|
77
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Per-model better-auth configuration block.
|
|
80
|
+
*
|
|
81
|
+
* Mirrors better-auth's own `BetterAuthDBOptions` (the `user`/`session`/
|
|
82
|
+
* `account`/`verification` config a developer already writes): `modelName`
|
|
83
|
+
* renames the table/list and `fields` maps individual better-auth field names
|
|
84
|
+
* to database column names. The auth plugin derives its Auth lists from this
|
|
85
|
+
* config so the generated lists carry the same keys and column maps as the
|
|
86
|
+
* developer's live better-auth tables.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* authPlugin({
|
|
91
|
+
* user: { modelName: 'AuthUser', fields: { name: 'full_name' } },
|
|
92
|
+
* session: { modelName: 'AuthSession' },
|
|
93
|
+
* })
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export type AuthModelConfig = {
|
|
97
|
+
/**
|
|
98
|
+
* The table/list name for this model.
|
|
99
|
+
* Becomes the OpenSaaS list key (and Prisma model name) and the table `@@map`.
|
|
100
|
+
* @default the default better-auth model name (e.g. 'User', 'Session')
|
|
101
|
+
*/
|
|
102
|
+
modelName?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Map better-auth field names to database column names.
|
|
105
|
+
* Each entry generates a `@map("column")` on the derived field.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* fields: { name: 'full_name', emailVerified: 'email_verified' }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
fields?: Record<string, string>;
|
|
113
|
+
/**
|
|
114
|
+
* Database schema (Postgres) for this auth model.
|
|
115
|
+
* Generates a `@@schema("...")` on the derived list, overriding the
|
|
116
|
+
* plugin-level {@link AuthConfig.schema} for this one model.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* // Place the verification table in a different schema from the rest
|
|
121
|
+
* verification: { schema: 'auth_internal' }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
schema?: string;
|
|
125
|
+
};
|
|
78
126
|
/**
|
|
79
127
|
* Auth configuration options
|
|
80
128
|
*/
|
|
@@ -102,9 +150,56 @@ export type AuthConfig = {
|
|
|
102
150
|
*/
|
|
103
151
|
socialProviders?: SocialProvidersConfig;
|
|
104
152
|
/**
|
|
105
|
-
* Session configuration
|
|
153
|
+
* Session configuration.
|
|
154
|
+
*
|
|
155
|
+
* Carries session expiry settings as well as the better-auth `session` model
|
|
156
|
+
* config (`modelName` + field column `fields` maps) used to derive the Auth
|
|
157
|
+
* session list.
|
|
158
|
+
*/
|
|
159
|
+
session?: SessionConfig & AuthModelConfig;
|
|
160
|
+
/**
|
|
161
|
+
* better-auth `user` model configuration (modelName + field column maps).
|
|
162
|
+
* Used to derive the Auth user list's key, table `@@map`, and field `@map`s.
|
|
163
|
+
*
|
|
164
|
+
* Custom fields beyond the better-auth basics are added via `extendUserList`.
|
|
106
165
|
*/
|
|
107
|
-
|
|
166
|
+
user?: AuthModelConfig;
|
|
167
|
+
/**
|
|
168
|
+
* better-auth `account` model configuration (modelName + field column maps).
|
|
169
|
+
*/
|
|
170
|
+
account?: AuthModelConfig;
|
|
171
|
+
/**
|
|
172
|
+
* better-auth `verification` model configuration (modelName + field column maps).
|
|
173
|
+
*/
|
|
174
|
+
verification?: AuthModelConfig;
|
|
175
|
+
/**
|
|
176
|
+
* Database schema (Postgres) for the generated Auth lists.
|
|
177
|
+
*
|
|
178
|
+
* When set, all four Auth lists (user/session/account/verification) are placed
|
|
179
|
+
* in this schema via `@@schema(...)`, and the stack's multi-schema support is
|
|
180
|
+
* wired automatically: the datasource `schemas` array gains this schema (plus
|
|
181
|
+
* `public`) and the `multiSchema` preview feature is enabled. A per-model
|
|
182
|
+
* {@link AuthModelConfig.schema} overrides this for an individual list.
|
|
183
|
+
*
|
|
184
|
+
* Useful for adopting an existing separate-schema better-auth installation
|
|
185
|
+
* (e.g. an `auth` Postgres schema) so the generated lists diff clean against
|
|
186
|
+
* the live tables. When unset, the Auth lists stay in the default `public`
|
|
187
|
+
* schema and no `@@schema` is emitted (greenfield default unchanged).
|
|
188
|
+
*
|
|
189
|
+
* Only applies to the `postgresql` provider.
|
|
190
|
+
*
|
|
191
|
+
* @example Adopt an `auth`-schema better-auth install
|
|
192
|
+
* ```typescript
|
|
193
|
+
* authPlugin({
|
|
194
|
+
* schema: 'auth',
|
|
195
|
+
* user: { modelName: 'AuthUser' },
|
|
196
|
+
* session: { modelName: 'AuthSession' },
|
|
197
|
+
* account: { modelName: 'AuthAccount' },
|
|
198
|
+
* verification: { modelName: 'AuthVerification' },
|
|
199
|
+
* })
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
schema?: string;
|
|
108
203
|
/**
|
|
109
204
|
* Which fields to include in the session object
|
|
110
205
|
* This determines what data is available in access control functions
|
|
@@ -194,14 +289,46 @@ export type AuthConfig = {
|
|
|
194
289
|
max?: number;
|
|
195
290
|
};
|
|
196
291
|
};
|
|
292
|
+
/**
|
|
293
|
+
* Resolved per-model auth configuration after normalization.
|
|
294
|
+
* Always carries a concrete `modelName` (the developer's override or the
|
|
295
|
+
* better-auth default) and a (possibly empty) `fields` column map. `schema`
|
|
296
|
+
* carries the resolved Postgres schema for the model (per-model override, else
|
|
297
|
+
* the plugin-level schema, else `undefined` for the default `public` schema).
|
|
298
|
+
*/
|
|
299
|
+
export type NormalizedAuthModelConfig = {
|
|
300
|
+
modelName: string;
|
|
301
|
+
fields: Record<string, string>;
|
|
302
|
+
schema?: string;
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* Resolved auth model configuration for all four better-auth models.
|
|
306
|
+
* Consumed by the Auth-list derivation and the runtime user-key resolution.
|
|
307
|
+
*/
|
|
308
|
+
export type NormalizedAuthModels = {
|
|
309
|
+
user: NormalizedAuthModelConfig;
|
|
310
|
+
session: NormalizedAuthModelConfig;
|
|
311
|
+
account: NormalizedAuthModelConfig;
|
|
312
|
+
verification: NormalizedAuthModelConfig;
|
|
313
|
+
};
|
|
197
314
|
/**
|
|
198
315
|
* Internal normalized auth configuration
|
|
199
316
|
* Used after parsing user config
|
|
200
317
|
*/
|
|
201
|
-
export type NormalizedAuthConfig = Required<Omit<AuthConfig, 'emailAndPassword' | 'emailVerification' | 'passwordReset' | 'betterAuthPlugins' | 'rateLimit'>> & {
|
|
318
|
+
export type NormalizedAuthConfig = Required<Omit<AuthConfig, 'emailAndPassword' | 'emailVerification' | 'passwordReset' | 'betterAuthPlugins' | 'rateLimit' | 'session' | 'user' | 'account' | 'verification' | 'schema'>> & {
|
|
202
319
|
emailAndPassword: Required<EmailPasswordConfig>;
|
|
203
320
|
emailVerification: Required<EmailVerificationConfig>;
|
|
204
321
|
passwordReset: Required<PasswordResetConfig>;
|
|
322
|
+
/** Resolved session expiry settings (model config lives under `models.session`). */
|
|
323
|
+
session: Required<SessionConfig>;
|
|
324
|
+
/** Resolved better-auth model config (modelName + field column maps + schema) for all auth models. */
|
|
325
|
+
models: NormalizedAuthModels;
|
|
326
|
+
/**
|
|
327
|
+
* Plugin-level Postgres schema for the Auth lists, if any. Resolved per-model
|
|
328
|
+
* schemas live on `models.<model>.schema`; this is the unresolved plugin-level
|
|
329
|
+
* default (used to wire the datasource `schemas` array during generation).
|
|
330
|
+
*/
|
|
331
|
+
schema?: string;
|
|
205
332
|
betterAuthPlugins: any[];
|
|
206
333
|
rateLimit?: {
|
|
207
334
|
enabled: boolean;
|