@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,22 +1,168 @@
1
- import { type BetterAuthPlugin } from "better-auth";
1
+ import {
2
+ type BetterAuthOptions,
3
+ type BetterAuthPlugin,
4
+ type Session,
5
+ type User,
6
+ } from "better-auth";
2
7
  import { createAuthMiddleware, sessionMiddleware } from "better-auth/api";
3
8
  import {
4
9
  createAuthEndpoint,
5
10
  jwt as jwtPlugin,
6
11
  bearer as bearerPlugin,
7
12
  oidcProvider as oidcProviderPlugin,
13
+ type JwtOptions,
14
+ type Jwk,
8
15
  } from "better-auth/plugins";
16
+ import { omit } from "convex-helpers";
17
+ import type { AuthConfig, AuthProvider } from "convex/server";
9
18
 
10
19
  export const JWT_COOKIE_NAME = "convex_jwt";
11
20
 
12
- export const convex = (
13
- opts: {
14
- jwtExpirationSeconds?: number;
15
- deleteExpiredSessionsOnLogin?: boolean;
16
- options?: { basePath?: string };
17
- } = {}
18
- ) => {
19
- const { jwtExpirationSeconds = 60 * 15 } = opts;
21
+ const getJwksAlg = (authProvider: AuthProvider) => {
22
+ const isCustomJwt =
23
+ "type" in authProvider && authProvider.type === "customJwt";
24
+ if (isCustomJwt && authProvider.algorithm !== "RS256") {
25
+ throw new Error("Only RS256 is supported for custom JWT with Better Auth");
26
+ }
27
+ return isCustomJwt ? authProvider.algorithm : "EdDSA";
28
+ };
29
+
30
+ const parseAuthConfig = (authConfig: AuthConfig, opts: { jwks?: string }) => {
31
+ const providerConfigs = authConfig.providers.filter(
32
+ (provider) => provider.applicationID === "convex"
33
+ );
34
+ if (providerConfigs.length > 1) {
35
+ throw new Error(
36
+ "Multiple auth providers with applicationID 'convex' detected. Please use only one."
37
+ );
38
+ }
39
+ const providerConfig = providerConfigs[0];
40
+ if (!providerConfig) {
41
+ throw new Error(
42
+ "No auth provider with applicationID 'convex' found. Please add one to your auth config."
43
+ );
44
+ }
45
+ if (!("type" in providerConfig) || providerConfig.type !== "customJwt") {
46
+ return providerConfig;
47
+ }
48
+
49
+ const isDataUriJwks = providerConfig.jwks?.startsWith("data:text/");
50
+
51
+ if (isDataUriJwks && !opts.jwks) {
52
+ throw new Error(
53
+ "Static JWKS detected in auth config, but missing from Convex plugin"
54
+ );
55
+ }
56
+ if (!isDataUriJwks && opts.jwks) {
57
+ console.warn(
58
+ "Static JWKS provided to Convex plugin, but not to auth config. This adds an unnecessary network request for token verification."
59
+ );
60
+ }
61
+ return providerConfig;
62
+ };
63
+
64
+ export const convex = (opts: {
65
+ /**
66
+ * @param {AuthConfig} authConfig - Auth config from your Convex project.
67
+ *
68
+ * Typically found in `convex/auth.config.ts`.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * // convex/auth.config.ts
73
+ * export default {
74
+ * providers: [getAuthConfigProvider({ jwks: process.env.JWKS })],
75
+ * } satisfies AuthConfig;
76
+ * ```
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * // convex/auth.ts
81
+ * import authConfig from './auth.config';
82
+ * export const createAuth = (ctx: GenericCtx<DataModel>) => {
83
+ * return betterAuth({
84
+ * // ...
85
+ * plugins: [convex({ authConfig })],
86
+ * });
87
+ * };
88
+ * ```
89
+ */
90
+ authConfig: AuthConfig;
91
+ /**
92
+ * @param {Object} jwt - JWT options.
93
+ * @param {number} jwt.expirationSeconds - JWT expiration seconds.
94
+ * @param {Function} jwt.definePayload - Function to define the JWT payload. `sessionId` and `iat` are added automatically.
95
+ */
96
+ jwt?: {
97
+ expirationSeconds?: number;
98
+ definePayload?: (session: {
99
+ user: User & Record<string, any>;
100
+ session: Session & Record<string, any>;
101
+ }) => Promise<Record<string, any>> | Record<string, any> | undefined;
102
+ };
103
+ /**
104
+ * @deprecated Use jwt.expirationSeconds instead.
105
+ */
106
+ jwtExpirationSeconds?: number;
107
+ /**
108
+ * @param {string} jwks - Optional static JWKS to avoid fetching from the database.
109
+ *
110
+ * This should be a stringified document from the Better Auth JWKS table. You
111
+ * can create one in the console.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * // convex/auth.ts
116
+ * export const rotateKeys = internalAction({
117
+ * args: {},
118
+ * handler: async (ctx) => {
119
+ * const auth = createAuth(ctx)
120
+ * return await auth.api.rotateKeys()
121
+ * },
122
+ * })
123
+ * ```
124
+ * Run the action and set the JWKS environment variable
125
+ *
126
+ * ```bash
127
+ * npx convex run auth:rotateKeys | npx convex env set JWKS
128
+ * ```
129
+ * Then use it in your auth config and Better Auth options:
130
+ *
131
+ * ```ts
132
+ * // convex/auth.config.ts
133
+ * export default {
134
+ * providers: [getAuthConfigProvider({ jwks: process.env.JWKS })],
135
+ * } satisfies AuthConfig;
136
+ *
137
+ * // convex/auth.ts
138
+ * export const createAuth = (ctx: GenericCtx<DataModel>) => {
139
+ * return betterAuth({
140
+ * // ...
141
+ * plugins: [convex({ authConfig, jwks: process.env.JWKS })],
142
+ * });
143
+ * };
144
+ * ```
145
+ */
146
+ jwks?: string;
147
+ /**
148
+ * @param {boolean} jwksRotateOnTokenGenerationError - Whether to rotate the JWKS on token generation error.
149
+ *
150
+ * Does nothing if a static JWKS is provided.
151
+ *
152
+ * Handles error that occurs when existing JWKS key does not match configured
153
+ * algorithm, which will be common for 0.10 upgrades switching from EdDSA to RS256.
154
+ *
155
+ * @default true
156
+ */
157
+ jwksRotateOnTokenGenerationError?: boolean;
158
+ /**
159
+ * @param {BetterAuthOptions} options - Better Auth options. Not required,
160
+ * currently used to pass the basePath to the oidcProvider plugin.
161
+ */
162
+ options?: BetterAuthOptions;
163
+ }) => {
164
+ const jwtExpirationSeconds =
165
+ opts.jwt?.expirationSeconds ?? opts.jwtExpirationSeconds ?? 60 * 15;
20
166
  const oidcProvider = oidcProviderPlugin({
21
167
  loginPage: "/not-used",
22
168
  metadata: {
@@ -24,18 +170,63 @@ export const convex = (
24
170
  jwks_uri: `${process.env.CONVEX_SITE_URL}${opts.options?.basePath ?? "/api/auth"}/convex/jwks`,
25
171
  },
26
172
  });
27
- const jwt = jwtPlugin({
173
+ const providerConfig = parseAuthConfig(opts.authConfig, opts);
174
+
175
+ const jwtOptions = {
28
176
  jwt: {
29
177
  issuer: `${process.env.CONVEX_SITE_URL}`,
30
178
  audience: "convex",
31
179
  expirationTime: `${jwtExpirationSeconds}s`,
32
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
- definePayload: ({ user: { id, image, ...user }, session }) => ({
34
- ...user,
180
+ definePayload: ({ user, session }) => ({
181
+ ...(opts.jwt?.definePayload
182
+ ? opts.jwt.definePayload({ user, session })
183
+ : omit(user, ["id", "image"])),
35
184
  sessionId: session.id,
36
185
  iat: Math.floor(new Date().getTime() / 1000),
37
186
  }),
38
187
  },
188
+ jwks: {
189
+ keyPairConfig: {
190
+ alg: getJwksAlg(providerConfig),
191
+ },
192
+ },
193
+ } satisfies JwtOptions;
194
+ const jwks = opts.jwks ? JSON.parse(opts.jwks) : undefined;
195
+ const jwt = jwtPlugin({
196
+ ...jwtOptions,
197
+ adapter: {
198
+ createJwk: async (webKey, ctx) => {
199
+ if (opts.jwks) {
200
+ throw new Error("Not implemented");
201
+ }
202
+ // TODO: remove when date parsing for jwks adapter is fixed upstream
203
+ return await ctx.context.adapter.create<Omit<Jwk, "id">, Jwk>({
204
+ model: "jwks",
205
+ data: {
206
+ ...webKey,
207
+ createdAt: new Date(),
208
+ },
209
+ });
210
+ },
211
+ getJwks: async (ctx) => {
212
+ if (opts.jwks) {
213
+ return jwks;
214
+ }
215
+ // TODO: remove when date parsing for jwks adapter is fixed upstream
216
+ const keys: Jwk[] = await ctx.context.adapter.findMany<Jwk>({
217
+ model: "jwks",
218
+ sortBy: {
219
+ field: "createdAt",
220
+ direction: "desc",
221
+ },
222
+ });
223
+ return keys.map((key) => ({
224
+ ...key,
225
+ createdAt: new Date(key.createdAt),
226
+ ...(key.expiresAt ? { expiresAt: new Date(key.expiresAt) } : {}),
227
+ }));
228
+ },
229
+ },
39
230
  });
40
231
  // Bearer plugin converts the session token to a cookie
41
232
  // for cross domain social login after code verification,
@@ -48,19 +239,23 @@ export const convex = (
48
239
  ...jwt.schema,
49
240
  };
50
241
 
51
- const parseSetCookie = (setCookieHeader: string) => {
52
- return setCookieHeader
53
- .split(", ")
54
- .map((cookie) => {
55
- const semiIdx = cookie.indexOf(";");
56
- const endIdx = semiIdx === -1 ? cookie.length + 1 : semiIdx;
57
- return cookie.slice(0, endIdx);
58
- })
59
- .join("; ");
60
- };
61
242
  return {
62
243
  id: "convex",
63
- init: ({ logger, options }) => {
244
+ init: (ctx) => {
245
+ const { options, logger } = ctx;
246
+ if (options.basePath !== "/api/auth" && !opts.options?.basePath) {
247
+ console.warn(
248
+ `Better Auth basePath set to ${options.basePath} but no basePath is set in the Convex plugin. This is probably a mistake.`
249
+ );
250
+ }
251
+ if (
252
+ opts.options?.basePath &&
253
+ options.basePath !== opts.options?.basePath
254
+ ) {
255
+ console.warn(
256
+ `Better Auth basePath ${options.basePath} does not match Convex plugin basePath ${opts.options?.basePath}. This is probably a mistake.`
257
+ );
258
+ }
64
259
  if (
65
260
  options.plugins?.every((p) => p.id !== "cross-domain") &&
66
261
  !options.baseURL
@@ -96,32 +291,30 @@ export const convex = (
96
291
  ...oidcProvider.hooks.after,
97
292
  {
98
293
  matcher: (ctx) => {
99
- return (
294
+ return Boolean(
100
295
  ctx.path.startsWith("/sign-in") ||
101
- ctx.path.startsWith("/sign-up") ||
102
- ctx.path.startsWith("/callback") ||
103
- ctx.path.startsWith("/oauth2/callback") ||
104
- ctx.path.startsWith("/magic-link/verify") ||
105
- ctx.path.startsWith("/email-otp/verify-email") ||
106
- ctx.path.startsWith("/phone-number/verify") ||
107
- ctx.path.startsWith("/siwe/verify")
296
+ ctx.path.startsWith("/sign-up") ||
297
+ ctx.path.startsWith("/callback") ||
298
+ ctx.path.startsWith("/oauth2/callback") ||
299
+ ctx.path.startsWith("/magic-link/verify") ||
300
+ ctx.path.startsWith("/email-otp/verify-email") ||
301
+ ctx.path.startsWith("/phone-number/verify") ||
302
+ ctx.path.startsWith("/siwe/verify") ||
303
+ (ctx.path.startsWith("/get-session") && ctx.context.session)
108
304
  );
109
305
  },
110
306
  handler: createAuthMiddleware(async (ctx) => {
111
- // Set jwt cookie at login for ssa using set-cookie header
112
- const setCookie =
113
- ctx.context.responseHeaders?.get("set-cookie") ?? "";
114
- if (!setCookie) {
115
- return;
116
- }
307
+ // Set jwt cookie at login for authenticated ssr
308
+ const originalSession = ctx.context.session;
117
309
  try {
310
+ ctx.context.session =
311
+ ctx.context.session ?? ctx.context.newSession;
118
312
  const { token } = await jwt.endpoints.getToken({
119
313
  ...ctx,
314
+ headers: {},
120
315
  method: "GET",
121
- headers: {
122
- cookie: parseSetCookie(setCookie),
123
- },
124
316
  returnHeaders: false,
317
+ returnStatus: false,
125
318
  });
126
319
  const jwtCookie = ctx.context.createAuthCookie(JWT_COOKIE_NAME, {
127
320
  maxAge: jwtExpirationSeconds,
@@ -132,13 +325,15 @@ export const convex = (
132
325
  // no-op, some sign-in calls (eg., when redirecting to 2fa)
133
326
  // 401 here
134
327
  }
328
+ ctx.context.session = originalSession;
135
329
  }),
136
330
  },
137
331
  {
138
332
  matcher: (ctx) => {
139
333
  return (
140
334
  ctx.path?.startsWith("/sign-out") ||
141
- ctx.path?.startsWith("/delete-user")
335
+ ctx.path?.startsWith("/delete-user") ||
336
+ (ctx.path?.startsWith("/get-session") && !ctx.context.session)
142
337
  );
143
338
  },
144
339
  handler: createAuthMiddleware(async (ctx) => {
@@ -158,11 +353,13 @@ export const convex = (
158
353
  metadata: {
159
354
  isAction: false,
160
355
  },
356
+ // TODO: properly type this
161
357
  },
162
358
  async (ctx) => {
163
359
  const response = await oidcProvider.endpoints.getOpenIdConfig({
164
360
  ...ctx,
165
361
  returnHeaders: false,
362
+ returnStatus: false,
166
363
  });
167
364
  return response;
168
365
  }
@@ -258,10 +455,76 @@ export const convex = (
258
455
  const response = await jwt.endpoints.getJwks({
259
456
  ...ctx,
260
457
  returnHeaders: false,
458
+ returnStatus: false,
261
459
  });
262
460
  return response;
263
461
  }
264
462
  ),
463
+ getLatestJwks: createAuthEndpoint(
464
+ {
465
+ isAction: true,
466
+ method: "POST",
467
+ metadata: {
468
+ SERVER_ONLY: true,
469
+ openapi: {
470
+ description:
471
+ "Delete and regenerate JWKS, and return the new JWKS for static usage",
472
+ },
473
+ },
474
+ },
475
+ async (ctx) => {
476
+ // Ensure at least one key exists
477
+ await jwtPlugin(jwtOptions).endpoints.getJwks({
478
+ ...ctx,
479
+ method: "GET",
480
+ });
481
+ const jwks: any[] = await ctx.context.adapter.findMany({
482
+ model: "jwks",
483
+ limit: 1,
484
+ sortBy: {
485
+ field: "createdAt",
486
+ direction: "desc",
487
+ },
488
+ });
489
+ // Add alg to jwks, otherwise Better Auth will default to EdDSA
490
+ jwks[0].alg = jwtOptions.jwks.keyPairConfig.alg;
491
+ return jwks;
492
+ }
493
+ ),
494
+ rotateKeys: createAuthEndpoint(
495
+ {
496
+ isAction: true,
497
+ method: "POST",
498
+ metadata: {
499
+ SERVER_ONLY: true,
500
+ openapi: {
501
+ description:
502
+ "Delete and regenerate JWKS, and return the new JWKS for static usage",
503
+ },
504
+ },
505
+ },
506
+ async (ctx) => {
507
+ await ctx.context.adapter.deleteMany({
508
+ model: "jwks",
509
+ where: [],
510
+ });
511
+
512
+ await jwtPlugin(jwtOptions).endpoints.getJwks({
513
+ ...ctx,
514
+ method: "GET",
515
+ });
516
+ const jwks: any[] = await ctx.context.adapter.findMany({
517
+ model: "jwks",
518
+ limit: 1,
519
+ sortBy: {
520
+ field: "createdAt",
521
+ direction: "desc",
522
+ },
523
+ });
524
+ jwks[0].alg = jwtOptions.jwks.keyPairConfig.alg;
525
+ return jwks;
526
+ }
527
+ ),
265
528
  getToken: createAuthEndpoint(
266
529
  "/convex/token",
267
530
  {
@@ -292,15 +555,38 @@ export const convex = (
292
555
  },
293
556
  },
294
557
  async (ctx) => {
295
- const response = await jwt.endpoints.getToken({
296
- ...ctx,
297
- returnHeaders: false,
298
- });
299
- const jwtCookie = ctx.context.createAuthCookie(JWT_COOKIE_NAME, {
300
- maxAge: jwtExpirationSeconds,
301
- });
302
- ctx.setCookie(jwtCookie.name, response.token, jwtCookie.attributes);
303
- return response;
558
+ const runEndpoint = async () => {
559
+ const response = await jwt.endpoints.getToken({
560
+ ...ctx,
561
+ returnHeaders: false,
562
+ returnStatus: false,
563
+ });
564
+ const jwtCookie = ctx.context.createAuthCookie(JWT_COOKIE_NAME, {
565
+ maxAge: jwtExpirationSeconds,
566
+ });
567
+ ctx.setCookie(jwtCookie.name, response.token, jwtCookie.attributes);
568
+ return response;
569
+ };
570
+ try {
571
+ return await runEndpoint();
572
+ } catch (error: any) {
573
+ // If alg config has changed and no longer matches one or more keys,
574
+ // roll the keys
575
+ if (!opts.jwks && error?.code === "ERR_JOSE_NOT_SUPPORTED") {
576
+ if (opts.jwksRotateOnTokenGenerationError) {
577
+ await ctx.context.adapter.deleteMany({
578
+ model: "jwks",
579
+ where: [],
580
+ });
581
+ return await runEndpoint();
582
+ } else {
583
+ console.error(
584
+ "Try temporarily setting jwksRotateOnTokenGenerationError: true on the Convex Better Auth plugin."
585
+ );
586
+ }
587
+ }
588
+ throw error;
589
+ }
304
590
  }
305
591
  ),
306
592
  },
@@ -1,4 +1,4 @@
1
- import { type BetterAuthPlugin, type HookEndpointContext } from "better-auth";
1
+ import { type BetterAuthPlugin } from "better-auth";
2
2
  import { setSessionCookie } from "better-auth/cookies";
3
3
  import { generateRandomString } from "better-auth/crypto";
4
4
  import {
@@ -19,7 +19,7 @@ export const crossDomain = ({ siteUrl }: { siteUrl: string }) => {
19
19
  return new URL(relativeCallbackURL, siteUrl).toString();
20
20
  };
21
21
 
22
- const isExpoNative = (ctx: HookEndpointContext) => {
22
+ const isExpoNative = (ctx: { headers?: Headers }) => {
23
23
  return ctx.headers?.has("expo-origin");
24
24
  };
25
25
 
@@ -36,6 +36,13 @@ export const crossDomain = ({ siteUrl }: { siteUrl: string }) => {
36
36
  },
37
37
  context: {
38
38
  oauthConfig: {
39
+ storeStateStrategy: "database",
40
+ // We could fake the cookie by sending a header, but it would need
41
+ // to be set on a 302 redirect from the identity provider, and we
42
+ // don't have a way to do that. This only means we can't stop an
43
+ // oauth flow that started in one browser from continuing in
44
+ // another. We still verify the state token from the query string
45
+ // against the database.
39
46
  skipStateCookieCheck: true,
40
47
  },
41
48
  },
@@ -181,6 +188,7 @@ export const crossDomain = ({ siteUrl }: { siteUrl: string }) => {
181
188
  const response = await oneTimeToken.endpoints.verifyOneTimeToken({
182
189
  ...ctx,
183
190
  returnHeaders: false,
191
+ returnStatus: false,
184
192
  });
185
193
  await setSessionCookie(ctx, response);
186
194
  return response;