@neutralauth/internal-auth 0.10.11

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 (147) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +39 -0
  3. package/dist/auth-config.d.ts +43 -0
  4. package/dist/auth-config.d.ts.map +1 -0
  5. package/dist/auth-config.js +43 -0
  6. package/dist/auth-config.js.map +1 -0
  7. package/dist/auth-options.d.ts +3 -0
  8. package/dist/auth-options.d.ts.map +1 -0
  9. package/dist/auth-options.js +40 -0
  10. package/dist/auth-options.js.map +1 -0
  11. package/dist/auth.d.ts +2 -0
  12. package/dist/auth.d.ts.map +1 -0
  13. package/dist/auth.js +4 -0
  14. package/dist/auth.js.map +1 -0
  15. package/dist/client/adapter-utils.d.ts +66 -0
  16. package/dist/client/adapter-utils.d.ts.map +1 -0
  17. package/dist/client/adapter-utils.js +437 -0
  18. package/dist/client/adapter-utils.js.map +1 -0
  19. package/dist/client/adapter.d.ts +14 -0
  20. package/dist/client/adapter.d.ts.map +1 -0
  21. package/dist/client/adapter.js +274 -0
  22. package/dist/client/adapter.js.map +1 -0
  23. package/dist/client/create-api.d.ts +141 -0
  24. package/dist/client/create-api.d.ts.map +1 -0
  25. package/dist/client/create-api.js +205 -0
  26. package/dist/client/create-api.js.map +1 -0
  27. package/dist/client/create-client.d.ts +183 -0
  28. package/dist/client/create-client.d.ts.map +1 -0
  29. package/dist/client/create-client.js +311 -0
  30. package/dist/client/create-client.js.map +1 -0
  31. package/dist/client/create-schema.d.ts +19 -0
  32. package/dist/client/create-schema.d.ts.map +1 -0
  33. package/dist/client/create-schema.js +114 -0
  34. package/dist/client/create-schema.js.map +1 -0
  35. package/dist/client/index.d.ts +7 -0
  36. package/dist/client/index.d.ts.map +1 -0
  37. package/dist/client/index.js +10 -0
  38. package/dist/client/index.js.map +1 -0
  39. package/dist/client/plugins/index.d.ts +3 -0
  40. package/dist/client/plugins/index.d.ts.map +1 -0
  41. package/dist/client/plugins/index.js +3 -0
  42. package/dist/client/plugins/index.js.map +1 -0
  43. package/dist/component/_generated/api.d.ts +36 -0
  44. package/dist/component/_generated/api.d.ts.map +1 -0
  45. package/dist/component/_generated/api.js +31 -0
  46. package/dist/component/_generated/api.js.map +1 -0
  47. package/dist/component/_generated/component.d.ts +787 -0
  48. package/dist/component/_generated/component.d.ts.map +1 -0
  49. package/dist/component/_generated/component.js +11 -0
  50. package/dist/component/_generated/component.js.map +1 -0
  51. package/dist/component/_generated/dataModel.d.ts +46 -0
  52. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  53. package/dist/component/_generated/dataModel.js +11 -0
  54. package/dist/component/_generated/dataModel.js.map +1 -0
  55. package/dist/component/_generated/server.d.ts +121 -0
  56. package/dist/component/_generated/server.d.ts.map +1 -0
  57. package/dist/component/_generated/server.js +78 -0
  58. package/dist/component/_generated/server.js.map +1 -0
  59. package/dist/component/adapter.d.ts +130 -0
  60. package/dist/component/adapter.d.ts.map +1 -0
  61. package/dist/component/adapter.js +5 -0
  62. package/dist/component/adapter.js.map +1 -0
  63. package/dist/component/adapterTest.d.ts +10 -0
  64. package/dist/component/adapterTest.d.ts.map +1 -0
  65. package/dist/component/adapterTest.js +409 -0
  66. package/dist/component/adapterTest.js.map +1 -0
  67. package/dist/component/convex.config.d.ts +3 -0
  68. package/dist/component/convex.config.d.ts.map +1 -0
  69. package/dist/component/convex.config.js +4 -0
  70. package/dist/component/convex.config.js.map +1 -0
  71. package/dist/component/schema.d.ts +474 -0
  72. package/dist/component/schema.d.ts.map +1 -0
  73. package/dist/component/schema.js +139 -0
  74. package/dist/component/schema.js.map +1 -0
  75. package/dist/nextjs/client.d.ts +4 -0
  76. package/dist/nextjs/client.d.ts.map +1 -0
  77. package/dist/nextjs/client.js +37 -0
  78. package/dist/nextjs/client.js.map +1 -0
  79. package/dist/nextjs/index.d.ts +22 -0
  80. package/dist/nextjs/index.d.ts.map +1 -0
  81. package/dist/nextjs/index.js +98 -0
  82. package/dist/nextjs/index.js.map +1 -0
  83. package/dist/plugins/convex/client.d.ts +6 -0
  84. package/dist/plugins/convex/client.d.ts.map +1 -0
  85. package/dist/plugins/convex/client.js +7 -0
  86. package/dist/plugins/convex/client.js.map +1 -0
  87. package/dist/plugins/convex/index.d.ts +322 -0
  88. package/dist/plugins/convex/index.d.ts.map +1 -0
  89. package/dist/plugins/convex/index.js +422 -0
  90. package/dist/plugins/convex/index.js.map +1 -0
  91. package/dist/plugins/cross-domain/client.d.ts +132 -0
  92. package/dist/plugins/cross-domain/client.d.ts.map +1 -0
  93. package/dist/plugins/cross-domain/client.js +192 -0
  94. package/dist/plugins/cross-domain/client.js.map +1 -0
  95. package/dist/plugins/cross-domain/index.d.ts +51 -0
  96. package/dist/plugins/cross-domain/index.d.ts.map +1 -0
  97. package/dist/plugins/cross-domain/index.js +173 -0
  98. package/dist/plugins/cross-domain/index.js.map +1 -0
  99. package/dist/plugins/index.d.ts +3 -0
  100. package/dist/plugins/index.d.ts.map +1 -0
  101. package/dist/plugins/index.js +3 -0
  102. package/dist/plugins/index.js.map +1 -0
  103. package/dist/react/index.d.ts +80 -0
  104. package/dist/react/index.d.ts.map +1 -0
  105. package/dist/react/index.js +190 -0
  106. package/dist/react/index.js.map +1 -0
  107. package/dist/react-start/index.d.ts +13 -0
  108. package/dist/react-start/index.d.ts.map +1 -0
  109. package/dist/react-start/index.js +101 -0
  110. package/dist/react-start/index.js.map +1 -0
  111. package/dist/utils/index.d.ts +33 -0
  112. package/dist/utils/index.d.ts.map +1 -0
  113. package/dist/utils/index.js +91 -0
  114. package/dist/utils/index.js.map +1 -0
  115. package/package.json +208 -0
  116. package/src/auth-config.ts +80 -0
  117. package/src/auth-options.ts +54 -0
  118. package/src/auth.ts +4 -0
  119. package/src/client/adapter-utils.ts +639 -0
  120. package/src/client/adapter.test.ts +83 -0
  121. package/src/client/adapter.ts +363 -0
  122. package/src/client/create-api.ts +339 -0
  123. package/src/client/create-client.ts +452 -0
  124. package/src/client/create-schema.ts +166 -0
  125. package/src/client/index.ts +22 -0
  126. package/src/client/plugins/index.ts +2 -0
  127. package/src/component/_generated/api.ts +52 -0
  128. package/src/component/_generated/component.ts +2008 -0
  129. package/src/component/_generated/dataModel.ts +60 -0
  130. package/src/component/_generated/server.ts +161 -0
  131. package/src/component/adapter.ts +13 -0
  132. package/src/component/adapterTest.ts +505 -0
  133. package/src/component/convex.config.ts +5 -0
  134. package/src/component/schema.ts +142 -0
  135. package/src/nextjs/client.tsx +54 -0
  136. package/src/nextjs/index.ts +152 -0
  137. package/src/plugins/convex/client.ts +9 -0
  138. package/src/plugins/convex/index.ts +596 -0
  139. package/src/plugins/cross-domain/client.test.ts +217 -0
  140. package/src/plugins/cross-domain/client.ts +234 -0
  141. package/src/plugins/cross-domain/index.ts +199 -0
  142. package/src/plugins/index.ts +2 -0
  143. package/src/react/index.tsx +304 -0
  144. package/src/react-start/index.ts +153 -0
  145. package/src/react-start/vite-env.d.ts +2 -0
  146. package/src/test.ts +18 -0
  147. package/src/utils/index.ts +171 -0
