@robelest/convex-auth 0.0.4-preview.30 → 0.0.4-preview.31

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 (105) hide show
  1. package/dist/bin.js +125 -36
  2. package/dist/browser/index.d.ts +3 -13
  3. package/dist/browser/index.js +47 -12
  4. package/dist/browser/navigation.js +1 -1
  5. package/dist/browser/passkey.js +7 -7
  6. package/dist/browser/runtime.js +13 -15
  7. package/dist/client/core/types.d.ts +179 -63
  8. package/dist/client/core/types.js +6 -0
  9. package/dist/client/factors/totp.js +1 -1
  10. package/dist/client/index.d.ts +5 -4
  11. package/dist/client/index.js +115 -56
  12. package/dist/client/runtime/mutex.js +3 -2
  13. package/dist/component/_generated/component.d.ts +40 -0
  14. package/dist/component/convex.config.d.ts +2 -2
  15. package/dist/component/http.js +9 -0
  16. package/dist/component/index.d.ts +1 -1
  17. package/dist/component/model.d.ts +25 -25
  18. package/dist/component/model.js +2 -1
  19. package/dist/component/modules.js +1 -0
  20. package/dist/component/public/factors/passkeys.js +31 -1
  21. package/dist/component/public/identity/codes.js +1 -1
  22. package/dist/component/public/identity/tokens.js +2 -1
  23. package/dist/component/public/identity/verifiers.js +15 -5
  24. package/dist/component/public.js +2 -2
  25. package/dist/component/schema.d.ts +292 -290
  26. package/dist/component/schema.js +2 -1
  27. package/dist/core/index.d.ts +8 -3
  28. package/dist/core/index.js +7 -2
  29. package/dist/expo/index.d.ts +21 -0
  30. package/dist/expo/index.js +148 -0
  31. package/dist/expo/passkey.js +174 -0
  32. package/dist/providers/apple.d.ts +1 -1
  33. package/dist/providers/apple.js +6 -8
  34. package/dist/providers/custom.d.ts +1 -1
  35. package/dist/providers/custom.js +4 -7
  36. package/dist/providers/github.d.ts +1 -1
  37. package/dist/providers/github.js +5 -8
  38. package/dist/providers/google.d.ts +1 -1
  39. package/dist/providers/google.js +5 -8
  40. package/dist/providers/microsoft.d.ts +1 -1
  41. package/dist/providers/microsoft.js +5 -9
  42. package/dist/providers/password.d.ts +18 -37
  43. package/dist/providers/password.js +170 -115
  44. package/dist/providers/redirect.d.ts +1 -0
  45. package/dist/providers/redirect.js +20 -0
  46. package/dist/server/auth.d.ts +6 -7
  47. package/dist/server/auth.js +3 -2
  48. package/dist/server/{ctxCache.js → cache/context.js} +2 -2
  49. package/dist/server/{componentContext.d.ts → component/context.d.ts} +2 -2
  50. package/dist/server/context.js +3 -10
  51. package/dist/server/contract.d.ts +2 -87
  52. package/dist/server/contract.js +1 -1
  53. package/dist/server/cookies.js +25 -1
  54. package/dist/server/core.js +1 -1
  55. package/dist/server/errors.js +24 -1
  56. package/dist/server/{auth-context.d.ts → facade.d.ts} +3 -45
  57. package/dist/server/{auth-context.js → facade.js} +11 -12
  58. package/dist/server/http.d.ts +7 -7
  59. package/dist/server/http.js +36 -7
  60. package/dist/server/{convexIdentity.d.ts → identity/convex.d.ts} +3 -3
  61. package/dist/server/index.d.ts +5 -3
  62. package/dist/server/index.js +3 -2
  63. package/dist/server/mounts.d.ts +175 -22
  64. package/dist/server/mutations/code.js +7 -1
  65. package/dist/server/mutations/{credentialsSignIn.js → credentials/signin.js} +10 -10
  66. package/dist/server/mutations/index.js +1 -1
  67. package/dist/server/mutations/invalidate.js +11 -1
  68. package/dist/server/mutations/oauth.js +25 -27
  69. package/dist/server/mutations/signin.js +6 -0
  70. package/dist/server/mutations/signout.js +5 -0
  71. package/dist/server/mutations/store.js +1 -1
  72. package/dist/server/oauth/factory.js +11 -3
  73. package/dist/server/passkey.js +126 -110
  74. package/dist/server/prefetch.js +8 -1
  75. package/dist/server/redirects.js +11 -3
  76. package/dist/server/refresh.js +6 -1
  77. package/dist/server/runtime.d.ts +58 -36
  78. package/dist/server/runtime.js +333 -36
  79. package/dist/server/services/group.js +4 -0
  80. package/dist/server/sessions.js +1 -0
  81. package/dist/server/signin.js +8 -6
  82. package/dist/server/sso/domain.d.ts +159 -16
  83. package/dist/server/sso/domain.js +1 -1
  84. package/dist/server/sso/http.js +144 -60
  85. package/dist/server/sso/oidc.js +28 -12
  86. package/dist/server/sso/policy.js +30 -14
  87. package/dist/server/sso/provision.js +1 -1
  88. package/dist/server/sso/saml.js +18 -9
  89. package/dist/server/sso/scim.js +12 -4
  90. package/dist/server/sso/shared.js +5 -5
  91. package/dist/server/telemetry.js +3 -0
  92. package/dist/server/tokens.js +10 -2
  93. package/dist/server/totp.js +127 -100
  94. package/dist/server/types.d.ts +224 -151
  95. package/dist/server/url.js +1 -1
  96. package/dist/server/users.js +93 -53
  97. package/dist/server/wellknown.d.ts +130 -0
  98. package/dist/server/wellknown.js +195 -0
  99. package/dist/shared/errors.js +0 -1
  100. package/package.json +36 -4
  101. package/dist/server/oauth/index.js +0 -12
  102. package/dist/server/utils/dispatch.js +0 -36
  103. package/dist/shared/authResults.d.ts +0 -16
  104. /package/dist/server/{componentContext.js → component/context.js} +0 -0
  105. /package/dist/server/{convexIdentity.js → identity/convex.js} +0 -0
