@robelest/convex-auth 0.0.2-preview.0 → 0.0.2-preview.2

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 (166) hide show
  1. package/dist/bin.cjs +17 -15
  2. package/dist/client/index.d.ts +84 -30
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +259 -59
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/component.d.ts +46 -120
  7. package/dist/component/_generated/component.d.ts.map +1 -1
  8. package/dist/component/index.d.ts +2 -4
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/index.js +2 -4
  11. package/dist/component/index.js.map +1 -1
  12. package/dist/component/public.d.ts +233 -167
  13. package/dist/component/public.d.ts.map +1 -1
  14. package/dist/component/public.js +328 -155
  15. package/dist/component/public.js.map +1 -1
  16. package/dist/component/schema.d.ts +127 -12
  17. package/dist/component/schema.d.ts.map +1 -1
  18. package/dist/component/schema.js +136 -10
  19. package/dist/component/schema.js.map +1 -1
  20. package/dist/providers/{Anonymous.d.ts → anonymous.d.ts} +8 -8
  21. package/dist/providers/{Anonymous.d.ts.map → anonymous.d.ts.map} +1 -1
  22. package/dist/providers/{Anonymous.js → anonymous.js} +9 -10
  23. package/dist/providers/anonymous.js.map +1 -0
  24. package/dist/providers/{ConvexCredentials.d.ts → credentials.d.ts} +11 -11
  25. package/dist/providers/credentials.d.ts.map +1 -0
  26. package/dist/providers/{ConvexCredentials.js → credentials.js} +8 -8
  27. package/dist/providers/credentials.js.map +1 -0
  28. package/dist/providers/{Email.d.ts → email.d.ts} +6 -6
  29. package/dist/providers/email.d.ts.map +1 -0
  30. package/dist/providers/{Email.js → email.js} +6 -6
  31. package/dist/providers/email.js.map +1 -0
  32. package/dist/providers/{Password.d.ts → password.d.ts} +10 -10
  33. package/dist/providers/{Password.d.ts.map → password.d.ts.map} +1 -1
  34. package/dist/providers/{Password.js → password.js} +19 -20
  35. package/dist/providers/password.js.map +1 -0
  36. package/dist/providers/{Phone.d.ts → phone.d.ts} +3 -3
  37. package/dist/providers/{Phone.d.ts.map → phone.d.ts.map} +1 -1
  38. package/dist/providers/{Phone.js → phone.js} +3 -3
  39. package/dist/providers/{Phone.js.map → phone.js.map} +1 -1
  40. package/dist/server/implementation/db.d.ts +5 -2
  41. package/dist/server/implementation/db.d.ts.map +1 -1
  42. package/dist/server/implementation/db.js +2 -1
  43. package/dist/server/implementation/db.js.map +1 -1
  44. package/dist/server/implementation/index.d.ts +285 -180
  45. package/dist/server/implementation/index.d.ts.map +1 -1
  46. package/dist/server/implementation/index.js +280 -173
  47. package/dist/server/implementation/index.js.map +1 -1
  48. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +1 -1
  49. package/dist/server/implementation/mutations/createAccountFromCredentials.js +8 -18
  50. package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +1 -1
  51. package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +1 -1
  52. package/dist/server/implementation/mutations/createVerificationCode.js +16 -44
  53. package/dist/server/implementation/mutations/createVerificationCode.js.map +1 -1
  54. package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +1 -1
  55. package/dist/server/implementation/mutations/invalidateSessions.js +4 -8
  56. package/dist/server/implementation/mutations/invalidateSessions.js.map +1 -1
  57. package/dist/server/implementation/mutations/modifyAccount.d.ts.map +1 -1
  58. package/dist/server/implementation/mutations/modifyAccount.js +8 -19
  59. package/dist/server/implementation/mutations/modifyAccount.js.map +1 -1
  60. package/dist/server/implementation/mutations/refreshSession.d.ts.map +1 -1
  61. package/dist/server/implementation/mutations/refreshSession.js +9 -23
  62. package/dist/server/implementation/mutations/refreshSession.js.map +1 -1
  63. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +1 -1
  64. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js +6 -12
  65. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +1 -1
  66. package/dist/server/implementation/mutations/signIn.d.ts.map +1 -1
  67. package/dist/server/implementation/mutations/signIn.js +2 -1
  68. package/dist/server/implementation/mutations/signIn.js.map +1 -1
  69. package/dist/server/implementation/mutations/signOut.d.ts.map +1 -1
  70. package/dist/server/implementation/mutations/signOut.js +5 -6
  71. package/dist/server/implementation/mutations/signOut.js.map +1 -1
  72. package/dist/server/implementation/mutations/storeRef.d.ts +8 -0
  73. package/dist/server/implementation/mutations/storeRef.d.ts.map +1 -0
  74. package/dist/server/implementation/mutations/storeRef.js +8 -0
  75. package/dist/server/implementation/mutations/storeRef.js.map +1 -0
  76. package/dist/server/implementation/mutations/userOAuth.d.ts.map +1 -1
  77. package/dist/server/implementation/mutations/userOAuth.js +16 -53
  78. package/dist/server/implementation/mutations/userOAuth.js.map +1 -1
  79. package/dist/server/implementation/mutations/verifier.d.ts.map +1 -1
  80. package/dist/server/implementation/mutations/verifier.js +4 -8
  81. package/dist/server/implementation/mutations/verifier.js.map +1 -1
  82. package/dist/server/implementation/mutations/verifierSignature.d.ts.map +1 -1
  83. package/dist/server/implementation/mutations/verifierSignature.js +6 -10
  84. package/dist/server/implementation/mutations/verifierSignature.js.map +1 -1
  85. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +1 -1
  86. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js +7 -16
  87. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +1 -1
  88. package/dist/server/implementation/provider.d.ts +2 -1
  89. package/dist/server/implementation/provider.d.ts.map +1 -1
  90. package/dist/server/implementation/provider.js.map +1 -1
  91. package/dist/server/implementation/rateLimit.d.ts.map +1 -1
  92. package/dist/server/implementation/rateLimit.js +13 -39
  93. package/dist/server/implementation/rateLimit.js.map +1 -1
  94. package/dist/server/implementation/refreshTokens.d.ts +1 -8
  95. package/dist/server/implementation/refreshTokens.d.ts.map +1 -1
  96. package/dist/server/implementation/refreshTokens.js +14 -58
  97. package/dist/server/implementation/refreshTokens.js.map +1 -1
  98. package/dist/server/implementation/sessions.d.ts +2 -20
  99. package/dist/server/implementation/sessions.d.ts.map +1 -1
  100. package/dist/server/implementation/sessions.js +8 -35
  101. package/dist/server/implementation/sessions.js.map +1 -1
  102. package/dist/server/implementation/types.d.ts +11 -267
  103. package/dist/server/implementation/types.d.ts.map +1 -1
  104. package/dist/server/implementation/types.js +1 -181
  105. package/dist/server/implementation/types.js.map +1 -1
  106. package/dist/server/implementation/users.d.ts.map +1 -1
  107. package/dist/server/implementation/users.js +19 -67
  108. package/dist/server/implementation/users.js.map +1 -1
  109. package/dist/server/index.d.ts +18 -0
  110. package/dist/server/index.d.ts.map +1 -1
  111. package/dist/server/index.js +255 -0
  112. package/dist/server/index.js.map +1 -1
  113. package/dist/server/provider_utils.d.ts +1 -1
  114. package/dist/server/provider_utils.d.ts.map +1 -1
  115. package/dist/server/provider_utils.js +2 -2
  116. package/dist/server/provider_utils.js.map +1 -1
  117. package/dist/server/types.d.ts +91 -52
  118. package/dist/server/types.d.ts.map +1 -1
  119. package/package.json +3 -6
  120. package/src/cli/index.ts +20 -19
  121. package/src/client/index.ts +347 -110
  122. package/src/component/_generated/component.ts +55 -214
  123. package/src/component/index.ts +1 -11
  124. package/src/component/public.ts +366 -178
  125. package/src/component/schema.ts +150 -19
  126. package/src/providers/{Anonymous.ts → anonymous.ts} +10 -11
  127. package/src/providers/{ConvexCredentials.ts → credentials.ts} +11 -11
  128. package/src/providers/{Email.ts → email.ts} +5 -5
  129. package/src/providers/{Password.ts → password.ts} +22 -27
  130. package/src/providers/{Phone.ts → phone.ts} +2 -2
  131. package/src/server/implementation/db.ts +5 -2
  132. package/src/server/implementation/index.ts +368 -313
  133. package/src/server/implementation/mutations/createAccountFromCredentials.ts +11 -25
  134. package/src/server/implementation/mutations/createVerificationCode.ts +16 -47
  135. package/src/server/implementation/mutations/invalidateSessions.ts +4 -9
  136. package/src/server/implementation/mutations/modifyAccount.ts +8 -22
  137. package/src/server/implementation/mutations/refreshSession.ts +11 -24
  138. package/src/server/implementation/mutations/retrieveAccountWithCredentials.ts +9 -17
  139. package/src/server/implementation/mutations/signIn.ts +2 -1
  140. package/src/server/implementation/mutations/signOut.ts +5 -8
  141. package/src/server/implementation/mutations/storeRef.ts +7 -0
  142. package/src/server/implementation/mutations/userOAuth.ts +10 -50
  143. package/src/server/implementation/mutations/verifier.ts +4 -9
  144. package/src/server/implementation/mutations/verifierSignature.ts +6 -12
  145. package/src/server/implementation/mutations/verifyCodeAndSignIn.ts +7 -18
  146. package/src/server/implementation/provider.ts +2 -1
  147. package/src/server/implementation/rateLimit.ts +15 -41
  148. package/src/server/implementation/refreshTokens.ts +26 -76
  149. package/src/server/implementation/sessions.ts +8 -39
  150. package/src/server/implementation/types.ts +16 -191
  151. package/src/server/implementation/users.ts +19 -66
  152. package/src/server/index.ts +373 -0
  153. package/src/server/provider_utils.ts +2 -2
  154. package/src/server/types.ts +116 -51
  155. package/dist/providers/Anonymous.js.map +0 -1
  156. package/dist/providers/ConvexCredentials.d.ts.map +0 -1
  157. package/dist/providers/ConvexCredentials.js.map +0 -1
  158. package/dist/providers/Email.d.ts.map +0 -1
  159. package/dist/providers/Email.js.map +0 -1
  160. package/dist/providers/Password.js.map +0 -1
  161. package/providers/Anonymous/package.json +0 -6
  162. package/providers/ConvexCredentials/package.json +0 -6
  163. package/providers/Email/package.json +0 -6
  164. package/providers/Password/package.json +0 -6
  165. package/providers/Phone/package.json +0 -6
  166. package/server/package.json +0 -6
