@robelest/convex-auth 0.0.4-preview.21 → 0.0.4-preview.23

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 (310) hide show
  1. package/dist/authorization/index.d.ts +1 -1
  2. package/dist/authorization/index.js +1 -1
  3. package/dist/authorization/index.js.map +1 -1
  4. package/dist/client/index.d.ts +1 -2
  5. package/dist/client/index.d.ts.map +1 -1
  6. package/dist/client/index.js +36 -39
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/component/client/index.d.ts +1 -2
  9. package/dist/component/convex.config.d.ts +2 -2
  10. package/dist/component/convex.config.d.ts.map +1 -1
  11. package/dist/component/model.d.ts +5 -5
  12. package/dist/component/model.d.ts.map +1 -1
  13. package/dist/component/public/enterprise/audit.d.ts.map +1 -1
  14. package/dist/component/public/enterprise/audit.js.map +1 -1
  15. package/dist/component/public/enterprise/core.d.ts.map +1 -1
  16. package/dist/component/public/enterprise/core.js.map +1 -1
  17. package/dist/component/public/enterprise/domains.d.ts.map +1 -1
  18. package/dist/component/public/enterprise/domains.js.map +1 -1
  19. package/dist/component/public/enterprise/scim.d.ts.map +1 -1
  20. package/dist/component/public/enterprise/scim.js.map +1 -1
  21. package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
  22. package/dist/component/public/enterprise/secrets.js.map +1 -1
  23. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
  24. package/dist/component/public/enterprise/webhooks.js.map +1 -1
  25. package/dist/component/public/factors/devices.d.ts.map +1 -1
  26. package/dist/component/public/factors/devices.js.map +1 -1
  27. package/dist/component/public/factors/passkeys.d.ts.map +1 -1
  28. package/dist/component/public/factors/passkeys.js.map +1 -1
  29. package/dist/component/public/factors/totp.d.ts.map +1 -1
  30. package/dist/component/public/factors/totp.js.map +1 -1
  31. package/dist/component/public/groups/core.js.map +1 -1
  32. package/dist/component/public/groups/invites.d.ts.map +1 -1
  33. package/dist/component/public/groups/invites.js.map +1 -1
  34. package/dist/component/public/groups/members.d.ts.map +1 -1
  35. package/dist/component/public/groups/members.js.map +1 -1
  36. package/dist/component/public/identity/accounts.d.ts.map +1 -1
  37. package/dist/component/public/identity/accounts.js.map +1 -1
  38. package/dist/component/public/identity/codes.d.ts.map +1 -1
  39. package/dist/component/public/identity/codes.js.map +1 -1
  40. package/dist/component/public/identity/sessions.d.ts.map +1 -1
  41. package/dist/component/public/identity/sessions.js.map +1 -1
  42. package/dist/component/public/identity/tokens.d.ts.map +1 -1
  43. package/dist/component/public/identity/tokens.js.map +1 -1
  44. package/dist/component/public/identity/users.d.ts.map +1 -1
  45. package/dist/component/public/identity/users.js.map +1 -1
  46. package/dist/component/public/identity/verifiers.d.ts.map +1 -1
  47. package/dist/component/public/identity/verifiers.js.map +1 -1
  48. package/dist/component/public/security/keys.d.ts.map +1 -1
  49. package/dist/component/public/security/keys.js.map +1 -1
  50. package/dist/component/public/security/limits.d.ts.map +1 -1
  51. package/dist/component/public/security/limits.js.map +1 -1
  52. package/dist/component/schema.d.ts +39 -39
  53. package/dist/component/server/auth.d.ts +95 -52
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +63 -43
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/core.js +116 -235
  58. package/dist/component/server/core.js.map +1 -1
  59. package/dist/component/server/crypto.js +25 -7
  60. package/dist/component/server/crypto.js.map +1 -1
  61. package/dist/component/server/device.js +58 -15
  62. package/dist/component/server/device.js.map +1 -1
  63. package/dist/component/server/enterprise/domain.js +148 -59
  64. package/dist/component/server/enterprise/domain.js.map +1 -1
  65. package/dist/component/server/enterprise/http.js +36 -15
  66. package/dist/component/server/enterprise/http.js.map +1 -1
  67. package/dist/component/server/enterprise/oidc.js +1 -1
  68. package/dist/component/server/http.js +26 -21
  69. package/dist/component/server/http.js.map +1 -1
  70. package/dist/component/server/identity.js +5 -2
  71. package/dist/component/server/identity.js.map +1 -1
  72. package/dist/component/server/limits.js +21 -30
  73. package/dist/component/server/limits.js.map +1 -1
  74. package/dist/component/server/mutations/account.js +12 -10
  75. package/dist/component/server/mutations/account.js.map +1 -1
  76. package/dist/component/server/mutations/code.js +5 -2
  77. package/dist/component/server/mutations/code.js.map +1 -1
  78. package/dist/component/server/mutations/invalidate.js +1 -1
  79. package/dist/component/server/mutations/invalidate.js.map +1 -1
  80. package/dist/component/server/mutations/oauth.js +10 -4
  81. package/dist/component/server/mutations/oauth.js.map +1 -1
  82. package/dist/component/server/mutations/refresh.js +2 -2
  83. package/dist/component/server/mutations/refresh.js.map +1 -1
  84. package/dist/component/server/mutations/register.js +46 -42
  85. package/dist/component/server/mutations/register.js.map +1 -1
  86. package/dist/component/server/mutations/retrieve.js +21 -25
  87. package/dist/component/server/mutations/retrieve.js.map +1 -1
  88. package/dist/component/server/mutations/signature.js +10 -4
  89. package/dist/component/server/mutations/signature.js.map +1 -1
  90. package/dist/component/server/mutations/signout.js.map +1 -1
  91. package/dist/component/server/mutations/store.js +9 -24
  92. package/dist/component/server/mutations/store.js.map +1 -1
  93. package/dist/component/server/mutations/verifier.js.map +1 -1
  94. package/dist/component/server/mutations/verify.js +1 -1
  95. package/dist/component/server/mutations/verify.js.map +1 -1
  96. package/dist/component/server/oauth.js +53 -16
  97. package/dist/component/server/oauth.js.map +1 -1
  98. package/dist/component/server/passkey.js +115 -31
  99. package/dist/component/server/passkey.js.map +1 -1
  100. package/dist/component/server/redirects.js +9 -3
  101. package/dist/component/server/redirects.js.map +1 -1
  102. package/dist/component/server/refresh.js +10 -7
  103. package/dist/component/server/refresh.js.map +1 -1
  104. package/dist/component/server/runtime.d.ts +3 -3
  105. package/dist/component/server/runtime.d.ts.map +1 -1
  106. package/dist/component/server/runtime.js +62 -20
  107. package/dist/component/server/runtime.js.map +1 -1
  108. package/dist/component/server/signin.js +34 -10
  109. package/dist/component/server/signin.js.map +1 -1
  110. package/dist/component/server/totp.js +79 -19
  111. package/dist/component/server/totp.js.map +1 -1
  112. package/dist/component/server/types.d.ts +12 -20
  113. package/dist/component/server/types.d.ts.map +1 -1
  114. package/dist/component/server/types.js.map +1 -1
  115. package/dist/component/server/users.js +6 -3
  116. package/dist/component/server/users.js.map +1 -1
  117. package/dist/component/server/utils.js +10 -4
  118. package/dist/component/server/utils.js.map +1 -1
  119. package/dist/core/types.d.ts +14 -22
  120. package/dist/core/types.d.ts.map +1 -1
  121. package/dist/factors/device.js +8 -9
  122. package/dist/factors/device.js.map +1 -1
  123. package/dist/factors/passkey.js +18 -21
  124. package/dist/factors/passkey.js.map +1 -1
  125. package/dist/providers/password.js +66 -81
  126. package/dist/providers/password.js.map +1 -1
  127. package/dist/runtime/invite.js +2 -8
  128. package/dist/runtime/invite.js.map +1 -1
  129. package/dist/server/auth.d.ts +95 -52
  130. package/dist/server/auth.d.ts.map +1 -1
  131. package/dist/server/auth.js +63 -43
  132. package/dist/server/auth.js.map +1 -1
  133. package/dist/server/core.d.ts +71 -159
  134. package/dist/server/core.d.ts.map +1 -1
  135. package/dist/server/core.js +116 -235
  136. package/dist/server/core.js.map +1 -1
  137. package/dist/server/crypto.d.ts.map +1 -1
  138. package/dist/server/crypto.js +25 -7
  139. package/dist/server/crypto.js.map +1 -1
  140. package/dist/server/device.js +58 -15
  141. package/dist/server/device.js.map +1 -1
  142. package/dist/server/enterprise/domain.d.ts +0 -8
  143. package/dist/server/enterprise/domain.d.ts.map +1 -1
  144. package/dist/server/enterprise/domain.js +148 -59
  145. package/dist/server/enterprise/domain.js.map +1 -1
  146. package/dist/server/enterprise/http.d.ts.map +1 -1
  147. package/dist/server/enterprise/http.js +35 -14
  148. package/dist/server/enterprise/http.js.map +1 -1
  149. package/dist/server/http.d.ts +2 -2
  150. package/dist/server/http.d.ts.map +1 -1
  151. package/dist/server/http.js +25 -20
  152. package/dist/server/http.js.map +1 -1
  153. package/dist/server/identity.js +5 -2
  154. package/dist/server/identity.js.map +1 -1
  155. package/dist/server/index.d.ts +2 -2
  156. package/dist/server/limits.js +21 -30
  157. package/dist/server/limits.js.map +1 -1
  158. package/dist/server/mounts.d.ts +26 -64
  159. package/dist/server/mounts.d.ts.map +1 -1
  160. package/dist/server/mounts.js +45 -106
  161. package/dist/server/mounts.js.map +1 -1
  162. package/dist/server/mutations/account.d.ts +8 -9
  163. package/dist/server/mutations/account.d.ts.map +1 -1
  164. package/dist/server/mutations/account.js +11 -9
  165. package/dist/server/mutations/account.js.map +1 -1
  166. package/dist/server/mutations/code.d.ts +13 -13
  167. package/dist/server/mutations/code.d.ts.map +1 -1
  168. package/dist/server/mutations/code.js +5 -2
  169. package/dist/server/mutations/code.js.map +1 -1
  170. package/dist/server/mutations/invalidate.d.ts +4 -4
  171. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  172. package/dist/server/mutations/invalidate.js.map +1 -1
  173. package/dist/server/mutations/oauth.d.ts +12 -10
  174. package/dist/server/mutations/oauth.d.ts.map +1 -1
  175. package/dist/server/mutations/oauth.js +9 -3
  176. package/dist/server/mutations/oauth.js.map +1 -1
  177. package/dist/server/mutations/refresh.d.ts +3 -3
  178. package/dist/server/mutations/refresh.d.ts.map +1 -1
  179. package/dist/server/mutations/refresh.js +1 -1
  180. package/dist/server/mutations/refresh.js.map +1 -1
  181. package/dist/server/mutations/register.d.ts +11 -11
  182. package/dist/server/mutations/register.d.ts.map +1 -1
  183. package/dist/server/mutations/register.js +45 -41
  184. package/dist/server/mutations/register.js.map +1 -1
  185. package/dist/server/mutations/retrieve.d.ts +6 -6
  186. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  187. package/dist/server/mutations/retrieve.js +20 -24
  188. package/dist/server/mutations/retrieve.js.map +1 -1
  189. package/dist/server/mutations/signature.d.ts +6 -7
  190. package/dist/server/mutations/signature.d.ts.map +1 -1
  191. package/dist/server/mutations/signature.js +9 -3
  192. package/dist/server/mutations/signature.js.map +1 -1
  193. package/dist/server/mutations/signin.d.ts +5 -5
  194. package/dist/server/mutations/signin.d.ts.map +1 -1
  195. package/dist/server/mutations/signout.js.map +1 -1
  196. package/dist/server/mutations/store.d.ts +97 -97
  197. package/dist/server/mutations/store.d.ts.map +1 -1
  198. package/dist/server/mutations/store.js +8 -23
  199. package/dist/server/mutations/store.js.map +1 -1
  200. package/dist/server/mutations/verifier.js.map +1 -1
  201. package/dist/server/mutations/verify.d.ts +10 -10
  202. package/dist/server/mutations/verify.d.ts.map +1 -1
  203. package/dist/server/mutations/verify.js.map +1 -1
  204. package/dist/server/oauth.js +53 -16
  205. package/dist/server/oauth.js.map +1 -1
  206. package/dist/server/passkey.d.ts +2 -2
  207. package/dist/server/passkey.d.ts.map +1 -1
  208. package/dist/server/passkey.js +114 -30
  209. package/dist/server/passkey.js.map +1 -1
  210. package/dist/server/redirects.js +9 -3
  211. package/dist/server/redirects.js.map +1 -1
  212. package/dist/server/refresh.js +10 -7
  213. package/dist/server/refresh.js.map +1 -1
  214. package/dist/server/runtime.d.ts +14 -14
  215. package/dist/server/runtime.d.ts.map +1 -1
  216. package/dist/server/runtime.js +61 -19
  217. package/dist/server/runtime.js.map +1 -1
  218. package/dist/server/signin.js +34 -10
  219. package/dist/server/signin.js.map +1 -1
  220. package/dist/server/ssr.d.ts.map +1 -1
  221. package/dist/server/ssr.js +175 -184
  222. package/dist/server/ssr.js.map +1 -1
  223. package/dist/server/totp.js +78 -18
  224. package/dist/server/totp.js.map +1 -1
  225. package/dist/server/types.d.ts +13 -21
  226. package/dist/server/types.d.ts.map +1 -1
  227. package/dist/server/types.js.map +1 -1
  228. package/dist/server/users.js +6 -3
  229. package/dist/server/users.js.map +1 -1
  230. package/dist/server/utils.js +10 -4
  231. package/dist/server/utils.js.map +1 -1
  232. package/package.json +2 -6
  233. package/src/authorization/index.ts +1 -1
  234. package/src/cli/index.ts +1 -1
  235. package/src/client/core/types.ts +14 -14
  236. package/src/client/factors/device.ts +10 -12
  237. package/src/client/factors/passkey.ts +23 -26
  238. package/src/client/index.ts +54 -64
  239. package/src/client/runtime/invite.ts +5 -7
  240. package/src/component/index.ts +1 -0
  241. package/src/component/public/enterprise/audit.ts +6 -1
  242. package/src/component/public/enterprise/core.ts +1 -0
  243. package/src/component/public/enterprise/domains.ts +5 -1
  244. package/src/component/public/enterprise/scim.ts +1 -0
  245. package/src/component/public/enterprise/secrets.ts +1 -0
  246. package/src/component/public/enterprise/webhooks.ts +1 -0
  247. package/src/component/public/factors/devices.ts +1 -0
  248. package/src/component/public/factors/passkeys.ts +1 -0
  249. package/src/component/public/factors/totp.ts +1 -0
  250. package/src/component/public/groups/core.ts +1 -1
  251. package/src/component/public/groups/invites.ts +7 -1
  252. package/src/component/public/groups/members.ts +1 -0
  253. package/src/component/public/identity/accounts.ts +1 -0
  254. package/src/component/public/identity/codes.ts +1 -0
  255. package/src/component/public/identity/sessions.ts +1 -0
  256. package/src/component/public/identity/tokens.ts +1 -0
  257. package/src/component/public/identity/users.ts +1 -0
  258. package/src/component/public/identity/verifiers.ts +1 -0
  259. package/src/component/public/security/keys.ts +1 -0
  260. package/src/component/public/security/limits.ts +1 -0
  261. package/src/providers/password.ts +89 -110
  262. package/src/server/auth.ts +177 -111
  263. package/src/server/core.ts +197 -233
  264. package/src/server/crypto.ts +31 -29
  265. package/src/server/device.ts +65 -32
  266. package/src/server/enterprise/domain.ts +158 -170
  267. package/src/server/enterprise/http.ts +46 -39
  268. package/src/server/http.ts +36 -30
  269. package/src/server/identity.ts +5 -5
  270. package/src/server/index.ts +2 -0
  271. package/src/server/limits.ts +53 -80
  272. package/src/server/mounts.ts +47 -74
  273. package/src/server/mutations/account.ts +22 -36
  274. package/src/server/mutations/code.ts +6 -6
  275. package/src/server/mutations/invalidate.ts +1 -1
  276. package/src/server/mutations/oauth.ts +14 -8
  277. package/src/server/mutations/refresh.ts +5 -4
  278. package/src/server/mutations/register.ts +87 -132
  279. package/src/server/mutations/retrieve.ts +44 -44
  280. package/src/server/mutations/signature.ts +13 -6
  281. package/src/server/mutations/signout.ts +1 -1
  282. package/src/server/mutations/store.ts +16 -31
  283. package/src/server/mutations/verifier.ts +1 -1
  284. package/src/server/mutations/verify.ts +3 -5
  285. package/src/server/oauth.ts +60 -69
  286. package/src/server/passkey.ts +567 -517
  287. package/src/server/redirects.ts +10 -6
  288. package/src/server/refresh.ts +14 -18
  289. package/src/server/runtime.ts +70 -55
  290. package/src/server/signin.ts +44 -37
  291. package/src/server/ssr.ts +390 -407
  292. package/src/server/totp.ts +85 -35
  293. package/src/server/types.ts +19 -22
  294. package/src/server/users.ts +7 -6
  295. package/src/server/utils.ts +10 -12
  296. package/dist/component/server/authError.js +0 -34
  297. package/dist/component/server/authError.js.map +0 -1
  298. package/dist/component/server/errors.d.ts +0 -1
  299. package/dist/component/server/errors.js +0 -137
  300. package/dist/component/server/errors.js.map +0 -1
  301. package/dist/server/authError.d.ts +0 -46
  302. package/dist/server/authError.d.ts.map +0 -1
  303. package/dist/server/authError.js +0 -34
  304. package/dist/server/authError.js.map +0 -1
  305. package/dist/server/errors.d.ts +0 -177
  306. package/dist/server/errors.d.ts.map +0 -1
  307. package/dist/server/errors.js +0 -212
  308. package/dist/server/errors.js.map +0 -1
  309. package/src/server/authError.ts +0 -44
  310. package/src/server/errors.ts +0 -290
