@opensaas/stack-auth 0.36.0 → 0.38.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 (47) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +146 -0
  3. package/CLAUDE.md +153 -9
  4. package/README.md +18 -7
  5. package/dist/config/adopt-better-auth-tables.d.ts +47 -0
  6. package/dist/config/adopt-better-auth-tables.d.ts.map +1 -1
  7. package/dist/config/adopt-better-auth-tables.js +29 -1
  8. package/dist/config/adopt-better-auth-tables.js.map +1 -1
  9. package/dist/config/derive-auth-lists.d.ts +7 -5
  10. package/dist/config/derive-auth-lists.d.ts.map +1 -1
  11. package/dist/config/derive-auth-lists.js +33 -34
  12. package/dist/config/derive-auth-lists.js.map +1 -1
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/index.js +41 -11
  15. package/dist/config/index.js.map +1 -1
  16. package/dist/config/plugin.d.ts.map +1 -1
  17. package/dist/config/plugin.js +39 -27
  18. package/dist/config/plugin.js.map +1 -1
  19. package/dist/config/types.d.ts +143 -28
  20. package/dist/config/types.d.ts.map +1 -1
  21. package/dist/server/build-better-auth-options.test.d.ts +2 -0
  22. package/dist/server/build-better-auth-options.test.d.ts.map +1 -0
  23. package/dist/server/build-better-auth-options.test.js +29 -0
  24. package/dist/server/build-better-auth-options.test.js.map +1 -0
  25. package/dist/server/index.d.ts +112 -8
  26. package/dist/server/index.d.ts.map +1 -1
  27. package/dist/server/index.js +284 -96
  28. package/dist/server/index.js.map +1 -1
  29. package/dist/server/schema-converter.d.ts +3 -3
  30. package/dist/server/schema-converter.d.ts.map +1 -1
  31. package/package.json +5 -5
  32. package/src/config/adopt-better-auth-tables.ts +70 -1
  33. package/src/config/derive-auth-lists.ts +37 -38
  34. package/src/config/index.ts +47 -12
  35. package/src/config/plugin.ts +40 -28
  36. package/src/config/types.ts +144 -27
  37. package/src/server/build-better-auth-options.test.ts +59 -0
  38. package/src/server/index.ts +470 -106
  39. package/src/server/schema-converter.ts +3 -3
  40. package/tests/adopt-better-auth-tables.test.ts +99 -0
  41. package/tests/config.test.ts +66 -8
  42. package/tests/derive-auth-lists.test.ts +79 -5
  43. package/tests/generated-fk-shape.test.ts +65 -0
  44. package/tests/plugin-derived-keys.test.ts +48 -0
  45. package/tests/server.test.ts +723 -0
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/vitest.config.ts +7 -1
@@ -1,5 +1,17 @@
1
1
  import type { ListConfig } from '@opensaas/stack-core';
2
+ import type { BetterAuthOptions, BetterAuthPlugin, User } from 'better-auth';
2
3
  import type { ExtendUserListConfig } from '../lists/index.js';
4
+ /**
5
+ * better-auth's own callback shape for `sendVerificationEmail`/
6
+ * `sendResetPassword` — `data` is exactly what better-auth passes (no stack
7
+ * abstraction layered on top), and `request` is the raw request that
8
+ * triggered it.
9
+ */
10
+ export type SendAuthEmail = (data: {
11
+ user: User;
12
+ url: string;
13
+ token: string;
14
+ }, request?: Request) => Promise<void>;
3
15
  /**
4
16
  * OAuth provider configuration
5
17
  */
