@robelest/convex-auth 0.0.3-preview → 0.0.3-preview.3

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 (304) hide show
  1. package/dist/bin.cjs +15 -15
  2. package/dist/client/index.d.ts +40 -12
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +73 -12
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -2
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/component.d.ts +1 -1
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/{portalBridge.d.ts → bridge.d.ts} +2 -2
  11. package/dist/component/bridge.d.ts.map +1 -0
  12. package/dist/component/{portalBridge.js → bridge.js} +2 -2
  13. package/dist/component/bridge.js.map +1 -0
  14. package/dist/component/index.d.ts +11 -4
  15. package/dist/component/index.d.ts.map +1 -1
  16. package/dist/component/index.js +8 -2
  17. package/dist/component/index.js.map +1 -1
  18. package/dist/component/public.d.ts +24 -17
  19. package/dist/component/public.d.ts.map +1 -1
  20. package/dist/component/public.js +23 -4
  21. package/dist/component/public.js.map +1 -1
  22. package/dist/component/schema.d.ts +11 -7
  23. package/dist/component/schema.d.ts.map +1 -1
  24. package/dist/component/schema.js +4 -1
  25. package/dist/component/schema.js.map +1 -1
  26. package/dist/providers/anonymous.d.ts +3 -0
  27. package/dist/providers/anonymous.d.ts.map +1 -1
  28. package/dist/providers/anonymous.js +3 -0
  29. package/dist/providers/anonymous.js.map +1 -1
  30. package/dist/providers/credentials.d.ts +3 -0
  31. package/dist/providers/credentials.d.ts.map +1 -1
  32. package/dist/providers/credentials.js +3 -0
  33. package/dist/providers/credentials.js.map +1 -1
  34. package/dist/providers/email.d.ts +3 -0
  35. package/dist/providers/email.d.ts.map +1 -1
  36. package/dist/providers/email.js +3 -0
  37. package/dist/providers/email.js.map +1 -1
  38. package/dist/providers/passkey.d.ts +7 -1
  39. package/dist/providers/passkey.d.ts.map +1 -1
  40. package/dist/providers/passkey.js +7 -1
  41. package/dist/providers/passkey.js.map +1 -1
  42. package/dist/providers/password.d.ts +3 -0
  43. package/dist/providers/password.d.ts.map +1 -1
  44. package/dist/providers/password.js +3 -0
  45. package/dist/providers/password.js.map +1 -1
  46. package/dist/providers/phone.d.ts +3 -0
  47. package/dist/providers/phone.d.ts.map +1 -1
  48. package/dist/providers/phone.js +3 -0
  49. package/dist/providers/phone.js.map +1 -1
  50. package/dist/providers/totp.d.ts +8 -0
  51. package/dist/providers/totp.d.ts.map +1 -1
  52. package/dist/providers/totp.js +8 -0
  53. package/dist/providers/totp.js.map +1 -1
  54. package/dist/server/{convex-auth.d.ts → auth.d.ts} +226 -36
  55. package/dist/server/auth.d.ts.map +1 -0
  56. package/dist/server/{convex-auth.js → auth.js} +287 -111
  57. package/dist/server/auth.js.map +1 -0
  58. package/dist/server/errors.d.ts +148 -0
  59. package/dist/server/errors.d.ts.map +1 -0
  60. package/dist/server/errors.js +179 -0
  61. package/dist/server/errors.js.map +1 -0
  62. package/dist/server/implementation/index.d.ts +170 -48
  63. package/dist/server/implementation/index.d.ts.map +1 -1
  64. package/dist/server/implementation/index.js +383 -167
  65. package/dist/server/implementation/index.js.map +1 -1
  66. package/dist/server/implementation/{apiKey.d.ts → keys.d.ts} +1 -1
  67. package/dist/server/implementation/keys.d.ts.map +1 -0
  68. package/dist/server/implementation/{apiKey.js → keys.js} +4 -5
  69. package/dist/server/implementation/keys.js.map +1 -0
  70. package/dist/server/implementation/mutations/{modifyAccount.d.ts → account.d.ts} +3 -3
  71. package/dist/server/implementation/mutations/account.d.ts.map +1 -0
  72. package/dist/server/implementation/mutations/{modifyAccount.js → account.js} +4 -3
  73. package/dist/server/implementation/mutations/account.js.map +1 -0
  74. package/dist/server/implementation/mutations/{createVerificationCode.d.ts → code.d.ts} +1 -1
  75. package/dist/server/implementation/mutations/code.d.ts.map +1 -0
  76. package/dist/server/implementation/mutations/{createVerificationCode.js → code.js} +2 -2
  77. package/dist/server/implementation/mutations/code.js.map +1 -0
  78. package/dist/server/implementation/mutations/index.d.ts +33 -33
  79. package/dist/server/implementation/mutations/index.d.ts.map +1 -1
  80. package/dist/server/implementation/mutations/index.js +22 -22
  81. package/dist/server/implementation/mutations/index.js.map +1 -1
  82. package/dist/server/implementation/mutations/{invalidateSessions.d.ts → invalidate.d.ts} +1 -1
  83. package/dist/server/implementation/mutations/invalidate.d.ts.map +1 -0
  84. package/dist/server/implementation/mutations/{invalidateSessions.js → invalidate.js} +2 -2
  85. package/dist/server/implementation/mutations/invalidate.js.map +1 -0
  86. package/dist/server/implementation/mutations/{userOAuth.d.ts → oauth.d.ts} +3 -3
  87. package/dist/server/implementation/mutations/oauth.d.ts.map +1 -0
  88. package/dist/server/implementation/mutations/{userOAuth.js → oauth.js} +4 -3
  89. package/dist/server/implementation/mutations/oauth.js.map +1 -0
  90. package/dist/server/implementation/mutations/{refreshSession.d.ts → refresh.d.ts} +1 -1
  91. package/dist/server/implementation/mutations/refresh.d.ts.map +1 -0
  92. package/dist/server/implementation/mutations/{refreshSession.js → refresh.js} +3 -3
  93. package/dist/server/implementation/mutations/refresh.js.map +1 -0
  94. package/dist/server/implementation/mutations/{createAccountFromCredentials.d.ts → register.d.ts} +4 -4
  95. package/dist/server/implementation/mutations/register.d.ts.map +1 -0
  96. package/dist/server/implementation/mutations/{createAccountFromCredentials.js → register.js} +4 -3
  97. package/dist/server/implementation/mutations/register.js.map +1 -0
  98. package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.d.ts → retrieve.d.ts} +3 -3
  99. package/dist/server/implementation/mutations/retrieve.d.ts.map +1 -0
  100. package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.js → retrieve.js} +3 -3
  101. package/dist/server/implementation/mutations/retrieve.js.map +1 -0
  102. package/dist/server/implementation/mutations/{verifierSignature.d.ts → signature.d.ts} +1 -1
  103. package/dist/server/implementation/mutations/signature.d.ts.map +1 -0
  104. package/dist/server/implementation/mutations/{verifierSignature.js → signature.js} +4 -3
  105. package/dist/server/implementation/mutations/signature.js.map +1 -0
  106. package/dist/server/implementation/mutations/{signIn.d.ts → signin.d.ts} +1 -1
  107. package/dist/server/implementation/mutations/{signIn.d.ts.map → signin.d.ts.map} +1 -1
  108. package/dist/server/implementation/mutations/{signIn.js → signin.js} +2 -2
  109. package/dist/server/implementation/mutations/{signIn.js.map → signin.js.map} +1 -1
  110. package/dist/server/implementation/mutations/{signOut.d.ts → signout.d.ts} +1 -1
  111. package/dist/server/implementation/mutations/{signOut.d.ts.map → signout.d.ts.map} +1 -1
  112. package/dist/server/implementation/mutations/{signOut.js → signout.js} +2 -2
  113. package/dist/server/implementation/mutations/{signOut.js.map → signout.js.map} +1 -1
  114. package/dist/server/implementation/mutations/{storeRef.d.ts → store.d.ts} +1 -1
  115. package/dist/server/implementation/mutations/store.d.ts.map +1 -0
  116. package/dist/server/implementation/mutations/{storeRef.js → store.js} +1 -1
  117. package/dist/server/implementation/mutations/store.js.map +1 -0
  118. package/dist/server/implementation/mutations/verifier.js +1 -1
  119. package/dist/server/implementation/mutations/verifier.js.map +1 -1
  120. package/dist/server/implementation/mutations/{verifyCodeAndSignIn.d.ts → verify.d.ts} +1 -1
  121. package/dist/server/implementation/mutations/verify.d.ts.map +1 -0
  122. package/dist/server/implementation/mutations/{verifyCodeAndSignIn.js → verify.js} +3 -3
  123. package/dist/server/implementation/mutations/verify.js.map +1 -0
  124. package/dist/server/implementation/passkey.d.ts.map +1 -1
  125. package/dist/server/implementation/passkey.js +47 -55
  126. package/dist/server/implementation/passkey.js.map +1 -1
  127. package/dist/server/implementation/provider.d.ts.map +1 -1
  128. package/dist/server/implementation/provider.js +5 -4
  129. package/dist/server/implementation/provider.js.map +1 -1
  130. package/dist/server/implementation/{rateLimit.d.ts → ratelimit.d.ts} +1 -1
  131. package/dist/server/implementation/{rateLimit.d.ts.map → ratelimit.d.ts.map} +1 -1
  132. package/dist/server/implementation/{rateLimit.js → ratelimit.js} +1 -1
  133. package/dist/server/implementation/{rateLimit.js.map → ratelimit.js.map} +1 -1
  134. package/dist/server/implementation/redirects.d.ts.map +1 -1
  135. package/dist/server/implementation/redirects.js +2 -1
  136. package/dist/server/implementation/redirects.js.map +1 -1
  137. package/dist/server/implementation/{refreshTokens.d.ts → refresh.d.ts} +1 -1
  138. package/dist/server/implementation/refresh.d.ts.map +1 -0
  139. package/dist/server/implementation/{refreshTokens.js → refresh.js} +3 -2
  140. package/dist/server/implementation/refresh.js.map +1 -0
  141. package/dist/server/implementation/sessions.js +1 -1
  142. package/dist/server/implementation/sessions.js.map +1 -1
  143. package/dist/server/implementation/{signIn.d.ts → signin.d.ts} +1 -1
  144. package/dist/server/implementation/{signIn.d.ts.map → signin.d.ts.map} +1 -1
  145. package/dist/server/implementation/{signIn.js → signin.js} +12 -8
  146. package/dist/server/implementation/signin.js.map +1 -0
  147. package/dist/server/implementation/totp.d.ts.map +1 -1
  148. package/dist/server/implementation/totp.js +29 -29
  149. package/dist/server/implementation/totp.js.map +1 -1
  150. package/dist/server/implementation/types.d.ts +131 -1
  151. package/dist/server/implementation/types.d.ts.map +1 -1
  152. package/dist/server/implementation/types.js +65 -1
  153. package/dist/server/implementation/types.js.map +1 -1
  154. package/dist/server/implementation/users.d.ts.map +1 -1
  155. package/dist/server/implementation/users.js +3 -2
  156. package/dist/server/implementation/users.js.map +1 -1
  157. package/dist/server/index.d.ts +131 -1
  158. package/dist/server/index.d.ts.map +1 -1
  159. package/dist/server/index.js +117 -1
  160. package/dist/server/index.js.map +1 -1
  161. package/dist/server/oauth/{authorizationUrl.d.ts → authorization.d.ts} +1 -1
  162. package/dist/server/oauth/authorization.d.ts.map +1 -0
  163. package/dist/server/oauth/{authorizationUrl.js → authorization.js} +4 -3
  164. package/dist/server/oauth/authorization.js.map +1 -0
  165. package/dist/server/oauth/callback.d.ts.map +1 -1
  166. package/dist/server/oauth/callback.js +7 -6
  167. package/dist/server/oauth/callback.js.map +1 -1
  168. package/dist/server/oauth/checks.d.ts.map +1 -1
  169. package/dist/server/oauth/checks.js +2 -1
  170. package/dist/server/oauth/checks.js.map +1 -1
  171. package/dist/server/oauth/{convexAuth.d.ts → helpers.d.ts} +1 -1
  172. package/dist/server/oauth/helpers.d.ts.map +1 -0
  173. package/dist/server/oauth/{convexAuth.js → helpers.js} +6 -5
  174. package/dist/server/oauth/helpers.js.map +1 -0
  175. package/dist/server/oauth/lib/utils/{customFetch.d.ts → fetch.d.ts} +1 -1
  176. package/dist/server/oauth/lib/utils/fetch.d.ts.map +1 -0
  177. package/dist/server/oauth/lib/utils/{customFetch.js → fetch.js} +1 -1
  178. package/dist/server/oauth/lib/utils/fetch.js.map +1 -0
  179. package/dist/server/{provider_utils.d.ts → providers.d.ts} +1 -1
  180. package/dist/server/providers.d.ts.map +1 -0
  181. package/dist/server/{provider_utils.js → providers.js} +1 -1
  182. package/dist/server/providers.js.map +1 -0
  183. package/dist/server/{email-templates.d.ts → templates.d.ts} +8 -1
  184. package/dist/server/templates.d.ts.map +1 -0
  185. package/dist/server/{portal-email.js → templates.js} +74 -3
  186. package/dist/server/templates.js.map +1 -0
  187. package/dist/server/types.d.ts +88 -5
  188. package/dist/server/types.d.ts.map +1 -1
  189. package/dist/server/utils.d.ts.map +1 -1
  190. package/dist/server/utils.js +2 -1
  191. package/dist/server/utils.js.map +1 -1
  192. package/dist/server/version.d.ts +1 -1
  193. package/dist/server/version.d.ts.map +1 -1
  194. package/dist/server/version.js +1 -1
  195. package/dist/server/version.js.map +1 -1
  196. package/package.json +5 -1
  197. package/src/cli/index.ts +5 -5
  198. package/src/cli/{portal-link.ts → link.ts} +1 -1
  199. package/src/cli/utils.ts +1 -1
  200. package/src/client/index.ts +102 -17
  201. package/src/component/_generated/api.ts +2 -2
  202. package/src/component/_generated/component.ts +1 -1
  203. package/src/component/{portalBridge.ts → bridge.ts} +2 -2
  204. package/src/component/index.ts +10 -2
  205. package/src/component/public.ts +25 -4
  206. package/src/component/schema.ts +4 -1
  207. package/src/providers/anonymous.ts +3 -0
  208. package/src/providers/credentials.ts +3 -0
  209. package/src/providers/email.ts +3 -0
  210. package/src/providers/passkey.ts +8 -1
  211. package/src/providers/password.ts +3 -0
  212. package/src/providers/phone.ts +3 -0
  213. package/src/providers/totp.ts +9 -0
  214. package/src/server/auth.ts +969 -0
  215. package/src/server/errors.ts +275 -0
  216. package/src/server/implementation/index.ts +370 -88
  217. package/src/server/implementation/{apiKey.ts → keys.ts} +7 -6
  218. package/src/server/implementation/mutations/{modifyAccount.ts → account.ts} +3 -4
  219. package/src/server/implementation/mutations/{createVerificationCode.ts → code.ts} +1 -1
  220. package/src/server/implementation/mutations/index.ts +22 -22
  221. package/src/server/implementation/mutations/{invalidateSessions.ts → invalidate.ts} +1 -1
  222. package/src/server/implementation/mutations/{userOAuth.ts → oauth.ts} +3 -2
  223. package/src/server/implementation/mutations/{refreshSession.ts → refresh.ts} +2 -2
  224. package/src/server/implementation/mutations/{createAccountFromCredentials.ts → register.ts} +3 -2
  225. package/src/server/implementation/mutations/{retrieveAccountWithCredentials.ts → retrieve.ts} +2 -2
  226. package/src/server/implementation/mutations/{verifierSignature.ts → signature.ts} +3 -2
  227. package/src/server/implementation/mutations/{signIn.ts → signin.ts} +1 -1
  228. package/src/server/implementation/mutations/{signOut.ts → signout.ts} +1 -1
  229. package/src/server/implementation/mutations/verifier.ts +1 -1
  230. package/src/server/implementation/mutations/{verifyCodeAndSignIn.ts → verify.ts} +2 -2
  231. package/src/server/implementation/passkey.ts +86 -116
  232. package/src/server/implementation/provider.ts +5 -8
  233. package/src/server/implementation/redirects.ts +2 -3
  234. package/src/server/implementation/{refreshTokens.ts → refresh.ts} +2 -1
  235. package/src/server/implementation/sessions.ts +1 -1
  236. package/src/server/implementation/{signIn.ts → signin.ts} +13 -11
  237. package/src/server/implementation/totp.ts +60 -84
  238. package/src/server/implementation/types.ts +316 -1
  239. package/src/server/implementation/users.ts +4 -7
  240. package/src/server/index.ts +142 -3
  241. package/src/server/oauth/{authorizationUrl.ts → authorization.ts} +3 -2
  242. package/src/server/oauth/callback.ts +7 -6
  243. package/src/server/oauth/checks.ts +3 -1
  244. package/src/server/oauth/{convexAuth.ts → helpers.ts} +8 -5
  245. package/src/server/{portal-email.ts → templates.ts} +78 -2
  246. package/src/server/types.ts +133 -4
  247. package/src/server/utils.ts +3 -1
  248. package/src/server/version.ts +1 -1
  249. package/dist/component/portalBridge.d.ts.map +0 -1
  250. package/dist/component/portalBridge.js.map +0 -1
  251. package/dist/server/convex-auth.d.ts.map +0 -1
  252. package/dist/server/convex-auth.js.map +0 -1
  253. package/dist/server/convex_types.d.ts +0 -17
  254. package/dist/server/convex_types.d.ts.map +0 -1
  255. package/dist/server/convex_types.js +0 -2
  256. package/dist/server/convex_types.js.map +0 -1
  257. package/dist/server/email-templates.d.ts.map +0 -1
  258. package/dist/server/email-templates.js +0 -74
  259. package/dist/server/email-templates.js.map +0 -1
  260. package/dist/server/implementation/apiKey.d.ts.map +0 -1
  261. package/dist/server/implementation/apiKey.js.map +0 -1
  262. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +0 -1
  263. package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +0 -1
  264. package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +0 -1
  265. package/dist/server/implementation/mutations/createVerificationCode.js.map +0 -1
  266. package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +0 -1
  267. package/dist/server/implementation/mutations/invalidateSessions.js.map +0 -1
  268. package/dist/server/implementation/mutations/modifyAccount.d.ts.map +0 -1
  269. package/dist/server/implementation/mutations/modifyAccount.js.map +0 -1
  270. package/dist/server/implementation/mutations/refreshSession.d.ts.map +0 -1
  271. package/dist/server/implementation/mutations/refreshSession.js.map +0 -1
  272. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +0 -1
  273. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +0 -1
  274. package/dist/server/implementation/mutations/storeRef.d.ts.map +0 -1
  275. package/dist/server/implementation/mutations/storeRef.js.map +0 -1
  276. package/dist/server/implementation/mutations/userOAuth.d.ts.map +0 -1
  277. package/dist/server/implementation/mutations/userOAuth.js.map +0 -1
  278. package/dist/server/implementation/mutations/verifierSignature.d.ts.map +0 -1
  279. package/dist/server/implementation/mutations/verifierSignature.js.map +0 -1
  280. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +0 -1
  281. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +0 -1
  282. package/dist/server/implementation/refreshTokens.d.ts.map +0 -1
  283. package/dist/server/implementation/refreshTokens.js.map +0 -1
  284. package/dist/server/implementation/signIn.js.map +0 -1
  285. package/dist/server/oauth/authorizationUrl.d.ts.map +0 -1
  286. package/dist/server/oauth/authorizationUrl.js.map +0 -1
  287. package/dist/server/oauth/convexAuth.d.ts.map +0 -1
  288. package/dist/server/oauth/convexAuth.js.map +0 -1
  289. package/dist/server/oauth/lib/utils/customFetch.d.ts.map +0 -1
  290. package/dist/server/oauth/lib/utils/customFetch.js.map +0 -1
  291. package/dist/server/portal-email.d.ts +0 -19
  292. package/dist/server/portal-email.d.ts.map +0 -1
  293. package/dist/server/portal-email.js.map +0 -1
  294. package/dist/server/provider_utils.d.ts.map +0 -1
  295. package/dist/server/provider_utils.js.map +0 -1
  296. package/src/server/convex-auth.ts +0 -602
  297. package/src/server/convex_types.ts +0 -55
  298. package/src/server/email-templates.ts +0 -77
  299. /package/src/cli/{generateKeys.ts → keys.ts} +0 -0
  300. /package/src/cli/{portal-upload.ts → upload.ts} +0 -0
  301. /package/src/server/implementation/mutations/{storeRef.ts → store.ts} +0 -0
  302. /package/src/server/implementation/{rateLimit.ts → ratelimit.ts} +0 -0
  303. /package/src/server/oauth/lib/utils/{customFetch.ts → fetch.ts} +0 -0
  304. /package/src/server/{provider_utils.ts → providers.ts} +0 -0
