@convex-dev/better-auth 0.9.11 → 0.10.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 (102) hide show
  1. package/dist/auth-config.d.ts +43 -0
  2. package/dist/auth-config.d.ts.map +1 -0
  3. package/dist/auth-config.js +45 -0
  4. package/dist/auth-config.js.map +1 -0
  5. package/dist/auth-options.d.ts +3 -0
  6. package/dist/auth-options.d.ts.map +1 -0
  7. package/dist/auth-options.js +41 -0
  8. package/dist/auth-options.js.map +1 -0
  9. package/dist/auth.d.ts +1 -3
  10. package/dist/auth.d.ts.map +1 -1
  11. package/dist/auth.js +2 -42
  12. package/dist/auth.js.map +1 -1
  13. package/dist/client/{adapterUtils.d.ts → adapter-utils.d.ts} +15 -15
  14. package/dist/client/adapter-utils.d.ts.map +1 -0
  15. package/dist/client/{adapterUtils.js → adapter-utils.js} +1 -1
  16. package/dist/client/adapter-utils.js.map +1 -0
  17. package/dist/client/adapter.d.ts +1 -2
  18. package/dist/client/adapter.d.ts.map +1 -1
  19. package/dist/client/adapter.js +4 -4
  20. package/dist/client/adapter.js.map +1 -1
  21. package/dist/client/create-api.d.ts +139 -0
  22. package/dist/client/create-api.d.ts.map +1 -0
  23. package/dist/client/create-api.js +204 -0
  24. package/dist/client/create-api.js.map +1 -0
  25. package/dist/client/create-client.d.ts +183 -0
  26. package/dist/client/create-client.d.ts.map +1 -0
  27. package/dist/client/create-client.js +311 -0
  28. package/dist/client/create-client.js.map +1 -0
  29. package/dist/client/{createSchema.d.ts → create-schema.d.ts} +1 -1
  30. package/dist/client/create-schema.d.ts.map +1 -0
  31. package/dist/client/{createSchema.js → create-schema.js} +11 -5
  32. package/dist/client/create-schema.js.map +1 -0
  33. package/dist/client/index.d.ts +4 -279
  34. package/dist/client/index.d.ts.map +1 -1
  35. package/dist/client/index.js +6 -463
  36. package/dist/client/index.js.map +1 -1
  37. package/dist/component/_generated/component.d.ts +0 -3
  38. package/dist/component/_generated/component.d.ts.map +1 -1
  39. package/dist/component/adapter.d.ts +19 -21
  40. package/dist/component/adapter.d.ts.map +1 -1
  41. package/dist/component/adapter.js +2 -2
  42. package/dist/component/adapter.js.map +1 -1
  43. package/dist/component/schema.d.ts +50 -50
  44. package/dist/nextjs/client.d.ts +4 -0
  45. package/dist/nextjs/client.d.ts.map +1 -0
  46. package/dist/nextjs/client.js +37 -0
  47. package/dist/nextjs/client.js.map +1 -0
  48. package/dist/nextjs/index.d.ts +19 -7
  49. package/dist/nextjs/index.d.ts.map +1 -1
  50. package/dist/nextjs/index.js +90 -36
  51. package/dist/nextjs/index.js.map +1 -1
  52. package/dist/plugins/convex/client.d.ts +1 -1
  53. package/dist/plugins/convex/client.d.ts.map +1 -1
  54. package/dist/plugins/convex/client.js +0 -1
  55. package/dist/plugins/convex/client.js.map +1 -1
  56. package/dist/plugins/convex/index.d.ts +239 -227
  57. package/dist/plugins/convex/index.d.ts.map +1 -1
  58. package/dist/plugins/convex/index.js +191 -37
  59. package/dist/plugins/convex/index.js.map +1 -1
  60. package/dist/plugins/cross-domain/client.d.ts +3 -3
  61. package/dist/plugins/cross-domain/client.d.ts.map +1 -1
  62. package/dist/plugins/cross-domain/index.d.ts +15 -70
  63. package/dist/plugins/cross-domain/index.d.ts.map +1 -1
  64. package/dist/plugins/cross-domain/index.js +8 -0
  65. package/dist/plugins/cross-domain/index.js.map +1 -1
  66. package/dist/react/index.d.ts +52 -2
  67. package/dist/react/index.d.ts.map +1 -1
  68. package/dist/react/index.js +133 -9
  69. package/dist/react/index.js.map +1 -1
  70. package/dist/react-start/index.d.ts +11 -41
  71. package/dist/react-start/index.d.ts.map +1 -1
  72. package/dist/react-start/index.js +82 -106
  73. package/dist/react-start/index.js.map +1 -1
  74. package/dist/utils/index.d.ts +20 -2
  75. package/dist/utils/index.d.ts.map +1 -1
  76. package/dist/utils/index.js +54 -1
  77. package/dist/utils/index.js.map +1 -1
  78. package/package.json +19 -12
  79. package/src/auth-config.ts +82 -0
  80. package/src/auth-options.ts +54 -0
  81. package/src/auth.ts +3 -56
  82. package/src/client/adapter.ts +5 -5
  83. package/src/client/create-api.ts +337 -0
  84. package/src/client/create-client.ts +446 -0
  85. package/src/client/{createSchema.ts → create-schema.ts} +10 -4
  86. package/src/client/index.ts +22 -771
  87. package/src/component/_generated/component.ts +0 -7
  88. package/src/component/adapter.ts +2 -3
  89. package/src/nextjs/client.tsx +52 -0
  90. package/src/nextjs/index.ts +138 -45
  91. package/src/plugins/convex/client.ts +1 -1
  92. package/src/plugins/convex/index.ts +337 -51
  93. package/src/plugins/cross-domain/index.ts +10 -2
  94. package/src/react/index.tsx +195 -9
  95. package/src/react-start/index.ts +126 -171
  96. package/src/test.ts +1 -1
  97. package/src/utils/index.ts +96 -1
  98. package/dist/client/adapterUtils.d.ts.map +0 -1
  99. package/dist/client/adapterUtils.js.map +0 -1
  100. package/dist/client/createSchema.d.ts.map +0 -1
  101. package/dist/client/createSchema.js.map +0 -1
  102. /package/src/client/{adapterUtils.ts → adapter-utils.ts} +0 -0