@@ -29,10 +41,36 @@ export type EmailPasswordConfig = {
29
41
  */
30
42
  minPasswordLength?: number;
31
43
  /**
32
- * Require password confirmation
44
+ * Require password confirmation (a second "confirm password" field).
45
+ *
46
+ * There is no better-auth server-side equivalent — this is purely a UI
47
+ * concern. `createAuth()` does not read it. Pass it directly to the
48
+ * pre-built forms instead: `<SignUpForm requirePasswordConfirmation={...} />`
49
+ * / `<ResetPasswordForm requirePasswordConfirmation={...} />` (both default
50
+ * to `true`). Setting it here has no effect and `createAuth()` warns if it
51
+ * is configured.
52
+ *
33
53
  * @default true
34
54
  */
35
55
  requireConfirmation?: boolean;
56
+ /**
57
+ * Send a password reset email. Passed straight through to better-auth's own
58
+ * `emailAndPassword.sendResetPassword` — the stack does not wrap or
59
+ * reshape it. If not provided, reset emails are logged to console instead
60
+ * of sent.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * sendResetPassword: async ({ user, url }) => {
65
+ * await resend.emails.send({
66
+ * to: user.email,
67
+ * subject: 'Reset your password',
68
+ * html: `<a href="${url}">Reset your password</a>`,
69
+ * })
70
+ * }
71
+ * ```
72
+ */
73
+ sendResetPassword?: SendAuthEmail;
36
74
  };
37
75
  /**
38
76
  * Email verification configuration
@@ -49,6 +87,24 @@ export type EmailVerificationConfig = {
49
87
  * @default 86400 (24 hours)
50
88
  */
51
89
  tokenExpiration?: number;
90
+ /**
91
+ * Send a verification email. Passed straight through to better-auth's own
92
+ * `emailVerification.sendVerificationEmail` — the stack does not wrap or
93
+ * reshape it. If not provided, verification emails are logged to console
94
+ * instead of sent.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * sendVerificationEmail: async ({ user, url }) => {
99
+ * await resend.emails.send({
100
+ * to: user.email,
101
+ * subject: 'Verify your email',
102
+ * html: `<a href="${url}">Verify your email</a>`,
103
+ * })
104
+ * }
105
+ * ```
106
+ */
107
+ sendVerificationEmail?: SendAuthEmail;
52
108
  };
53
109
  /**
54
110
  * Password reset configuration
@@ -71,10 +127,12 @@ export type SessionConfig = {
71
127
  */
72
128
  expiresIn?: number;
73
129
  /**
74
- * Update session expiration on each request
75
- * @default true
130
+ * How often the session should be refreshed, in seconds. Passed straight
131
+ * through to better-auth's own `session.updateAge`. Set `false` to disable
132
+ * refresh entirely (the session expiry is then fixed at creation time).
133
+ * @default 86400 (1 day, matching better-auth's own default)
76
134
  */
77
- updateAge?: boolean;
135
+ updateAge?: number | false;
78
136
  };
79
137
  /**
80
138
  * Per-model better-auth configuration block.
@@ -138,10 +196,30 @@ export type AuthAccessConfig = {
138
196
  export type AuthModelConfig = {
139
197
  /**
140
198
  * The table/list name for this model.
141
- * Becomes the OpenSaaS list key (and Prisma model name) and the table `@@map`.
199
+ * Becomes the OpenSaaS list key and Prisma model name.
142
200
  * @default the default better-auth model name (e.g. 'User', 'Session')
143
201
  */
144
202
  modelName?: string;
203
+ /**
204
+ * The physical database table name for this model, independent of
205
+ * `modelName`. Generates a `@@map("...")` on the derived list.
206
+ *
207
+ * Lets a renamed list key (e.g. `modelName: 'AuthUser'`, to avoid colliding
208
+ * with an app's own domain `User`) still adopt a live table under a
209
+ * different name — most commonly better-auth's own default lowercase
210
+ * table names (`user`, `session`, `account`, `verification`).
211
+ *
212
+ * @default `modelName` when it differs from the better-auth default model
213
+ * name, otherwise unset (no `@@map`) — i.e. today's behaviour when this
214
+ * option is not set.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * // List key AuthUser, but the live table is still called `user`.
219
+ * user: { modelName: 'AuthUser', tableName: 'user' }
220
+ * ```
221
+ */
222
+ tableName?: string;
145
223
  /**
146
224
  * Map better-auth field names to database column names.
147
225
  * Each entry generates a `@map("column")` on the derived field.
@@ -243,8 +321,21 @@ export type AuthConfig = {
243
321
  */