@@ -59,12 +59,16 @@ function buildSignInRequestKey(provider, params) {
59
59
  params
60
60
  });
61
61
  }
62
+ function formDataEntries(formData) {
63
+ return formData;
64
+ }
62
65
  /**
63
66
  * Create a framework-agnostic auth client.
64
67
  *
65
68
  * Returns an object with `signIn`, `signOut`, `onChange`, `state`, and any
66
- * factor helpers enabled by your configured providers. Browser-specific
67
- * passkey support is added by the `@robelest/convex-auth/browser` entrypoint.
69
+ * factor helpers enabled by your configured providers. Platform-specific
70
+ * passkey support is added by higher-level entrypoints such as
71
+ * `@robelest/convex-auth/browser`.
68
72
  *
69
73
  * ### SPA mode (default)
70
74
  *
@@ -124,7 +128,6 @@ function client(options) {
124
128
  }
125
129
  const storage = options.storage !== void 0 ? options.storage : runtime.storage !== void 0 ? runtime.storage : null;
126
130
  const proxyRuntime = proxy ? requireProxyRuntime() : null;
127
- const replaceUrl = options.replaceUrl ?? (runtime.location ? (url$1) => runtime.location.replace(url$1) : (_url) => {});
128
131
  function getLocation() {
129
132
  if (typeof options.location === "function") return options.location();
130
133
  if (options.location instanceof URL) return options.location;
@@ -151,16 +154,20 @@ function client(options) {
151
154
  function cleanUrlParams(params) {
152
155
  const loc = getLocation();
153
156
  if (!loc) return;
157
+ if (!runtime.location) return;
154
158
  const searchParams = new URLSearchParams(loc.search);
155
159
  let changed = false;
156
160
  for (const p of params) if (searchParams.has(p)) {
157
161
  searchParams.delete(p);
158
162
  changed = true;
159
163
  }
160
- if (changed) replaceUrl(searchParams.toString() ? `${loc.pathname}?${searchParams}` : loc.pathname);
164
+ if (changed) {
165
+ const next = searchParams.toString() ? `${loc.pathname}?${searchParams}` : loc.pathname;
166
+ runtime.location.replace(next);
167
+ }
161
168
  }
162
169
  const url = proxy ? void 0 : resolveUrl(convex, options.url);
163
- const escapedNamespace = proxy ? proxy.replace(/[^a-zA-Z0-9]/g, "") : url.replace(/[^a-zA-Z0-9]/g, "");
170
+ const escapedNamespace = (proxy ?? url).replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_");
164
171
  const key = (name) => `${name}_${escapedNamespace}`;
165
172
  const { get: storageGet, set: storageSet, remove: storageRemove } = createStorageHelpers({
166
173
  storage,
@@ -197,6 +204,7 @@ function client(options) {
197
204
  let authEpoch = 0;
198
205
  let destroyed = false;
199
206
  let activeSignIn = null;
207
+ let initializePromise = null;
200
208
  const handshakeWaiters = /* @__PURE__ */ new Set();