@@ -1,778 +1,29 @@
1
- import {
2
- type DataModelFromSchemaDefinition,
3
- type DefaultFunctionArgs,
4
- type FunctionHandle,
5
- type FunctionReference,
6
- type GenericActionCtx,
7
- type GenericDataModel,
8
- type GenericMutationCtx,
9
- type GenericQueryCtx,
10
- type GenericSchema,
11
- type HttpRouter,
12
- type SchemaDefinition,
13
- httpActionGeneric,
14
- internalMutationGeneric,
15
- mutationGeneric,
16
- paginationOptsValidator,
17
- queryGeneric,
18
- } from "convex/server";
19
- import { type GenericId, type Infer, v } from "convex/values";
20
1
  import { convexAdapter } from "./adapter.js";
21
- import { type Auth, betterAuth } from "better-auth";
22
- import type { DBAdapterInstance } from "better-auth/adapters";
23
- import { asyncMap } from "convex-helpers";
24
- import { partial } from "convex-helpers/validators";
25
- import {
26
- adapterWhereValidator,
27
- checkUniqueFields,
28
- hasUniqueFields,
29
- listOne,
30
- paginate,
31
- selectFields,
32
- } from "./adapterUtils.js";
33
- import { corsRouter } from "convex-helpers/server/cors";
34
2
  import { version as convexVersion } from "convex";