244
322
  schema?: string;
245
323
  /**
246
- * Which fields to include in the session object
247
- * This determines what data is available in access control functions
324
+ * Which fields to include in the session object passed to access control
325
+ * functions a **flattened projection** of the resolved better-auth
326
+ * session, not the session's own shape. `getSessionFromAuth` (the
327
+ * implementation the scaffolded `getSession()` calls) resolves each name
328
+ * against a fixed precedence: a top-level key on the resolved session
329
+ * object, then the `user` object, then the `session` sub-object.
330
+ * `userId` is special-cased to the authenticated user's `id`.
331
+ *
332
+ * A `customSession` better-auth plugin fully replaces the resolved shape
333
+ * (it can nest fields anywhere, e.g. under its own custom key) —
334
+ * reconciling that shape against `sessionFields` is the application's job.
335
+ * A listed name that can't be resolved is omitted and warns once per
336
+ * field per process, naming what was checked, rather than silently
337
+ * becoming `undefined` in an access control function.
338
+ *
248
339
  * @default ['userId', 'email', 'name']
249
340
  *
250
341
  * @example
@@ -284,22 +375,6 @@ export type AuthConfig = {
284
375
  * narrower, User-specific override.
285
376
  */
286
377
  access?: AuthAccessConfig;
287
- /**
288
- * Custom email sending function for verification and password reset
289
- * If not provided, emails will be logged to console
290
- *
291
- * @example
292
- * ```typescript
293
- * sendEmail: async ({ to, subject, html }) => {
294
- * await resend.emails.send({ to, subject, html })
295
- * }
296
- * ```
297
- */
298
- sendEmail?: (params: {
299
- to: string;
300
- subject: string;
301
- html: string;
302
- }) => Promise<void>;
303
378
  /**
304
379
  * Additional Better Auth plugins to enable
305
380
  * Allows integrating any Better Auth plugin (MCP, 2FA, etc.)
@@ -313,7 +388,7 @@ export type AuthConfig = {
313
388
  * ]
314
389
  * ```
315
390
  */
316
- betterAuthPlugins?: any[];
391
+ betterAuthPlugins?: BetterAuthPlugin[];
317
392
  /**
318
393
  * Rate limiting configuration
319
394
  * Controls rate limiting for authentication endpoints
@@ -346,16 +421,56 @@ export type AuthConfig = {
346
421
  */
347
422
  max?: number;
348
423
  };
424
+ /**
425
+ * Escape hatch for better-auth options the stack doesn't model — typed as
426
+ * better-auth's own `BetterAuthOptions` so it stays in step with
427
+ * better-auth's surface without the stack re-declaring it. Deep-merged into
428
+ * the options `createAuth()` builds, applied LAST: a plain-object value at a
429
+ * given key merges recursively with whatever the stack already set there
430
+ * (so e.g. `session: { cookieCache: {...} }` adds alongside the stack's own
431
+ * `session.expiresIn`/`updateAge` rather than clobbering them); any other
432
+ * value (including arrays) replaces the stack's value outright. On a genuine
433
+ * key collision, this option wins.
434
+ *
435
+ * `database` and `plugins` are rejected — they're already the dedicated
436
+ * seams (the stack's `db` config, and `betterAuthPlugins` respectively) and
437
+ * accepting them here would create two unranked ways to set the same thing.
438
+ * So is `additionalFields` under `user`/`session`/`account`/`verification` —
439
+ * it has schema consequences (new columns) that a passthrough can't also
440
+ * apply to the generated Prisma schema; add fields to the derived list
441
+ * instead (`extendUserList` for the user model, or declare the list
442
+ * yourself for the others — see `packages/auth/CLAUDE.md`).
443
+ *
444
+ * The same options object is available standalone via
445
+ * `buildBetterAuthOptions()` (`@opensaas/stack-auth/server`) for apps that
446
+ * still need to hand-wire their own `betterAuth()` instance.
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * authPlugin({
451
+ * betterAuthOptions: {
452
+ * databaseHooks: { user: { create: { after: syncDomainUser } } },
453
+ * session: { cookieCache: { enabled: true, maxAge: 300 } },
454
+ * verification: { storeIdentifier: 'hashed' },
455
+ * baseURL: process.env.BETTER_AUTH_URL,
456
+ * },
457
+ * })
458
+ * ```
459
+ */
460
+ betterAuthOptions?: Partial<BetterAuthOptions>;
349
461
  };
