@convex-dev/better-auth 0.6.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 (181) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +44 -0
  3. package/dist/commonjs/client/adapter.d.ts +4 -0
  4. package/dist/commonjs/client/adapter.d.ts.map +1 -0
  5. package/dist/commonjs/client/adapter.js +189 -0
  6. package/dist/commonjs/client/adapter.js.map +1 -0
  7. package/dist/commonjs/client/cors.d.ts +72 -0
  8. package/dist/commonjs/client/cors.d.ts.map +1 -0
  9. package/dist/commonjs/client/cors.js +281 -0
  10. package/dist/commonjs/client/cors.js.map +1 -0
  11. package/dist/commonjs/client/index.d.ts +302 -0
  12. package/dist/commonjs/client/index.d.ts.map +1 -0
  13. package/dist/commonjs/client/index.js +232 -0
  14. package/dist/commonjs/client/index.js.map +1 -0
  15. package/dist/commonjs/client/plugins/index.d.ts +3 -0
  16. package/dist/commonjs/client/plugins/index.d.ts.map +1 -0
  17. package/dist/commonjs/client/plugins/index.js +3 -0
  18. package/dist/commonjs/client/plugins/index.js.map +1 -0
  19. package/dist/commonjs/component/_generated/api.d.ts +12 -0
  20. package/dist/commonjs/component/_generated/api.d.ts.map +1 -0
  21. package/dist/commonjs/component/_generated/api.js +22 -0
  22. package/dist/commonjs/component/_generated/api.js.map +1 -0
  23. package/dist/commonjs/component/_generated/server.d.ts +64 -0
  24. package/dist/commonjs/component/_generated/server.d.ts.map +1 -0
  25. package/dist/commonjs/component/_generated/server.js +74 -0
  26. package/dist/commonjs/component/_generated/server.js.map +1 -0
  27. package/dist/commonjs/component/convex.config.d.ts +3 -0
  28. package/dist/commonjs/component/convex.config.d.ts.map +1 -0
  29. package/dist/commonjs/component/convex.config.js +4 -0
  30. package/dist/commonjs/component/convex.config.js.map +1 -0
  31. package/dist/commonjs/component/lib.d.ts +584 -0
  32. package/dist/commonjs/component/lib.d.ts.map +1 -0
  33. package/dist/commonjs/component/lib.js +323 -0
  34. package/dist/commonjs/component/lib.js.map +1 -0
  35. package/dist/commonjs/component/schema.d.ts +116 -0
  36. package/dist/commonjs/component/schema.d.ts.map +1 -0
  37. package/dist/commonjs/component/schema.js +68 -0
  38. package/dist/commonjs/component/schema.js.map +1 -0
  39. package/dist/commonjs/component/util.d.ts +394 -0
  40. package/dist/commonjs/component/util.d.ts.map +1 -0
  41. package/dist/commonjs/component/util.js +4 -0
  42. package/dist/commonjs/component/util.js.map +1 -0
  43. package/dist/commonjs/nextjs/index.d.ts +10 -0
  44. package/dist/commonjs/nextjs/index.d.ts.map +1 -0
  45. package/dist/commonjs/nextjs/index.js +23 -0
  46. package/dist/commonjs/nextjs/index.js.map +1 -0
  47. package/dist/commonjs/package.json +3 -0
  48. package/dist/commonjs/plugins/convex/client.d.ts +6 -0
  49. package/dist/commonjs/plugins/convex/client.d.ts.map +1 -0
  50. package/dist/commonjs/plugins/convex/client.js +7 -0
  51. package/dist/commonjs/plugins/convex/client.js.map +1 -0
  52. package/dist/commonjs/plugins/convex/index.d.ts +280 -0
  53. package/dist/commonjs/plugins/convex/index.d.ts.map +1 -0
  54. package/dist/commonjs/plugins/convex/index.js +253 -0
  55. package/dist/commonjs/plugins/convex/index.js.map +1 -0
  56. package/dist/commonjs/plugins/cross-domain/client.d.ts +123 -0
  57. package/dist/commonjs/plugins/cross-domain/client.d.ts.map +1 -0
  58. package/dist/commonjs/plugins/cross-domain/client.js +164 -0
  59. package/dist/commonjs/plugins/cross-domain/client.js.map +1 -0
  60. package/dist/commonjs/plugins/cross-domain/index.d.ts +81 -0
  61. package/dist/commonjs/plugins/cross-domain/index.d.ts.map +1 -0
  62. package/dist/commonjs/plugins/cross-domain/index.js +135 -0
  63. package/dist/commonjs/plugins/cross-domain/index.js.map +1 -0
  64. package/dist/commonjs/plugins/index.d.ts +3 -0
  65. package/dist/commonjs/plugins/index.d.ts.map +1 -0
  66. package/dist/commonjs/plugins/index.js +3 -0
  67. package/dist/commonjs/plugins/index.js.map +1 -0
  68. package/dist/commonjs/react/client.d.ts +31 -0
  69. package/dist/commonjs/react/client.d.ts.map +1 -0
  70. package/dist/commonjs/react/client.js +102 -0
  71. package/dist/commonjs/react/client.js.map +1 -0
  72. package/dist/commonjs/react/index.d.ts +9 -0
  73. package/dist/commonjs/react/index.d.ts.map +1 -0
  74. package/dist/commonjs/react/index.js +15 -0
  75. package/dist/commonjs/react/index.js.map +1 -0
  76. package/dist/commonjs/react-start/index.d.ts +10 -0
  77. package/dist/commonjs/react-start/index.d.ts.map +1 -0
  78. package/dist/commonjs/react-start/index.js +32 -0
  79. package/dist/commonjs/react-start/index.js.map +1 -0
  80. package/dist/esm/client/adapter.d.ts +4 -0
  81. package/dist/esm/client/adapter.d.ts.map +1 -0
  82. package/dist/esm/client/adapter.js +189 -0
  83. package/dist/esm/client/adapter.js.map +1 -0
  84. package/dist/esm/client/cors.d.ts +72 -0
  85. package/dist/esm/client/cors.d.ts.map +1 -0
  86. package/dist/esm/client/cors.js +281 -0
  87. package/dist/esm/client/cors.js.map +1 -0
  88. package/dist/esm/client/index.d.ts +302 -0
  89. package/dist/esm/client/index.d.ts.map +1 -0
  90. package/dist/esm/client/index.js +232 -0
  91. package/dist/esm/client/index.js.map +1 -0
  92. package/dist/esm/client/plugins/index.d.ts +3 -0
  93. package/dist/esm/client/plugins/index.d.ts.map +1 -0
  94. package/dist/esm/client/plugins/index.js +3 -0
  95. package/dist/esm/client/plugins/index.js.map +1 -0
  96. package/dist/esm/component/_generated/api.d.ts +12 -0
  97. package/dist/esm/component/_generated/api.d.ts.map +1 -0
  98. package/dist/esm/component/_generated/api.js +22 -0
  99. package/dist/esm/component/_generated/api.js.map +1 -0
  100. package/dist/esm/component/_generated/server.d.ts +64 -0
  101. package/dist/esm/component/_generated/server.d.ts.map +1 -0
  102. package/dist/esm/component/_generated/server.js +74 -0
  103. package/dist/esm/component/_generated/server.js.map +1 -0
  104. package/dist/esm/component/convex.config.d.ts +3 -0
  105. package/dist/esm/component/convex.config.d.ts.map +1 -0
  106. package/dist/esm/component/convex.config.js +4 -0
  107. package/dist/esm/component/convex.config.js.map +1 -0
  108. package/dist/esm/component/lib.d.ts +584 -0
  109. package/dist/esm/component/lib.d.ts.map +1 -0
  110. package/dist/esm/component/lib.js +323 -0
  111. package/dist/esm/component/lib.js.map +1 -0
  112. package/dist/esm/component/schema.d.ts +116 -0
  113. package/dist/esm/component/schema.d.ts.map +1 -0
  114. package/dist/esm/component/schema.js +68 -0
  115. package/dist/esm/component/schema.js.map +1 -0
  116. package/dist/esm/component/util.d.ts +394 -0
  117. package/dist/esm/component/util.d.ts.map +1 -0
  118. package/dist/esm/component/util.js +4 -0
  119. package/dist/esm/component/util.js.map +1 -0
  120. package/dist/esm/nextjs/index.d.ts +10 -0
  121. package/dist/esm/nextjs/index.d.ts.map +1 -0
  122. package/dist/esm/nextjs/index.js +23 -0
  123. package/dist/esm/nextjs/index.js.map +1 -0
  124. package/dist/esm/package.json +3 -0
  125. package/dist/esm/plugins/convex/client.d.ts +6 -0
  126. package/dist/esm/plugins/convex/client.d.ts.map +1 -0
  127. package/dist/esm/plugins/convex/client.js +7 -0
  128. package/dist/esm/plugins/convex/client.js.map +1 -0
  129. package/dist/esm/plugins/convex/index.d.ts +280 -0
  130. package/dist/esm/plugins/convex/index.d.ts.map +1 -0
  131. package/dist/esm/plugins/convex/index.js +253 -0
  132. package/dist/esm/plugins/convex/index.js.map +1 -0
  133. package/dist/esm/plugins/cross-domain/client.d.ts +123 -0
  134. package/dist/esm/plugins/cross-domain/client.d.ts.map +1 -0
  135. package/dist/esm/plugins/cross-domain/client.js +164 -0
  136. package/dist/esm/plugins/cross-domain/client.js.map +1 -0
  137. package/dist/esm/plugins/cross-domain/index.d.ts +81 -0
  138. package/dist/esm/plugins/cross-domain/index.d.ts.map +1 -0
  139. package/dist/esm/plugins/cross-domain/index.js +135 -0
  140. package/dist/esm/plugins/cross-domain/index.js.map +1 -0
  141. package/dist/esm/plugins/index.d.ts +3 -0
  142. package/dist/esm/plugins/index.d.ts.map +1 -0
  143. package/dist/esm/plugins/index.js +3 -0
  144. package/dist/esm/plugins/index.js.map +1 -0
  145. package/dist/esm/react/client.d.ts +31 -0
  146. package/dist/esm/react/client.d.ts.map +1 -0
  147. package/dist/esm/react/client.js +102 -0
  148. package/dist/esm/react/client.js.map +1 -0
  149. package/dist/esm/react/index.d.ts +9 -0
  150. package/dist/esm/react/index.d.ts.map +1 -0
  151. package/dist/esm/react/index.js +15 -0
  152. package/dist/esm/react/index.js.map +1 -0
  153. package/dist/esm/react-start/index.d.ts +10 -0
  154. package/dist/esm/react-start/index.d.ts.map +1 -0
  155. package/dist/esm/react-start/index.js +32 -0
  156. package/dist/esm/react-start/index.js.map +1 -0
  157. package/package.json +161 -0
  158. package/plugins/package.json +5 -0
  159. package/react/package.json +5 -0
  160. package/src/client/adapter.ts +236 -0
  161. package/src/client/cors.ts +403 -0
  162. package/src/client/index.ts +381 -0
  163. package/src/client/plugins/index.ts +2 -0
  164. package/src/component/_generated/api.d.ts +313 -0
  165. package/src/component/_generated/api.js +23 -0
  166. package/src/component/_generated/dataModel.d.ts +60 -0
  167. package/src/component/_generated/server.d.ts +149 -0
  168. package/src/component/_generated/server.js +90 -0
  169. package/src/component/convex.config.ts +5 -0
  170. package/src/component/lib.ts +391 -0
  171. package/src/component/schema.ts +74 -0
  172. package/src/component/util.ts +4 -0
  173. package/src/nextjs/index.ts +30 -0
  174. package/src/plugins/convex/client.ts +9 -0
  175. package/src/plugins/convex/index.ts +296 -0
  176. package/src/plugins/cross-domain/client.ts +209 -0
  177. package/src/plugins/cross-domain/index.ts +156 -0
  178. package/src/plugins/index.ts +2 -0
  179. package/src/react/client.tsx +184 -0
  180. package/src/react/index.tsx +38 -0
  181. package/src/react-start/index.ts +51 -0