35
- import semver from "semver";
36
- import defaultSchema from "../component/schema.js";
37
- import { getAuthTables } from "better-auth/db";
38
- import type { ComponentApi } from "../component/_generated/component.js";
39
- import type { TableNames } from "../component/_generated/dataModel.js";
40
-
41
- export { convexAdapter };
42
-
43
- export type CreateAdapter = <Ctx extends GenericCtx<GenericDataModel>>(
44
- ctx: Ctx
45
- ) => DBAdapterInstance;
46
-
47
- export type CreateAuth<
48
- DataModel extends GenericDataModel,
49
- A extends ReturnType<typeof betterAuth> = Auth,
50
- > =
51
- | ((ctx: GenericCtx<DataModel>) => A)
52
- | ((ctx: GenericCtx<DataModel>, opts?: { optionsOnly?: boolean }) => A);
53
-
54
- export const getStaticAuth = <
55
- DataModel extends GenericDataModel,
56
- Auth extends ReturnType<typeof betterAuth>,
57
- >(
58
- createAuth: CreateAuth<DataModel, Auth>
59
- ): Auth => {
60
- return createAuth({} as any, { optionsOnly: true });
61
- };
3
+ import semverLt from "semver/functions/lt.js";
4
+ import {
5
+ type AuthFunctions,
6
+ createClient,
7
+ type Triggers,
8
+ } from "./create-client.js";
9
+ import { createApi } from "./create-api.js";
10
+ import {
11
+ type CreateAuth,
12
+ type EventFunction,
13
+ type GenericCtx,
14
+ } from "../utils/index.js";
62
15
 