@@ -1 +1 @@
1
- {"version":3,"file":"users.js","names":[],"sources":["../../src/server/users.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"./db\";\nimport { AuthError } from \"./authError\";\nimport { Doc, MutationCtx } from \"./types\";\nimport { AuthProviderMaterializedConfig, ConvexAuthConfig } from \"./types\";\nimport { LOG_LEVELS, logWithLevel } from \"./utils\";\n\ntype CreateOrUpdateUserArgs = {\n type: \"oauth\" | \"credentials\" | \"email\" | \"phone\" | \"verification\";\n provider: AuthProviderMaterializedConfig;\n profile: Record<string, unknown> & {\n email?: string;\n phone?: string;\n emailVerified?: boolean;\n phoneVerified?: boolean;\n };\n accountExtend?: Record<string, unknown>;\n shouldLinkViaEmail?: boolean;\n shouldLinkViaPhone?: boolean;\n};\n\nfunction mergeExtend(\n existing: unknown,\n incoming: Record<string, unknown> | undefined,\n) {\n if (!incoming) {\n return undefined;\n }\n const existingRecord =\n typeof existing === \"object\" &&\n existing !== null &&\n !Array.isArray(existing)\n ? (existing as Record<string, unknown>)\n : undefined;\n return existingRecord ? { ...existingRecord, ...incoming } : incoming;\n}\n\n/** @internal */\nexport async function upsertUserAndAccount(\n ctx: MutationCtx,\n sessionId: GenericId<\"Session\"> | null,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n opts?: { existingUserId?: GenericId<\"User\"> },\n): Promise<{\n userId: GenericId<\"User\">;\n accountId: GenericId<\"Account\">;\n}> {\n const userId = await defaultCreateOrUpdateUser(\n ctx,\n sessionId,\n \"existingAccount\" in account ? account.existingAccount : null,\n args,\n config,\n opts?.existingUserId ?? null,\n );\n const accountId = await createOrUpdateAccount(\n ctx,\n userId,\n account,\n args,\n config,\n );\n return { userId, accountId };\n}\n\nasync function defaultCreateOrUpdateUser(\n ctx: MutationCtx,\n existingSessionId: GenericId<\"Session\"> | null,\n existingAccount: Doc<\"Account\"> | null,\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n existingUserIdOverride: GenericId<\"User\"> | null,\n) {\n logWithLevel(LOG_LEVELS.DEBUG, \"defaultCreateOrUpdateUser args:\", {\n existingAccountId: existingAccount?._id,\n existingSessionId,\n args,\n });\n const existingUserId = existingAccount?.userId ?? null;\n const db = authDb(ctx, config);\n if (config.callbacks?.createOrUpdateUser !== undefined) {\n logWithLevel(LOG_LEVELS.DEBUG, \"Using custom createOrUpdateUser callback\");\n return await config.callbacks.createOrUpdateUser(ctx, {\n existingUserId,\n ...args,\n });\n }\n\n const {\n provider,\n profile: {\n id: _profileId,\n emailVerified: profileEmailVerified,\n phoneVerified: profilePhoneVerified,\n ...profile\n },\n } = args;\n const emailVerified =\n profileEmailVerified ??\n (provider.type === \"oauth\" && provider.accountLinking !== \"none\");\n const phoneVerified = profilePhoneVerified ?? false;\n const shouldLinkViaEmail =\n args.shouldLinkViaEmail || emailVerified || provider.type === \"email\";\n const shouldLinkViaPhone =\n args.shouldLinkViaPhone || phoneVerified || provider.type === \"phone\";\n\n let userId = existingUserId ?? existingUserIdOverride;\n if (existingUserId === null) {\n const existingUserWithVerifiedEmailId =\n typeof profile.email === \"string\" && shouldLinkViaEmail\n ? ((await uniqueUserWithVerifiedEmail(ctx, profile.email, config))\n ?._id ?? null)\n : null;\n\n const existingUserWithVerifiedPhoneId =\n typeof profile.phone === \"string\" && shouldLinkViaPhone\n ? ((await uniqueUserWithVerifiedPhone(ctx, profile.phone, config))\n ?._id ?? null)\n : null;\n const linkDispatch = {\n tag:\n existingUserWithVerifiedEmailId !== null &&\n existingUserWithVerifiedPhoneId !== null\n ? \"both\"\n : existingUserWithVerifiedEmailId !== null\n ? \"email\"\n : existingUserWithVerifiedPhoneId !== null\n ? \"phone\"\n : \"none\",\n existingUserWithVerifiedEmailId,\n existingUserWithVerifiedPhoneId,\n } as const;\n\n const linkHandlers = {\n both: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email and phone verified users, so not linking: email: ${linkDispatch.existingUserWithVerifiedEmailId}, phone: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return null;\n }),\n email: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email verified user, linking: ${linkDispatch.existingUserWithVerifiedEmailId}`,\n );\n return linkDispatch.existingUserWithVerifiedEmailId;\n }),\n phone: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing phone verified user, linking: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return linkDispatch.existingUserWithVerifiedPhoneId;\n }),\n none: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No existing verified users found, creating new user\",\n );\n return null;\n }),\n } as const;\n\n userId = await Fx.run(linkHandlers[linkDispatch.tag]());\n }\n const userData = {\n ...(emailVerified ? { emailVerificationTime: Date.now() } : null),\n ...(phoneVerified ? { phoneVerificationTime: Date.now() } : null),\n ...profile,\n };\n const existingOrLinkedUserId = userId;\n if (userId !== null) {\n await Fx.run(\n Fx.from({\n ok: () => db.users.patch(userId!, userData),\n err: (error) =>\n new AuthError(\n \"USER_UPDATE_FAILED\",\n `Could not update user document with ID \\`${userId}\\`, ` +\n `either the user has been deleted but their account has not, ` +\n `or the profile data doesn't match the \\`users\\` table schema: ` +\n `${(error as Error).message}`,\n ),\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n } else {\n userId = (await db.users.insert(userData)) as GenericId<\"User\">;\n }\n const afterUserCreatedOrUpdated = config.callbacks?.afterUserCreatedOrUpdated;\n if (afterUserCreatedOrUpdated !== undefined) {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"Calling custom afterUserCreatedOrUpdated callback\",\n );\n await afterUserCreatedOrUpdated(ctx, {\n userId,\n existingUserId: existingOrLinkedUserId,\n ...args,\n });\n } else {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No custom afterUserCreatedOrUpdated callback, skipping\",\n );\n }\n return userId;\n}\n\nasync function uniqueUserWithVerifiedEmail(\n ctx: MutationCtx,\n email: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedEmail(email)) as Doc<\"User\"> | null;\n}\n\nasync function uniqueUserWithVerifiedPhone(\n ctx: MutationCtx,\n phone: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedPhone(phone)) as Doc<\"User\"> | null;\n}\n\nasync function createOrUpdateAccount(\n ctx: MutationCtx,\n userId: GenericId<\"User\">,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n const mergedExtend =\n \"existingAccount\" in account\n ? mergeExtend(account.existingAccount.extend, args.accountExtend)\n : args.accountExtend;\n const accountId =\n \"existingAccount\" in account\n ? account.existingAccount._id\n : ((await db.accounts.create({\n userId,\n provider: args.provider.id,\n providerAccountId: account.providerAccountId,\n secret: account.secret,\n extend: mergedExtend,\n })) as GenericId<\"Account\">);\n // This is never used with the default `createOrUpdateUser` implementation,\n // but it is used for manual linking via custom `createOrUpdateUser`:\n if (\n \"existingAccount\" in account &&\n account.existingAccount.userId !== userId\n ) {\n await db.accounts.patch(accountId, { userId });\n }\n const accountPatchData: Record<string, unknown> = {};\n if (mergedExtend) {\n accountPatchData.extend = mergedExtend;\n }\n if (args.profile.emailVerified) {\n accountPatchData.emailVerified = args.profile.email;\n }\n if (args.profile.phoneVerified) {\n accountPatchData.phoneVerified = args.profile.phone;\n }\n if (Object.keys(accountPatchData).length > 0) {\n await db.accounts.patch(accountId, accountPatchData);\n }\n return accountId;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"users.js","names":[],"sources":["../../src/server/users.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"./db\";\nimport { Doc, MutationCtx } from \"./types\";\nimport { AuthProviderMaterializedConfig, ConvexAuthConfig } from \"./types\";\nimport { LOG_LEVELS, logWithLevel } from \"./utils\";\n\ntype CreateOrUpdateUserArgs = {\n type: \"oauth\" | \"credentials\" | \"email\" | \"phone\" | \"verification\";\n provider: AuthProviderMaterializedConfig;\n profile: Record<string, unknown> & {\n email?: string;\n phone?: string;\n emailVerified?: boolean;\n phoneVerified?: boolean;\n };\n accountExtend?: Record<string, unknown>;\n shouldLinkViaEmail?: boolean;\n shouldLinkViaPhone?: boolean;\n};\n\nfunction mergeExtend(\n existing: unknown,\n incoming: Record<string, unknown> | undefined,\n) {\n if (!incoming) {\n return undefined;\n }\n const existingRecord =\n typeof existing === \"object\" &&\n existing !== null &&\n !Array.isArray(existing)\n ? (existing as Record<string, unknown>)\n : undefined;\n return existingRecord ? { ...existingRecord, ...incoming } : incoming;\n}\n\n/** @internal */\nexport async function upsertUserAndAccount(\n ctx: MutationCtx,\n sessionId: GenericId<\"Session\"> | null,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n opts?: { existingUserId?: GenericId<\"User\"> },\n): Promise<{\n userId: GenericId<\"User\">;\n accountId: GenericId<\"Account\">;\n}> {\n const userId = await defaultCreateOrUpdateUser(\n ctx,\n sessionId,\n \"existingAccount\" in account ? account.existingAccount : null,\n args,\n config,\n opts?.existingUserId ?? null,\n );\n const accountId = await createOrUpdateAccount(\n ctx,\n userId,\n account,\n args,\n config,\n );\n return { userId, accountId };\n}\n\nasync function defaultCreateOrUpdateUser(\n ctx: MutationCtx,\n existingSessionId: GenericId<\"Session\"> | null,\n existingAccount: Doc<\"Account\"> | null,\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n existingUserIdOverride: GenericId<\"User\"> | null,\n) {\n logWithLevel(LOG_LEVELS.DEBUG, \"defaultCreateOrUpdateUser args:\", {\n existingAccountId: existingAccount?._id,\n existingSessionId,\n args,\n });\n const existingUserId = existingAccount?.userId ?? null;\n const db = authDb(ctx, config);\n if (config.callbacks?.createOrUpdateUser !== undefined) {\n logWithLevel(LOG_LEVELS.DEBUG, \"Using custom createOrUpdateUser callback\");\n return await config.callbacks.createOrUpdateUser(ctx, {\n existingUserId,\n ...args,\n });\n }\n\n const {\n provider,\n profile: {\n id: _profileId,\n emailVerified: profileEmailVerified,\n phoneVerified: profilePhoneVerified,\n ...profile\n },\n } = args;\n const emailVerified =\n profileEmailVerified ??\n (provider.type === \"oauth\" && provider.accountLinking !== \"none\");\n const phoneVerified = profilePhoneVerified ?? false;\n const shouldLinkViaEmail =\n args.shouldLinkViaEmail || emailVerified || provider.type === \"email\";\n const shouldLinkViaPhone =\n args.shouldLinkViaPhone || phoneVerified || provider.type === \"phone\";\n\n let userId = existingUserId ?? existingUserIdOverride;\n if (existingUserId === null) {\n const existingUserWithVerifiedEmailId =\n typeof profile.email === \"string\" && shouldLinkViaEmail\n ? ((await uniqueUserWithVerifiedEmail(ctx, profile.email, config))\n ?._id ?? null)\n : null;\n\n const existingUserWithVerifiedPhoneId =\n typeof profile.phone === \"string\" && shouldLinkViaPhone\n ? ((await uniqueUserWithVerifiedPhone(ctx, profile.phone, config))\n ?._id ?? null)\n : null;\n const linkDispatch = {\n tag:\n existingUserWithVerifiedEmailId !== null &&\n existingUserWithVerifiedPhoneId !== null\n ? \"both\"\n : existingUserWithVerifiedEmailId !== null\n ? \"email\"\n : existingUserWithVerifiedPhoneId !== null\n ? \"phone\"\n : \"none\",\n existingUserWithVerifiedEmailId,\n existingUserWithVerifiedPhoneId,\n } as const;\n\n const linkHandlers = {\n both: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email and phone verified users, so not linking: email: ${linkDispatch.existingUserWithVerifiedEmailId}, phone: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return null;\n }),\n email: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email verified user, linking: ${linkDispatch.existingUserWithVerifiedEmailId}`,\n );\n return linkDispatch.existingUserWithVerifiedEmailId;\n }),\n phone: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing phone verified user, linking: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return linkDispatch.existingUserWithVerifiedPhoneId;\n }),\n none: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No existing verified users found, creating new user\",\n );\n return null;\n }),\n } as const;\n\n userId = await Fx.run(linkHandlers[linkDispatch.tag]());\n }\n const userData = {\n ...(emailVerified ? { emailVerificationTime: Date.now() } : null),\n ...(phoneVerified ? { phoneVerificationTime: Date.now() } : null),\n ...profile,\n };\n const existingOrLinkedUserId = userId;\n if (userId !== null) {\n await Fx.run(\n Fx.from({\n ok: () => db.users.patch(userId!, userData),\n err: (error) =>\n Cv.error({\n code: \"USER_UPDATE_FAILED\",\n message:\n `Could not update user document with ID \\`${userId}\\`, ` +\n `either the user has been deleted but their account has not, ` +\n `or the profile data doesn't match the \\`users\\` table schema: ` +\n `${(error as Error).message}`,\n }),\n }).pipe(Fx.recover((e) => Fx.fatal(e))),\n );\n } else {\n userId = (await db.users.insert(userData)) as GenericId<\"User\">;\n }\n const afterUserCreatedOrUpdated = config.callbacks?.afterUserCreatedOrUpdated;\n if (afterUserCreatedOrUpdated !== undefined) {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"Calling custom afterUserCreatedOrUpdated callback\",\n );\n await afterUserCreatedOrUpdated(ctx, {\n userId,\n existingUserId: existingOrLinkedUserId,\n ...args,\n });\n } else {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No custom afterUserCreatedOrUpdated callback, skipping\",\n );\n }\n return userId;\n}\n\nasync function uniqueUserWithVerifiedEmail(\n ctx: MutationCtx,\n email: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedEmail(email)) as Doc<\"User\"> | null;\n}\n\nasync function uniqueUserWithVerifiedPhone(\n ctx: MutationCtx,\n phone: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedPhone(phone)) as Doc<\"User\"> | null;\n}\n\nasync function createOrUpdateAccount(\n ctx: MutationCtx,\n userId: GenericId<\"User\">,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n const mergedExtend =\n \"existingAccount\" in account\n ? mergeExtend(account.existingAccount.extend, args.accountExtend)\n : args.accountExtend;\n const accountId =\n \"existingAccount\" in account\n ? account.existingAccount._id\n : ((await db.accounts.create({\n userId,\n provider: args.provider.id,\n providerAccountId: account.providerAccountId,\n secret: account.secret,\n extend: mergedExtend,\n })) as GenericId<\"Account\">);\n // This is never used with the default `createOrUpdateUser` implementation,\n // but it is used for manual linking via custom `createOrUpdateUser`:\n if (\n \"existingAccount\" in account &&\n account.existingAccount.userId !== userId\n ) {\n await db.accounts.patch(accountId, { userId });\n }\n const accountPatchData: Record<string, unknown> = {};\n if (mergedExtend) {\n accountPatchData.extend = mergedExtend;\n }\n if (args.profile.emailVerified) {\n accountPatchData.emailVerified = args.profile.email;\n }\n if (args.profile.phoneVerified) {\n accountPatchData.phoneVerified = args.profile.phone;\n }\n if (Object.keys(accountPatchData).length > 0) {\n await db.accounts.patch(accountId, accountPatchData);\n }\n return accountId;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AuthError } from "./authError.js";
1
+ import { Cv } from "@robelest/fx/convex";
2
2
  import { generateRandomString as generateRandomString$1 } from "@oslojs/crypto/random";
3
3
  import { sha256 as sha256$1 } from "@oslojs/crypto/sha2";
4
4
  import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding, encodeHexLowerCase } from "@oslojs/encoding";
@@ -7,13 +7,16 @@ import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding, encodeHexLowerC
7
7
  /**
8
8
  * Require an environment variable to be set, throwing at config time if missing.
9
9
  *
10
- * Uses `AuthError.toConvexError()` directly since this is a synchronous guard
10
+ * Uses `Cv.error()` directly since this is a synchronous guard
11
11
  * called inline in many expressions — not suitable for Fx pipeline wrapping.
12
12
  */
13
13
  /** @internal */
14
14
  function requireEnv(name) {
15
15
  const value = process.env[name];
16
- if (value === void 0) throw new AuthError("MISSING_ENV_VAR", `Missing environment variable \`${name}\``, { variable: name }).toConvexError();
16
+ if (value === void 0) throw Cv.error({
17
+ code: "MISSING_ENV_VAR",
18
+ message: `Missing environment variable \`${name}\``
19
+ });
17
20
  return value;
18
21
  }
19
22
  /** @internal */
@@ -107,7 +110,10 @@ async function encryptSecret(value) {
107
110
  /** @internal */
108
111
  async function decryptSecret(ciphertext) {
109
112
  const [ivEncoded, payloadEncoded] = ciphertext.split(".");
110
- if (!ivEncoded || !payloadEncoded) throw new AuthError("INVALID_PARAMETERS", "Stored enterprise secret is malformed.").toConvexError();
113
+ if (!ivEncoded || !payloadEncoded) throw Cv.error({
114
+ code: "INVALID_PARAMETERS",
115
+ message: "Stored enterprise secret is malformed."
116
+ });
111
117
  const key = await getSecretCryptoKey();
112
118
  const decrypted = await crypto.subtle.decrypt({
113
119
  name: "AES-GCM",
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":["rawSha256","osloGenerateRandomString"],"sources":["../../src/server/utils.ts"],"sourcesContent":["import {\n RandomReader,\n generateRandomString as osloGenerateRandomString,\n} from \"@oslojs/crypto/random\";\nimport { sha256 as rawSha256 } from \"@oslojs/crypto/sha2\";\nimport {\n decodeBase64urlIgnorePadding,\n encodeBase64urlNoPadding,\n encodeHexLowerCase,\n} from \"@oslojs/encoding\";\n\nimport { AuthError } from \"./authError\";\n\n/**\n * Require an environment variable to be set, throwing at config time if missing.\n *\n * Uses `AuthError.toConvexError()` directly since this is a synchronous guard\n * called inline in many expressions — not suitable for Fx pipeline wrapping.\n */\n/** @internal */\nexport function requireEnv(name: string) {\n const value = process.env[name];\n if (value === undefined) {\n throw new AuthError(\n \"MISSING_ENV_VAR\",\n `Missing environment variable \\`${name}\\``,\n { variable: name },\n ).toConvexError();\n }\n return value;\n}\n\n/** @internal */\nexport function isLocalHost(host?: string) {\n if (host === undefined) {\n return false;\n }\n const raw = host.includes(\"://\") ? host : `http://${host}`;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return false;\n }\n return (\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\"\n );\n}\n\n// Internal server utilities (merged from former internalUtils.ts)\n\n/** @internal */\nexport const TOKEN_SUB_CLAIM_DIVIDER = \"|\";\n/** @internal */\nexport const REFRESH_TOKEN_DIVIDER = \"|\";\n\n/** @internal */\nexport async function sha256(input: string) {\n return encodeHexLowerCase(rawSha256(new TextEncoder().encode(input)));\n}\n\n/** @internal */\nexport function generateRandomString(length: number, alphabet: string) {\n const random: RandomReader = {\n read(bytes) {\n crypto.getRandomValues(bytes as Uint8Array<ArrayBuffer>);\n },\n };\n\n return osloGenerateRandomString(random, alphabet, length);\n}\n\n/** @internal */\nexport function errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** @internal */\nexport function logError(error: unknown) {\n logWithLevel(\n LOG_LEVELS.ERROR,\n error instanceof Error\n ? error.message + \"\\n\" + error.stack?.replace(\"\\\\n\", \"\\n\")\n : error,\n );\n}\n\n/** @internal */\nexport const LOG_LEVELS = {\n ERROR: \"ERROR\",\n WARN: \"WARN\",\n INFO: \"INFO\",\n DEBUG: \"DEBUG\",\n} as const;\ntype LogLevel = keyof typeof LOG_LEVELS;\n\n/** @internal */\nexport function logWithLevel(level: LogLevel, ...args: unknown[]) {\n const configuredLogLevel =\n LOG_LEVELS[\n (process.env.AUTH_LOG_LEVEL as LogLevel | undefined) ?? \"INFO\"\n ] ?? \"INFO\";\n switch (level) {\n case \"ERROR\":\n console.error(...args);\n break;\n case \"WARN\":\n if (configuredLogLevel !== \"ERROR\") {\n console.warn(...args);\n }\n break;\n case \"INFO\":\n if (configuredLogLevel === \"INFO\" || configuredLogLevel === \"DEBUG\") {\n console.info(...args);\n }\n break;\n case \"DEBUG\":\n if (configuredLogLevel === \"DEBUG\") {\n console.debug(...args);\n }\n break;\n }\n}\n\nconst UNREDACTED_LENGTH = 5;\n/** @internal */\nexport function maybeRedact(value: string) {\n if (value === \"\") {\n return \"\";\n }\n const shouldRedact = process.env.AUTH_LOG_SECRETS !== \"true\";\n if (shouldRedact) {\n if (value.length < UNREDACTED_LENGTH * 2) {\n return \"<redacted>\";\n }\n return (\n value.substring(0, UNREDACTED_LENGTH) +\n \"<redacted>\" +\n value.substring(value.length - UNREDACTED_LENGTH)\n );\n } else {\n return value;\n }\n}\n\nconst SECRET_KEY_ENV = \"AUTH_SECRET_ENCRYPTION_KEY\";\nconst SECRET_IV_LENGTH = 12;\n\nfunction toArrayBuffer(bytes: Uint8Array) {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nasync function getSecretCryptoKey() {\n const material = requireEnv(SECRET_KEY_ENV);\n const rawKey = rawSha256(new TextEncoder().encode(material));\n return await crypto.subtle.importKey(\n \"raw\",\n toArrayBuffer(rawKey),\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\n/** @internal */\nexport async function encryptSecret(value: string) {\n const key = await getSecretCryptoKey();\n const iv = crypto.getRandomValues(new Uint8Array(SECRET_IV_LENGTH));\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: toArrayBuffer(iv) },\n key,\n toArrayBuffer(new TextEncoder().encode(value)),\n );\n return `${encodeBase64urlNoPadding(iv)}.${encodeBase64urlNoPadding(new Uint8Array(encrypted))}`;\n}\n\n/** @internal */\nexport async function decryptSecret(ciphertext: string) {\n const [ivEncoded, payloadEncoded] = ciphertext.split(\".\");\n if (!ivEncoded || !payloadEncoded) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Stored enterprise secret is malformed.\",\n ).toConvexError();\n }\n const key = await getSecretCryptoKey();\n const decrypted = await crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: toArrayBuffer(decodeBase64urlIgnorePadding(ivEncoded)),\n },\n key,\n toArrayBuffer(decodeBase64urlIgnorePadding(payloadEncoded)),\n );\n return new TextDecoder().decode(decrypted);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":["rawSha256","osloGenerateRandomString"],"sources":["../../src/server/utils.ts"],"sourcesContent":["import {\n RandomReader,\n generateRandomString as osloGenerateRandomString,\n} from \"@oslojs/crypto/random\";\nimport { sha256 as rawSha256 } from \"@oslojs/crypto/sha2\";\nimport {\n decodeBase64urlIgnorePadding,\n encodeBase64urlNoPadding,\n encodeHexLowerCase,\n} from \"@oslojs/encoding\";\nimport { Cv } from \"@robelest/fx/convex\";\n\n/**\n * Require an environment variable to be set, throwing at config time if missing.\n *\n * Uses `Cv.error()` directly since this is a synchronous guard\n * called inline in many expressions — not suitable for Fx pipeline wrapping.\n */\n/** @internal */\nexport function requireEnv(name: string) {\n const value = process.env[name];\n if (value === undefined) {\n throw Cv.error({\n code: \"MISSING_ENV_VAR\",\n message: `Missing environment variable \\`${name}\\``,\n });\n }\n return value;\n}\n\n/** @internal */\nexport function isLocalHost(host?: string) {\n if (host === undefined) {\n return false;\n }\n const raw = host.includes(\"://\") ? host : `http://${host}`;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return false;\n }\n return (\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\"\n );\n}\n\n// Internal server utilities (merged from former internalUtils.ts)\n\n/** @internal */\nexport const TOKEN_SUB_CLAIM_DIVIDER = \"|\";\n/** @internal */\nexport const REFRESH_TOKEN_DIVIDER = \"|\";\n\n/** @internal */\nexport async function sha256(input: string) {\n return encodeHexLowerCase(rawSha256(new TextEncoder().encode(input)));\n}\n\n/** @internal */\nexport function generateRandomString(length: number, alphabet: string) {\n const random: RandomReader = {\n read(bytes) {\n crypto.getRandomValues(bytes as Uint8Array<ArrayBuffer>);\n },\n };\n\n return osloGenerateRandomString(random, alphabet, length);\n}\n\n/** @internal */\nexport function errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** @internal */\nexport function logError(error: unknown) {\n logWithLevel(\n LOG_LEVELS.ERROR,\n error instanceof Error\n ? error.message + \"\\n\" + error.stack?.replace(\"\\\\n\", \"\\n\")\n : error,\n );\n}\n\n/** @internal */\nexport const LOG_LEVELS = {\n ERROR: \"ERROR\",\n WARN: \"WARN\",\n INFO: \"INFO\",\n DEBUG: \"DEBUG\",\n} as const;\ntype LogLevel = keyof typeof LOG_LEVELS;\n\n/** @internal */\nexport function logWithLevel(level: LogLevel, ...args: unknown[]) {\n const configuredLogLevel =\n LOG_LEVELS[\n (process.env.AUTH_LOG_LEVEL as LogLevel | undefined) ?? \"INFO\"\n ] ?? \"INFO\";\n switch (level) {\n case \"ERROR\":\n console.error(...args);\n break;\n case \"WARN\":\n if (configuredLogLevel !== \"ERROR\") {\n console.warn(...args);\n }\n break;\n case \"INFO\":\n if (configuredLogLevel === \"INFO\" || configuredLogLevel === \"DEBUG\") {\n console.info(...args);\n }\n break;\n case \"DEBUG\":\n if (configuredLogLevel === \"DEBUG\") {\n console.debug(...args);\n }\n break;\n }\n}\n\nconst UNREDACTED_LENGTH = 5;\n/** @internal */\nexport function maybeRedact(value: string) {\n if (value === \"\") {\n return \"\";\n }\n const shouldRedact = process.env.AUTH_LOG_SECRETS !== \"true\";\n if (shouldRedact) {\n if (value.length < UNREDACTED_LENGTH * 2) {\n return \"<redacted>\";\n }\n return (\n value.substring(0, UNREDACTED_LENGTH) +\n \"<redacted>\" +\n value.substring(value.length - UNREDACTED_LENGTH)\n );\n } else {\n return value;\n }\n}\n\nconst SECRET_KEY_ENV = \"AUTH_SECRET_ENCRYPTION_KEY\";\nconst SECRET_IV_LENGTH = 12;\n\nfunction toArrayBuffer(bytes: Uint8Array) {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nasync function getSecretCryptoKey() {\n const material = requireEnv(SECRET_KEY_ENV);\n const rawKey = rawSha256(new TextEncoder().encode(material));\n return await crypto.subtle.importKey(\n \"raw\",\n toArrayBuffer(rawKey),\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\n/** @internal */\nexport async function encryptSecret(value: string) {\n const key = await getSecretCryptoKey();\n const iv = crypto.getRandomValues(new Uint8Array(SECRET_IV_LENGTH));\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: toArrayBuffer(iv) },\n key,\n toArrayBuffer(new TextEncoder().encode(value)),\n );\n return `${encodeBase64urlNoPadding(iv)}.${encodeBase64urlNoPadding(new Uint8Array(encrypted))}`;\n}\n\n/** @internal */\nexport async function decryptSecret(ciphertext: string) {\n const [ivEncoded, payloadEncoded] = ciphertext.split(\".\");\n if (!ivEncoded || !payloadEncoded) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Stored enterprise secret is malformed.\",\n });\n }\n const key = await getSecretCryptoKey();\n const decrypted = await crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: toArrayBuffer(decodeBase64urlIgnorePadding(ivEncoded)),\n },\n key,\n toArrayBuffer(decodeBase64urlIgnorePadding(payloadEncoded)),\n );\n return new TextDecoder().decode(decrypted);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robelest/convex-auth",
3
- "version": "0.0.4-preview.21",
3
+ "version": "0.0.4-preview.23",
4
4
  "description": "Authentication for Convex",
5
5
  "keywords": [
6
6
  "auth",
@@ -54,10 +54,6 @@
54
54
  "types": "./dist/component/convex.config.d.ts",
55
55
  "default": "./dist/component/convex.config.js"
56
56
  },
57
- "./errors": {
58
- "types": "./dist/server/errors.d.ts",
59
- "import": "./dist/server/errors.js"
60
- },
61
57
  "./providers": {
62
58
  "types": "./dist/providers/index.d.ts",
63
59
  "import": "./dist/providers/index.js"
@@ -103,7 +99,7 @@
103
99
  },
104
100
  "@comment devDependencies": [
105
101
  "The CLI deps (@clack/prompts, @commander-js/extra-typings, dotenv,",
106
- "valibot) are devDependencies because they are bundled into dist/bin.cjs",
102
+ "valibot) are devDependencies because they are bundled into dist/bin.js",
107
103
  "via esbuild."
108
104
  ],
109
105
  "scripts": {
@@ -11,7 +11,7 @@
11
11
  * map where each role has an `id` (matching its key), an optional `label`,
12
12
  * and a typed `grants` array. The returned object is fully type-safe:
13
13
  * role IDs and grant strings are narrowed to their literal types, so
14
- * `member.resolve()` and `member.create()` calls are checked at compile time.
14
+ * `member.inspect()` / `member.require()` and `member.create()` calls are checked at compile time.
15
15
  * This is the canonical way to define role metadata for the auth library.
16
16
  *
17
17
  * @param roles - An object mapping role IDs to their configuration.
package/src/cli/index.ts CHANGED
@@ -22,7 +22,7 @@ import { generateKeys } from "./keys";
22
22
  // ---------------------------------------------------------------------------
23
23
 
24
24
  function getPackageVersion(): string {
25
- // When bundled to dist/bin.cjs the package.json is one level up
25
+ // When bundled to dist/bin.js the package.json is one level up
26
26
  for (const relative of ["..", "../.."]) {
27
27
  try {
28
28
  const pkgPath = path.resolve(__dirname, relative, "package.json");
@@ -286,21 +286,19 @@ export interface DeviceClient {
286
286
  *
287
287
  * @param opts - Poll options.
288
288
  * @param opts.code - The {@link DeviceCodeResult} returned from `signIn("device")`.
289
- * @returns `{ ok: true }` when authorized, or `{ ok: false, expired }` on failure.
289
+ * @throws `ConvexError({ code: "DEVICE_CODE_EXPIRED" })` when the code expires before authorization.
290
290
  *
291
291
  * @example
292
292
  * ```ts
293
293
  * const result = await auth.signIn("device");
294
294
  * if (result.kind === "deviceCode") {
295
295
  * // Display result.deviceCode.userCode to the user
296
- * const poll = await auth.device.poll({ code: result.deviceCode });
297
- * if (poll.ok) console.log("Device authorized!");
296
+ * await auth.device.poll({ code: result.deviceCode });
297
+ * console.log("Device authorized!");
298
298
  * }
299
299
  * ```
300
300
  */
301
- poll(opts: {
302
- code: DeviceCodeResult;
303
- }): Promise<{ ok: true } | { ok: false; expired: boolean }>;
301
+ poll(opts: { code: DeviceCodeResult }): Promise<void>;
304
302
 
305
303
  /**
306
304
  * Approve a device flow from the verification page using the displayed user code.
@@ -310,17 +308,14 @@ export interface DeviceClient {
310
308
  *
311
309
  * @param opts - Verification options.
312
310
  * @param opts.code - The user code string (e.g. `"WDJB-MJHT"`).
313
- * @returns `{ ok: true }` on success, or `{ ok: false, message }` on failure.
311
+ * @throws `ConvexError({ code: "DEVICE_AUTHORIZATION_FAILED" })` when verification fails.
314
312
  *
315
313
  * @example
316
314
  * ```ts
317
- * const result = await auth.device.verify({ code: "WDJB-MJHT" });
318
- * if (!result.ok) console.error(result.message);
315
+ * await auth.device.verify({ code: "WDJB-MJHT" });
319
316
  * ```
320
317
  */
321
- verify(opts: {
322
- code: string;
323
- }): Promise<{ ok: true } | { ok: false; message: string }>;
318
+ verify(opts: { code: string }): Promise<void>;
324
319
  }
325
320
 
326
321
  /**
@@ -345,8 +340,13 @@ export interface PendingInvite {
345
340
  * @readonly
346
341
  */
347
342
  readonly email: string | null;
348
- /** Consume the invite: clears storage/URL params and returns the token. */
349
- accept(): Promise<{ ok: boolean; token?: string; message?: string }>;
343
+ /**
344
+ * Consume the invite: clears storage/URL params and returns the token.
345
+ *
346
+ * @returns The invite token.
347
+ * @throws When there is no pending invite to accept.
348
+ */
349
+ accept(): Promise<{ token: string }>;
350
350
  }
351
351
 
352
352
  /** Base auth client — always present. */
@@ -36,9 +36,7 @@ export function createDeviceClient(deps: DeviceDeps): DeviceClient {
36
36
  deps;
37
37
 
38
38
  return {
39
- poll: async (opts: {
40
- code: DeviceCodeResult;
41
- }): Promise<{ ok: true } | { ok: false; expired: boolean }> => {
39
+ poll: async (opts: { code: DeviceCodeResult }): Promise<void> => {
42
40
  const { code } = opts;
43
41
  const intervalMs = code.interval * 1000;
44
42
  const expiresAt = Date.now() + code.expiresIn * 1000;
@@ -121,16 +119,17 @@ export function createDeviceClient(deps: DeviceDeps): DeviceClient {
121
119
  context: { provider: "device", flow: "poll" },
122
120
  });
123
121
  }
124
- return { ok: true as const };
122
+ return;
125
123
  }
126
124
  }
127
125
 
128
- return { ok: false as const, expired: true };
126
+ throw new ConvexError({
127
+ code: "DEVICE_CODE_EXPIRED",
128
+ message: "Device code expired before authorization was completed.",
129
+ });
129
130
  },
130
131
 
131
- verify: async (opts: {
132
- code: string;
133
- }): Promise<{ ok: true } | { ok: false; message: string }> => {
132
+ verify: async (opts: { code: string }): Promise<void> => {
134
133
  const params: Record<string, any> = {
135
134
  flow: "verify",
136
135
  userCode: opts.code,
@@ -148,12 +147,11 @@ export function createDeviceClient(deps: DeviceDeps): DeviceClient {
148
147
  params,
149
148
  });
150
149
  }
151
- return { ok: true as const };
152
150
  } catch (e: unknown) {
153
- return {
154
- ok: false as const,
151
+ throw new ConvexError({
152
+ code: "DEVICE_AUTHORIZATION_FAILED",
155
153
  message: e instanceof Error ? e.message : "Invalid or expired code.",
156
- };
154
+ });
157
155
  }
158
156
  },
159
157
  };
@@ -1,6 +1,5 @@
1
1
  import { Fx } from "@robelest/fx";
2
2
 
3
- import { base64urlDecode, base64urlEncode } from "../runtime/browser";
4
3
  import type {
5
4
  AuthSession,
6
5
  ConvexTransport,
@@ -8,6 +7,7 @@ import type {
8
7
  SignInActionResult,
9
8
  SignInResult,
10
9
  } from "../core/types";
10
+ import { base64urlDecode, base64urlEncode } from "../runtime/browser";
11
11
 
12
12
  type PasskeyDeps = {
13
13
  proxy: string | undefined;
@@ -43,31 +43,28 @@ export function createPasskeyClient(deps: PasskeyDeps): PasskeyClient {
43
43
  return Fx.run(
44
44
  Fx.match(result, result.kind, {
45
45
  signedIn: (signedInResult) =>
46
- Fx.from({
47
- ok: async () => {
48
- const signingIn = await setTokenAndMaybeWait(
49
- proxy
50
- ? {
51
- shouldStore: false as const,
52
- tokens:
53
- signedInResult.tokens === null
54
- ? null
55
- : { token: signedInResult.tokens.token },
56
- waitForHandshake: true,
57
- context: { provider: "passkey", flow },
58
- }
59
- : {
60
- shouldStore: true as const,
61
- tokens: signedInResult.tokens,
62
- waitForHandshake: true,
63
- context: { provider: "passkey", flow },
64
- },
65
- );
66
- return signingIn
67
- ? ({ kind: "signedIn" as const } as SignInResult)
68
- : ({ kind: "started" as const } as SignInResult);
69
- },
70
- err: (e) => e as never,
46
+ Fx.promise(async () => {
47
+ const signingIn = await setTokenAndMaybeWait(
48
+ proxy
49
+ ? {
50
+ shouldStore: false as const,
51
+ tokens:
52
+ signedInResult.tokens === null
53
+ ? null
54
+ : { token: signedInResult.tokens.token },
55
+ waitForHandshake: true,
56
+ context: { provider: "passkey", flow },
57
+ }
58
+ : {
59
+ shouldStore: true as const,
60
+ tokens: signedInResult.tokens,
61
+ waitForHandshake: true,
62
+ context: { provider: "passkey", flow },
63
+ },
64
+ );
65
+ return signingIn
66
+ ? ({ kind: "signedIn" as const } as SignInResult)
67
+ : ({ kind: "started" as const } as SignInResult);
71
68
  }),
72
69
  redirect: () => Fx.succeed({ kind: "started" as const }),
73
70
  started: () => Fx.succeed({ kind: "started" as const }),
@@ -2,18 +2,6 @@ import { Fx } from "@robelest/fx";
2
2
  import { ConvexHttpClient } from "convex/browser";
3
3
  import { ConvexError, Value } from "convex/values";
4
4
 
5
- import { AUTH_ERRORS } from "../server/errors";
6
- import { browserMutex, getStorageListenerRegistry } from "./runtime/browser";
7
- import { createDeviceClient } from "./factors/device";
8
- import { createInviteManager } from "./runtime/invite";
9
- import { createPasskeyClient } from "./factors/passkey";
10
- import {
11
- createProxyHelpers,
12
- isRetriableProxyRefreshError,
13
- isTransientNetworkError,
14
- } from "./runtime/proxy";
15
- import { createStorageHelpers } from "./runtime/storage";
16
- import { createTotpClient } from "./factors/totp";
17
5
  import type {
18
6
  AuthApiRefs,
19
7
  AuthClient,
@@ -33,14 +21,18 @@ import type {
33
21
  Storage,
34
22
  TotpClient,
35
23
  } from "./core/types";
24
+ import { createDeviceClient } from "./factors/device";
25
+ import { createPasskeyClient } from "./factors/passkey";
26
+ import { createTotpClient } from "./factors/totp";
27
+ import { browserMutex, getStorageListenerRegistry } from "./runtime/browser";
28
+ import { createInviteManager } from "./runtime/invite";
29
+ import {
30
+ createProxyHelpers,
31
+ isRetriableProxyRefreshError,
32
+ isTransientNetworkError,
33
+ } from "./runtime/proxy";
34
+ import { createStorageHelpers } from "./runtime/storage";
36
35
 
37
- // Re-export error utilities so consumers can import from `@robelest/convex-auth/client`.
38
- export {
39
- isAuthError,
40
- parseAuthError,
41
- AUTH_ERRORS,
42
- type AuthErrorCode,
43
- } from "../server/errors";
44
36
  export type {
45
37
  AuthApiRefs,
46
38
  AuthClient,
@@ -253,13 +245,20 @@ export function client<
253
245
  };
254
246
  let handlingCodeFlow = false;
255
247
 
248
+ const HANDSHAKE_ERROR_MESSAGES: Record<AuthHandshakeErrorCode, string> = {
249
+ AUTH_HANDSHAKE_TIMEOUT:
250
+ "Sign-in succeeded but authentication confirmation timed out.",
251
+ AUTH_HANDSHAKE_REJECTED:
252
+ "Authentication was rejected while confirming the session.",
253
+ };
254
+
256
255
  const createHandshakeError = (
257
256
  code: AuthHandshakeErrorCode,
258
257
  context: Record<string, unknown>,
259
258
  ) => {
260
259
  return new ConvexError({
261
260
  code,
262
- message: AUTH_ERRORS[code],
261
+ message: HANDSHAKE_ERROR_MESSAGES[code],
263
262
  ...context,
264
263
  } as Value);
265
264
  };
@@ -652,25 +651,19 @@ export function client<
652
651
  Fx.run(
653
652
  Fx.match(result, result.kind, {
654
653
  redirect: (redirectResult) =>
655
- Fx.from({
656
- ok: async () => {
657
- const redirectUrl = new URL(redirectResult.redirect);
658
- if (options.persistVerifier) {
659
- await storageSet(
660
- VERIFIER_STORAGE_KEY,
661
- redirectResult.verifier,
662
- );
663
- }
664
- if (typeof window !== "undefined") {
665
- window.location.href = redirectUrl.toString();
666
- }
667
- return {
668
- kind: "redirect" as const,
669
- redirect: redirectUrl,
670
- verifier: redirectResult.verifier,
671
- };
672
- },
673
- err: (e) => e as never,
654
+ Fx.promise(async () => {
655
+ const redirectUrl = new URL(redirectResult.redirect);
656
+ if (options.persistVerifier) {
657
+ await storageSet(VERIFIER_STORAGE_KEY, redirectResult.verifier);
658
+ }
659
+ if (typeof window !== "undefined") {
660
+ window.location.href = redirectUrl.toString();
661
+ }
662
+ return {
663
+ kind: "redirect" as const,
664
+ redirect: redirectUrl,
665
+ verifier: redirectResult.verifier,
666
+ };
674
667
  }),
675
668
  totpRequired: (totpRequiredResult) =>
676
669
  Fx.succeed({
@@ -685,31 +678,28 @@ export function client<
685
678
  ),
686
679
  }),
687
680
  signedIn: (signedInResult) =>
688
- Fx.from({
689
- ok: async () => {
690
- const signingIn = await setTokenAndMaybeWait(
691
- options.shouldStore
692
- ? {
693
- shouldStore: true as const,
694
- tokens: signedInResult.tokens,
695
- waitForHandshake: true,
696
- context: { provider, flow },
697
- }
698
- : {
699
- shouldStore: false as const,
700
- tokens:
701
- signedInResult.tokens === null
702
- ? null
703
- : { token: signedInResult.tokens.token },
704
- waitForHandshake: true,
705
- context: { provider, flow },
706
- },
707
- );
708
- return signingIn
709
- ? ({ kind: "signedIn" as const } as SignInResult)
710
- : ({ kind: "started" as const } as SignInResult);
711
- },
712
- err: (e) => e as never,
681
+ Fx.promise(async () => {
682
+ const signingIn = await setTokenAndMaybeWait(
683
+ options.shouldStore
684
+ ? {
685
+ shouldStore: true as const,
686
+ tokens: signedInResult.tokens,
687
+ waitForHandshake: true,
688
+ context: { provider, flow },
689
+ }
690
+ : {
691
+ shouldStore: false as const,
692
+ tokens:
693
+ signedInResult.tokens === null
694
+ ? null
695
+ : { token: signedInResult.tokens.token },
696
+ waitForHandshake: true,
697
+ context: { provider, flow },
698
+ },
699
+ );
700
+ return signingIn
701
+ ? ({ kind: "signedIn" as const } as SignInResult)
702
+ : ({ kind: "started" as const } as SignInResult);
713
703
  }),
714
704
  started: (_startedResult) => Fx.succeed({ kind: "started" as const }),
715
705
  passkeyOptions: (_passkeyOptionsResult) =>
@@ -48,18 +48,16 @@ export function createInviteManager(args: {
48
48
  await storageSet(emailKey, pendingInvite.email);
49
49
  }
50
50
  },
51
- async acceptInvite(): Promise<{
52
- ok: boolean;
53
- token?: string;
54
- message?: string;
55
- }> {
56
- if (!pendingInvite) return { ok: false, message: "No pending invite" };
51
+ async acceptInvite(): Promise<{ token: string }> {
52
+ if (!pendingInvite) {
53
+ throw new Error("No pending invite to accept.");
54
+ }
57
55
  const { token } = pendingInvite;
58
56
  pendingInvite = null;
59
57
  void storageRemove(tokenKey);
60
58
  void storageRemove(emailKey);
61
59
  cleanUrlParams(["invite", "email"]);
62
- return { ok: true, token };
60
+ return { token };
63
61
  },
64
62
  };
65
63
  }
@@ -10,6 +10,7 @@
10
10
  export { AuthCtx, createAuth } from "../server/auth";
11
11
  export type {
12
12
  AuthApi,
13
+ AuthContext,
13
14
  AuthConfig,
14
15
  AuthCtxConfig,
15
16
  InferAuth,
@@ -1,6 +1,11 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
- import { vAuditActorType, vAuditStatus, vEnterpriseAuditEventDoc } from "../../model";
4
+ import {
5
+ vAuditActorType,
6
+ vAuditStatus,
7
+ vEnterpriseAuditEventDoc,
8
+ } from "../../model";
4
9
 
5
10
  /**
6
11
  * Record a new audit event for an enterprise.
@@ -1,4 +1,5 @@
1
1
  import { ConvexError, v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import {
4
5
  vEnterpriseDoc,
@@ -1,6 +1,10 @@
1
1
  import { ConvexError, v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
- import { vEnterpriseDomainDoc, vEnterpriseDomainVerificationDoc } from "../../model";
4
+ import {
5
+ vEnterpriseDomainDoc,
6
+ vEnterpriseDomainVerificationDoc,
7
+ } from "../../model";
4
8
 
5
9
  /**
6
10
  * Link a domain to an enterprise record, or update an existing link.
@@ -1,4 +1,5 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import {
4
5
  vEnterpriseScimConfigDoc,
@@ -1,4 +1,5 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import { vEnterpriseSecretDoc, vEnterpriseSecretKind } from "../../model";
4
5
 
@@ -1,4 +1,5 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import {
4
5
  vEnterpriseWebhookDeliveryDoc,
@@ -1,4 +1,5 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import { vDeviceCodeDoc, vDeviceStatus } from "../../model";
4
5
 
@@ -1,4 +1,5 @@
1
1
  import { v } from "convex/values";
2
+
2
3
  import { mutation, query } from "../../functions";
3
4
  import { vPasskeyDoc } from "../../model";
4
5