201
209
  let snapshot = {
202
210
  phase: hasServerToken ? "authenticated" : isLoading ? "loading" : "unauthenticated",
@@ -204,7 +212,6 @@ function client(options) {
204
212
  isAuthenticated: hasServerToken,
205
213
  token
206
214
  };
207
- let handlingCodeFlow = false;
208
215
  const settleHandshakeWaiters = (epoch, outcome) => {
209
216
  for (const waiter of Array.from(handshakeWaiters)) {
210
217
  if (waiter.epoch !== epoch) continue;
@@ -404,18 +411,19 @@ function client(options) {
404
411
  * }
405
412
  * ```
406
413
  *
407
- * @example OAuth (triggers redirect)
414
+ * @example OAuth (returns a redirect URL)
408
415
  * ```ts
409
- * await auth.signIn('google'); // redirects to Google
416
+ * const result = await auth.signIn('google');
417
+ * if (result.kind === 'redirect') {
418
+ * window.location.href = result.redirect.toString();
419
+ * }
410
420
  * ```
411
421
  */
412
422
  const signIn = async (provider, args) => {
413
423
  await persistInvite();
414
424
  const params = args instanceof FormData ? (() => {
415
425
  const formParams = {};
416
- args.forEach((value, key$1) => {
417
- formParams[key$1] = typeof value === "string" ? value : value.name;
418
- });
426
+ for (const [key$1, value] of formDataEntries(args)) formParams[key$1] = typeof value === "string" ? value : value.name;
419
427
  return formParams;
420
428
  })() : args ?? {};
421
429
  const flow = typeof params.flow === "string" && params.flow.length > 0 ? params.flow : "signIn";
@@ -424,7 +432,6 @@ function client(options) {
424
432
  if (result.kind === "redirect") {
425
433
  const redirectUrl = new URL(result.redirect);
426
434
  if (resultOptions.persistVerifier) await storageSet(VERIFIER_STORAGE_KEY, result.verifier);
427
- if (runtime.location) await runtime.location.redirect(redirectUrl);
428
435
  return {
429
436
  kind: "redirect",
430
437
  redirect: redirectUrl,
@@ -474,15 +481,24 @@ function client(options) {
474
481
  persistVerifier: false
475
482
  });
476
483
  const verifier = await storageGet(VERIFIER_STORAGE_KEY) ?? void 0;
477
- await storageRemove(VERIFIER_STORAGE_KEY);
478
- return await handleSignInActionResult(await convex.action(requireApiRefs().signIn, {
479
- provider,
480
- params,
481
- verifier
482
- }), {
483
- shouldStore: true,
484
- persistVerifier: true
485
- });
484
+ try {
485
+ const result = await convex.action(requireApiRefs().signIn, {
486
+ provider,
487
+ params,
488
+ verifier
489
+ });
490
+ if (params.code !== void 0) await storageRemove(VERIFIER_STORAGE_KEY);
491
+ return await handleSignInActionResult(result, {
492
+ shouldStore: true,
493
+ persistVerifier: true
494
+ });
495
+ } catch (error) {
496
+ if (params.code !== void 0) {
497
+ const convexCode = error instanceof ConvexError && typeof error.data?.code === "string" ? error.data.code : null;
498
+ if (convexCode !== null && ["INVALID_VERIFICATION_CODE", "INVALID_VERIFIER"].includes(convexCode)) await storageRemove(VERIFIER_STORAGE_KEY);
499
+ }
500
+ throw error;
501
+ }
486
502
  })();
487
503
  activeSignIn = {
488
504
  key: signInKey,
@@ -531,7 +547,7 @@ function client(options) {
531
547
  const withMutex = mutex ? (key$1, callback) => mutex.withKey(key$1, callback) : localMutex;
532
548
  if (proxy) {
533
549
  const tokenBeforeRefresh = token;
534
- return await withMutex("__convexAuthProxyRefresh", async () => {
550
+ return await withMutex(`__convexAuthProxyRefresh_${escapedNamespace}`, async () => {
535
551
  if (token !== tokenBeforeRefresh) return token;
536
552
  try {
537
553
  const result = await retryWithJitteredBackoff(() => proxyFetch({
@@ -556,41 +572,98 @@ function client(options) {
556
572
  });
557
573
  }
558
574
  const tokenBeforeLockAcquisition = token;
559
- return await withMutex(REFRESH_TOKEN_STORAGE_KEY, async () => {
575
+ return await withMutex(key(REFRESH_TOKEN_STORAGE_KEY), async () => {
560
576
  const tokenAfterLockAcquisition = token;
561
577
  if (tokenAfterLockAcquisition !== tokenBeforeLockAcquisition) return tokenAfterLockAcquisition;
562
578
  const refreshToken = await storageGet(REFRESH_TOKEN_STORAGE_KEY) ?? null;
563
579
  if (!refreshToken) {
564
- finalizeLoadingState();
580
+ await setToken({
581
+ shouldStore: true,
582
+ tokens: null,
583
+ resyncConvexAuth: false
584
+ });
565
585
  return null;
566
586
  }
567
587
  await verifyCodeAndSetToken({ refreshToken }, { resyncConvexAuth: false });
568
588
  return token;
569
589
  });
570
590
  };
571
- const handleCodeFlow = async () => {
572
- if (handlingCodeFlow) return;
573
- const location = getLocation();
574
- if (!location) return;
575
- const code = location.searchParams.get("code");
576
- if (!code) return;
577
- handlingCodeFlow = true;
591
+ const resolveOAuthInput = (input) => {
592
+ if (input instanceof URL) return {
593
+ code: input.searchParams.get("code"),
594
+ cleanupUrl: input.searchParams.has("code") ? (() => {
595
+ const next = new URL(input.toString());
596
+ next.searchParams.delete("code");
597
+ return next;
598
+ })() : null
599
+ };
600
+ if (typeof input === "object") return {
601
+ code: input.code,
602
+ cleanupUrl: null
603
+ };
578
604
  try {
579
- await signIn(void 0, { code });
580
- const codeUrl = new URL(location.toString());
581
- codeUrl.searchParams.delete("code");
582
- await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
583
- } catch {} finally {
584
- handlingCodeFlow = false;
605
+ const url$1 = new URL(input);
606
+ return {
607
+ code: url$1.searchParams.get("code"),
608
+ cleanupUrl: url$1.searchParams.has("code") ? (() => {
609
+ const next = new URL(url$1.toString());
610
+ next.searchParams.delete("code");
611
+ return next;
612
+ })() : null
613
+ };
614
+ } catch {
615
+ return {
616
+ code: input,
617
+ cleanupUrl: null
618
+ };
585
619
  }
586
620
  };
621
+ const completeOAuth = async (input) => {
622
+ const { code, cleanupUrl } = resolveOAuthInput(input);
623
+ if (!code) return { handled: false };
624
+ if ((await signIn(void 0, { code })).kind !== "signedIn") throw new Error("OAuth code exchange did not complete sign-in.");
625
+ return {
626
+ handled: true,
627
+ cleanupUrl
628
+ };
629
+ };
587
630
  const hydrateFromStorage = async () => {
588
631
  const storedToken = await storageGet(JWT_STORAGE_KEY) ?? null;
589
632
  await setToken({
590
633
  shouldStore: false,
591
- tokens: storedToken === null ? null : { token: storedToken }
634
+ tokens: storedToken === null ? null : { token: storedToken },
635
+ resyncConvexAuth: storedToken !== null
592
636
  });
593
637
  };
638
+ const initialize = async () => {
639
+ if (initializePromise !== null) return await initializePromise;
640
+ initializePromise = (async () => {
641
+ if (proxy) {
642
+ if (!hasServerToken && runtime.environment !== "server") try {
643
+ await fetchAccessToken({ forceRefreshToken: true });
644
+ } catch (error) {
645
+ logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy token refresh failed:", error]);
646
+ }
647
+ return;
648
+ }
649
+ try {
650
+ await hydrateFromStorage();
651
+ } catch {
652
+ try {
653
+ await setToken({
654
+ shouldStore: false,
655
+ tokens: null,
656
+ resyncConvexAuth: false
657
+ });
658
+ } catch {}
659
+ }
660
+ })();
661
+ try {
662
+ await initializePromise;
663
+ } finally {
664
+ initializePromise = Promise.resolve();
665
+ }
666
+ };
594
667
  /**
595
668
  * Subscribe to auth state changes. Invokes the callback immediately
596
669
  * with the current state, then again on every state transition.
@@ -619,29 +692,14 @@ function client(options) {
619
692
  });
620
693
  }) ?? null;
621
694
  bindConvexAuth();
622
- if (proxy) {
623
- if (!hasServerToken && runtime.environment !== "server") fetchAccessToken({ forceRefreshToken: true }).catch((error) => {
624
- logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy token refresh failed:", error]);
625
- });
626
- } else (async () => {
627
- try {
628
- await hydrateFromStorage();
629
- await handleCodeFlow();
630
- } catch {
631
- try {
632
- await setToken({
633
- shouldStore: false,
634
- tokens: null
635
- });
636
- } catch {}
637
- }
638
- })().catch((error) => {
639
- logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] SPA initialization failed:", error]);
695
+ initialize().catch((error) => {
696
+ logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Client initialization failed:", error]);
640
697
  });
641
698
  return {
642
699
  get state() {
643
700
  return snapshot;
644
701
  },
702
+ initialize,
645
703
  param,
646
704
  get invite() {
647
705
  const pendingInvite = getPendingInvite();
@@ -652,6 +710,7 @@ function client(options) {
652
710
  accept: acceptInvite
653
711
  };
654
712
  },
713
+ completeOAuth,
655
714
  signIn,
656
715
  signOut,
657
716
  onChange,
@@ -7,7 +7,8 @@ async function localMutex(key, callback) {
7
7
  const currentTail = new Promise((resolve) => {
8
8
  releaseCurrent = resolve;
9
9
  });
10
- mutexTails[key] = previousTail.then(() => currentTail, () => currentTail);
10
+ const currentTailPromise = previousTail.then(() => currentTail, () => currentTail);
11
+ mutexTails[key] = currentTailPromise;
11
12
  try {
12
13
  await previousTail;
13
14
  } catch {}
@@ -15,7 +16,7 @@ async function localMutex(key, callback) {
15
16
  return await callback();
16
17
  } finally {
17
18
  releaseCurrent?.();
18
- if (mutexTails[key] === currentTail) delete mutexTails[key];
19
+ if (mutexTails[key] === currentTailPromise) delete mutexTails[key];
19
20
  }
20
21
  }
21
22
 
@@ -187,6 +187,23 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
187
187
  transports?: Array<string>;
188
188
  userId: string;
189
189
  } | null, Name>;
190
+ passkeyGetById: FunctionReference<"query", "internal", {
191
+ passkeyId: string;
192
+ }, {
193
+ _creationTime: number;
194
+ _id: string;
195
+ algorithm: number;
196
+ backedUp: boolean;
197
+ counter: number;
198
+ createdAt: number;
199
+ credentialId: string;
200
+ deviceType: string;
201
+ lastUsedAt?: number;
202
+ name?: string;
203
+ publicKey: ArrayBuffer;
204
+ transports?: Array<string>;
205
+ userId: string;
206
+ } | null, Name>;
190
207
  passkeyInsert: FunctionReference<"mutation", "internal", {
191
208
  algorithm: number;
192
209
  backedUp: boolean;
@@ -1788,6 +1805,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
1788
1805
  };
1789
1806
  verifiers: {
1790
1807
  verifierCreate: FunctionReference<"mutation", "internal", {
1808
+ expirationTime?: number;
1791
1809
  sessionId?: string;
1792
1810
  signature?: string;
1793
1811
  }, string, Name>;
@@ -1799,6 +1817,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
1799
1817
  }, {
1800
1818
  _creationTime: number;
1801
1819
  _id: string;
1820
+ expirationTime?: number;
1802
1821
  sessionId?: string;
1803
1822
  signature?: string;
1804
1823
  } | null, Name>;
@@ -1807,6 +1826,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
1807
1826
  }, {
1808
1827
  _creationTime: number;
1809
1828
  _id: string;
1829
+ expirationTime?: number;
1810
1830
  sessionId?: string;
1811
1831
  signature?: string;
1812
1832
  } | null, Name>;
@@ -2159,6 +2179,23 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
2159
2179
  transports?: Array<string>;
2160
2180
  userId: string;
2161
2181
  } | null, Name>;
2182
+ passkeyGetById: FunctionReference<"query", "internal", {
2183
+ passkeyId: string;
2184
+ }, {
2185
+ _creationTime: number;
2186
+ _id: string;
2187
+ algorithm: number;
2188
+ backedUp: boolean;
2189
+ counter: number;
2190
+ createdAt: number;
2191
+ credentialId: string;
2192
+ deviceType: string;
2193
+ lastUsedAt?: number;
2194
+ name?: string;
2195
+ publicKey: ArrayBuffer;
2196
+ transports?: Array<string>;
2197
+ userId: string;
2198
+ } | null, Name>;
2162
2199
  passkeyInsert: FunctionReference<"mutation", "internal", {
2163
2200
  algorithm: number;
2164
2201
  backedUp: boolean;
@@ -3150,6 +3187,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
3150
3187
  verifier?: string;
3151
3188
  } | null, Name>;
3152
3189
  verifierCreate: FunctionReference<"mutation", "internal", {
3190
+ expirationTime?: number;
3153
3191
  sessionId?: string;
3154
3192
  signature?: string;
3155
3193
  }, string, Name>;
@@ -3161,6 +3199,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
3161
3199
  }, {
3162
3200
  _creationTime: number;
3163
3201
  _id: string;
3202
+ expirationTime?: number;
3164
3203
  sessionId?: string;
3165
3204
  signature?: string;
3166
3205
  } | null, Name>;
@@ -3169,6 +3208,7 @@ type ComponentApi<Name extends string | undefined = string | undefined> = {
3169
3208
  }, {
3170
3209
  _creationTime: number;
3171
3210
  _id: string;
3211
+ expirationTime?: number;
3172
3212
  sessionId?: string;
3173
3213
  signature?: string;
3174
3214
  } | null, Name>;
@@ -1,7 +1,7 @@
1
- import * as convex_server0 from "convex/server";
1
+ import * as convex_server4 from "convex/server";
2
2
 
3
3
  //#region src/component/convex.config.d.ts
4
- declare const component: convex_server0.ComponentDefinition<any>;
4
+ declare const component: convex_server4.ComponentDefinition<any>;
5
5
  //#endregion
6
6
  export { component as default };
7
7
  //# sourceMappingURL=convex.config.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { httpRouter } from "convex/server";
2
+
3
+ //#region src/component/http.ts
4
+ const http = httpRouter();
5
+ var http_default = http;
6
+
7
+ //#endregion
8
+ export { http_default as default };
9
+ //# sourceMappingURL=http.js.map
@@ -1,4 +1,4 @@
1
1
  import { AuthProviderConfig, ConvexAuthConfig, ConvexCredentialsConfig, CorsConfig, DeviceProviderConfig, EmailConfig, EmailUserConfig, GenericDoc, GroupConnectionPolicy, GroupConnectionPolicyPatch, HttpKeyContext, KeyScope, PhoneConfig, PhoneUserConfig, ScopeChecker } from "../server/types.js";
2
- import { AuthConfig, AuthContext, AuthContextConfig, InferAuth, OptionalAuthContext, UserDoc } from "../server/auth-context.js";
2
+ import { AuthConfig, AuthContext, AuthContextConfig, InferAuth, OptionalAuthContext, UserDoc } from "../server/facade.js";
3
3
  import { HttpAuthContext, HttpAuthContextConfig, OptionalHttpAuthContext } from "../server/http.js";
4
4
  import { AuthApi, createAuth } from "../server/auth.js";
@@ -1,7 +1,7 @@
1
- import * as convex_values480 from "convex/values";
1
+ import * as convex_values440 from "convex/values";
2
2
 
3
3
  //#region src/component/model.d.ts
4
- declare const vApiKeyDoc: convex_values480.VObject<{
4
+ declare const vApiKeyDoc: convex_values440.VObject<{
5
5
  lastUsedAt?: number | undefined;
6
6
  expiresAt?: number | undefined;
7
7
  rateLimit?: {
@@ -16,7 +16,7 @@ declare const vApiKeyDoc: convex_values480.VObject<{
16
16
  _creationTime: number;
17
17
  name: string;
18
18
  revoked: boolean;
19
- userId: convex_values480.GenericId<"User">;
19
+ userId: convex_values440.GenericId<"User">;
20
20
  prefix: string;
21
21
  hashedKey: string;
22
22
  scopes: {
@@ -24,43 +24,43 @@ declare const vApiKeyDoc: convex_values480.VObject<{
24
24
  actions: string[];
25
25
  }[];
26
26
  createdAt: number;
27
- _id: convex_values480.GenericId<"ApiKey">;
27
+ _id: convex_values440.GenericId<"ApiKey">;
28
28
  }, {
29
- userId: convex_values480.VId<convex_values480.GenericId<"User">, "required">;
30
- prefix: convex_values480.VString<string, "required">;
31
- hashedKey: convex_values480.VString<string, "required">;
32
- name: convex_values480.VString<string, "required">;
33
- scopes: convex_values480.VArray<{
29
+ userId: convex_values440.VId<convex_values440.GenericId<"User">, "required">;
30
+ prefix: convex_values440.VString<string, "required">;
31
+ hashedKey: convex_values440.VString<string, "required">;
32
+ name: convex_values440.VString<string, "required">;
33
+ scopes: convex_values440.VArray<{
34
34
  resource: string;
35
35
  actions: string[];
36
- }[], convex_values480.VObject<{
36
+ }[], convex_values440.VObject<{
37
37
  resource: string;
38
38
  actions: string[];
39
39
  }, {
40
- resource: convex_values480.VString<string, "required">;
41
- actions: convex_values480.VArray<string[], convex_values480.VString<string, "required">, "required">;
40
+ resource: convex_values440.VString<string, "required">;
41
+ actions: convex_values440.VArray<string[], convex_values440.VString<string, "required">, "required">;
42
42
  }, "required", "resource" | "actions">, "required">;
43
- rateLimit: convex_values480.VObject<{
43
+ rateLimit: convex_values440.VObject<{
44
44
  maxRequests: number;
45
45
  windowMs: number;
46
46
  } | undefined, {
47
- maxRequests: convex_values480.VFloat64<number, "required">;
48
- windowMs: convex_values480.VFloat64<number, "required">;
47
+ maxRequests: convex_values440.VFloat64<number, "required">;
48
+ windowMs: convex_values440.VFloat64<number, "required">;
49
49
  }, "optional", "maxRequests" | "windowMs">;
50
- rateLimitState: convex_values480.VObject<{
50
+ rateLimitState: convex_values440.VObject<{
51
51
  attemptsLeft: number;
52
52
  lastAttemptTime: number;
53
53
  } | undefined, {
54
- attemptsLeft: convex_values480.VFloat64<number, "required">;
55
- lastAttemptTime: convex_values480.VFloat64<number, "required">;
54
+ attemptsLeft: convex_values440.VFloat64<number, "required">;
55
+ lastAttemptTime: convex_values440.VFloat64<number, "required">;
56
56
  }, "optional", "attemptsLeft" | "lastAttemptTime">;
57
- expiresAt: convex_values480.VFloat64<number | undefined, "optional">;
58
- lastUsedAt: convex_values480.VFloat64<number | undefined, "optional">;
59
- createdAt: convex_values480.VFloat64<number, "required">;
60
- revoked: convex_values480.VBoolean<boolean, "required">;
61
- metadata: convex_values480.VAny<any, "optional", string>;
62
- _id: convex_values480.VId<convex_values480.GenericId<"ApiKey">, "required">;
63
- _creationTime: convex_values480.VFloat64<number, "required">;
57
+ expiresAt: convex_values440.VFloat64<number | undefined, "optional">;
58
+ lastUsedAt: convex_values440.VFloat64<number | undefined, "optional">;
59
+ createdAt: convex_values440.VFloat64<number, "required">;
60
+ revoked: convex_values440.VBoolean<boolean, "required">;
61
+ metadata: convex_values440.VAny<any, "optional", string>;
62
+ _id: convex_values440.VId<convex_values440.GenericId<"ApiKey">, "required">;
63
+ _creationTime: convex_values440.VFloat64<number, "required">;
64
64
  }, "required", "_creationTime" | "name" | "revoked" | "lastUsedAt" | "expiresAt" | "userId" | "prefix" | "hashedKey" | "scopes" | "rateLimit" | "rateLimitState" | "createdAt" | "metadata" | "_id" | "rateLimit.maxRequests" | "rateLimit.windowMs" | "rateLimitState.attemptsLeft" | "rateLimitState.lastAttemptTime" | `metadata.${string}`>;
65
65
  //#endregion
66
66
  export { vApiKeyDoc };
@@ -137,7 +137,8 @@ const vAccountDoc = v.object({
137
137
  const vAuthVerifierDoc = v.object({
138
138
  ...vDocMeta(TABLES.AuthVerifier),
139
139
  sessionId: v.optional(v.id(TABLES.Session)),
140
- signature: v.optional(v.string())
140
+ signature: v.optional(v.string()),
141
+ expirationTime: v.optional(v.number())
141
142
  });
142
143
  const vVerificationCodeDoc = v.object({
143
144
  ...vDocMeta(TABLES.VerificationCode),
@@ -6,6 +6,7 @@ const modules = {
6
6
  "./component/_generated/server.ts": () => import("./_generated/server.js"),
7
7
  "./component/convex.config.ts": () => import("./convex.config.js"),
8
8
  "./component/functions.ts": () => import("./functions.js"),
9
+ "./component/http.ts": () => import("./http.js"),
9
10
  "./component/index.ts": () => import("./index.js"),
10
11
  "./component/model.ts": () => import("./model.js"),
11
12
  "./component/public.ts": () => import("./public.js"),
@@ -100,6 +100,36 @@ const passkeyGetByCredentialId = query({
100
100
  }
101
101
  });
102
102
  /**
103
+ * Get a single passkey by its document ID.
104
+ *
105
+ * Performs a direct point lookup on the `Passkey` table. Returns `null`
106
+ * when no passkey exists with the given ID (e.g. it was already deleted).
107
+ * Useful when callers already hold a passkey ID and need to inspect the
108
+ * document — for example to capture the owning `userId` before deletion.
109
+ *
110
+ * @param passkeyId - The `_id` of the `Passkey` document to retrieve.
111
+ * @returns The `Passkey` document, or `null` if no passkey exists with the
112
+ * given ID.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const passkey = await ctx.runQuery(
117
+ * components.auth.factors.passkeys.passkeyGetById,
118
+ * { passkeyId },
119
+ * );
120
+ * if (passkey === null) {
121
+ * throw new Error("Passkey not found");
122
+ * }
123
+ * ```
124
+ */
125
+ const passkeyGetById = query({
126
+ args: { passkeyId: v.id("Passkey") },
127
+ returns: v.union(vPasskeyDoc, v.null()),
128
+ handler: async (ctx, { passkeyId }) => {
129
+ return await ctx.db.get("Passkey", passkeyId);
130
+ }
131
+ });
132
+ /**
103
133
  * List all passkeys registered to a user.
104
134
  *
105
135
  * Retrieves every `Passkey` document associated with the given user via
@@ -234,5 +264,5 @@ const passkeyDelete = mutation({
234
264
  });
235
265
 
236
266
  //#endregion
237
- export { passkeyDelete, passkeyGetByCredentialId, passkeyInsert, passkeyListByUserId, passkeyUpdateCounter, passkeyUpdateMeta };
267
+ export { passkeyDelete, passkeyGetByCredentialId, passkeyGetById, passkeyInsert, passkeyListByUserId, passkeyUpdateCounter, passkeyUpdateMeta };
238
268
  //# sourceMappingURL=passkeys.js.map
@@ -56,7 +56,7 @@ const verificationCodeGetByCode = query({
56
56
  args: { code: v.string() },
57
57
  returns: v.union(vVerificationCodeDoc, v.null()),
58
58
  handler: async (ctx, { code }) => {
59
- return await ctx.db.query("VerificationCode").withIndex("code", (q) => q.eq("code", code)).unique();
59
+ return await ctx.db.query("VerificationCode").withIndex("code", (q) => q.eq("code", code)).first();
60
60
  }
61
61
  });
62
62
  /**
@@ -212,7 +212,8 @@ const refreshTokenExchange = mutation({
212
212
  await Promise.all(tokens.map((token) => ctx.db.delete("RefreshToken", token._id)));
213
213
  };
214
214
  const refreshTokenDoc = await ctx.db.get("RefreshToken", args.refreshTokenId);
215
- if (refreshTokenDoc === null || refreshTokenDoc.expirationTime < args.now || refreshTokenDoc.sessionId !== args.sessionId) {
215
+ if (refreshTokenDoc === null || refreshTokenDoc.sessionId !== args.sessionId) return null;
216
+ if (refreshTokenDoc.expirationTime < args.now) {
216
217
  await cleanupSessionArtifacts();
217
218
  return null;
218
219
  }
@@ -3,6 +3,12 @@ import { mutation, query } from "../../functions.js";
3
3
  import { v } from "convex/values";
4
4
 
5
5
  //#region src/component/public/identity/verifiers.ts
6
+ const DEFAULT_VERIFIER_TTL_MS = 1e3 * 60 * 15;
7
+ async function getUnexpiredVerifier(ctx, verifierId) {
8
+ const verifier = await ctx.db.get("AuthVerifier", verifierId);
9
+ if (verifier?.expirationTime !== void 0 && verifier.expirationTime < Date.now()) return null;
10
+ return verifier;
11
+ }
6
12
  /**
7
13
  * Create a new PKCE verifier, optionally linked to a session.
8
14
  *
@@ -26,13 +32,15 @@ import { v } from "convex/values";
26
32
  const verifierCreate = mutation({
27
33
  args: {
28
34
  sessionId: v.optional(v.id("Session")),
29
- signature: v.optional(v.string())
35
+ signature: v.optional(v.string()),
36
+ expirationTime: v.optional(v.number())
30
37
  },
31
38
  returns: v.id("AuthVerifier"),
32
- handler: async (ctx, { sessionId, signature }) => {
39
+ handler: async (ctx, { sessionId, signature, expirationTime }) => {
33
40
  return await ctx.db.insert("AuthVerifier", {
34
41
  sessionId,
35
- signature
42
+ signature,
43
+ expirationTime: expirationTime ?? Date.now() + DEFAULT_VERIFIER_TTL_MS
36
44
  });
37
45
  }
38
46
  });
@@ -60,7 +68,7 @@ const verifierGetById = query({
60
68
  args: { verifierId: v.id("AuthVerifier") },
61
69
  returns: v.union(vAuthVerifierDoc, v.null()),
62
70
  handler: async (ctx, { verifierId }) => {
63
- return await ctx.db.get("AuthVerifier", verifierId);
71
+ return await getUnexpiredVerifier(ctx, verifierId);
64
72
  }
65
73
  });
66
74
  /**
@@ -89,7 +97,9 @@ const verifierGetBySignature = query({
89
97
  args: { signature: v.string() },
90
98
  returns: v.union(vAuthVerifierDoc, v.null()),
91
99
  handler: async (ctx, { signature }) => {
92
- return await ctx.db.query("AuthVerifier").withIndex("signature", (q) => q.eq("signature", signature)).unique();
100
+ const verifier = await ctx.db.query("AuthVerifier").withIndex("signature", (q) => q.eq("signature", signature)).unique();
101
+ if (verifier?.expirationTime !== void 0 && verifier.expirationTime < Date.now()) return null;
102
+ return verifier;
93
103
  }
94
104
  });
95
105
  /**