@@ -0,0 +1,156 @@
1
+ import { setSessionCookie } from "better-auth/cookies";
2
+ import { generateRandomString } from "better-auth/crypto";
3
+ import {
4
+ BetterAuthPlugin,
5
+ createAuthEndpoint,
6
+ createAuthMiddleware,
7
+ oneTimeToken as oneTimeTokenPlugin,
8
+ } from "better-auth/plugins";
9
+ import { z } from "zod";
10
+
11
+ export const crossDomain = ({ siteUrl }: { siteUrl: string }) => {
12
+ const oneTimeToken = oneTimeTokenPlugin();
13
+
14
+ const rewriteCallbackURL = (callbackURL?: string) => {
15
+ if (callbackURL && !callbackURL.startsWith("/")) {
16
+ return callbackURL;
17
+ }
18
+ const relativeCallbackURL = callbackURL || "/";
19
+ return new URL(relativeCallbackURL, siteUrl).toString();
20
+ };
21
+
22
+ return {
23
+ id: "cross-domain",
24
+ hooks: {
25
+ before: [
26
+ {
27
+ matcher(context) {
28
+ return Boolean(
29
+ context.request?.headers.get("better-auth-cookie") ||
30
+ context.headers?.get("better-auth-cookie")
31
+ );
32
+ },
33
+ handler: createAuthMiddleware(async (c) => {
34
+ const existingHeaders = (c.request?.headers ||
35
+ c.headers) as Headers;
36
+ const headers = new Headers({
37
+ ...Object.fromEntries(existingHeaders?.entries()),
38
+ });
39
+ // Skip if the request has an authorization header
40
+ if (headers.get("authorization")) {
41
+ return;
42
+ }
43
+ const cookie = headers.get("better-auth-cookie");
44
+ if (!cookie) {
45
+ return;
46
+ }
47
+ headers.append("cookie", cookie);
48
+ return {
49
+ context: {
50
+ headers,
51
+ },
52
+ };
53
+ }),
54
+ },
55
+ {
56
+ matcher: (ctx) => {
57
+ return (
58
+ ctx.path.startsWith("/link-social") ||
59
+ ctx.path.startsWith("/send-verification-email") ||
60
+ ctx.path.startsWith("/verify-email") ||
61
+ ctx.path.startsWith("/sign-in/email") ||
62
+ ctx.path.startsWith("/sign-in/social") ||
63
+ ctx.path.startsWith("/sign-in/magic-link") ||
64
+ ctx.path.startsWith("/delete-user") ||
65
+ ctx.path.startsWith("/change-email")
66
+ );
67
+ },
68
+ handler: createAuthMiddleware(async (ctx) => {
69
+ const isSignIn = ctx.path.startsWith("/sign-in");
70
+ ctx.body.callbackURL = rewriteCallbackURL(ctx.body.callbackURL);
71
+ if (isSignIn && ctx.body.newUserCallbackURL) {
72
+ ctx.body.newUserCallbackURL = rewriteCallbackURL(
73
+ ctx.body.newUserCallbackURL
74
+ );
75
+ }
76
+ if (isSignIn && ctx.body.errorCallbackURL) {
77
+ ctx.body.errorCallbackURL = rewriteCallbackURL(
78
+ ctx.body.errorCallbackURL
79
+ );
80
+ }
81
+ return { context: ctx };
82
+ }),
83
+ },
84
+ ],
85
+ after: [
86
+ {
87
+ matcher() {
88
+ return true;
89
+ },
90
+ handler: createAuthMiddleware(async (ctx) => {
91
+ const setCookie = ctx.context.responseHeaders?.get("set-cookie");
92
+ if (!setCookie) {
93
+ return;
94
+ }
95
+ ctx.context.responseHeaders?.delete("set-cookie");
96
+ ctx.setHeader("Set-Better-Auth-Cookie", setCookie);
97
+ }),
98
+ },
99
+ {
100
+ matcher: (ctx) => {
101
+ return (
102
+ ctx.path?.startsWith("/callback") ||
103
+ ctx.path?.startsWith("/oauth2/callback") ||
104
+ ctx.path?.startsWith("/magic-link/verify")
105
+ );
106
+ },
107
+ handler: createAuthMiddleware(async (ctx) => {
108
+ // Mostly copied from the one-time-token plugin
109
+ const session = ctx.context.newSession;
110
+ if (!session) {
111
+ console.error("No session found");
112
+ return;
113
+ }
114
+ const token = generateRandomString(32);
115
+ const expiresAt = new Date(Date.now() + 3 * 60 * 1000);
116
+ await ctx.context.internalAdapter.createVerificationValue({
117
+ value: session.session.token,
118
+ identifier: `one-time-token:${token}`,
119
+ expiresAt,
120
+ });
121
+ const redirectTo = ctx.context.responseHeaders?.get("location");
122
+ if (!redirectTo) {
123
+ console.error("No redirect to found");
124
+ return;
125
+ }
126
+ const url = new URL(redirectTo);
127
+ url.searchParams.set("ott", token);
128
+ throw ctx.redirect(url.toString());
129
+ }),
130
+ },
131
+ ],
132
+ },
133
+ endpoints: {
134
+ verifyOneTimeToken: createAuthEndpoint(
135
+ "/cross-domain/one-time-token/verify",
136
+ {
137
+ method: "POST",
138
+ body: z.object({
139
+ token: z.string(),
140
+ }),
141
+ },
142
+ async (ctx) => {
143
+ const response = await oneTimeToken.endpoints.verifyOneTimeToken({
144
+ ...ctx,
145
+ returnHeaders: false,
146
+ });
147
+ await setSessionCookie(ctx, response);
148
+ return response;
149
+ }
150
+ ),
151
+ },
152
+ options: {
153
+ trustedOrigins: [siteUrl],
154
+ },
155
+ } satisfies BetterAuthPlugin;
156
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./convex";
2
+ export * from "./cross-domain";
@@ -0,0 +1,184 @@
1
+ import { ConvexReactClient } from "convex/react";
2
+ import isNetworkError from "is-network-error";
3
+ import {
4
+ createContext,
5
+ ReactNode,
6
+ useCallback,
7
+ useContext,
8
+ useEffect,
9
+ useMemo,
10
+ } from "react";
11
+ import { createAuthClient } from "better-auth/react";
12
+ import { convexClient, crossDomainClient } from "../client/plugins";
13
+ import { BetterAuthClientPlugin, ClientOptions } from "better-auth";
14
+
15
+ const ConvexAuthInternalContext = createContext<{
16
+ isLoading: boolean;
17
+ isAuthenticated: boolean;
18
+ fetchAccessToken: ({
19
+ forceRefreshToken,
20
+ }: {
21
+ forceRefreshToken: boolean;
22
+ }) => Promise<string | null>;
23
+ }>(undefined as any);
24
+
25
+ export function useAuth() {
26
+ return useContext(ConvexAuthInternalContext);
27
+ }
28
+
29
+ export type ConvexAuthClient = {
30
+ verbose: boolean | undefined;
31
+ logger?: ConvexReactClient["logger"];
32
+ };
33
+
34
+ type CrossDomainClient = ReturnType<typeof crossDomainClient>;
35
+ type ConvexClient = ReturnType<typeof convexClient>;
36
+ type PluginsWithCrossDomain = (
37
+ | CrossDomainClient
38
+ | ConvexClient
39
+ | BetterAuthClientPlugin
40
+ )[];
41
+ type PluginsWithoutCrossDomain = (ConvexClient | BetterAuthClientPlugin)[];
42
+ type AuthClientWithPlugins<
43
+ Plugins extends PluginsWithCrossDomain | PluginsWithoutCrossDomain,
44
+ > = ReturnType<
45
+ typeof createAuthClient<
46
+ ClientOptions & {
47
+ plugins: Plugins;
48
+ }
49
+ >
50
+ >;
51
+ export type AuthClient =
52
+ | AuthClientWithPlugins<PluginsWithCrossDomain>
53
+ | AuthClientWithPlugins<PluginsWithoutCrossDomain>;
54
+
55
+ export function AuthProvider({
56
+ client,
57
+ authClient,
58
+ children,
59
+ }: {
60
+ client: ConvexAuthClient;
61
+ authClient: AuthClient;
62
+ children: ReactNode;
63
+ }) {
64
+ const { data: session, isPending: isSessionPending } =
65
+ authClient.useSession();
66
+
67
+ const verbose: boolean = (client as any).options?.verbose ?? false;
68
+ const logVerbose = useCallback(
69
+ (message: string) => {
70
+ if (verbose) {
71
+ console.debug(`${new Date().toISOString()} ${message}`);
72
+ client.logger?.logVerbose(message);
73
+ }
74
+ },
75
+ [verbose]
76
+ );
77
+
78
+ const fetchToken = useCallback(async () => {
79
+ const initialBackoff = 100;
80
+ const maxBackoff = 1000;
81
+ let retries = 0;
82
+
83
+ const nextBackoff = () => {
84
+ const baseBackoff = initialBackoff * Math.pow(2, retries);
85
+ retries += 1;
86
+ const actualBackoff = Math.min(baseBackoff, maxBackoff);
87
+ const jitter = actualBackoff * (Math.random() - 0.5);
88
+ return actualBackoff + jitter;
89
+ };
90
+
91
+ const fetchWithRetry = async () => {
92
+ try {
93
+ const { data } = await authClient.convex.token();
94
+ return data?.token || null;
95
+ } catch (e) {
96
+ if (!isNetworkError(e)) {
97
+ throw e;
98
+ }
99
+ if (retries > 10) {
100
+ logVerbose(`fetchToken failed with network error, giving up`);
101
+ throw e;
102
+ }
103
+ const backoff = nextBackoff();
104
+ logVerbose(
105
+ `fetchToken failed with network error, attempting retrying in ${backoff}ms`
106
+ );
107
+ await new Promise((resolve) => setTimeout(resolve, backoff));
108
+ return fetchWithRetry();
109
+ }
110
+ };
111
+
112
+ return fetchWithRetry();
113
+ }, [client]);
114
+
115
+ const fetchAccessToken = useCallback(
116
+ async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
117
+ if (forceRefreshToken) {
118
+ const token = await fetchToken();
119
+ logVerbose(`returning retrieved token`);
120
+ return token;
121
+ }
122
+ return null;
123
+ },
124
+ [fetchToken]
125
+ );
126
+
127
+ useEffect(
128
+ () => {
129
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
130
+ (async () => {
131
+ // Return early if cross domain plugin is not configured.
132
+ // Apparently there's no sane way to do this type check. Only the in
133
+ // keyword narrows the type effectively but it doesn't work on functions.
134
+ if (!(authClient as any)["crossDomain"]) {
135
+ return;
136
+ }
137
+ const authClientWithCrossDomain =
138
+ authClient as AuthClientWithPlugins<PluginsWithCrossDomain>;
139
+ const url = new URL(window.location.href);
140
+ const token = url.searchParams.get("ott");
141
+ if (token) {
142
+ url.searchParams.delete("ott");
143
+ const result =
144
+ await authClientWithCrossDomain.crossDomain.oneTimeToken.verify({
145
+ token,
146
+ });
147
+ const session = result.data?.session;
148
+ if (session) {
149
+ await authClient.getSession({
150
+ fetchOptions: {
151
+ headers: {
152
+ Authorization: `Bearer ${session.token}`,
153
+ },
154
+ },
155
+ });
156
+ authClientWithCrossDomain.updateSession();
157
+ }
158
+ window.history.replaceState({}, "", url);
159
+ }
160
+ })();
161
+ },
162
+ // Explicitly chosen dependencies.
163
+ // This effect should mostly only run once
164
+ // on mount.
165
+ [client, authClient]
166
+ );
167
+
168
+ const isAuthenticated = session !== null;
169
+ const isLoading = isSessionPending;
170
+ const authState = useMemo(
171
+ () => ({
172
+ isLoading,
173
+ isAuthenticated,
174
+ fetchAccessToken,
175
+ }),
176
+ [fetchAccessToken, isLoading, isAuthenticated]
177
+ );
178
+
179
+ return (
180
+ <ConvexAuthInternalContext.Provider value={authState}>
181
+ {children}
182
+ </ConvexAuthInternalContext.Provider>
183
+ );
184
+ }
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import { ConvexProviderWithAuth, ConvexReactClient } from "convex/react";
4
+ import { PropsWithChildren, useMemo } from "react";
5
+ import {
6
+ AuthClient,
7
+ AuthProvider,
8
+ useAuth,
9
+ type ConvexAuthClient,
10
+ } from "./client";
11
+
12
+ export function ConvexBetterAuthProvider({
13
+ client,
14
+ authClient,
15
+ children,
16
+ }: PropsWithChildren<{
17
+ client: ConvexReactClient;
18
+ authClient: AuthClient;
19
+ }>) {
20
+ const convexAuthClient = useMemo(
21
+ () =>
22
+ ({
23
+ verbose: (client as any).options?.verbose,
24
+ logger: client.logger,
25
+ }) satisfies ConvexAuthClient,
26
+ [client]
27
+ );
28
+ return (
29
+ <AuthProvider client={convexAuthClient} authClient={authClient}>
30
+ <ConvexProviderWithAuth client={client} useAuth={useAuth}>
31
+ {children}
32
+ </ConvexProviderWithAuth>
33
+ </AuthProvider>
34
+ );
35
+ }
36
+
37
+ // TODO: Remove, short-lived alias
38
+ export { ConvexBetterAuthProvider as ConvexProviderWithBetterAuth };
@@ -0,0 +1,51 @@
1
+ import { Auth, betterAuth } from "better-auth";
2
+ import { createCookieGetter } from "better-auth/cookies";
3
+ import { betterFetch } from "@better-fetch/fetch";
4
+ import { GenericActionCtx } from "convex/server";
5
+
6
+ export const getCookieName = async (
7
+ createAuth: (ctx: GenericActionCtx<any>) => ReturnType<typeof betterAuth>
8
+ ) => {
9
+ const auth = createAuth({} as any);
10
+ const createCookie = createCookieGetter(auth.options);
11
+ const cookie = createCookie("convex_jwt");
12
+ return cookie.name;
13
+ };
14
+
15
+ export const fetchSession = async <
16
+ T extends (ctx: GenericActionCtx<any>) => ReturnType<typeof betterAuth>,
17
+ >(
18
+ createAuth: T,
19
+ request?: Request
20
+ ) => {
21
+ type Session = ReturnType<T>["$Infer"]["Session"];
22
+
23
+ if (!request) {
24
+ throw new Error("No request found");
25
+ }
26
+ const baseURL = new URL(request.url).origin;
27
+ const { data: session } = await betterFetch<Session>(
28
+ "/api/auth/get-session",
29
+ {
30
+ baseURL,
31
+ headers: {
32
+ cookie: request.headers.get("cookie") ?? "",
33
+ origin: baseURL,
34
+ },
35
+ }
36
+ );
37
+ return {
38
+ session,
39
+ };
40
+ };
41
+
42
+ export const reactStartHandler = (
43
+ request: Request,
44
+ opts?: { convexSiteUrl?: string }
45
+ ) => {
46
+ const convexSiteUrl = opts?.convexSiteUrl ?? process.env.CONVEX_SITE_URL;
47
+ const requestUrl = new URL(request.url);
48
+ const nextUrl = `${convexSiteUrl}${requestUrl.pathname}${requestUrl.search}`;
49
+ request.headers.set("accept-encoding", "application/json");
50
+ return fetch(nextUrl, new Request(request, { redirect: "manual" }));
51
+ };