@@ -2,7 +2,7 @@ import { GenericId } from "convex/values";
2
2
  import { Doc, MutationCtx } from "./types.js";
3
3
  import { AuthProviderMaterializedConfig, ConvexAuthConfig } from "../types.js";
4
4
  import { LOG_LEVELS, logWithLevel } from "./utils.js";
5
- import { createAuthDb } from "./db.js";
5
+ import { authDb } from "./db.js";
6
6
 
7
7
  type CreateOrUpdateUserArgs = {
8
8
  type: "oauth" | "credentials" | "email" | "phone" | "verification";
@@ -56,8 +56,7 @@ async function defaultCreateOrUpdateUser(
56
56
  args,
57
57
  });
58
58
  const existingUserId = existingAccount?.userId ?? null;
59
- const authDb =
60
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
59
+ const db = authDb(ctx, config);
61
60
  if (config.callbacks?.createOrUpdateUser !== undefined) {
62
61
  logWithLevel(LOG_LEVELS.DEBUG, "Using custom createOrUpdateUser callback");
63
62
  return await config.callbacks.createOrUpdateUser(ctx, {
@@ -136,11 +135,7 @@ async function defaultCreateOrUpdateUser(
136
135
  const existingOrLinkedUserId = userId;
137
136
  if (userId !== null) {
138
137
  try {
139
- if (authDb !== null) {
140
- await authDb.users.patch(userId, userData);
141
- } else {
142
- await ctx.db.patch(userId, userData);
143
- }
138
+ await db.users.patch(userId, userData);
144
139
  } catch (error) {
145
140
  throw new Error(
146
141
  `Could not update user document with ID \`${userId}\`, ` +
@@ -150,10 +145,7 @@ async function defaultCreateOrUpdateUser(
150
145
  );
151
146
  }
152
147
  } else {
153
- userId =
154
- authDb !== null
155
- ? ((await authDb.users.insert(userData)) as GenericId<"user">)
156
- : await ctx.db.insert("user", userData);
148
+ userId = (await db.users.insert(userData)) as GenericId<"user">;
157
149
  }
158
150
  const afterUserCreatedOrUpdated = config.callbacks?.afterUserCreatedOrUpdated;
159
151
  if (afterUserCreatedOrUpdated !== undefined) {
@@ -180,16 +172,8 @@ async function uniqueUserWithVerifiedEmail(
180
172
  email: string,
181
173
  config: ConvexAuthConfig,
182
174
  ) {
183
- if (config.component !== undefined) {
184
- const authDb = createAuthDb(ctx, config.component);
185
- return (await authDb.users.findByVerifiedEmail(email)) as Doc<"user"> | null;
186
- }
187
- const users = await ctx.db
188
- .query("user")
189
- .withIndex("email", (q) => q.eq("email", email))
190
- .filter((q) => q.neq(q.field("emailVerificationTime"), undefined))
191
- .take(2);
192
- return users.length === 1 ? users[0] : null;
175
+ const db = authDb(ctx, config);
176
+ return (await db.users.findByVerifiedEmail(email)) as Doc<"user"> | null;
193
177
  }
194
178
 
195
179
  async function uniqueUserWithVerifiedPhone(
@@ -197,16 +181,8 @@ async function uniqueUserWithVerifiedPhone(
197
181
  phone: string,
198
182
  config: ConvexAuthConfig,
199
183
  ) {
200
- if (config.component !== undefined) {
201
- const authDb = createAuthDb(ctx, config.component);
202
- return (await authDb.users.findByVerifiedPhone(phone)) as Doc<"user"> | null;
203
- }
204
- const users = await ctx.db
205
- .query("user")
206
- .withIndex("phone", (q) => q.eq("phone", phone))
207
- .filter((q) => q.neq(q.field("phoneVerificationTime"), undefined))
208
- .take(2);
209
- return users.length === 1 ? users[0] : null;
184
+ const db = authDb(ctx, config);
185
+ return (await db.users.findByVerifiedPhone(phone)) as Doc<"user"> | null;
210
186
  }
211
187
 
212
188
  async function createOrUpdateAccount(
@@ -221,49 +197,29 @@ async function createOrUpdateAccount(
221
197
  args: CreateOrUpdateUserArgs,
222
198
  config: ConvexAuthConfig,
223
199
  ) {
224
- const authDb =
225
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
200
+ const db = authDb(ctx, config);
226
201
  const accountId =
227
202
  "existingAccount" in account
228
203
  ? account.existingAccount._id
229
- : authDb !== null
230
- ? ((await authDb.accounts.create({
231
- userId,
232
- provider: args.provider.id,
233
- providerAccountId: account.providerAccountId,
234
- secret: account.secret,
235
- })) as GenericId<"account">)
236
- : await ctx.db.insert("account", {
237
- userId,
238
- provider: args.provider.id,
239
- providerAccountId: account.providerAccountId,
240
- secret: account.secret,
241
- });
204
+ : ((await db.accounts.create({
205
+ userId,
206
+ provider: args.provider.id,
207
+ providerAccountId: account.providerAccountId,
208
+ secret: account.secret,
209
+ })) as GenericId<"account">);
242
210
  // This is never used with the default `createOrUpdateUser` implementation,
243
211
  // but it is used for manual linking via custom `createOrUpdateUser`:
244
212
  if (
245
213
  "existingAccount" in account &&
246
214
  account.existingAccount.userId !== userId
247
215
  ) {
248
- if (authDb !== null) {
249
- await authDb.accounts.patch(accountId, { userId });
250
- } else {
251
- await ctx.db.patch(accountId, { userId });
252
- }
216
+ await db.accounts.patch(accountId, { userId });
253
217
  }
254
218
  if (args.profile.emailVerified) {
255
- if (authDb !== null) {
256
- await authDb.accounts.patch(accountId, { emailVerified: args.profile.email });
257
- } else {
258
- await ctx.db.patch(accountId, { emailVerified: args.profile.email });
259
- }
219
+ await db.accounts.patch(accountId, { emailVerified: args.profile.email });
260
220
  }
261
221
  if (args.profile.phoneVerified) {
262
- if (authDb !== null) {
263
- await authDb.accounts.patch(accountId, { phoneVerified: args.profile.phone });
264
- } else {
265
- await ctx.db.patch(accountId, { phoneVerified: args.profile.phone });
266
- }
222
+ await db.accounts.patch(accountId, { phoneVerified: args.profile.phone });
267
223
  }
268
224
  return accountId;
269
225
  }
@@ -273,10 +229,7 @@ export async function getAccountOrThrow(
273
229
  existingAccountId: GenericId<"account">,
274
230
  config: ConvexAuthConfig,
275
231
  ) {
276
- const existingAccount =
277
- config.component !== undefined
278
- ? await createAuthDb(ctx, config.component).accounts.getById(existingAccountId)
279
- : await ctx.db.get(existingAccountId);
232
+ const existingAccount = await authDb(ctx, config).accounts.getById(existingAccountId);
280
233
  if (existingAccount === null) {
281
234
  throw new Error(
282
235
  `Expected an account to exist for ID "${existingAccountId}"`,
@@ -1,4 +1,10 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { jwtDecode } from "jwt-decode";
1
3
  import { parse, serialize } from "cookie";
4
+ import type {
5
+ SignInAction,
6
+ SignOutAction,
7
+ } from "./implementation/index.js";
2
8
  import { isLocalHost } from "./utils.js";
3
9
 
4
10
  export type AuthCookieConfig = {
@@ -11,6 +17,20 @@ export type AuthCookies = {
11
17
  verifier: string | null;
12
18
  };
13
19
 
20
+ export type ServerOptions = {
21
+ /** Convex deployment URL. */
22
+ url: string;
23
+ apiRoute?: string;
24
+ cookieMaxAge?: number | null;
25
+ verbose?: boolean;
26
+ shouldHandleCode?: ((request: Request) => boolean | Promise<boolean>) | boolean;
27
+ };
28
+
29
+ export type RefreshResult = {
30
+ response?: Response;
31
+ cookies?: string[];
32
+ };
33
+
14
34
  export function authCookieNames(host?: string) {
15
35
  const prefix = isLocalHost(host) ? "" : "__Host-";
16
36
  return {
@@ -72,3 +92,356 @@ export function shouldProxyAuthAction(pathname: string, apiRoute: string) {
72
92
  }
73
93
  return pathname === apiRoute || pathname === `${apiRoute}/`;
74
94
  }
95
+
96
+ const REQUIRED_TOKEN_LIFETIME_MS = 60_000;
97
+ const MINIMUM_REQUIRED_TOKEN_LIFETIME_MS = 10_000;
98
+
99
+ type DecodedToken = { exp?: number; iat?: number };
100
+
101
+ export function server(options: ServerOptions) {
102
+ const convexUrl = options.url;
103
+ const apiRoute = options.apiRoute ?? "/api/auth";
104
+ const cookieConfig = { maxAge: options.cookieMaxAge ?? null };
105
+ const verbose = options.verbose ?? false;
106
+
107
+ const logVerbose = (message: string) => {
108
+ if (!verbose) {
109
+ return;
110
+ }
111
+ console.debug(
112
+ `${new Date().toISOString()} [convex-auth/server] ${message}`,
113
+ );
114
+ };
115
+
116
+ const cookieHost = (request: Request) => {
117
+ return request.headers.get("host") ?? new URL(request.url).host;
118
+ };
119
+
120
+ const parseRequestCookies = (request: Request) => {
121
+ return parseAuthCookies(request.headers.get("cookie"), cookieHost(request));
122
+ };
123
+
124
+ const attachCookies = (response: Response, cookies: string[]) => {
125
+ for (const value of cookies) {
126
+ response.headers.append("Set-Cookie", value);
127
+ }
128
+ return response;
129
+ };
130
+
131
+ const jsonResponse = (body: unknown, status = 200) => {
132
+ return new Response(JSON.stringify(body), {
133
+ status,
134
+ headers: {
135
+ "Content-Type": "application/json",
136
+ },
137
+ });
138
+ };
139
+
140
+ const isCorsRequest = (request: Request) => {
141
+ const originHeader = request.headers.get("origin");
142
+ if (originHeader === null) {
143
+ return false;
144
+ }
145
+ const requestUrl = new URL(request.url);
146
+ const originUrl = new URL(originHeader);
147
+ return (
148
+ originUrl.host !== requestUrl.host ||
149
+ originUrl.protocol !== requestUrl.protocol
150
+ );
151
+ };
152
+
153
+ const decodeToken = (token: string): DecodedToken | null => {
154
+ try {
155
+ return jwtDecode<DecodedToken>(token);
156
+ } catch {
157
+ return null;
158
+ }
159
+ };
160
+
161
+ const convexClient = (token?: string | null) => {
162
+ const client = new ConvexHttpClient(convexUrl);
163
+ if (token !== undefined && token !== null) {
164
+ client.setAuth(token);
165
+ }
166
+ return client;
167
+ };
168
+
169
+ const refreshTokens = async (
170
+ request: Request,
171
+ ): Promise<{ token: string; refreshToken: string } | null | undefined> => {
172
+ const cookies = parseRequestCookies(request);
173
+ const { token, refreshToken } = cookies;
174
+ if (refreshToken === null && token === null) {
175
+ logVerbose("No auth cookies found, skipping refresh");
176
+ return undefined;
177
+ }
178
+ if (refreshToken === null || token === null) {
179
+ logVerbose("Only one auth cookie present, clearing auth cookies");
180
+ return null;
181
+ }
182
+ const decodedToken = decodeToken(token);
183
+ if (decodedToken?.exp === undefined || decodedToken.iat === undefined) {
184
+ logVerbose("Failed to decode token, clearing auth cookies");
185
+ return null;
186
+ }
187
+ const totalTokenLifetimeMs = decodedToken.exp * 1000 - decodedToken.iat * 1000;
188
+ const minimumExpiration =
189
+ Date.now() +
190
+ Math.min(
191
+ REQUIRED_TOKEN_LIFETIME_MS,
192
+ Math.max(MINIMUM_REQUIRED_TOKEN_LIFETIME_MS, totalTokenLifetimeMs / 10),
193
+ );
194
+ if (decodedToken.exp * 1000 > minimumExpiration) {
195
+ logVerbose("Token valid long enough, skipping refresh");
196
+ return undefined;
197
+ }
198
+
199
+ try {
200
+ const result = await convexClient().action(
201
+ "auth:signIn" as unknown as SignInAction,
202
+ {
203
+ refreshToken,
204
+ },
205
+ );
206
+ if (result.tokens === undefined) {
207
+ throw new Error("Invalid `auth:signIn` result for token refresh");
208
+ }
209
+ logVerbose(`Refreshed tokens, null=${result.tokens === null}`);
210
+ return result.tokens;
211
+ } catch (error) {
212
+ console.error(error);
213
+ logVerbose("Token refresh failed, clearing auth cookies");
214
+ return null;
215
+ }
216
+ };
217
+
218
+ return {
219
+ token(request: Request): string | null {
220
+ return parseRequestCookies(request).token;
221
+ },
222
+
223
+ async verify(request: Request): Promise<boolean> {
224
+ const token = parseRequestCookies(request).token;
225
+ if (token === null) {
226
+ return false;
227
+ }
228
+ const decodedToken = decodeToken(token);
229
+ if (decodedToken?.exp === undefined) {
230
+ return false;
231
+ }
232
+ return decodedToken.exp * 1000 > Date.now();
233
+ },
234
+
235
+ async proxy(request: Request): Promise<Response> {
236
+ const requestUrl = new URL(request.url);
237
+ if (!shouldProxyAuthAction(requestUrl.pathname, apiRoute)) {
238
+ return new Response("Invalid route", { status: 404 });
239
+ }
240
+ if (request.method !== "POST") {
241
+ return new Response("Invalid method", { status: 405 });
242
+ }
243
+ if (isCorsRequest(request)) {
244
+ return new Response("Invalid origin", { status: 403 });
245
+ }
246
+
247
+ const body = await request.json();
248
+ const action = body.action as string;
249
+ const args = (body.args ?? {}) as Record<string, any>;
250
+
251
+ if (action !== "auth:signIn" && action !== "auth:signOut") {
252
+ return new Response("Invalid action", { status: 400 });
253
+ }
254
+
255
+ const currentCookies = parseRequestCookies(request);
256
+ const host = cookieHost(request);
257
+
258
+ if (action === "auth:signIn") {
259
+ if (args.refreshToken !== undefined) {
260
+ if (currentCookies.refreshToken === null) {
261
+ return jsonResponse({ tokens: null });
262
+ }
263
+ args.refreshToken = currentCookies.refreshToken;
264
+ }
265
+ const client = convexClient(
266
+ args.refreshToken !== undefined || args.params?.code !== undefined
267
+ ? null
268
+ : currentCookies.token,
269
+ );
270
+
271
+ try {
272
+ const result = await client.action(
273
+ "auth:signIn" as unknown as SignInAction,
274
+ args,
275
+ );
276
+ if (result.redirect !== undefined) {
277
+ const response = jsonResponse({ redirect: result.redirect });
278
+ return attachCookies(
279
+ response,
280
+ serializeAuthCookies(
281
+ {
282
+ ...currentCookies,
283
+ verifier: result.verifier ?? null,
284
+ },
285
+ host,
286
+ cookieConfig,
287
+ ),
288
+ );
289
+ }
290
+ if (result.tokens !== undefined) {
291
+ const response = jsonResponse({
292
+ tokens:
293
+ result.tokens === null
294
+ ? null
295
+ : { token: result.tokens.token, refreshToken: "dummy" },
296
+ });
297
+ return attachCookies(
298
+ response,
299
+ serializeAuthCookies(
300
+ {
301
+ token: result.tokens?.token ?? null,
302
+ refreshToken: result.tokens?.refreshToken ?? null,
303
+ verifier: null,
304
+ },
305
+ host,
306
+ cookieConfig,
307
+ ),
308
+ );
309
+ }
310
+ return jsonResponse(result);
311
+ } catch (error) {
312
+ const response = jsonResponse({ error: (error as Error).message }, 400);
313
+ return attachCookies(
314
+ response,
315
+ serializeAuthCookies(
316
+ {
317
+ token: null,
318
+ refreshToken: null,
319
+ verifier: null,
320
+ },
321
+ host,
322
+ cookieConfig,
323
+ ),
324
+ );
325
+ }
326
+ }
327
+
328
+ try {
329
+ await convexClient(currentCookies.token).action(
330
+ "auth:signOut" as unknown as SignOutAction,
331
+ );
332
+ } catch (error) {
333
+ console.error(error);
334
+ }
335
+ return attachCookies(
336
+ jsonResponse(null),
337
+ serializeAuthCookies(
338
+ {
339
+ token: null,
340
+ refreshToken: null,
341
+ verifier: null,
342
+ },
343
+ host,
344
+ cookieConfig,
345
+ ),
346
+ );
347
+ },
348
+
349
+ async refresh(request: Request): Promise<RefreshResult> {
350
+ const host = cookieHost(request);
351
+
352
+ if (isCorsRequest(request)) {
353
+ return {
354
+ cookies: serializeAuthCookies(
355
+ {
356
+ token: null,
357
+ refreshToken: null,
358
+ verifier: null,
359
+ },
360
+ host,
361
+ cookieConfig,
362
+ ),
363
+ };
364
+ }
365
+
366
+ const requestUrl = new URL(request.url);
367
+ const code = requestUrl.searchParams.get("code");
368
+ const shouldHandleCode =
369
+ options.shouldHandleCode === undefined
370
+ ? true
371
+ : typeof options.shouldHandleCode === "function"
372
+ ? await options.shouldHandleCode(request)
373
+ : options.shouldHandleCode;
374
+
375
+ if (
376
+ code !== null &&
377
+ request.method === "GET" &&
378
+ request.headers.get("accept")?.includes("text/html") &&
379
+ shouldHandleCode
380
+ ) {
381
+ const requestCookies = parseRequestCookies(request);
382
+ const redirectUrl = new URL(requestUrl);
383
+ redirectUrl.searchParams.delete("code");
384
+ try {
385
+ const result = await convexClient().action(
386
+ "auth:signIn" as unknown as SignInAction,
387
+ {
388
+ params: { code },
389
+ verifier: requestCookies.verifier ?? undefined,
390
+ },
391
+ );
392
+ if (result.tokens === undefined) {
393
+ throw new Error("Invalid `auth:signIn` result for code exchange");
394
+ }
395
+ const response = Response.redirect(redirectUrl.toString(), 302);
396
+ return {
397
+ response: attachCookies(
398
+ response,
399
+ serializeAuthCookies(
400
+ {
401
+ token: result.tokens?.token ?? null,
402
+ refreshToken: result.tokens?.refreshToken ?? null,
403
+ verifier: null,
404
+ },
405
+ host,
406
+ cookieConfig,
407
+ ),
408
+ ),
409
+ };
410
+ } catch (error) {
411
+ console.error(error);
412
+ const response = Response.redirect(redirectUrl.toString(), 302);
413
+ return {
414
+ response: attachCookies(
415
+ response,
416
+ serializeAuthCookies(
417
+ {
418
+ token: null,
419
+ refreshToken: null,
420
+ verifier: null,
421
+ },
422
+ host,
423
+ cookieConfig,
424
+ ),
425
+ ),
426
+ };
427
+ }
428
+ }
429
+
430
+ const tokens = await refreshTokens(request);
431
+ if (tokens === undefined) {
432
+ return {};
433
+ }
434
+ return {
435
+ cookies: serializeAuthCookies(
436
+ {
437
+ token: tokens?.token ?? null,
438
+ refreshToken: tokens?.refreshToken ?? null,
439
+ verifier: null,
440
+ },
441
+ host,
442
+ cookieConfig,
443
+ ),
444
+ };
445
+ },
446
+ };
447
+ }
@@ -59,13 +59,13 @@ export function configDefaults(config_: ConvexAuthConfig) {
59
59
  * @internal
60
60
  */
61
61
  export function materializeProvider(provider: AuthProviderConfig) {
62
- const config = { providers: [provider] };
62
+ const config = { providers: [provider], component: {} as any };
63
63
  materializeAndDefaultProviders(config);
64
64
  return config.providers[0] as AuthProviderMaterializedConfig;
65
65
  }
66
66
 
67
67
  function materializeProviders(providers: AuthProviderConfig[]) {
68
- const config = { providers };
68
+ const config = { providers, component: {} as any };
69
69
  materializeAndDefaultProviders(config);
70
70
  return config.providers as AuthProviderMaterializedConfig[];
71
71
  }