350
462
  /**
351
463
  * Resolved per-model auth configuration after normalization.
352
464
  * Always carries a concrete `modelName` (the developer's override or the
353
- * better-auth default) and a (possibly empty) `fields` column map. `schema`
354
- * carries the resolved Postgres schema for the model (per-model override, else
355
- * the plugin-level schema, else `undefined` for the default `public` schema).
465
+ * better-auth default) and a (possibly empty) `fields` column map. `tableName`
466
+ * is the resolved physical table name `undefined` means no `@@map` is
467
+ * emitted (the list key doubles as the table name). `schema` carries the
468
+ * resolved Postgres schema for the model (per-model override, else the
469
+ * plugin-level schema, else `undefined` for the default `public` schema).
356
470
  */
357
471
  export type NormalizedAuthModelConfig = {
358
472
  modelName: string;
473
+ tableName?: string;
359
474
  fields: Record<string, string>;
360
475
  schema?: string;
361
476
  };
@@ -387,7 +502,7 @@ export type NormalizedAuthConfig = Required<Omit<AuthConfig, 'emailAndPassword'
387
502
  * default (used to wire the datasource `schemas` array during generation).
388
503
  */
389
504
  schema?: string;
390
- betterAuthPlugins: any[];
505
+ betterAuthPlugins: BetterAuthPlugin[];
391
506
  rateLimit?: {
392
507
  enabled: boolean;
393
508
  window?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=build-better-auth-options.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-better-auth-options.test.d.ts","sourceRoot":"","sources":["../../src/server/build-better-auth-options.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import { describe, it, expectTypeOf } from 'vitest';
2
+ import { buildBetterAuthOptions } from './index.js';
3
+ // `ReturnType<typeof buildBetterAuthOptions>` on the overloaded export itself
4
+ // resolves against its last (generic) signature, not the call-site-selected
5
+ // one — wrapping each call shape in its own ordinary function and reading
6
+ // `ReturnType<typeof wrapper>` instead forces TS to resolve overloads exactly
7
+ // as a real call site would.
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- referenced only via `typeof` below
9
+ function callWithNoPlugins(config, context) {
10
+ return buildBetterAuthOptions(config, context);
11
+ }
12
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- referenced only via `typeof` below
13
+ function callWithPlugins(config, context, plugins) {
14
+ return buildBetterAuthOptions(config, context, plugins);
15
+ }
16
+ describe('buildBetterAuthOptions plugin-tuple typing', () => {
17
+ it('back-compat: the no-argument call still returns the widened BetterAuthOptions', () => {
18
+ expectTypeOf().toEqualTypeOf();
19
+ });
20
+ it('preserves plugin-derived auth.api.* endpoints and a customSession shape', () => {
21
+ // emailOTP() endpoints exist on the constructed Auth's `api` — erased entirely
22
+ // when constructed from the widened `BetterAuthOptions` (see #876).
23
+ expectTypeOf().not.toBeNever();
24
+ expectTypeOf().not.toBeNever();
25
+ expectTypeOf().not.toBeNever();
26
+ expectTypeOf().toEqualTypeOf();
27
+ });
28
+ });
29
+ //# sourceMappingURL=build-better-auth-options.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-better-auth-options.test.js","sourceRoot":"","sources":["../../src/server/build-better-auth-options.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAKnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAWnD,8EAA8E;AAC9E,4EAA4E;AAC5E,0EAA0E;AAC1E,8EAA8E;AAC9E,6BAA6B;AAC7B,mGAAmG;AACnG,SAAS,iBAAiB,CACxB,MAAgD,EAChD,OAA+C;IAE/C,OAAO,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAChD,CAAC;AAED,mGAAmG;AACnG,SAAS,eAAe,CACtB,MAAgD,EAChD,OAA+C,EAC/C,OAAoB;IAEpB,OAAO,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;AACzD,CAAC;AAMD,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,YAAY,EAAe,CAAC,aAAa,EAAqB,CAAA;IAChE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,+EAA+E;QAC/E,oEAAoE;QACpE,YAAY,EAA4C,CAAC,GAAG,CAAC,SAAS,EAAE,CAAA;QACxE,YAAY,EAAiD,CAAC,GAAG,CAAC,SAAS,EAAE,CAAA;QAC7E,YAAY,EAAkD,CAAC,GAAG,CAAC,SAAS,EAAE,CAAA;QAI9E,YAAY,EAAoB,CAAC,aAAa,EAAqB,CAAA;IACrE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,25 +1,129 @@
1
1
  import { betterAuth } from 'better-auth';
2
- import type { BetterAuthOptions } from 'better-auth';
3
- import type { OpenSaasConfig, AccessContext } from '@opensaas/stack-core';
2
+ import { nextCookies } from 'better-auth/next-js';
3
+ import type { Auth, BetterAuthOptions, BetterAuthPlugin } from 'better-auth';
4
+ import type { OpenSaasConfig, AccessContext, Session } from '@opensaas/stack-core';
5
+ /**
6
+ * The `BetterAuthOptions` shape produced when an app's own plugin tuple is
7
+ * passed to `buildBetterAuthOptions()`/`createAuth()` — the tuple plus the
8
+ * `nextCookies()` plugin the stack always appends last. Carrying the literal
9
+ * tuple type (rather than the widened `BetterAuthPlugin[]`) is what lets
10
+ * `betterAuth()` re-infer plugin endpoints (e.g. `emailOTP()`'s
11
+ * `api.signInEmailOTP`) and a `customSession()` plugin's replaced session
12
+ * shape from the resulting options object.
13
+ */
14
+ type ResolvedBetterAuthOptions<TPlugins extends readonly BetterAuthPlugin[]> = Omit<BetterAuthOptions, 'plugins'> & {
15
+ plugins: [...TPlugins, ReturnType<typeof nextCookies>];
16
+ };
17
+ /**
18
+ * Build the `BetterAuthOptions` a better-auth instance for this OpenSaas
19
+ * config should be constructed with — the same options `createAuth()` uses
20
+ * internally, available standalone for an app that still needs to hand-wire
21
+ * its own `betterAuth()` instance (e.g. a third-party contract that requires
22
+ * a resolved instance at module-init time). Keeps the auth plugin
23
+ * authoritative for everything it models; the app's additions on top become
24
+ * an explicit, reviewable diff instead of a parallel, hand-duplicated config.
25
+ *
26
+ * Called with just `(config, context)`, the return type is the widened
27
+ * `BetterAuthOptions` — `betterAuth()` infers its plugin/session types from
28
+ * the *literal* type of the options object, so constructing from this
29
+ * widened return erases plugin endpoints (e.g. `emailOTP()`'s
30
+ * `api.signInEmailOTP`) and a `customSession()` plugin's replaced session
31
+ * shape. **If your app reads `auth.api.*` in typed code and uses either of
32
+ * those, pass its plugin tuple as the third argument** — the exact same
33
+ * array already passed to `authPlugin({ betterAuthPlugins })` — so the
34
+ * return type carries the literal tuple instead:
35
+ *
36
+ * ```typescript
37
+ * import { betterAuth } from 'better-auth'
38
+ * import { emailOTP } from 'better-auth/plugins'
39
+ * import { buildBetterAuthOptions } from '@opensaas/stack-auth/server'
40
+ *
41
+ * export const appBetterAuthPlugins = [emailOTP()] // same array passed to authPlugin({ betterAuthPlugins })
42
+ *
43
+ * export const auth = betterAuth({
44
+ * ...(await buildBetterAuthOptions(config, context, appBetterAuthPlugins)),
45
+ * databaseHooks: { user: { create: { after: syncDomainUser } } },
46
+ * })
47
+ * // auth.api.signInEmailOTP / auth.api.getSession()'s customSession shape are now typed.
48
+ * ```
49
+ *
50
+ * The supplied tuple is for typing only — the array actually used at runtime
51
+ * is always the one resolved from `authPlugin({ betterAuthPlugins })`, with
52
+ * exactly one `nextCookies()` appended last. Passing a tuple that isn't the
53
+ * same plugin instances in the same order throws, so the two can't silently
54
+ * drift apart.
55
+ *
56
+ * Note `createAuth()`'s lazy Proxy does not behave identically to a real
57
+ * `Auth` instance for every property (see its own doc comment) — reach for
58
+ * this builder plus `betterAuth()` instead when the app reads `auth.api.*`
59
+ * in typed code.
60
+ */
61
+ export declare function buildBetterAuthOptions(opensaasConfig: OpenSaasConfig | Promise<OpenSaasConfig>, context: AccessContext | Promise<AccessContext>): Promise<BetterAuthOptions>;
62
+ export declare function buildBetterAuthOptions<const TPlugins extends readonly BetterAuthPlugin[]>(opensaasConfig: OpenSaasConfig | Promise<OpenSaasConfig>, context: AccessContext | Promise<AccessContext>, plugins: TPlugins): Promise<ResolvedBetterAuthOptions<TPlugins>>;
4
63
  /**
5
64
  * Create a better-auth instance from OpenSaas config
6
65
  * This should be called once at app startup
7
66
  *
8
- * @example
67
+ * Returns a lazy `Proxy` (see the caveat below), typed as `Auth<BetterAuthOptions>`
68
+ * when called with just `(config, context)` — the widened type, same erasure
69
+ * caveat as {@link buildBetterAuthOptions}'s no-argument form. **If your app
70
+ * reads `auth.api.*` in typed code and relies on a plugin's endpoints (e.g.
71
+ * `emailOTP()`) or a `customSession()`'s replaced session shape, pass its
72
+ * plugin tuple as the third argument** — the exact same array already passed
73
+ * to `authPlugin({ betterAuthPlugins })` — so the declared type carries the
74
+ * literal tuple instead:
75
+ *
9
76
  * ```typescript
10
77
  * // lib/auth.ts
11
78
  * import { createAuth } from '@opensaas/stack-auth/server'
12
79
  * import config from '../opensaas.config'
13
80
  * import { rawOpensaasContext } from '@/.opensaas/context'
14
81
  *
15
- * export const auth = createAuth(config, rawOpensaasContext)
82
+ * export const appBetterAuthPlugins = [emailOTP()] // same array passed to authPlugin({ betterAuthPlugins })
83
+ *
84
+ * export const auth = createAuth(config, rawOpensaasContext, appBetterAuthPlugins)
16
85
  * ```
86
+ *
87
+ * As with the builder, the supplied tuple is for typing only, and a tuple
88
+ * that isn't the same plugin instances in the same order throws.
89
+ *
90
+ * **Proxy caveat:** the lazy `Proxy` this returns does not behave identically
91
+ * to a real `Auth` instance for every property — every access, including a
92
+ * non-function property, is surfaced through an `async` wrapper (so e.g.
93
+ * `auth.options` reads back as a `Promise`, not the plain object a real
94
+ * instance would return synchronously). The declared type does not model
95
+ * this difference; where it matters, reach for {@link buildBetterAuthOptions}
96
+ * plus `betterAuth()` instead, which constructs a real instance.
17
97
  */
18
- export declare function createAuth(opensaasConfig: OpenSaasConfig | Promise<OpenSaasConfig>, context: AccessContext | Promise<AccessContext>): import("better-auth").Auth<BetterAuthOptions>;
98
+ export declare function createAuth(opensaasConfig: OpenSaasConfig | Promise<OpenSaasConfig>, context: AccessContext | Promise<AccessContext>): Auth<BetterAuthOptions>;
99
+ export declare function createAuth<const TPlugins extends readonly BetterAuthPlugin[]>(opensaasConfig: OpenSaasConfig | Promise<OpenSaasConfig>, context: AccessContext | Promise<AccessContext>, plugins: TPlugins): Auth<ResolvedBetterAuthOptions<TPlugins>>;
19
100
  /**
20
- * Get session from better-auth and transform it to OpenSaas session format
21
- * This is used internally by the generated context
101
+ * Get session from better-auth and transform it to OpenSaas session format
102
+ * a flattened projection of `sessionFields` off the *resolved* session
103
+ * object, not just its `user` sub-object. This is what makes a
104
+ * `customSession` plugin's fields (added at the top level, or a
105
+ * session-only field like the admin plugin's `impersonatedBy`) reachable.
106
+ * See the `sessionFields` reference for the resolution precedence.
107
+ *
108
+ * Returns `null` only when there is genuinely no session — a resolved
109
+ * session with no `user` key (a `customSession` plugin that dropped it) is
110
+ * still a session and still gets projected, never misreported as anonymous.
111
+ * A listed field that can't be resolved from the session shape is omitted
112
+ * and warns once per field per process (see `warnUnresolvedSessionField`)
113
+ * instead of silently vanishing into an access-control function reading
114
+ * `undefined`.
115
+ *
116
+ * Errors from the underlying `auth.api.getSession()` call propagate rather
117
+ * than becoming `null` — collapsing a lookup failure (e.g. a session-store
118
+ * outage) into "anonymous" is indistinguishable from a mass sign-out under
119
+ * fail-closed access control, so the caller must see it.
120
+ *
121
+ * Not called by any generated code before this helper existed — apps used to
122
+ * hand-roll this same transform against `auth.api.getSession({ headers:
123
+ * await headers() })`. Exported as the single reusable implementation; pass
124
+ * the caller's request headers (e.g. Next.js `await headers()` in a Server
125
+ * Component/action) so a session cookie can actually be resolved.
22
126
  */
23
- export declare function getSessionFromAuth(auth: ReturnType<typeof betterAuth>, sessionFields: string[]): Promise<Record<string, unknown> | null>;
127
+ export declare function getSessionFromAuth(auth: ReturnType<typeof betterAuth>, sessionFields: string[], headers: Headers): Promise<Session | null>;
24
128
  export type { BetterAuthOptions };
25
129
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAgCzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,iDA8IhD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EACnC,aAAa,EAAE,MAAM,EAAE,2CA0BxB;AAED,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAIlF;;;;;;;;GAQG;AACH,KAAK,yBAAyB,CAAC,QAAQ,SAAS,SAAS,gBAAgB,EAAE,IAAI,IAAI,CACjF,iBAAiB,EACjB,SAAS,CACV,GAAG;IACF,OAAO,EAAE,CAAC,GAAG,QAAQ,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAA;CACvD,CAAA;AA+ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,sBAAsB,CAC1C,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAC9C,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAC7B,wBAAsB,sBAAsB,CAAC,KAAK,CAAC,QAAQ,SAAS,SAAS,gBAAgB,EAAE,EAC7F,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,EAC/C,OAAO,EAAE,QAAQ,GAChB,OAAO,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAA;AA2I/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,GAC9C,IAAI,CAAC,iBAAiB,CAAC,CAAA;AAC1B,wBAAgB,UAAU,CAAC,KAAK,CAAC,QAAQ,SAAS,SAAS,gBAAgB,EAAE,EAC3E,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,EACxD,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,EAC/C,OAAO,EAAE,QAAQ,GAChB,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAA;AA8I5C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,EACnC,aAAa,EAAE,MAAM,EAAE,EACvB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAoBzB;AAED,YAAY,EAAE,iBAAiB,EAAE,CAAA"}