63
- if (semver.lt(convexVersion, "1.25.0")) {
16
+ if (semverLt(convexVersion, "1.25.0")) {
64
17
  throw new Error("Convex version must be at least 1.25.0");
65
18
  }
66
19
 
67
- const whereValidator = (
68
- schema: SchemaDefinition<any, any>,
69
- tableName: TableNames
70
- ) =>
71
- v.object({
72
- field: v.union(
73
- ...Object.keys(schema.tables[tableName].validator.fields).map((field) =>
74
- v.literal(field)
75
- ),
76
- v.literal("_id")
77
- ),
78
- operator: v.optional(
79
- v.union(
80
- v.literal("lt"),
81
- v.literal("lte"),
82
- v.literal("gt"),
83
- v.literal("gte"),
84
- v.literal("eq"),
85
- v.literal("in"),
86
- v.literal("not_in"),
87
- v.literal("ne"),
88
- v.literal("contains"),
89
- v.literal("starts_with"),
90
- v.literal("ends_with")
91
- )
92
- ),
93
- value: v.union(
94
- v.string(),
95
- v.number(),
96
- v.boolean(),
97
- v.array(v.string()),
98
- v.array(v.number()),
99
- v.null()
100
- ),
101
- connector: v.optional(v.union(v.literal("AND"), v.literal("OR"))),
102
- });
103
-
104
- export type EventFunction<T extends DefaultFunctionArgs> = FunctionReference<
105
- "mutation",
106
- "internal" | "public",
107
- T
108
- >;
109
-
110
- export type GenericCtx<DataModel extends GenericDataModel = GenericDataModel> =
111
- | GenericQueryCtx<DataModel>
112
- | GenericMutationCtx<DataModel>
113
- | GenericActionCtx<DataModel>;
114
-
115
- export type AuthFunctions = {
116
- onCreate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
117
- onUpdate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
118
- onDelete?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
119
- };
120
-
121
- export const createApi = <
122
- DataModel extends GenericDataModel,
123
- Schema extends SchemaDefinition<any, any>,
124
- >(
125
- schema: Schema,
126
- createAuth: CreateAuth<DataModel>
127
- ) => {
128
- const betterAuthSchema = getAuthTables(getStaticAuth(createAuth).options);
129
- return {
130
- migrationRemoveUserId: mutationGeneric({
131
- args: {
132
- userId: v.string(),
133
- },
134
- handler: async (ctx, args) => {
135
- await ctx.db.patch(args.userId as GenericId<"user">, {
136
- userId: undefined,
137
- });
138
- },
139
- }),
140
- create: mutationGeneric({
141
- args: {
142
- input: v.union(
143
- ...Object.entries(schema.tables).map(([model, table]) =>
144
- v.object({
145
- model: v.literal(model),
146
- data: v.object((table as any).validator.fields),
147
- })
148
- )
149
- ),
150
- select: v.optional(v.array(v.string())),
151
- onCreateHandle: v.optional(v.string()),
152
- },
153
- handler: async (ctx, args) => {
154
- await checkUniqueFields(
155
- ctx,
156
- schema,
157
- betterAuthSchema,
158
- args.input.model,
159
- args.input.data
160
- );
161
- const id = await ctx.db.insert(
162
- args.input.model as any,
163
- args.input.data
164
- );
165
- const doc = await ctx.db.get(id);
166
- if (!doc) {
167
- throw new Error(`Failed to create ${args.input.model}`);
168
- }
169
- const result = selectFields(doc, args.select);
170
- if (args.onCreateHandle) {
171
- await ctx.runMutation(
172
- args.onCreateHandle as FunctionHandle<"mutation">,
173
- {
174
- model: args.input.model,
175
- doc,
176
- }
177
- );
178
- }
179
- return result;
180
- },
181
- }),
182
- findOne: queryGeneric({
183
- args: {
184
- model: v.union(
185
- ...Object.keys(schema.tables).map((model) => v.literal(model))
186
- ),
187
- where: v.optional(v.array(adapterWhereValidator)),
188
- select: v.optional(v.array(v.string())),
189
- },
190
- handler: async (ctx, args) => {
191
- return await listOne(ctx, schema, betterAuthSchema, args);
192
- },
193
- }),
194
- findMany: queryGeneric({
195
- args: {
196
- model: v.union(
197
- ...Object.keys(schema.tables).map((model) => v.literal(model))
198
- ),
199
- where: v.optional(v.array(adapterWhereValidator)),
200
- limit: v.optional(v.number()),
201
- sortBy: v.optional(
202
- v.object({
203
- direction: v.union(v.literal("asc"), v.literal("desc")),
204
- field: v.string(),
205
- })
206
- ),
207
- offset: v.optional(v.number()),
208
- paginationOpts: paginationOptsValidator,
209
- },
210
- handler: async (ctx, args) => {
211
- return await paginate(ctx, schema, betterAuthSchema, args);
212
- },
213
- }),
214
- updateOne: mutationGeneric({
215
- args: {
216
- input: v.union(
217
- ...Object.entries(schema.tables).map(
218
- ([name, table]: [string, Schema["tables"][string]]) => {
219
- const tableName = name as TableNames;
220
- const fields = partial(table.validator.fields);
221
- return v.object({
222
- model: v.literal(tableName),
223
- update: v.object(fields),
224
- where: v.optional(v.array(whereValidator(schema, tableName))),
225
- });
226
- }
227
- )
228
- ),
229
- onUpdateHandle: v.optional(v.string()),
230
- },
231
- handler: async (ctx, args) => {
232
- const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
233
- if (!doc) {
234
- throw new Error(`Failed to update ${args.input.model}`);
235
- }
236
- await checkUniqueFields(
237
- ctx,
238
- schema,
239
- betterAuthSchema,
240
- args.input.model,
241
- args.input.update,
242
- doc
243
- );
244
- await ctx.db.patch(
245
- doc._id as GenericId<string>,
246
- args.input.update as any
247
- );
248
- const updatedDoc = await ctx.db.get(doc._id as GenericId<string>);
249
- if (!updatedDoc) {
250
- throw new Error(`Failed to update ${args.input.model}`);
251
- }
252
- if (args.onUpdateHandle) {
253
- await ctx.runMutation(
254
- args.onUpdateHandle as FunctionHandle<"mutation">,
255
- {
256
- model: args.input.model,
257
- newDoc: updatedDoc,
258
- oldDoc: doc,
259
- }
260
- );
261
- }
262
- return updatedDoc;
263
- },
264
- }),
265
- updateMany: mutationGeneric({
266
- args: {
267
- input: v.union(
268
- ...Object.entries(schema.tables).map(
269
- ([name, table]: [string, Schema["tables"][string]]) => {
270
- const tableName = name as TableNames;
271
- const fields = partial(table.validator.fields);
272
- return v.object({
273
- model: v.literal(tableName),
274
- update: v.object(fields),
275
- where: v.optional(v.array(whereValidator(schema, tableName))),
276
- });
277
- }
278
- )
279
- ),
280
- paginationOpts: paginationOptsValidator,
281
- onUpdateHandle: v.optional(v.string()),
282
- },
283
- handler: async (ctx, args) => {
284
- const { page, ...result } = await paginate(
285
- ctx,
286
- schema,
287
- betterAuthSchema,
288
- {
289
- ...args.input,
290
- paginationOpts: args.paginationOpts,
291
- }
292
- );
293
- if (args.input.update) {
294
- if (
295
- hasUniqueFields(
296
- betterAuthSchema,
297
- args.input.model,
298
- args.input.update ?? {}
299
- ) &&
300
- page.length > 1
301
- ) {
302
- throw new Error(
303
- `Attempted to set unique fields in multiple documents in ${args.input.model} with the same value. Fields: ${Object.keys(args.input.update ?? {}).join(", ")}`
304
- );
305
- }
306
- await asyncMap(page, async (doc) => {
307
- await checkUniqueFields(
308
- ctx,
309
- schema,
310
- betterAuthSchema,
311
- args.input.model,
312
- args.input.update ?? {},
313
- doc
314
- );
315
- await ctx.db.patch(
316
- doc._id as GenericId<string>,
317
- args.input.update as any
318
- );
319
-
320
- if (args.onUpdateHandle) {
321
- await ctx.runMutation(
322
- args.onUpdateHandle as FunctionHandle<"mutation">,
323
- {
324
- model: args.input.model,
325
- newDoc: await ctx.db.get(doc._id as GenericId<string>),
326
- oldDoc: doc,
327
- }
328
- );
329
- }
330
- });
331
- }
332
- return {
333
- ...result,
334
- count: page.length,
335
- ids: page.map((doc) => doc._id),
336
- };
337
- },
338
- }),
339
- deleteOne: mutationGeneric({
340
- args: {
341
- input: v.union(
342
- ...Object.keys(schema.tables).map((name: string) => {
343
- const tableName = name as TableNames;
344
- return v.object({
345
- model: v.literal(tableName),
346
- where: v.optional(v.array(whereValidator(schema, tableName))),
347
- });
348
- })
349
- ),
350
- onDeleteHandle: v.optional(v.string()),
351
- },
352
- handler: async (ctx, args) => {
353
- const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
354
- if (!doc) {
355
- return;
356
- }
357
- await ctx.db.delete(doc._id as GenericId<string>);
358
- if (args.onDeleteHandle) {
359
- await ctx.runMutation(
360
- args.onDeleteHandle as FunctionHandle<"mutation">,
361
- { model: args.input.model, doc }
362
- );
363
- }
364
- return doc;
365
- },
366
- }),
367
- deleteMany: mutationGeneric({
368
- args: {
369
- input: v.union(
370
- ...Object.keys(schema.tables).map((name: string) => {
371
- const tableName = name as TableNames;
372
- return v.object({
373
- model: v.literal(tableName),
374
- where: v.optional(v.array(whereValidator(schema, tableName))),
375
- });
376
- })
377
- ),
378
- paginationOpts: paginationOptsValidator,
379
- onDeleteHandle: v.optional(v.string()),
380
- },
381
- handler: async (ctx, args) => {
382
- const { page, ...result } = await paginate(
383
- ctx,
384
- schema,
385
- betterAuthSchema,
386
- {
387
- ...args.input,
388
- paginationOpts: args.paginationOpts,
389
- }
390
- );
391
- await asyncMap(page, async (doc) => {
392
- if (args.onDeleteHandle) {
393
- await ctx.runMutation(
394
- args.onDeleteHandle as FunctionHandle<"mutation">,
395
- {
396
- model: args.input.model,
397
- doc,
398
- }
399
- );
400
- }
401
- await ctx.db.delete(doc._id as GenericId<string>);
402
- });
403
- return {
404
- ...result,
405
- count: page.length,
406
- ids: page.map((doc) => doc._id),
407
- };
408
- },
409
- }),
410
- };
411
- };
412
-
413
- export type Triggers<
414
- DataModel extends GenericDataModel,
415
- Schema extends SchemaDefinition<any, any>,
416
- > = {
417
- [K in keyof Schema["tables"]]?: {
418
- onCreate?: <Ctx extends GenericMutationCtx<DataModel>>(
419
- ctx: Ctx,
420
- doc: Infer<Schema["tables"][K]["validator"]> & {
421
- _id: string;
422
- _creationTime: number;
423
- }
424
- ) => Promise<void>;
425
- onUpdate?: <Ctx extends GenericMutationCtx<DataModel>>(
426
- ctx: Ctx,
427
- newDoc: Infer<Schema["tables"][K]["validator"]> & {
428
- _id: string;
429
- _creationTime: number;
430
- },
431
- oldDoc: Infer<Schema["tables"][K]["validator"]> & {
432
- _id: string;
433
- _creationTime: number;
434
- }
435
- ) => Promise<void>;
436
- onDelete?: <Ctx extends GenericMutationCtx<DataModel>>(
437
- ctx: Ctx,
438
- doc: Infer<Schema["tables"][K]["validator"]> & {
439
- _id: string;
440
- _creationTime: number;
441
- }
442
- ) => Promise<void>;
443
- };
444
- };
445
-
446
- type SlimComponentApi = {
447
- adapter: {
448
- create: FunctionReference<"mutation", "internal">;
449
- findOne: FunctionReference<"query", "internal">;
450
- findMany: FunctionReference<"query", "internal">;
451
- updateOne: FunctionReference<"mutation", "internal">;
452
- updateMany: FunctionReference<"mutation", "internal">;
453
- deleteOne: FunctionReference<"mutation", "internal">;
454
- deleteMany: FunctionReference<"mutation", "internal">;
455
- migrationRemoveUserId?: FunctionReference<"mutation", "internal">;
456
- };
457
- adapterTest?: ComponentApi["adapterTest"];
458
- };
459
-
460
- export const createClient = <
461
- DataModel extends GenericDataModel,
462
- Schema extends SchemaDefinition<GenericSchema, true> = typeof defaultSchema,
463
- Api extends SlimComponentApi = SlimComponentApi,
464
- >(
465
- component: Api,
466
- config?: {
467
- local?: {
468
- schema?: Schema;
469
- };
470
- verbose?: boolean;
471
- } & (
472
- | {
473
- triggers: Triggers<DataModel, Schema>;
474
- authFunctions: AuthFunctions;
475
- }
476
- | { triggers?: undefined }
477
- )
478
- ) => {
479
- type BetterAuthDataModel = DataModelFromSchemaDefinition<Schema>;
480
-
481
- const safeGetAuthUser = async (ctx: GenericCtx<DataModel>) => {
482
- const identity = await ctx.auth.getUserIdentity();
483
- if (!identity) {
484
- return;
485
- }
486
-
487
- const doc = (await ctx.runQuery(component.adapter.findOne, {
488
- model: "user",
489
- where: [
490
- {
491
- field: "_id",
492
- value: identity.subject,
493
- },
494
- ],
495
- })) as BetterAuthDataModel["user"]["document"] | null;
496
- if (!doc) {
497
- return;
498
- }
499
- return doc;
500
- };
501
-
502
- const getHeaders = async (ctx: GenericCtx<DataModel>) => {
503
- const identity = await ctx.auth.getUserIdentity();
504
- if (!identity) {
505
- return new Headers();
506
- }
507
- const session = await ctx.runQuery(component.adapter.findOne, {
508
- model: "session",
509
- where: [
510
- {
511
- field: "_id",
512
- value: identity.sessionId as string,
513
- },
514
- ],
515
- });
516
- return new Headers({
517
- ...(session?.token ? { authorization: `Bearer ${session.token}` } : {}),
518
- ...(session?.ipAddress
519
- ? { "x-forwarded-for": session.ipAddress as string }
520
- : {}),
521
- });
522
- };
523
-
524
- return {
525
- component,
526
- adapter: (ctx: GenericCtx<DataModel>) =>
527
- convexAdapter<DataModel, typeof ctx, Schema>(ctx, component, {
528
- ...config,
529
- debugLogs: config?.verbose,
530
- }),
531
-
532
- getAuth: async <T extends CreateAuth<DataModel>>(
533
- createAuth: T,
534
- ctx: GenericCtx<DataModel>
535
- ) => ({
536
- auth: createAuth(ctx) as ReturnType<T>,
537
- headers: await getHeaders(ctx),
538
- }),
539
-
540
- getHeaders,
541
-
542
- /**
543
- * Returns the current user or null if the user is not found
544
- * @param ctx - The Convex context
545
- * @returns The user or null if the user is not found
546
- */
547
- safeGetAuthUser,
548
-
549
- /**
550
- * Returns the current user.
551
- * @param ctx - The Convex context
552
- * @returns The user or throws an error if the user is not found
553
- */
554
- getAuthUser: async (ctx: GenericCtx<DataModel>) => {
555
- const user = await safeGetAuthUser(ctx);
556
- if (!user) {
557
- throw new Error("Unauthenticated");
558
- }
559
- return user;
560
- },
561
-
562
- /**
563
- * Returns a user by their Better Auth user id.
564
- * @param ctx - The Convex context
565
- * @param id - The Better Auth user id
566
- * @returns The user or null if the user is not found
567
- */
568
- getAnyUserById: async (ctx: GenericCtx<DataModel>, id: string) => {
569
- return (await ctx.runQuery(component.adapter.findOne, {
570
- model: "user",
571
- where: [{ field: "_id", value: id }],
572
- })) as BetterAuthDataModel["user"]["document"] | null;
573
- },
574
-
575
- // Replaces 0.7 behavior of returning a new user id from
576
- // onCreateUser, deprecated in 0.9
577
- /**
578
- * Replaces 0.7 behavior of returning a new user id from
579
- * onCreateUser
580
- * @param ctx - The Convex context
581
- * @param authId - The Better Auth user id
582
- * @param userId - The app user id
583
- * @deprecated in 0.9
584
- */
585
- setUserId: async (
586
- ctx: GenericMutationCtx<DataModel>,
587
- authId: string,
588
- userId: string
589
- ) => {
590
- await ctx.runMutation(component.adapter.updateOne, {
591
- input: {
592
- model: "user",
593
- where: [{ field: "_id", value: authId }],
594
- update: { userId },
595
- },
596
- });
597
- },
598
-
599
- /**
600
- * Temporary method to simplify 0.9 migration, gets a user by `userId` field
601
- * @param ctx - The Convex context
602
- * @param userId - The app user id
603
- * @returns The user or null if the user is not found
604
- */
605
- migrationGetUser: async (
606
- ctx: GenericMutationCtx<DataModel>,
607
- userId: string
608
- ) => {
609
- return (await ctx.runQuery(component.adapter.findOne, {
610
- model: "user",
611
- where: [{ field: "userId", value: userId }],
612
- })) as BetterAuthDataModel["user"]["document"] | null;
613
- },
614
-
615
- /**
616
- * Temporary method to simplify 0.9 migration, removes the `userId` field
617
- * from the Better Auth user record
618
- * @param ctx - The Convex context
619
- * @param userId - The app user id
620
- */
621
- migrationRemoveUserId: async (
622
- ctx: GenericMutationCtx<DataModel>,
623
- userId: string
624
- ) => {
625
- if (!component.adapter.migrationRemoveUserId) {
626
- throw new Error("migrationRemoveUserId not found");
627
- }
628
- await ctx.runMutation(component.adapter.migrationRemoveUserId, {
629
- userId,
630
- });
631
- },
632
-
633
- triggersApi: () => ({
634
- onCreate: internalMutationGeneric({
635
- args: {
636
- doc: v.any(),
637
- model: v.string(),
638
- },
639
- handler: async (ctx, args) => {
640
- await config?.triggers?.[args.model]?.onCreate?.(ctx, args.doc);
641
- },
642
- }),
643
- onUpdate: internalMutationGeneric({
644
- args: {
645
- oldDoc: v.any(),
646
- newDoc: v.any(),
647
- model: v.string(),
648
- },
649
- handler: async (ctx, args) => {
650
- await config?.triggers?.[args.model]?.onUpdate?.(
651
- ctx,
652
- args.newDoc,
653
- args.oldDoc
654
- );
655
- },
656
- }),
657
- onDelete: internalMutationGeneric({
658
- args: {
659
- doc: v.any(),
660
- model: v.string(),
661
- },
662
- handler: async (ctx, args) => {
663
- await config?.triggers?.[args.model]?.onDelete?.(ctx, args.doc);
664
- },
665
- }),
666
- }),
667
-
668
- registerRoutes: (
669
- http: HttpRouter,
670
- createAuth: CreateAuth<DataModel>,
671
- opts: {
672
- cors?:
673
- | boolean
674
- | {
675
- // These values are appended to the default values
676
- allowedOrigins?: string[];
677
- allowedHeaders?: string[];
678
- exposedHeaders?: string[];
679
- };
680
- } = {}
681
- ) => {
682
- const staticAuth = getStaticAuth(createAuth);
683
- const path = staticAuth.options.basePath ?? "/api/auth";
684
- const authRequestHandler = httpActionGeneric(async (ctx, request) => {
685
- if (config?.verbose) {
686
- console.log("options.baseURL", staticAuth.options.baseURL);
687
- console.log("request headers", request.headers);
688
- }
689
- const auth = createAuth(ctx as any);
690
- const response = await auth.handler(request);
691
- if (config?.verbose) {
692
- console.log("response headers", response.headers);
693
- }
694
- return response;
695
- });
696
- const wellKnown = http.lookup("/.well-known/openid-configuration", "GET");
697
-
698
- // If registerRoutes is used multiple times, this may already be defined
699
- if (!wellKnown) {
700
- // Redirect root well-known to api well-known
701
- http.route({
702
- path: "/.well-known/openid-configuration",
703
- method: "GET",
704
- handler: httpActionGeneric(async () => {
705
- const url = `${process.env.CONVEX_SITE_URL}${path}/convex/.well-known/openid-configuration`;
706
- return Response.redirect(url);
707
- }),
708
- });
709
- }
710
-
711
- if (!opts.cors) {
712
- http.route({
713
- pathPrefix: `${path}/`,
714
- method: "GET",
715
- handler: authRequestHandler,
716
- });
717
-
718
- http.route({
719
- pathPrefix: `${path}/`,
720
- method: "POST",
721
- handler: authRequestHandler,
722
- });
723
-
724
- return;
725
- }
726
- const corsOpts =
727
- typeof opts.cors === "boolean"
728
- ? { allowedOrigins: [], allowedHeaders: [], exposedHeaders: [] }
729
- : opts.cors;
730
- let trustedOriginsOption:
731
- | string[]
732
- | ((request: Request) => string[] | Promise<string[]>)
733
- | undefined;
734
- const cors = corsRouter(http, {
735
- allowedOrigins: async (request) => {
736
- trustedOriginsOption =
737
- trustedOriginsOption ??
738
- (await staticAuth.$context).options.trustedOrigins ??
739
- [];
740
- const trustedOrigins = Array.isArray(trustedOriginsOption)
741
- ? trustedOriginsOption
742
- : await trustedOriginsOption(request);
743
- return trustedOrigins
744
- .map((origin) =>
745
- // Strip trailing wildcards, unsupported for allowedOrigins
746
- origin.endsWith("*") && origin.length > 1
747
- ? origin.slice(0, -1)
748
- : origin
749
- )
750
- .concat(corsOpts.allowedOrigins ?? []);
751
- },
752
- allowCredentials: true,
753
- allowedHeaders: [
754
- "Content-Type",
755
- "Better-Auth-Cookie",
756
- "Authorization",
757
- ].concat(corsOpts.allowedHeaders ?? []),
758
- exposedHeaders: ["Set-Better-Auth-Cookie"].concat(
759
- corsOpts.exposedHeaders ?? []
760
- ),
761
- debug: config?.verbose,
762
- enforceAllowOrigins: false,
763
- });
764
-
765
- cors.route({
766
- pathPrefix: `${path}/`,
767
- method: "GET",
768
- handler: authRequestHandler,
769
- });
770
-
771
- cors.route({
772
- pathPrefix: `${path}/`,
773
- method: "POST",
774
- handler: authRequestHandler,
775
- });
776
- },
777
- };
20
+ export {
21
+ convexAdapter,
22
+ createClient,
23
+ createApi,
24
+ type CreateAuth,
25
+ type EventFunction,
26
+ type GenericCtx,
27
+ type Triggers,
28
+ type AuthFunctions,
778
29
  };