@@ -11,7 +11,6 @@
11
11
  * `@oslojs/crypto` for signature verification.
12
12
  */
13
13
 
14
- import { GenericId } from "convex/values";
15
14
  import {
16
15
  parseAttestationObject,
17
16
  parseClientDataJSON,
@@ -22,9 +21,7 @@ import {
22
21
  coseAlgorithmRS256,
23
22
  COSEKeyType,
24
23
  } from "@oslojs/webauthn";
25
- import type { AuthenticatorData } from "@oslojs/webauthn";
26
24
  import {
27
- ECDSAPublicKey,
28
25
  p256,
29
26
  verifyECDSASignature,
30
27
  decodeSEC1PublicKey,
@@ -44,10 +41,22 @@ import {
44
41
  PasskeyProviderConfig,
45
42
  GenericActionCtxWithAuthConfig,
46
43
  } from "../types.js";
47
- import { AuthDataModel, SessionInfo } from "./types.js";
44
+ import {
45
+ AuthDataModel,
46
+ SessionInfo,
47
+ queryUserById,
48
+ queryUserByVerifiedEmail,
49
+ queryPasskeysByUserId,
50
+ queryPasskeyByCredentialId,
51
+ queryVerifierById,
52
+ mutatePasskeyInsert,
53
+ mutatePasskeyUpdateCounter,
54
+ mutateVerifierDelete,
55
+ } from "./types.js";
48
56
  import { callSignIn, callVerifier } from "./mutations/index.js";
49
- import { callVerifierSignature } from "./mutations/verifierSignature.js";
57
+ import { callVerifierSignature } from "./mutations/signature.js";
50
58
  import { authDb } from "./db.js";
59
+ import { throwAuthError } from "../errors.js";
51
60
 
52
61
 
53
62
  type EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;
@@ -64,7 +73,8 @@ function resolveRpOptions(provider: PasskeyProviderConfig) {
64
73
  // because the RP ID wouldn't match the page origin.
65
74
  const siteUrl = process.env.SITE_URL;
66
75
  if (!siteUrl && !provider.options.rpId) {
67
- throw new Error(
76
+ throwAuthError(
77
+ "PASSKEY_MISSING_CONFIG",
68
78
  "Passkey provider requires SITE_URL env var (your frontend URL) " +
69
79
  "or explicit rpId / origin in the provider config. " +
70
80
  "CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain.",
@@ -124,10 +134,7 @@ async function handleRegisterOptions(
124
134
  // Passkey registration requires an authenticated user
125
135
  const identity = await ctx.auth.getUserIdentity();
126
136
  if (identity === null) {
127
- throw new Error(
128
- "Passkey registration requires an authenticated user. " +
129
- "Sign in first, then add a passkey to your account.",
130
- );
137
+ throwAuthError("PASSKEY_AUTH_REQUIRED");
131
138
  }
132
139
  const [userId] = identity.subject.split("|");
133
140
 
@@ -143,25 +150,16 @@ async function handleRegisterOptions(
143
150
  });
144
151
 
145
152
  // Get the user's profile for credential metadata
146
- const user = await ctx.runQuery(
147
- ctx.auth.config.component.public.userGetById,
148
- { userId: userId! },
149
- );
150
- const userName = params.userName ?? (user as any)?.email ?? "user";
151
- const userDisplayName = params.userDisplayName ?? (user as any)?.name ?? userName;
153
+ const user = await queryUserById(ctx, userId!);
154
+ const userName = params.userName ?? user?.email ?? "user";
155
+ const userDisplayName = params.userDisplayName ?? user?.name ?? userName;
152
156
 
153
157
  // Collect existing credentials to prevent re-registration
154
- let excludeCredentials: Array<{ id: string; transports?: string[] }> = [];
155
- const existing = await ctx.runQuery(
156
- ctx.auth.config.component.public.passkeyListByUserId,
157
- { userId: userId! },
158
- );
159
- if (existing) {
160
- excludeCredentials = (existing as any[]).map((pk: any) => ({
161
- id: pk.credentialId,
162
- transports: pk.transports,
163
- }));
164
- }
158
+ const existing = await queryPasskeysByUserId(ctx, userId!);
159
+ const excludeCredentials = existing.map((pk) => ({
160
+ id: pk.credentialId,
161
+ transports: pk.transports,
162
+ }));
165
163
 
166
164
  // User handle is derived from the Convex userId
167
165
  const userHandle = encodeBase64urlNoPadding(
@@ -215,17 +213,14 @@ async function handleRegisterVerify(
215
213
  // Passkey registration requires an authenticated user
216
214
  const identity = await ctx.auth.getUserIdentity();
217
215
  if (identity === null) {
218
- throw new Error(
219
- "Passkey registration requires an authenticated user. " +
220
- "Sign in first, then add a passkey to your account.",
221
- );
216
+ throwAuthError("PASSKEY_AUTH_REQUIRED");
222
217
  }
223
218
  const [userId] = identity.subject.split("|");
224
219
 
225
220
  const rp = resolveRpOptions(provider);
226
221
 
227
222
  if (!verifierValue) {
228
- throw new Error("Missing verifier for passkey registration");
223
+ throwAuthError("PASSKEY_MISSING_VERIFIER");
229
224
  }
230
225
 
231
226
  // Decode client data
@@ -234,13 +229,14 @@ async function handleRegisterVerify(
234
229
 
235
230
  // Verify client data type is "webauthn.create"
236
231
  if (clientData.type !== ClientDataType.Create) {
237
- throw new Error("Invalid client data type: expected webauthn.create");
232
+ throwAuthError("PASSKEY_INVALID_CLIENT_DATA", "Invalid client data type: expected webauthn.create");
238
233
  }
239
234
 
240
235
  // Verify origin
241
236
  const allowedOrigins = Array.isArray(rp.origin) ? rp.origin : [rp.origin];
242
237
  if (!allowedOrigins.includes(clientData.origin)) {
243
- throw new Error(
238
+ throwAuthError(
239
+ "PASSKEY_INVALID_ORIGIN",
244
240
  `Invalid origin: ${clientData.origin}, expected one of: ${allowedOrigins.join(", ")}`,
245
241
  );
246
242
  }
@@ -249,19 +245,13 @@ async function handleRegisterVerify(
249
245
  const challengeHash = encodeBase64urlNoPadding(
250
246
  new Uint8Array(sha256(clientData.challenge)),
251
247
  );
252
- const verifierDoc = await ctx.runQuery(
253
- ctx.auth.config.component.public.verifierGetById,
254
- { verifierId: verifierValue },
255
- );
256
- if (!verifierDoc || (verifierDoc as any).signature !== challengeHash) {
257
- throw new Error("Invalid or expired challenge");
248
+ const verifierDoc = await queryVerifierById(ctx, verifierValue);
249
+ if (!verifierDoc || verifierDoc.signature !== challengeHash) {
250
+ throwAuthError("PASSKEY_INVALID_CHALLENGE");
258
251
  }
259
252
 
260
253
  // Clean up the verifier
261
- await ctx.runMutation(
262
- ctx.auth.config.component.public.verifierDelete,
263
- { verifierId: verifierValue },
264
- );
254
+ await mutateVerifierDelete(ctx, verifierValue);
265
255
 
266
256
  // Parse attestation object
267
257
  const attestationObjectBytes = decodeBase64urlIgnorePadding(params.attestationObject);
@@ -270,21 +260,21 @@ async function handleRegisterVerify(
270
260
 
271
261
  // Verify RP ID hash
272
262
  if (!authenticatorData.verifyRelyingPartyIdHash(rp.rpId)) {
273
- throw new Error("Relying party ID mismatch");
263
+ throwAuthError("PASSKEY_RP_MISMATCH");
274
264
  }
275
265
 
276
266
  // Verify user presence and verification flags
277
267
  if (!authenticatorData.userPresent) {
278
- throw new Error("User presence flag not set");
268
+ throwAuthError("PASSKEY_USER_PRESENCE");
279
269
  }
280
270
  if (rp.userVerification === "required" && !authenticatorData.userVerified) {
281
- throw new Error("User verification required but not performed");
271
+ throwAuthError("PASSKEY_USER_VERIFICATION");
282
272
  }
283
273
 
284
274
  // Extract credential
285
275
  const credential = authenticatorData.credential;
286
276
  if (!credential) {
287
- throw new Error("No credential in attestation");
277
+ throwAuthError("PASSKEY_NO_CREDENTIAL");
288
278
  }
289
279
 
290
280
  const credentialId = encodeBase64urlNoPadding(credential.id);
@@ -320,7 +310,7 @@ async function handleRegisterVerify(
320
310
  const rsaPubKey = new RSAPublicKey(rsa.n, rsa.e);
321
311
  publicKeyBytes = rsaPubKey.encodePKCS1();
322
312
  } else {
323
- throw new Error(`Unsupported algorithm: ${algorithm}`);
313
+ throwAuthError("PASSKEY_UNSUPPORTED_ALGORITHM", `Unsupported algorithm: ${algorithm}`);
324
314
  }
325
315
 
326
316
  const deviceType = params.deviceType ?? "single-device";
@@ -337,24 +327,21 @@ async function handleRegisterVerify(
337
327
  });
338
328
 
339
329
  // Store the passkey credential
340
- await ctx.runMutation(
341
- ctx.auth.config.component.public.passkeyInsert,
342
- {
343
- userId: userId!,
344
- credentialId,
345
- publicKey: publicKeyBytes.buffer.slice(
346
- publicKeyBytes.byteOffset,
347
- publicKeyBytes.byteOffset + publicKeyBytes.byteLength,
348
- ),
349
- algorithm,
350
- counter: authenticatorData.signatureCounter,
351
- transports: params.transports,
352
- deviceType,
353
- backedUp,
354
- name: params.passkeyName,
355
- createdAt: Date.now(),
356
- },
357
- );
330
+ await mutatePasskeyInsert(ctx, {
331
+ userId: userId!,
332
+ credentialId,
333
+ publicKey: publicKeyBytes.buffer.slice(
334
+ publicKeyBytes.byteOffset,
335
+ publicKeyBytes.byteOffset + publicKeyBytes.byteLength,
336
+ ),
337
+ algorithm,
338
+ counter: authenticatorData.signatureCounter,
339
+ transports: params.transports,
340
+ deviceType,
341
+ backedUp,
342
+ name: params.passkeyName,
343
+ createdAt: Date.now(),
344
+ });
358
345
 
359
346
  // Return tokens for the existing session
360
347
  const signInResult = await callSignIn(ctx, {
@@ -399,17 +386,11 @@ async function handleAuthOptions(
399
386
  let allowCredentials: Array<{ type: string; id: string; transports?: string[] }> | undefined;
400
387
  if (params.email) {
401
388
  // Look up user by email, then find their passkeys
402
- const user = await ctx.runQuery(
403
- ctx.auth.config.component.public.userFindByVerifiedEmail,
404
- { email: params.email },
405
- );
389
+ const user = await queryUserByVerifiedEmail(ctx, params.email);
406
390
  if (user) {
407
- const passkeys = await ctx.runQuery(
408
- ctx.auth.config.component.public.passkeyListByUserId,
409
- { userId: (user as any)._id },
410
- );
411
- if (passkeys && (passkeys as any[]).length > 0) {
412
- allowCredentials = (passkeys as any[]).map((pk: any) => ({
391
+ const passkeys = await queryPasskeysByUserId(ctx, user._id);
392
+ if (passkeys.length > 0) {
393
+ allowCredentials = passkeys.map((pk) => ({
413
394
  type: "public-key",
414
395
  id: pk.credentialId,
415
396
  transports: pk.transports,
@@ -447,7 +428,7 @@ async function handleAuthVerify(
447
428
  const rp = resolveRpOptions(provider);
448
429
 
449
430
  if (!verifierValue) {
450
- throw new Error("Missing verifier for passkey authentication");
431
+ throwAuthError("PASSKEY_MISSING_VERIFIER");
451
432
  }
452
433
 
453
434
  // Decode client data
@@ -456,13 +437,14 @@ async function handleAuthVerify(
456
437
 
457
438
  // Verify client data type is "webauthn.get"
458
439
  if (clientData.type !== ClientDataType.Get) {
459
- throw new Error("Invalid client data type: expected webauthn.get");
440
+ throwAuthError("PASSKEY_INVALID_CLIENT_DATA", "Invalid client data type: expected webauthn.get");
460
441
  }
461
442
 
462
443
  // Verify origin
463
444
  const allowedOrigins = Array.isArray(rp.origin) ? rp.origin : [rp.origin];
464
445
  if (!allowedOrigins.includes(clientData.origin)) {
465
- throw new Error(
446
+ throwAuthError(
447
+ "PASSKEY_INVALID_ORIGIN",
466
448
  `Invalid origin: ${clientData.origin}, expected one of: ${allowedOrigins.join(", ")}`,
467
449
  );
468
450
  }
@@ -471,34 +453,24 @@ async function handleAuthVerify(
471
453
  const challengeHash = encodeBase64urlNoPadding(
472
454
  new Uint8Array(sha256(clientData.challenge)),
473
455
  );
474
- const verifierDoc = await ctx.runQuery(
475
- ctx.auth.config.component.public.verifierGetById,
476
- { verifierId: verifierValue },
477
- );
478
- if (!verifierDoc || (verifierDoc as any).signature !== challengeHash) {
479
- throw new Error("Invalid or expired challenge");
456
+ const verifierDoc = await queryVerifierById(ctx, verifierValue);
457
+ if (!verifierDoc || verifierDoc.signature !== challengeHash) {
458
+ throwAuthError("PASSKEY_INVALID_CHALLENGE");
480
459
  }
481
460
 
482
461
  // Clean up the verifier
483
- await ctx.runMutation(
484
- ctx.auth.config.component.public.verifierDelete,
485
- { verifierId: verifierValue },
486
- );
462
+ await mutateVerifierDelete(ctx, verifierValue);
487
463
 
488
464
  // Look up the credential
489
465
  const credentialId = params.credentialId;
490
466
  if (!credentialId) {
491
- throw new Error("Missing credential ID");
467
+ throwAuthError("PASSKEY_UNKNOWN_CREDENTIAL", "Missing credential ID");
492
468
  }
493
469
 
494
- const passkeyDoc = await ctx.runQuery(
495
- ctx.auth.config.component.public.passkeyGetByCredentialId,
496
- { credentialId },
497
- );
498
- if (!passkeyDoc) {
499
- throw new Error("Unknown credential");
470
+ const passkey = await queryPasskeyByCredentialId(ctx, credentialId);
471
+ if (!passkey) {
472
+ throwAuthError("PASSKEY_UNKNOWN_CREDENTIAL", "Unknown credential");
500
473
  }
501
- const passkey = passkeyDoc as any;
502
474
 
503
475
  // Parse authenticator data
504
476
  const authenticatorDataBytes = decodeBase64urlIgnorePadding(params.authenticatorData);
@@ -506,15 +478,15 @@ async function handleAuthVerify(
506
478
 
507
479
  // Verify RP ID hash
508
480
  if (!authenticatorData.verifyRelyingPartyIdHash(rp.rpId)) {
509
- throw new Error("Relying party ID mismatch");
481
+ throwAuthError("PASSKEY_RP_MISMATCH");
510
482
  }
511
483
 
512
484
  // Verify user presence
513
485
  if (!authenticatorData.userPresent) {
514
- throw new Error("User presence flag not set");
486
+ throwAuthError("PASSKEY_USER_PRESENCE");
515
487
  }
516
488
  if (rp.userVerification === "required" && !authenticatorData.userVerified) {
517
- throw new Error("User verification required but not performed");
489
+ throwAuthError("PASSKEY_USER_VERIFICATION");
518
490
  }
519
491
 
520
492
  // Verify signature
@@ -538,7 +510,7 @@ async function handleAuthVerify(
538
510
  ecdsaSignature,
539
511
  );
540
512
  if (!valid) {
541
- throw new Error("Invalid signature");
513
+ throwAuthError("PASSKEY_INVALID_SIGNATURE");
542
514
  }
543
515
  } else if (passkey.algorithm === coseAlgorithmRS256) {
544
516
  // RSA PKCS#1 v1.5 with SHA-256 verification
@@ -552,10 +524,10 @@ async function handleAuthVerify(
552
524
  signature,
553
525
  );
554
526
  if (!valid) {
555
- throw new Error("Invalid signature");
527
+ throwAuthError("PASSKEY_INVALID_SIGNATURE");
556
528
  }
557
529
  } else {
558
- throw new Error(`Unsupported algorithm: ${passkey.algorithm}`);
530
+ throwAuthError("PASSKEY_UNSUPPORTED_ALGORITHM", `Unsupported algorithm: ${passkey.algorithm}`);
559
531
  }
560
532
 
561
533
  // Verify counter (clone detection)
@@ -565,19 +537,15 @@ async function handleAuthVerify(
565
537
  authenticatorData.signatureCounter !== 0 &&
566
538
  authenticatorData.signatureCounter <= passkey.counter
567
539
  ) {
568
- throw new Error(
569
- "Authenticator counter did not increase — possible credential cloning detected",
570
- );
540
+ throwAuthError("PASSKEY_COUNTER_ERROR");
571
541
  }
572
542
 
573
543
  // Update counter and last used timestamp
574
- await ctx.runMutation(
575
- ctx.auth.config.component.public.passkeyUpdateCounter,
576
- {
577
- passkeyId: passkey._id,
578
- counter: authenticatorData.signatureCounter,
579
- lastUsedAt: Date.now(),
580
- },
544
+ await mutatePasskeyUpdateCounter(
545
+ ctx,
546
+ passkey._id,
547
+ authenticatorData.signatureCounter,
548
+ Date.now(),
581
549
  );
582
550
 
583
551
  // Sign in the user
@@ -611,7 +579,8 @@ export async function handlePasskey(
611
579
  > {
612
580
  const flow = args.params?.flow;
613
581
  if (!flow) {
614
- throw new Error(
582
+ throwAuthError(
583
+ "PASSKEY_MISSING_FLOW",
615
584
  "Missing `flow` parameter. Expected one of: register-options, register-verify, auth-options, auth-verify",
616
585
  );
617
586
  }
@@ -626,7 +595,8 @@ export async function handlePasskey(
626
595
  case "auth-verify":
627
596
  return handleAuthVerify(ctx, provider, args.params ?? {}, args.verifier);
628
597
  default:
629
- throw new Error(
598
+ throwAuthError(
599
+ "PASSKEY_UNKNOWN_FLOW",
630
600
  `Unknown passkey flow: ${flow}. Expected one of: register-options, register-verify, auth-options, auth-verify`,
631
601
  );
632
602
  }
@@ -1,15 +1,14 @@
1
1
  import { AuthProviderMaterializedConfig } from "../types.js";
2
2
  import { ConvexAuthMaterializedConfig } from "../types.js";
3
+ import { throwAuthError } from "../errors.js";
3
4
 
4
5
  export async function hash(provider: any, secret: string) {
5
6
  if (provider.type !== "credentials") {
6
- throw new Error(`Provider ${provider.id} is not a credentials provider`);
7
+ throwAuthError("INVALID_CREDENTIALS_PROVIDER", `Provider ${provider.id} is not a credentials provider`, { provider: provider.id });
7
8
  }
8
9
  const hashSecretFn = provider.crypto?.hashSecret;
9
10
  if (hashSecretFn === undefined) {
10
- throw new Error(
11
- `Provider ${provider.id} does not have a \`crypto.hashSecret\` function`,
12
- );
11
+ throwAuthError("MISSING_CRYPTO_FUNCTION", `Provider ${provider.id} does not have a \`crypto.hashSecret\` function`, { provider: provider.id });
13
12
  }
14
13
  return await hashSecretFn(secret);
15
14
  }
@@ -20,13 +19,11 @@ export async function verify(
20
19
  hash: string,
21
20
  ) {
22
21
  if (provider.type !== "credentials") {
23
- throw new Error(`Provider ${provider.id} is not a credentials provider`);
22
+ throwAuthError("INVALID_CREDENTIALS_PROVIDER", `Provider ${provider.id} is not a credentials provider`, { provider: provider.id });
24
23
  }
25
24
  const verifySecretFn = provider.crypto?.verifySecret;
26
25
  if (verifySecretFn === undefined) {
27
- throw new Error(
28
- `Provider ${provider.id} does not have a \`crypto.verifySecret\` function`,
29
- );
26
+ throwAuthError("MISSING_CRYPTO_FUNCTION", `Provider ${provider.id} does not have a \`crypto.verifySecret\` function`, { provider: provider.id });
30
27
  }
31
28
  return await verifySecretFn(secret, hash);
32
29
  }
@@ -1,5 +1,6 @@
1
1
  import { ConvexAuthMaterializedConfig } from "../types.js";
2
2
  import { requireEnv } from "../utils.js";
3
+ import { throwAuthError } from "../errors.js";
3
4
 
4
5
  export async function redirectAbsoluteUrl(
5
6
  config: ConvexAuthMaterializedConfig,
@@ -7,9 +8,7 @@ export async function redirectAbsoluteUrl(
7
8
  ) {
8
9
  if (params.redirectTo !== undefined) {
9
10
  if (typeof params.redirectTo !== "string") {
10
- throw new Error(
11
- `Expected \`redirectTo\` to be a string, got ${params.redirectTo as any}`,
12
- );
11
+ throwAuthError("INVALID_REDIRECT", `Expected \`redirectTo\` to be a string, got ${params.redirectTo as any}`);
13
12
  }
14
13
  const redirectCallback =
15
14
  config.callbacks?.redirect ?? defaultRedirectCallback;
@@ -1,5 +1,6 @@
1
1
  import { GenericId } from "convex/values";
2
2
  import { ConvexAuthConfig } from "../types.js";
3
+ import { throwAuthError } from "../errors.js";
3
4
  import { Doc, MutationCtx } from "./types.js";
4
5
  import {
5
6
  LOG_LEVELS,
@@ -47,7 +48,7 @@ export const parseRefreshToken = (
47
48
  } => {
48
49
  const [refreshTokenId, sessionId] = refreshToken.split(REFRESH_TOKEN_DIVIDER);
49
50
  if (!refreshTokenId || !sessionId) {
50
- throw new Error(`Can't parse refresh token: ${maybeRedact(refreshToken)}`);
51
+ throwAuthError("INVALID_REFRESH_TOKEN", `Can't parse refresh token: ${maybeRedact(refreshToken)}`);
51
52
  }
52
53
  return {
53
54
  refreshTokenId: refreshTokenId as GenericId<"token">,
@@ -14,7 +14,7 @@ import {
14
14
  createRefreshToken,
15
15
  formatRefreshToken,
16
16
  deleteAllRefreshTokens,
17
- } from "./refreshTokens.js";
17
+ } from "./refresh.js";
18
18
  import { authDb } from "./db.js";
19
19
 
20
20
  const DEFAULT_SESSION_TOTAL_DURATION_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
@@ -4,9 +4,7 @@ import {
4
4
  ConvexCredentialsConfig,
5
5
  EmailConfig,
6
6
  GenericActionCtxWithAuthConfig,
7
- PasskeyProviderConfig,
8
7
  PhoneConfig,
9
- TotpProviderConfig,
10
8
  } from "../types.js";
11
9
  import {
12
10
  AuthDataModel,
@@ -28,6 +26,7 @@ import { OAuth2Config, OIDCConfig } from "@auth/core/providers/oauth.js";
28
26
  import { generateRandomString } from "./utils.js";
29
27
  import { handlePasskey } from "./passkey.js";
30
28
  import { handleTotp, checkTotpRequired } from "./totp.js";
29
+ import { throwAuthError } from "../errors.js";
31
30
 
32
31
  const DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 60 * 60 * 24; // 24 hours
33
32
 
@@ -63,9 +62,12 @@ export async function signInImpl(
63
62
  | { kind: "totpSetup"; uri: string; secret: string; verifier: string; totpId: string }
64
63
  > {
65
64
  if (provider === null && args.refreshToken) {
66
- const tokens: Tokens = (await callRefreshSession(ctx, {
65
+ const tokens = await callRefreshSession(ctx, {
67
66
  refreshToken: args.refreshToken,
68
- }))!;
67
+ });
68
+ if (tokens === null) {
69
+ return { kind: "signedIn", signedIn: null };
70
+ }
69
71
  return { kind: "refreshTokens", signedIn: { tokens } };
70
72
  }
71
73
  if (provider === null && args.params?.code !== undefined) {
@@ -82,9 +84,7 @@ export async function signInImpl(
82
84
  }
83
85
 
84
86
  if (provider === null) {
85
- throw new Error(
86
- "Cannot sign in: Missing `provider`, `params.code` or `refreshToken`",
87
- );
87
+ throwAuthError("SIGN_IN_MISSING_PARAMS");
88
88
  }
89
89
  if (provider.type === "email" || provider.type === "phone") {
90
90
  return handleEmailAndPhoneProvider(ctx, provider, args, options);
@@ -102,7 +102,8 @@ export async function signInImpl(
102
102
  return handleTotp(ctx, provider, args);
103
103
  }
104
104
  const _typecheck: never = provider;
105
- throw new Error(
105
+ throwAuthError(
106
+ "UNSUPPORTED_PROVIDER_TYPE",
106
107
  `Provider type ${(provider as any).type} is not supported yet`,
107
108
  );
108
109
  }
@@ -130,7 +131,7 @@ async function handleEmailAndPhoneProvider(
130
131
  allowExtraProviders: options.allowExtraProviders,
131
132
  });
132
133
  if (result === null) {
133
- throw new Error("Could not verify code");
134
+ throwAuthError("INVALID_VERIFICATION_CODE");
134
135
  }
135
136
  return {
136
137
  kind: "signedIn",
@@ -206,7 +207,7 @@ async function handleCredentials(
206
207
  const hasTotpEnrolled = await checkTotpRequired(ctx, result.userId);
207
208
  if (hasTotpEnrolled) {
208
209
  // Create session but withhold tokens — TOTP verification needed
209
- const idsWithoutTokens = await callSignIn(ctx, {
210
+ await callSignIn(ctx, {
210
211
  userId: result.userId,
211
212
  sessionId: result.sessionId,
212
213
  generateTokens: false,
@@ -271,7 +272,8 @@ async function handleOAuthProvider(
271
272
  redirect.searchParams.set("code", verifier);
272
273
  if (args.params?.redirectTo !== undefined) {
273
274
  if (typeof args.params.redirectTo !== "string") {
274
- throw new Error(
275
+ throwAuthError(
276
+ "INVALID_REDIRECT",
275
277
  `Expected \`redirectTo\` to be a string, got ${args.params.redirectTo}`,
276
278
  );
277
279
  }