@@ -0,0 +1,452 @@
1
+ import {
2
+ httpActionGeneric,
3
+ internalMutationGeneric,
4
+ queryGeneric,
5
+ } from "convex/server";
6
+ import type {
7
+ DataModelFromSchemaDefinition,
8
+ FunctionReference,
9
+ GenericDataModel,
10
+ GenericMutationCtx,
11
+ GenericSchema,
12
+ HttpRouter,
13
+ SchemaDefinition,
14
+ } from "convex/server";
15
+ import { ConvexError, v } from "convex/values";
16
+ import type { Infer } from "convex/values";
17
+ import { convexAdapter } from "./adapter.js";
18
+ import { corsRouter } from "convex-helpers/server/cors";
19
+ import type defaultSchema from "../component/schema.js";
20
+ import type { ComponentApi } from "../component/_generated/component.js";
21
+ import type { CreateAuth, GenericCtx } from "./index.js";
22
+
23
+ export type AuthFunctions = {
24
+ onCreate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
25
+ onUpdate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
26
+ onDelete?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
27
+ };
28
+
29
+ export type Triggers<
30
+ DataModel extends GenericDataModel,
31
+ Schema extends SchemaDefinition<any, any>,
32
+ > = {
33
+ [K in keyof Schema["tables"]]?: {
34
+ onCreate?: <Ctx extends GenericMutationCtx<DataModel>>(
35
+ ctx: Ctx,
36
+ doc: Infer<Schema["tables"][K]["validator"]> & {
37
+ _id: string;
38
+ _creationTime: number;
39
+ }
40
+ ) => Promise<void>;
41
+ onUpdate?: <Ctx extends GenericMutationCtx<DataModel>>(
42
+ ctx: Ctx,
43
+ newDoc: Infer<Schema["tables"][K]["validator"]> & {
44
+ _id: string;
45
+ _creationTime: number;
46
+ },
47
+ oldDoc: Infer<Schema["tables"][K]["validator"]> & {
48
+ _id: string;
49
+ _creationTime: number;
50
+ }
51
+ ) => Promise<void>;
52
+ onDelete?: <Ctx extends GenericMutationCtx<DataModel>>(
53
+ ctx: Ctx,
54
+ doc: Infer<Schema["tables"][K]["validator"]> & {
55
+ _id: string;
56
+ _creationTime: number;
57
+ }
58
+ ) => Promise<void>;
59
+ };
60
+ };
61
+
62
+ type SlimComponentApi = {
63
+ adapter: {
64
+ create: FunctionReference<"mutation", "internal">;
65
+ findOne: FunctionReference<"query", "internal">;
66
+ findMany: FunctionReference<"query", "internal">;
67
+ updateOne: FunctionReference<"mutation", "internal">;
68
+ updateMany: FunctionReference<"mutation", "internal">;
69
+ deleteOne: FunctionReference<"mutation", "internal">;
70
+ deleteMany: FunctionReference<"mutation", "internal">;
71
+ };
72
+ adapterTest?: ComponentApi["adapterTest"];
73
+ };
74
+
75
+ /**
76
+ * Backend API for the Better Auth component.
77
+ * Responsible for exposing the `client` and `triggers` APIs to the client, http
78
+ * route registration, and having convenience methods for interacting with the
79
+ * component from the backend.
80
+ *
81
+ * @param component - Generally `components.betterAuth` from
82
+ * `./_generated/api` once you've configured it in `convex.config.ts`.
83
+ * @param config - Configuration options for the component.
84
+ * @param config.local - Local schema configuration.
85
+ * @param config.verbose - Whether to enable verbose logging.
86
+ * @param config.triggers - Triggers configuration.
87
+ * @param config.authFunctions - Authentication functions configuration.
88
+ */
89
+ export const createClient = <
90
+ DataModel extends GenericDataModel,
91
+ Schema extends SchemaDefinition<GenericSchema, true> = typeof defaultSchema,
92
+ Api extends SlimComponentApi = SlimComponentApi,
93
+ >(
94
+ component: Api,
95
+ config?: {
96
+ local?: {
97
+ schema?: Schema;
98
+ };
99
+ verbose?: boolean;
100
+ } & (
101
+ | {
102
+ triggers: Triggers<DataModel, Schema>;
103
+ authFunctions: AuthFunctions;
104
+ }
105
+ | { triggers?: undefined }
106
+ )
107
+ ) => {
108
+ type BetterAuthDataModel = DataModelFromSchemaDefinition<Schema>;
109
+
110
+ const safeGetAuthUser = async (ctx: GenericCtx<DataModel>) => {
111
+ const identity = await ctx.auth.getUserIdentity();
112
+ if (!identity) {
113
+ return;
114
+ }
115
+ const session = (await ctx.runQuery(component.adapter.findOne, {
116
+ model: "session",
117
+ where: [
118
+ {
119
+ field: "_id",
120
+ value: identity.sessionId as string,
121
+ },
122
+ {
123
+ field: "expiresAt",
124
+ operator: "gt",
125
+ value: new Date().getTime(),
126
+ },
127
+ ],
128
+ })) as BetterAuthDataModel["session"]["document"] | null;
129
+
130
+ if (!session) {
131
+ return;
132
+ }
133
+
134
+ const doc = (await ctx.runQuery(component.adapter.findOne, {
135
+ model: "user",
136
+ where: [
137
+ {
138
+ field: "_id",
139
+ value: identity.subject,
140
+ },
141
+ ],
142
+ })) as BetterAuthDataModel["user"]["document"] | null;
143
+ if (!doc) {
144
+ return;
145
+ }
146
+ return doc;
147
+ };
148
+
149
+ const getAuthUser = async (ctx: GenericCtx<DataModel>) => {
150
+ const user = await safeGetAuthUser(ctx);
151
+ if (!user) {
152
+ throw new ConvexError("Unauthenticated");
153
+ }
154
+ return user;
155
+ };
156
+
157
+ const getHeaders = async (ctx: GenericCtx<DataModel>) => {
158
+ const identity = await ctx.auth.getUserIdentity();
159
+ if (!identity) {
160
+ return new Headers();
161
+ }
162
+ // Don't validate the session here, let Better Auth handle that
163
+ const session = await ctx.runQuery(component.adapter.findOne, {
164
+ model: "session",
165
+ where: [
166
+ {
167
+ field: "_id",
168
+ value: identity.sessionId as string,
169
+ },
170
+ ],
171
+ });
172
+ return new Headers({
173
+ ...(session?.token ? { authorization: `Bearer ${session.token}` } : {}),
174
+ ...(session?.ipAddress
175
+ ? { "x-forwarded-for": session.ipAddress as string }
176
+ : {}),
177
+ });
178
+ };
179
+
180
+ return {
181
+ /**
182
+ * Returns the Convex database adapter for use in Better Auth options.
183
+ * @param ctx - The Convex context
184
+ * @returns The Convex database adapter
185
+ */
186
+ adapter: (ctx: GenericCtx<DataModel>) =>
187
+ convexAdapter<DataModel, typeof ctx, Schema>(ctx, component, {
188
+ ...config,
189
+ debugLogs: config?.verbose,
190
+ }),
191
+
192
+ /**
193
+ * Returns the Better Auth auth object and headers for using Better Auth API
194
+ * methods directly in a Convex mutation or query. Convex functions don't
195
+ * have access to request headers, so the headers object is created at
196
+ * runtime with the token for the current session as a Bearer token.
197
+ *
198
+ * @param createAuth - The createAuth function
199
+ * @param ctx - The Convex context
200
+ * @returns A promise that resolves to the Better Auth `auth` API object and
201
+ * headers.
202
+ */
203
+ getAuth: async <T extends CreateAuth<DataModel>>(
204
+ createAuth: T,
205
+ ctx: GenericCtx<DataModel>
206
+ ) => ({
207
+ auth: createAuth(ctx) as ReturnType<T>,
208
+ headers: await getHeaders(ctx),
209
+ }),
210
+
211
+ /**
212
+ * Returns a Headers object for the current session using the session id
213
+ * from the current user identity via `ctx.auth.getUserIdentity()`. This is
214
+ * used to pass the headers to the Better Auth API methods when using the
215
+ * `getAuth` method.
216
+ *
217
+ * @param ctx - The Convex context
218
+ * @returns The headers
219
+ */
220
+ getHeaders,
221
+
222
+ /**
223
+ * Returns the current user or null if the user is not found
224
+ * @param ctx - The Convex context
225
+ * @returns The user or null if the user is not found
226
+ */
227
+ safeGetAuthUser,
228
+
229
+ /**
230
+ * Returns the current user or throws an error if the user is not found
231
+ *
232
+ * @param ctx - The Convex context
233
+ * @returns The user or throws an error if the user is not found
234
+ */
235
+ getAuthUser,
236
+
237
+ /**
238
+ * Returns a user by their Better Auth user id.
239
+ * @param ctx - The Convex context
240
+ * @param id - The Better Auth user id
241
+ * @returns The user or null if the user is not found
242
+ */
243
+ getAnyUserById: async (ctx: GenericCtx<DataModel>, id: string) => {
244
+ return (await ctx.runQuery(component.adapter.findOne, {
245
+ model: "user",
246
+ where: [{ field: "_id", value: id }],
247
+ })) as BetterAuthDataModel["user"]["document"] | null;
248
+ },
249
+
250
+ /**
251
+ * Replaces 0.7 behavior of returning a new user id from
252
+ * onCreateUser
253
+ * @param ctx - The Convex context
254
+ * @param authId - The Better Auth user id
255
+ * @param userId - The app user id
256
+ * @deprecated in 0.9
257
+ */
258
+ setUserId: async (
259
+ ctx: GenericMutationCtx<DataModel>,
260
+ authId: string,
261
+ userId: string
262
+ ) => {
263
+ await ctx.runMutation(component.adapter.updateOne, {
264
+ input: {
265
+ model: "user",
266
+ where: [{ field: "_id", value: authId }],
267
+ update: { userId },
268
+ },
269
+ });
270
+ },
271
+
272
+ /**
273
+ * Exposes functions for use with the ClientAuthBoundary component. Currently
274
+ * only contains getAuthUser.
275
+ * @returns Functions to pass to the ClientAuthBoundary component.
276
+ */
277
+ clientApi: () => ({
278
+ /**
279
+ * Convex query to get the current user. For use with the ClientAuthBoundary component.
280
+ *
281
+ * ```ts title="convex/auth.ts"
282
+ * export const { getAuthUser } = authComponent.clientApi();
283
+ * ```
284
+ *
285
+ * @returns The user or throws an error if the user is not found
286
+ */
287
+ getAuthUser: queryGeneric({
288
+ args: {},
289
+ handler: async (ctx: GenericCtx<DataModel>) => {
290
+ return await getAuthUser(ctx);
291
+ },
292
+ }),
293
+ }),
294
+
295
+ /**
296
+ * Exposes functions for executing trigger callbacks in the app context.
297
+ *
298
+ * Callbacks are defined in the `triggers` option to the component client config.
299
+ *
300
+ * See {@link createClient} for more information.
301
+ *
302
+ * @returns Functions to execute trigger callbacks in the app context.
303
+ */
304
+ triggersApi: () => ({
305
+ onCreate: internalMutationGeneric({
306
+ args: {
307
+ doc: v.any(),
308
+ model: v.string(),
309
+ },
310
+ handler: async (ctx, args) => {
311
+ await config?.triggers?.[args.model]?.onCreate?.(ctx, args.doc);
312
+ },
313
+ }),
314
+ onUpdate: internalMutationGeneric({
315
+ args: {
316
+ oldDoc: v.any(),
317
+ newDoc: v.any(),
318
+ model: v.string(),
319
+ },
320
+ handler: async (ctx, args) => {
321
+ await config?.triggers?.[args.model]?.onUpdate?.(
322
+ ctx,
323
+ args.newDoc,
324
+ args.oldDoc
325
+ );
326
+ },
327
+ }),
328
+ onDelete: internalMutationGeneric({
329
+ args: {
330
+ doc: v.any(),
331
+ model: v.string(),
332
+ },
333
+ handler: async (ctx, args) => {
334
+ await config?.triggers?.[args.model]?.onDelete?.(ctx, args.doc);
335
+ },
336
+ }),
337
+ }),
338
+
339
+ registerRoutes: (
340
+ http: HttpRouter,
341
+ createAuth: CreateAuth<DataModel>,
342
+ opts: {
343
+ cors?:
344
+ | boolean
345
+ | {
346
+ // These values are appended to the default values
347
+ allowedOrigins?: string[];
348
+ allowedHeaders?: string[];
349
+ exposedHeaders?: string[];
350
+ };
351
+ } = {}
352
+ ) => {
353
+ const staticAuth = createAuth({} as any);
354
+ const path = staticAuth.options.basePath ?? "/api/auth";
355
+ const authRequestHandler = httpActionGeneric(async (ctx, request) => {
356
+ if (config?.verbose) {
357
+ // eslint-disable-next-line no-console
358
+ console.log("options.baseURL", staticAuth.options.baseURL);
359
+ // eslint-disable-next-line no-console
360
+ console.log("request headers", request.headers);
361
+ }
362
+ const auth = createAuth(ctx as any);
363
+ const response = await auth.handler(request);
364
+ if (config?.verbose) {
365
+ // eslint-disable-next-line no-console
366
+ console.log("response headers", response.headers);
367
+ }
368
+ return response;
369
+ });
370
+ const wellKnown = http.lookup("/.well-known/openid-configuration", "GET");
371
+
372
+ // If registerRoutes is used multiple times, this may already be defined
373
+ if (!wellKnown) {
374
+ // Redirect root well-known to api well-known
375
+ http.route({
376
+ path: "/.well-known/openid-configuration",
377
+ method: "GET",
378
+ handler: httpActionGeneric(async () => {
379
+ const url = `${process.env.CONVEX_SITE_URL}${path}/convex/.well-known/openid-configuration`;
380
+ return Response.redirect(url);
381
+ }),
382
+ });
383
+ }
384
+
385
+ if (!opts.cors) {
386
+ http.route({
387
+ pathPrefix: `${path}/`,
388
+ method: "GET",
389
+ handler: authRequestHandler,
390
+ });
391
+
392
+ http.route({
393
+ pathPrefix: `${path}/`,
394
+ method: "POST",
395
+ handler: authRequestHandler,
396
+ });
397
+
398
+ return;
399
+ }
400
+ const corsOpts =
401
+ typeof opts.cors === "boolean"
402
+ ? { allowedOrigins: [], allowedHeaders: [], exposedHeaders: [] }
403
+ : opts.cors;
404
+ let trustedOriginsOption:
405
+ | string[]
406
+ | ((request: Request) => string[] | Promise<string[]>)
407
+ | undefined;
408
+ const cors = corsRouter(http, {
409
+ allowedOrigins: async (request) => {
410
+ trustedOriginsOption =
411
+ trustedOriginsOption ??
412
+ (await staticAuth.$context).options.trustedOrigins ??
413
+ [];
414
+ const trustedOrigins = Array.isArray(trustedOriginsOption)
415
+ ? trustedOriginsOption
416
+ : await trustedOriginsOption(request);
417
+ return trustedOrigins
418
+ .map((origin) =>
419
+ // Strip trailing wildcards, unsupported for allowedOrigins
420
+ origin.endsWith("*") && origin.length > 1
421
+ ? origin.slice(0, -1)
422
+ : origin
423
+ )
424
+ .concat(corsOpts.allowedOrigins ?? []);
425
+ },
426
+ allowCredentials: true,
427
+ allowedHeaders: [
428
+ "Content-Type",
429
+ "Better-Auth-Cookie",
430
+ "Authorization",
431
+ ].concat(corsOpts.allowedHeaders ?? []),
432
+ exposedHeaders: ["Set-Better-Auth-Cookie"].concat(
433
+ corsOpts.exposedHeaders ?? []
434
+ ),
435
+ debug: config?.verbose,
436
+ enforceAllowOrigins: false,
437
+ });
438
+
439
+ cors.route({
440
+ pathPrefix: `${path}/`,
441
+ method: "GET",
442
+ handler: authRequestHandler,
443
+ });
444
+
445
+ cors.route({
446
+ pathPrefix: `${path}/`,
447
+ method: "POST",
448
+ handler: authRequestHandler,
449
+ });
450
+ },
451
+ };
452
+ };
@@ -0,0 +1,166 @@
1
+ import type { BetterAuthDBSchema, DBFieldAttribute } from "better-auth/db";
2
+
3
+ // Manually add fields to index on for schema generation,
4
+ // all fields in the schema specialFields are automatically indexed
5
+ export const indexFields = {
6
+ account: ["accountId", ["accountId", "providerId"], ["providerId", "userId"]],
7
+ rateLimit: ["key"],
8
+ session: ["expiresAt", ["expiresAt", "userId"]],
9
+ verification: ["expiresAt", "identifier"],
10
+ user: [["email", "name"], "name", "userId"],
11
+ passkey: ["credentialID"],
12
+ oauthConsent: [["clientId", "userId"]],
13
+ };
14
+
15
+ // Return map of unique, sortable, and reference fields
16
+ const specialFields = (tables: BetterAuthDBSchema) =>
17
+ Object.fromEntries(
18
+ Object.entries(tables)
19
+ .map(([key, table]) => {
20
+ const fields = Object.fromEntries(
21
+ Object.entries(table.fields)
22
+ .map(([fieldKey, field]) => [
23
+ field.fieldName ?? fieldKey,
24
+ {
25
+ ...(field.sortable ? { sortable: true } : {}),
26
+ ...(field.unique ? { unique: true } : {}),
27
+ ...(field.references ? { references: field.references } : {}),
28
+ },
29
+ ])
30
+ .filter(([_key, value]) =>
31
+ typeof value === "object" ? Object.keys(value).length > 0 : true
32
+ )
33
+ );
34
+ return [key, fields];
35
+ })
36
+ .filter(([_key, value]) =>
37
+ typeof value === "object" ? Object.keys(value).length > 0 : true
38
+ )
39
+ );
40
+
41
+ const mergedIndexFields = (tables: BetterAuthDBSchema) =>
42
+ Object.fromEntries(
43
+ Object.entries(tables).map(([key, table]) => {
44
+ const manualIndexes =
45
+ indexFields[key as keyof typeof indexFields]?.map((index) => {
46
+ return typeof index === "string"
47
+ ? (table.fields[index]?.fieldName ?? index)
48
+ : index.map((i) => table.fields[i]?.fieldName ?? i);
49
+ }) || [];
50
+ const specialFieldIndexes = Object.keys(
51
+ specialFields(tables)[key as keyof ReturnType<typeof specialFields>] ||
52
+ {}
53
+ ).filter(
54
+ (index) =>
55
+ !manualIndexes.some((m) =>
56
+ Array.isArray(m) ? m[0] === index : m === index
57
+ )
58
+ );
59
+ return [key, manualIndexes.concat(specialFieldIndexes)];
60
+ })
61
+ );
62
+
63
+ export const createSchema = async ({
64
+ file,
65
+ tables,
66
+ }: {
67
+ tables: BetterAuthDBSchema;
68
+ file?: string;
69
+ }) => {
70
+ // stop convex esbuild from throwing over this import, only runs
71
+ // in the better auth cli
72
+ const pathImport = "path";
73
+ const path = await import(pathImport);
74
+ const baseName = path.basename(path.resolve(process.cwd(), file ?? ""));
75
+ // if the target directory is named "convex", they're almost definitely
76
+ // generating the schema in the wrong directory, likely would replace the
77
+ // app schema
78
+ if (baseName === "convex") {
79
+ throw new Error(
80
+ "Better Auth schema must be generated in the Better Auth component directory."
81
+ );
82
+ }
83
+ let code: string = `/**
84
+ * This file is auto-generated. Do not edit this file manually.
85
+ * To regenerate the schema, run:
86
+ * \`npx @better-auth/cli generate --output ${file} -y\`
87
+ *
88
+ * To customize the schema, generate to an alternate file and import
89
+ * the table definitions to your schema file. See
90
+ * https://labs.convex.dev/better-auth/features/local-install#adding-custom-indexes.
91
+ */
92
+
93
+ import { defineSchema, defineTable } from "convex/server";
94
+ import { v } from "convex/values";
95
+
96
+ export const tables = {
97
+ `;
98
+
99
+ for (const tableKey in tables) {
100
+ const table = tables[tableKey]!;
101
+ const modelName = table.modelName;
102
+
103
+ // No id fields in Convex schema
104
+ const fields = Object.fromEntries(
105
+ Object.entries(table.fields).filter(([key]) => key !== "id")
106
+ );
107
+
108
+ function getType(name: string, field: DBFieldAttribute) {
109
+ const type = field.type as
110
+ | "string"
111
+ | "number"
112
+ | "boolean"
113
+ | "date"
114
+ | "json"
115
+ | `${"string" | "number"}[]`;
116
+
117
+ const typeMap: Record<typeof type, string> = {
118
+ string: `v.string()`,
119
+ boolean: `v.boolean()`,
120
+ number: `v.number()`,
121
+ date: `v.number()`,
122
+ json: `v.string()`,
123
+ "number[]": `v.array(v.number())`,
124
+ "string[]": `v.array(v.string())`,
125
+ } as const;
126
+ return typeMap[type];
127
+ }
128
+
129
+ const indexes =
130
+ mergedIndexFields(tables)[
131
+ tableKey as keyof typeof mergedIndexFields
132
+ ]?.map((index) => {
133
+ const indexArray = Array.isArray(index) ? index.sort() : [index];
134
+ const indexName = indexArray.join("_");
135
+ return `.index("${indexName}", ${JSON.stringify(indexArray)})`;
136
+ }) || [];
137
+
138
+ const schema = `${modelName}: defineTable({
139
+ ${Object.keys(fields)
140
+ .map((field) => {
141
+ const attr = fields[field]!;
142
+ const type = getType(field, attr as DBFieldAttribute);
143
+ const optional = (fieldSchema: string) =>
144
+ attr.required
145
+ ? fieldSchema
146
+ : `v.optional(v.union(v.null(), ${fieldSchema}))`;
147
+ return ` ${attr.fieldName ?? field}: ${optional(type)},`;
148
+ })
149
+ .join("\n")}
150
+ })${indexes.length > 0 ? `\n ${indexes.join("\n ")}` : ""},\n`;
151
+ code += ` ${schema}`;
152
+ }
153
+
154
+ code += `};
155
+
156
+ const schema = defineSchema(tables);
157
+
158
+ export default schema;
159
+ `;
160
+
161
+ return {
162
+ code,
163
+ path: file ?? "./schema.ts",
164
+ overwrite: true,
165
+ };
166
+ };
@@ -0,0 +1,22 @@
1
+ import { convexAdapter } from "./adapter.js";
2
+ import { version as convexVersion } from "convex";
3
+ import semverLt from "semver/functions/lt.js";
4
+ import { createClient } from "./create-client.js";
5
+ import type { AuthFunctions, Triggers } from "./create-client.js";
6
+ import { createApi } from "./create-api.js";
7
+ import type { CreateAuth, EventFunction, GenericCtx } from "../utils/index.js";
8
+
9
+ if (semverLt(convexVersion, "1.25.0")) {
10
+ throw new Error("Convex version must be at least 1.25.0");
11
+ }
12
+
13
+ export {
14
+ convexAdapter,
15
+ createClient,
16
+ createApi,
17
+ type CreateAuth,
18
+ type EventFunction,
19
+ type GenericCtx,
20
+ type Triggers,
21
+ type AuthFunctions,
22
+ };
@@ -0,0 +1,2 @@
1
+ export * from "../../plugins/convex/client.js";
2
+ export * from "../../plugins/cross-domain/client.js";
@@ -0,0 +1,52 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `api` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type * as adapter from "../adapter.js";
12
+ import type * as adapterTest from "../adapterTest.js";
13
+
14
+ import type {
15
+ ApiFromModules,
16
+ FilterApi,
17
+ FunctionReference,
18
+ } from "convex/server";
19
+ import { anyApi, componentsGeneric } from "convex/server";
20
+
21
+ const fullApi: ApiFromModules<{
22
+ adapter: typeof adapter;
23
+ adapterTest: typeof adapterTest;
24
+ }> = anyApi as any;
25
+
26
+ /**
27
+ * A utility for referencing Convex functions in your app's public API.
28
+ *
29
+ * Usage:
30
+ * ```js
31
+ * const myFunctionReference = api.myModule.myFunction;
32
+ * ```
33
+ */
34
+ export const api: FilterApi<
35
+ typeof fullApi,
36
+ FunctionReference<any, "public">
37
+ > = anyApi as any;
38
+
39
+ /**
40
+ * A utility for referencing Convex functions in your app's internal API.
41
+ *
42
+ * Usage:
43
+ * ```js
44
+ * const myFunctionReference = internal.myModule.myFunction;
45
+ * ```
46
+ */
47
+ export const internal: FilterApi<
48
+ typeof fullApi,
49
+ FunctionReference<any, "internal">
50
+ > = anyApi as any;
51
+
52
+ export const components = componentsGeneric() as unknown as {};