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

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 (798) hide show
  1. package/README.md +67 -26
  2. package/dist/authorization/index.d.ts +63 -0
  3. package/dist/authorization/index.d.ts.map +1 -0
  4. package/dist/authorization/index.js +63 -0
  5. package/dist/authorization/index.js.map +1 -0
  6. package/dist/bin.js +6185 -0
  7. package/dist/client/core/types.d.ts +20 -0
  8. package/dist/client/core/types.d.ts.map +1 -0
  9. package/dist/client/index.d.ts +2 -299
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +407 -534
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +42 -0
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts +2546 -90
  17. package/dist/component/_generated/component.d.ts.map +1 -1
  18. package/dist/component/client/core/types.d.ts +2 -0
  19. package/dist/component/client/index.d.ts +2 -0
  20. package/dist/component/convex.config.d.ts +2 -2
  21. package/dist/component/functions.d.ts +11 -9
  22. package/dist/component/functions.d.ts.map +1 -1
  23. package/dist/component/functions.js.map +1 -1
  24. package/dist/component/index.d.ts +7 -11
  25. package/dist/component/index.js +2 -3
  26. package/dist/component/model.d.ts +153 -0
  27. package/dist/component/model.d.ts.map +1 -0
  28. package/dist/component/model.js +349 -0
  29. package/dist/component/model.js.map +1 -0
  30. package/dist/component/providers/anonymous.d.ts +54 -0
  31. package/dist/component/providers/anonymous.d.ts.map +1 -0
  32. package/dist/component/providers/credentials.d.ts +5 -5
  33. package/dist/component/providers/credentials.d.ts.map +1 -1
  34. package/dist/component/providers/device.d.ts +67 -0
  35. package/dist/component/providers/device.d.ts.map +1 -0
  36. package/dist/component/providers/email.d.ts +62 -0
  37. package/dist/component/providers/email.d.ts.map +1 -0
  38. package/dist/component/providers/oauth.d.ts.map +1 -1
  39. package/dist/component/providers/oauth.js.map +1 -1
  40. package/dist/component/providers/passkey.d.ts +57 -0
  41. package/dist/component/providers/passkey.d.ts.map +1 -0
  42. package/dist/component/providers/password.d.ts +88 -0
  43. package/dist/component/providers/password.d.ts.map +1 -0
  44. package/dist/component/providers/phone.d.ts +48 -0
  45. package/dist/component/providers/phone.d.ts.map +1 -0
  46. package/dist/component/providers/sso.d.ts +50 -0
  47. package/dist/component/providers/sso.d.ts.map +1 -0
  48. package/dist/component/providers/totp.d.ts +45 -0
  49. package/dist/component/providers/totp.d.ts.map +1 -0
  50. package/dist/component/public/enterprise/audit.d.ts +73 -0
  51. package/dist/component/public/enterprise/audit.d.ts.map +1 -0
  52. package/dist/component/public/enterprise/audit.js +108 -0
  53. package/dist/component/public/enterprise/audit.js.map +1 -0
  54. package/dist/component/public/enterprise/core.d.ts +176 -0
  55. package/dist/component/public/enterprise/core.d.ts.map +1 -0
  56. package/dist/component/public/enterprise/core.js +292 -0
  57. package/dist/component/public/enterprise/core.js.map +1 -0
  58. package/dist/component/public/enterprise/domains.d.ts +174 -0
  59. package/dist/component/public/enterprise/domains.d.ts.map +1 -0
  60. package/dist/component/public/enterprise/domains.js +271 -0
  61. package/dist/component/public/enterprise/domains.js.map +1 -0
  62. package/dist/component/public/enterprise/scim.d.ts +245 -0
  63. package/dist/component/public/enterprise/scim.d.ts.map +1 -0
  64. package/dist/component/public/enterprise/scim.js +344 -0
  65. package/dist/component/public/enterprise/scim.js.map +1 -0
  66. package/dist/component/public/enterprise/secrets.d.ts +78 -0
  67. package/dist/component/public/enterprise/secrets.d.ts.map +1 -0
  68. package/dist/component/public/enterprise/secrets.js +118 -0
  69. package/dist/component/public/enterprise/secrets.js.map +1 -0
  70. package/dist/component/public/enterprise/webhooks.d.ts +211 -0
  71. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -0
  72. package/dist/component/public/enterprise/webhooks.js +300 -0
  73. package/dist/component/public/enterprise/webhooks.js.map +1 -0
  74. package/dist/component/public/factors/devices.d.ts +157 -0
  75. package/dist/component/public/factors/devices.d.ts.map +1 -0
  76. package/dist/component/public/factors/devices.js +216 -0
  77. package/dist/component/public/factors/devices.js.map +1 -0
  78. package/dist/component/public/factors/passkeys.d.ts +175 -0
  79. package/dist/component/public/factors/passkeys.d.ts.map +1 -0
  80. package/dist/component/public/factors/passkeys.js +238 -0
  81. package/dist/component/public/factors/passkeys.js.map +1 -0
  82. package/dist/component/public/factors/totp.d.ts +189 -0
  83. package/dist/component/public/factors/totp.d.ts.map +1 -0
  84. package/dist/component/public/factors/totp.js +254 -0
  85. package/dist/component/public/factors/totp.js.map +1 -0
  86. package/dist/component/public/groups/core.d.ts +137 -0
  87. package/dist/component/public/groups/core.d.ts.map +1 -0
  88. package/dist/component/public/groups/core.js +321 -0
  89. package/dist/component/public/groups/core.js.map +1 -0
  90. package/dist/component/public/groups/invites.d.ts +217 -0
  91. package/dist/component/public/groups/invites.d.ts.map +1 -0
  92. package/dist/component/public/groups/invites.js +457 -0
  93. package/dist/component/public/groups/invites.js.map +1 -0
  94. package/dist/component/public/groups/members.d.ts +204 -0
  95. package/dist/component/public/groups/members.d.ts.map +1 -0
  96. package/dist/component/public/groups/members.js +355 -0
  97. package/dist/component/public/groups/members.js.map +1 -0
  98. package/dist/component/public/identity/accounts.d.ts +147 -0
  99. package/dist/component/public/identity/accounts.d.ts.map +1 -0
  100. package/dist/component/public/identity/accounts.js +200 -0
  101. package/dist/component/public/identity/accounts.js.map +1 -0
  102. package/dist/component/public/identity/codes.d.ts +104 -0
  103. package/dist/component/public/identity/codes.d.ts.map +1 -0
  104. package/dist/component/public/identity/codes.js +140 -0
  105. package/dist/component/public/identity/codes.js.map +1 -0
  106. package/dist/component/public/identity/sessions.d.ts +128 -0
  107. package/dist/component/public/identity/sessions.d.ts.map +1 -0
  108. package/dist/component/public/identity/sessions.js +192 -0
  109. package/dist/component/public/identity/sessions.js.map +1 -0
  110. package/dist/component/public/identity/tokens.d.ts +169 -0
  111. package/dist/component/public/identity/tokens.d.ts.map +1 -0
  112. package/dist/component/public/identity/tokens.js +227 -0
  113. package/dist/component/public/identity/tokens.js.map +1 -0
  114. package/dist/component/public/identity/users.d.ts +212 -0
  115. package/dist/component/public/identity/users.d.ts.map +1 -0
  116. package/dist/component/public/identity/users.js +311 -0
  117. package/dist/component/public/identity/users.js.map +1 -0
  118. package/dist/component/public/identity/verifiers.d.ts +116 -0
  119. package/dist/component/public/identity/verifiers.d.ts.map +1 -0
  120. package/dist/component/public/identity/verifiers.js +154 -0
  121. package/dist/component/public/identity/verifiers.js.map +1 -0
  122. package/dist/component/public/security/keys.d.ts +209 -0
  123. package/dist/component/public/security/keys.d.ts.map +1 -0
  124. package/dist/component/public/security/keys.js +319 -0
  125. package/dist/component/public/security/keys.js.map +1 -0
  126. package/dist/component/public/security/limits.d.ts +114 -0
  127. package/dist/component/public/security/limits.d.ts.map +1 -0
  128. package/dist/component/public/security/limits.js +169 -0
  129. package/dist/component/public/security/limits.js.map +1 -0
  130. package/dist/component/public.d.ts +24 -271
  131. package/dist/component/public.d.ts.map +1 -1
  132. package/dist/component/public.js +21 -1229
  133. package/dist/component/schema.d.ts +473 -110
  134. package/dist/component/schema.js +162 -73
  135. package/dist/component/schema.js.map +1 -1
  136. package/dist/component/server/auth.d.ts +318 -373
  137. package/dist/component/server/auth.d.ts.map +1 -1
  138. package/dist/component/server/auth.js +204 -123
  139. package/dist/component/server/auth.js.map +1 -1
  140. package/dist/component/server/authError.js +34 -0
  141. package/dist/component/server/authError.js.map +1 -0
  142. package/dist/component/server/{providers.js → config.js} +43 -12
  143. package/dist/component/server/config.js.map +1 -0
  144. package/dist/component/server/cookies.js +3 -0
  145. package/dist/component/server/cookies.js.map +1 -1
  146. package/dist/component/server/core.js +713 -0
  147. package/dist/component/server/core.js.map +1 -0
  148. package/dist/component/server/crypto.js +38 -0
  149. package/dist/component/server/crypto.js.map +1 -0
  150. package/dist/component/server/{implementation/db.js → db.js} +2 -1
  151. package/dist/component/server/db.js.map +1 -0
  152. package/dist/component/server/device.js +109 -0
  153. package/dist/component/server/device.js.map +1 -0
  154. package/dist/component/server/enterprise/config.js +46 -0
  155. package/dist/component/server/enterprise/config.js.map +1 -0
  156. package/dist/component/server/enterprise/domain.js +885 -0
  157. package/dist/component/server/enterprise/domain.js.map +1 -0
  158. package/dist/component/server/enterprise/http.js +766 -0
  159. package/dist/component/server/enterprise/http.js.map +1 -0
  160. package/dist/component/server/enterprise/oidc.js +248 -0
  161. package/dist/component/server/enterprise/oidc.js.map +1 -0
  162. package/dist/component/server/enterprise/policy.js +85 -0
  163. package/dist/component/server/enterprise/policy.js.map +1 -0
  164. package/dist/component/server/enterprise/saml.js +338 -0
  165. package/dist/component/server/enterprise/saml.js.map +1 -0
  166. package/dist/component/server/enterprise/scim.js +97 -0
  167. package/dist/component/server/enterprise/scim.js.map +1 -0
  168. package/dist/component/server/enterprise/shared.js +51 -0
  169. package/dist/component/server/enterprise/shared.js.map +1 -0
  170. package/dist/component/server/errors.d.ts +1 -0
  171. package/dist/component/server/errors.js +24 -16
  172. package/dist/component/server/errors.js.map +1 -1
  173. package/dist/component/server/http.js +288 -0
  174. package/dist/component/server/http.js.map +1 -0
  175. package/dist/component/server/identity.js +13 -0
  176. package/dist/component/server/identity.js.map +1 -0
  177. package/dist/{server/implementation → component/server}/keys.js +9 -31
  178. package/dist/component/server/keys.js.map +1 -0
  179. package/dist/component/server/limits.js +61 -0
  180. package/dist/component/server/limits.js.map +1 -0
  181. package/dist/component/server/mutations/account.js +44 -0
  182. package/dist/component/server/mutations/account.js.map +1 -0
  183. package/dist/component/server/{implementation/mutations → mutations}/code.js +7 -4
  184. package/dist/component/server/mutations/code.js.map +1 -0
  185. package/dist/component/server/mutations/invalidate.js +32 -0
  186. package/dist/component/server/mutations/invalidate.js.map +1 -0
  187. package/dist/component/server/mutations/oauth.js +110 -0
  188. package/dist/component/server/mutations/oauth.js.map +1 -0
  189. package/dist/component/server/mutations/refresh.js +119 -0
  190. package/dist/component/server/mutations/refresh.js.map +1 -0
  191. package/dist/component/server/mutations/register.js +83 -0
  192. package/dist/component/server/mutations/register.js.map +1 -0
  193. package/dist/component/server/mutations/retrieve.js +65 -0
  194. package/dist/component/server/mutations/retrieve.js.map +1 -0
  195. package/dist/component/server/mutations/signature.js +32 -0
  196. package/dist/component/server/mutations/signature.js.map +1 -0
  197. package/dist/component/server/{implementation/mutations → mutations}/signin.js +2 -2
  198. package/dist/component/server/mutations/signin.js.map +1 -0
  199. package/dist/component/server/mutations/signout.js +27 -0
  200. package/dist/component/server/mutations/signout.js.map +1 -0
  201. package/dist/component/server/mutations/store/refs.js +15 -0
  202. package/dist/component/server/mutations/store/refs.js.map +1 -0
  203. package/dist/component/server/mutations/store.js +85 -0
  204. package/dist/component/server/mutations/store.js.map +1 -0
  205. package/dist/component/server/mutations/verifier.js +18 -0
  206. package/dist/component/server/mutations/verifier.js.map +1 -0
  207. package/dist/component/server/mutations/verify.js +98 -0
  208. package/dist/component/server/mutations/verify.js.map +1 -0
  209. package/dist/component/server/oauth.js +106 -60
  210. package/dist/component/server/oauth.js.map +1 -1
  211. package/dist/component/server/passkey.js +328 -0
  212. package/dist/component/server/passkey.js.map +1 -0
  213. package/dist/{server/implementation → component/server}/redirects.js +13 -11
  214. package/dist/component/server/redirects.js.map +1 -0
  215. package/dist/component/server/refresh.js +96 -0
  216. package/dist/component/server/refresh.js.map +1 -0
  217. package/dist/component/server/runtime.d.ts +136 -0
  218. package/dist/component/server/runtime.d.ts.map +1 -0
  219. package/dist/component/server/runtime.js +413 -0
  220. package/dist/component/server/runtime.js.map +1 -0
  221. package/dist/{server/implementation → component/server}/sessions.js +14 -8
  222. package/dist/component/server/sessions.js.map +1 -0
  223. package/dist/component/server/signin.js +201 -0
  224. package/dist/component/server/signin.js.map +1 -0
  225. package/dist/component/server/tokens.js +17 -0
  226. package/dist/component/server/tokens.js.map +1 -0
  227. package/dist/component/server/totp.js +148 -0
  228. package/dist/component/server/totp.js.map +1 -0
  229. package/dist/component/server/types.d.ts +387 -298
  230. package/dist/component/server/types.d.ts.map +1 -1
  231. package/dist/component/server/{implementation/types.js → types.js} +1 -1
  232. package/dist/component/server/types.js.map +1 -0
  233. package/dist/component/server/{implementation/users.js → users.js} +54 -35
  234. package/dist/component/server/users.js.map +1 -0
  235. package/dist/component/server/utils.js +110 -4
  236. package/dist/component/server/utils.js.map +1 -1
  237. package/dist/core/types.d.ts +369 -0
  238. package/dist/core/types.d.ts.map +1 -0
  239. package/dist/factors/device.js +105 -0
  240. package/dist/factors/device.js.map +1 -0
  241. package/dist/factors/passkey.js +181 -0
  242. package/dist/factors/passkey.js.map +1 -0
  243. package/dist/factors/totp.js +122 -0
  244. package/dist/factors/totp.js.map +1 -0
  245. package/dist/providers/anonymous.d.ts +3 -9
  246. package/dist/providers/anonymous.d.ts.map +1 -1
  247. package/dist/providers/anonymous.js +1 -18
  248. package/dist/providers/anonymous.js.map +1 -1
  249. package/dist/providers/credentials.d.ts +8 -10
  250. package/dist/providers/credentials.d.ts.map +1 -1
  251. package/dist/providers/credentials.js +3 -5
  252. package/dist/providers/credentials.js.map +1 -1
  253. package/dist/providers/device.d.ts +18 -10
  254. package/dist/providers/device.d.ts.map +1 -1
  255. package/dist/providers/device.js +4 -8
  256. package/dist/providers/device.js.map +1 -1
  257. package/dist/providers/email.d.ts +50 -23
  258. package/dist/providers/email.d.ts.map +1 -1
  259. package/dist/providers/email.js +58 -34
  260. package/dist/providers/email.js.map +1 -1
  261. package/dist/providers/index.d.ts +7 -3
  262. package/dist/providers/index.js +4 -1
  263. package/dist/providers/oauth.d.ts.map +1 -1
  264. package/dist/providers/oauth.js.map +1 -1
  265. package/dist/providers/passkey.d.ts +12 -9
  266. package/dist/providers/passkey.d.ts.map +1 -1
  267. package/dist/providers/passkey.js +1 -7
  268. package/dist/providers/passkey.js.map +1 -1
  269. package/dist/providers/password.d.ts +6 -12
  270. package/dist/providers/password.d.ts.map +1 -1
  271. package/dist/providers/password.js +189 -89
  272. package/dist/providers/password.js.map +1 -1
  273. package/dist/providers/phone.d.ts +40 -11
  274. package/dist/providers/phone.d.ts.map +1 -1
  275. package/dist/providers/phone.js +52 -21
  276. package/dist/providers/phone.js.map +1 -1
  277. package/dist/providers/sso.d.ts +50 -0
  278. package/dist/providers/sso.d.ts.map +1 -0
  279. package/dist/providers/sso.js +34 -0
  280. package/dist/providers/sso.js.map +1 -0
  281. package/dist/providers/totp.d.ts +12 -9
  282. package/dist/providers/totp.d.ts.map +1 -1
  283. package/dist/providers/totp.js +1 -7
  284. package/dist/providers/totp.js.map +1 -1
  285. package/dist/runtime/browser.js +68 -0
  286. package/dist/runtime/browser.js.map +1 -0
  287. package/dist/runtime/invite.js +51 -0
  288. package/dist/runtime/invite.js.map +1 -0
  289. package/dist/runtime/proxy.js +70 -0
  290. package/dist/runtime/proxy.js.map +1 -0
  291. package/dist/runtime/storage.js +37 -0
  292. package/dist/runtime/storage.js.map +1 -0
  293. package/dist/server/auth.d.ts +335 -370
  294. package/dist/server/auth.d.ts.map +1 -1
  295. package/dist/server/auth.js +204 -123
  296. package/dist/server/auth.js.map +1 -1
  297. package/dist/server/authError.d.ts +46 -0
  298. package/dist/server/authError.d.ts.map +1 -0
  299. package/dist/server/authError.js +34 -0
  300. package/dist/server/authError.js.map +1 -0
  301. package/dist/server/config.d.ts +1 -0
  302. package/dist/server/{providers.js → config.js} +43 -12
  303. package/dist/server/config.js.map +1 -0
  304. package/dist/server/cookies.d.ts +1 -38
  305. package/dist/server/cookies.js +3 -0
  306. package/dist/server/cookies.js.map +1 -1
  307. package/dist/server/core.d.ts +1436 -0
  308. package/dist/server/core.d.ts.map +1 -0
  309. package/dist/server/core.js +713 -0
  310. package/dist/server/core.js.map +1 -0
  311. package/dist/server/crypto.d.ts +8 -0
  312. package/dist/server/crypto.d.ts.map +1 -0
  313. package/dist/server/crypto.js +38 -0
  314. package/dist/server/crypto.js.map +1 -0
  315. package/dist/server/db.d.ts +1 -0
  316. package/dist/server/{implementation/db.js → db.js} +2 -1
  317. package/dist/server/db.js.map +1 -0
  318. package/dist/server/device.d.ts +1 -0
  319. package/dist/server/device.js +109 -0
  320. package/dist/server/device.js.map +1 -0
  321. package/dist/server/enterprise/config.d.ts +1 -0
  322. package/dist/server/enterprise/config.js +46 -0
  323. package/dist/server/enterprise/config.js.map +1 -0
  324. package/dist/server/enterprise/domain.d.ts +409 -0
  325. package/dist/server/enterprise/domain.d.ts.map +1 -0
  326. package/dist/server/enterprise/domain.js +885 -0
  327. package/dist/server/enterprise/domain.js.map +1 -0
  328. package/dist/server/enterprise/http.d.ts +26 -0
  329. package/dist/server/enterprise/http.d.ts.map +1 -0
  330. package/dist/server/enterprise/http.js +766 -0
  331. package/dist/server/enterprise/http.js.map +1 -0
  332. package/dist/server/enterprise/oidc.d.ts +1 -0
  333. package/dist/server/enterprise/oidc.js +248 -0
  334. package/dist/server/enterprise/oidc.js.map +1 -0
  335. package/dist/server/enterprise/policy.d.ts +1 -0
  336. package/dist/server/enterprise/policy.js +85 -0
  337. package/dist/server/enterprise/policy.js.map +1 -0
  338. package/dist/server/enterprise/saml.d.ts +1 -0
  339. package/dist/server/enterprise/saml.js +338 -0
  340. package/dist/server/enterprise/saml.js.map +1 -0
  341. package/dist/server/enterprise/scim.d.ts +1 -0
  342. package/dist/server/enterprise/scim.js +97 -0
  343. package/dist/server/enterprise/scim.js.map +1 -0
  344. package/dist/server/enterprise/shared.d.ts +5 -0
  345. package/dist/server/enterprise/shared.d.ts.map +1 -0
  346. package/dist/server/enterprise/shared.js +51 -0
  347. package/dist/server/enterprise/shared.js.map +1 -0
  348. package/dist/server/enterprise/validators.d.ts +1 -0
  349. package/dist/server/enterprise/validators.js +60 -0
  350. package/dist/server/enterprise/validators.js.map +1 -0
  351. package/dist/server/errors.d.ts +33 -1
  352. package/dist/server/errors.d.ts.map +1 -1
  353. package/dist/server/errors.js +44 -1
  354. package/dist/server/errors.js.map +1 -1
  355. package/dist/server/http.d.ts +59 -0
  356. package/dist/server/http.d.ts.map +1 -0
  357. package/dist/server/http.js +288 -0
  358. package/dist/server/http.js.map +1 -0
  359. package/dist/server/identity.d.ts +1 -0
  360. package/dist/server/identity.js +13 -0
  361. package/dist/server/identity.js.map +1 -0
  362. package/dist/server/index.d.ts +4 -182
  363. package/dist/server/index.js +4 -376
  364. package/dist/server/keys.d.ts +1 -0
  365. package/dist/{component/server/implementation → server}/keys.js +9 -31
  366. package/dist/server/keys.js.map +1 -0
  367. package/dist/server/limits.d.ts +1 -0
  368. package/dist/server/limits.js +61 -0
  369. package/dist/server/limits.js.map +1 -0
  370. package/dist/server/mounts.d.ts +647 -0
  371. package/dist/server/mounts.d.ts.map +1 -0
  372. package/dist/server/mounts.js +643 -0
  373. package/dist/server/mounts.js.map +1 -0
  374. package/dist/server/mutations/account.d.ts +30 -0
  375. package/dist/server/mutations/account.d.ts.map +1 -0
  376. package/dist/server/mutations/account.js +44 -0
  377. package/dist/server/mutations/account.js.map +1 -0
  378. package/dist/server/mutations/code.d.ts +30 -0
  379. package/dist/server/mutations/code.d.ts.map +1 -0
  380. package/dist/server/{implementation/mutations → mutations}/code.js +7 -4
  381. package/dist/server/mutations/code.js.map +1 -0
  382. package/dist/server/mutations/index.d.ts +14 -0
  383. package/dist/server/mutations/index.js +15 -0
  384. package/dist/server/mutations/invalidate.d.ts +20 -0
  385. package/dist/server/mutations/invalidate.d.ts.map +1 -0
  386. package/dist/server/mutations/invalidate.js +32 -0
  387. package/dist/server/mutations/invalidate.js.map +1 -0
  388. package/dist/server/mutations/oauth.d.ts +28 -0
  389. package/dist/server/mutations/oauth.d.ts.map +1 -0
  390. package/dist/server/mutations/oauth.js +110 -0
  391. package/dist/server/mutations/oauth.js.map +1 -0
  392. package/dist/server/mutations/refresh.d.ts +21 -0
  393. package/dist/server/mutations/refresh.d.ts.map +1 -0
  394. package/dist/server/mutations/refresh.js +119 -0
  395. package/dist/server/mutations/refresh.js.map +1 -0
  396. package/dist/server/mutations/register.d.ts +38 -0
  397. package/dist/server/mutations/register.d.ts.map +1 -0
  398. package/dist/server/mutations/register.js +83 -0
  399. package/dist/server/mutations/register.js.map +1 -0
  400. package/dist/server/mutations/retrieve.d.ts +33 -0
  401. package/dist/server/mutations/retrieve.d.ts.map +1 -0
  402. package/dist/server/mutations/retrieve.js +65 -0
  403. package/dist/server/mutations/retrieve.js.map +1 -0
  404. package/dist/server/mutations/signature.d.ts +22 -0
  405. package/dist/server/mutations/signature.d.ts.map +1 -0
  406. package/dist/server/mutations/signature.js +32 -0
  407. package/dist/server/mutations/signature.js.map +1 -0
  408. package/dist/server/mutations/signin.d.ts +22 -0
  409. package/dist/server/mutations/signin.d.ts.map +1 -0
  410. package/dist/server/{implementation/mutations → mutations}/signin.js +2 -2
  411. package/dist/server/mutations/signin.js.map +1 -0
  412. package/dist/server/mutations/signout.d.ts +16 -0
  413. package/dist/server/mutations/signout.d.ts.map +1 -0
  414. package/dist/server/mutations/signout.js +27 -0
  415. package/dist/server/mutations/signout.js.map +1 -0
  416. package/dist/server/mutations/store/refs.d.ts +12 -0
  417. package/dist/server/mutations/store/refs.d.ts.map +1 -0
  418. package/dist/server/mutations/store/refs.js +15 -0
  419. package/dist/server/mutations/store/refs.js.map +1 -0
  420. package/dist/server/mutations/store.d.ts +306 -0
  421. package/dist/server/mutations/store.d.ts.map +1 -0
  422. package/dist/server/mutations/store.js +85 -0
  423. package/dist/server/mutations/store.js.map +1 -0
  424. package/dist/server/mutations/verifier.d.ts +13 -0
  425. package/dist/server/mutations/verifier.d.ts.map +1 -0
  426. package/dist/server/mutations/verifier.js +18 -0
  427. package/dist/server/mutations/verifier.js.map +1 -0
  428. package/dist/server/mutations/verify.d.ts +26 -0
  429. package/dist/server/mutations/verify.d.ts.map +1 -0
  430. package/dist/server/mutations/verify.js +98 -0
  431. package/dist/server/mutations/verify.js.map +1 -0
  432. package/dist/server/oauth.d.ts +1 -48
  433. package/dist/server/oauth.js +107 -64
  434. package/dist/server/oauth.js.map +1 -1
  435. package/dist/server/passkey.d.ts +27 -0
  436. package/dist/server/passkey.d.ts.map +1 -0
  437. package/dist/server/passkey.js +328 -0
  438. package/dist/server/passkey.js.map +1 -0
  439. package/dist/server/redirects.d.ts +1 -0
  440. package/dist/{component/server/implementation → server}/redirects.js +13 -11
  441. package/dist/server/redirects.js.map +1 -0
  442. package/dist/server/refresh.d.ts +1 -0
  443. package/dist/server/refresh.js +96 -0
  444. package/dist/server/refresh.js.map +1 -0
  445. package/dist/server/runtime.d.ts +136 -0
  446. package/dist/server/runtime.d.ts.map +1 -0
  447. package/dist/server/runtime.js +413 -0
  448. package/dist/server/runtime.js.map +1 -0
  449. package/dist/server/sessions.d.ts +1 -0
  450. package/dist/{component/server/implementation → server}/sessions.js +14 -8
  451. package/dist/server/sessions.js.map +1 -0
  452. package/dist/server/signin.d.ts +1 -0
  453. package/dist/server/signin.js +201 -0
  454. package/dist/server/signin.js.map +1 -0
  455. package/dist/server/ssr.d.ts +226 -0
  456. package/dist/server/ssr.d.ts.map +1 -0
  457. package/dist/server/ssr.js +786 -0
  458. package/dist/server/ssr.js.map +1 -0
  459. package/dist/server/templates.d.ts +1 -21
  460. package/dist/server/templates.js +2 -1
  461. package/dist/server/templates.js.map +1 -1
  462. package/dist/server/tokens.d.ts +1 -0
  463. package/dist/server/tokens.js +17 -0
  464. package/dist/server/tokens.js.map +1 -0
  465. package/dist/server/totp.d.ts +1 -0
  466. package/dist/server/totp.js +148 -0
  467. package/dist/server/totp.js.map +1 -0
  468. package/dist/server/types.d.ts +498 -306
  469. package/dist/server/types.d.ts.map +1 -1
  470. package/dist/server/types.js +108 -1
  471. package/dist/server/types.js.map +1 -0
  472. package/dist/server/users.d.ts +1 -0
  473. package/dist/server/{implementation/users.js → users.js} +54 -35
  474. package/dist/server/users.js.map +1 -0
  475. package/dist/server/utils.d.ts +1 -6
  476. package/dist/server/utils.js +110 -4
  477. package/dist/server/utils.js.map +1 -1
  478. package/package.json +49 -46
  479. package/src/authorization/index.ts +83 -0
  480. package/src/cli/bin.ts +5 -0
  481. package/src/cli/command.ts +6 -5
  482. package/src/cli/index.ts +456 -248
  483. package/src/cli/keys.ts +3 -0
  484. package/src/client/core/types.ts +437 -0
  485. package/src/client/factors/device.ts +160 -0
  486. package/src/client/factors/passkey.ts +282 -0
  487. package/src/client/factors/totp.ts +150 -0
  488. package/src/client/index.ts +745 -989
  489. package/src/client/runtime/browser.ts +112 -0
  490. package/src/client/runtime/invite.ts +65 -0
  491. package/src/client/runtime/proxy.ts +111 -0
  492. package/src/client/runtime/storage.ts +79 -0
  493. package/src/component/_generated/api.ts +42 -0
  494. package/src/component/_generated/component.ts +3123 -102
  495. package/src/component/functions.ts +38 -22
  496. package/src/component/index.ts +10 -20
  497. package/src/component/model.ts +449 -0
  498. package/src/component/public/enterprise/audit.ts +120 -0
  499. package/src/component/public/enterprise/core.ts +354 -0
  500. package/src/component/public/enterprise/domains.ts +323 -0
  501. package/src/component/public/enterprise/scim.ts +396 -0
  502. package/src/component/public/enterprise/secrets.ts +132 -0
  503. package/src/component/public/enterprise/webhooks.ts +306 -0
  504. package/src/component/public/factors/devices.ts +223 -0
  505. package/src/component/public/factors/passkeys.ts +242 -0
  506. package/src/component/public/factors/totp.ts +258 -0
  507. package/src/component/public/groups/core.ts +481 -0
  508. package/src/component/public/groups/invites.ts +602 -0
  509. package/src/component/public/groups/members.ts +409 -0
  510. package/src/component/public/identity/accounts.ts +206 -0
  511. package/src/component/public/identity/codes.ts +148 -0
  512. package/src/component/public/identity/sessions.ts +209 -0
  513. package/src/component/public/identity/tokens.ts +250 -0
  514. package/src/component/public/identity/users.ts +354 -0
  515. package/src/component/public/identity/verifiers.ts +157 -0
  516. package/src/component/public/security/keys.ts +365 -0
  517. package/src/component/public/security/limits.ts +173 -0
  518. package/src/component/public.ts +26 -1766
  519. package/src/component/schema.ts +273 -100
  520. package/src/providers/anonymous.ts +10 -20
  521. package/src/providers/credentials.ts +14 -22
  522. package/src/providers/device.ts +3 -14
  523. package/src/providers/email.ts +83 -47
  524. package/src/providers/index.ts +7 -0
  525. package/src/providers/oauth.ts +5 -3
  526. package/src/providers/passkey.ts +0 -13
  527. package/src/providers/password.ts +307 -130
  528. package/src/providers/phone.ts +81 -37
  529. package/src/providers/sso.ts +54 -0
  530. package/src/providers/totp.ts +0 -13
  531. package/src/samlify.d.ts +53 -0
  532. package/src/server/auth.ts +701 -247
  533. package/src/server/authError.ts +44 -0
  534. package/src/server/{providers.ts → config.ts} +84 -15
  535. package/src/server/cookies.ts +8 -1
  536. package/src/server/core.ts +2095 -0
  537. package/src/server/crypto.ts +88 -0
  538. package/src/server/{implementation/db.ts → db.ts} +90 -15
  539. package/src/server/device.ts +221 -0
  540. package/src/server/enterprise/config.ts +51 -0
  541. package/src/server/enterprise/domain.ts +1751 -0
  542. package/src/server/enterprise/http.ts +1324 -0
  543. package/src/server/enterprise/oidc.ts +500 -0
  544. package/src/server/enterprise/policy.ts +128 -0
  545. package/src/server/enterprise/saml.ts +578 -0
  546. package/src/server/enterprise/scim.ts +135 -0
  547. package/src/server/enterprise/shared.ts +134 -0
  548. package/src/server/enterprise/validators.ts +93 -0
  549. package/src/server/errors.ts +130 -119
  550. package/src/server/http.ts +531 -0
  551. package/src/server/identity.ts +18 -0
  552. package/src/server/index.ts +32 -650
  553. package/src/server/{implementation/keys.ts → keys.ts} +16 -44
  554. package/src/server/limits.ts +134 -0
  555. package/src/server/mounts.ts +948 -0
  556. package/src/server/mutations/account.ts +76 -0
  557. package/src/server/{implementation/mutations → mutations}/code.ts +22 -11
  558. package/src/server/mutations/index.ts +13 -0
  559. package/src/server/mutations/invalidate.ts +50 -0
  560. package/src/server/mutations/oauth.ts +237 -0
  561. package/src/server/mutations/refresh.ts +298 -0
  562. package/src/server/mutations/register.ts +200 -0
  563. package/src/server/mutations/retrieve.ts +109 -0
  564. package/src/server/mutations/signature.ts +50 -0
  565. package/src/server/{implementation/mutations → mutations}/signin.ts +9 -7
  566. package/src/server/mutations/signout.ts +43 -0
  567. package/src/server/mutations/store/refs.ts +10 -0
  568. package/src/server/mutations/store.ts +138 -0
  569. package/src/server/mutations/verifier.ts +34 -0
  570. package/src/server/mutations/verify.ts +202 -0
  571. package/src/server/oauth.ts +243 -131
  572. package/src/server/passkey.ts +784 -0
  573. package/src/server/{implementation/redirects.ts → redirects.ts} +21 -16
  574. package/src/server/refresh.ts +222 -0
  575. package/src/server/runtime.ts +880 -0
  576. package/src/server/{implementation/sessions.ts → sessions.ts} +33 -25
  577. package/src/server/signin.ts +438 -0
  578. package/src/server/ssr.ts +1764 -0
  579. package/src/server/templates.ts +8 -3
  580. package/src/server/{implementation/tokens.ts → tokens.ts} +11 -5
  581. package/src/server/totp.ts +349 -0
  582. package/src/server/types.ts +972 -207
  583. package/src/server/{implementation/users.ts → users.ts} +129 -75
  584. package/src/server/utils.ts +192 -5
  585. package/src/test.ts +28 -4
  586. package/dist/bin.cjs +0 -27757
  587. package/dist/component/providers/email.js +0 -47
  588. package/dist/component/providers/email.js.map +0 -1
  589. package/dist/component/public.js.map +0 -1
  590. package/dist/component/server/implementation/db.js.map +0 -1
  591. package/dist/component/server/implementation/device.js +0 -135
  592. package/dist/component/server/implementation/device.js.map +0 -1
  593. package/dist/component/server/implementation/index.d.ts +0 -870
  594. package/dist/component/server/implementation/index.d.ts.map +0 -1
  595. package/dist/component/server/implementation/index.js +0 -610
  596. package/dist/component/server/implementation/index.js.map +0 -1
  597. package/dist/component/server/implementation/keys.js.map +0 -1
  598. package/dist/component/server/implementation/mutations/account.js +0 -39
  599. package/dist/component/server/implementation/mutations/account.js.map +0 -1
  600. package/dist/component/server/implementation/mutations/code.js.map +0 -1
  601. package/dist/component/server/implementation/mutations/index.js +0 -70
  602. package/dist/component/server/implementation/mutations/index.js.map +0 -1
  603. package/dist/component/server/implementation/mutations/invalidate.js +0 -29
  604. package/dist/component/server/implementation/mutations/invalidate.js.map +0 -1
  605. package/dist/component/server/implementation/mutations/oauth.js +0 -51
  606. package/dist/component/server/implementation/mutations/oauth.js.map +0 -1
  607. package/dist/component/server/implementation/mutations/refresh.js +0 -85
  608. package/dist/component/server/implementation/mutations/refresh.js.map +0 -1
  609. package/dist/component/server/implementation/mutations/register.js +0 -65
  610. package/dist/component/server/implementation/mutations/register.js.map +0 -1
  611. package/dist/component/server/implementation/mutations/retrieve.js +0 -50
  612. package/dist/component/server/implementation/mutations/retrieve.js.map +0 -1
  613. package/dist/component/server/implementation/mutations/signature.js +0 -27
  614. package/dist/component/server/implementation/mutations/signature.js.map +0 -1
  615. package/dist/component/server/implementation/mutations/signin.js.map +0 -1
  616. package/dist/component/server/implementation/mutations/signout.js +0 -27
  617. package/dist/component/server/implementation/mutations/signout.js.map +0 -1
  618. package/dist/component/server/implementation/mutations/store.js +0 -12
  619. package/dist/component/server/implementation/mutations/store.js.map +0 -1
  620. package/dist/component/server/implementation/mutations/verifier.js +0 -16
  621. package/dist/component/server/implementation/mutations/verifier.js.map +0 -1
  622. package/dist/component/server/implementation/mutations/verify.js +0 -105
  623. package/dist/component/server/implementation/mutations/verify.js.map +0 -1
  624. package/dist/component/server/implementation/passkey.js +0 -307
  625. package/dist/component/server/implementation/passkey.js.map +0 -1
  626. package/dist/component/server/implementation/provider.js +0 -19
  627. package/dist/component/server/implementation/provider.js.map +0 -1
  628. package/dist/component/server/implementation/ratelimit.js +0 -48
  629. package/dist/component/server/implementation/ratelimit.js.map +0 -1
  630. package/dist/component/server/implementation/redirects.js.map +0 -1
  631. package/dist/component/server/implementation/refresh.js +0 -109
  632. package/dist/component/server/implementation/refresh.js.map +0 -1
  633. package/dist/component/server/implementation/sessions.js.map +0 -1
  634. package/dist/component/server/implementation/signin.js +0 -148
  635. package/dist/component/server/implementation/signin.js.map +0 -1
  636. package/dist/component/server/implementation/tokens.js +0 -15
  637. package/dist/component/server/implementation/tokens.js.map +0 -1
  638. package/dist/component/server/implementation/totp.js +0 -142
  639. package/dist/component/server/implementation/totp.js.map +0 -1
  640. package/dist/component/server/implementation/types.d.ts +0 -42
  641. package/dist/component/server/implementation/types.d.ts.map +0 -1
  642. package/dist/component/server/implementation/types.js.map +0 -1
  643. package/dist/component/server/implementation/users.js.map +0 -1
  644. package/dist/component/server/implementation/utils.js +0 -56
  645. package/dist/component/server/implementation/utils.js.map +0 -1
  646. package/dist/component/server/providers.js.map +0 -1
  647. package/dist/component/server/templates.js +0 -84
  648. package/dist/component/server/templates.js.map +0 -1
  649. package/dist/server/cookies.d.ts.map +0 -1
  650. package/dist/server/implementation/db.d.ts +0 -86
  651. package/dist/server/implementation/db.d.ts.map +0 -1
  652. package/dist/server/implementation/db.js.map +0 -1
  653. package/dist/server/implementation/device.d.ts +0 -30
  654. package/dist/server/implementation/device.d.ts.map +0 -1
  655. package/dist/server/implementation/device.js +0 -135
  656. package/dist/server/implementation/device.js.map +0 -1
  657. package/dist/server/implementation/index.d.ts +0 -870
  658. package/dist/server/implementation/index.d.ts.map +0 -1
  659. package/dist/server/implementation/index.js +0 -610
  660. package/dist/server/implementation/index.js.map +0 -1
  661. package/dist/server/implementation/keys.d.ts +0 -66
  662. package/dist/server/implementation/keys.d.ts.map +0 -1
  663. package/dist/server/implementation/keys.js.map +0 -1
  664. package/dist/server/implementation/mutations/account.d.ts +0 -27
  665. package/dist/server/implementation/mutations/account.d.ts.map +0 -1
  666. package/dist/server/implementation/mutations/account.js +0 -39
  667. package/dist/server/implementation/mutations/account.js.map +0 -1
  668. package/dist/server/implementation/mutations/code.d.ts +0 -29
  669. package/dist/server/implementation/mutations/code.d.ts.map +0 -1
  670. package/dist/server/implementation/mutations/code.js.map +0 -1
  671. package/dist/server/implementation/mutations/index.d.ts +0 -310
  672. package/dist/server/implementation/mutations/index.d.ts.map +0 -1
  673. package/dist/server/implementation/mutations/index.js +0 -70
  674. package/dist/server/implementation/mutations/index.js.map +0 -1
  675. package/dist/server/implementation/mutations/invalidate.d.ts +0 -18
  676. package/dist/server/implementation/mutations/invalidate.d.ts.map +0 -1
  677. package/dist/server/implementation/mutations/invalidate.js +0 -29
  678. package/dist/server/implementation/mutations/invalidate.js.map +0 -1
  679. package/dist/server/implementation/mutations/oauth.d.ts +0 -23
  680. package/dist/server/implementation/mutations/oauth.d.ts.map +0 -1
  681. package/dist/server/implementation/mutations/oauth.js +0 -51
  682. package/dist/server/implementation/mutations/oauth.js.map +0 -1
  683. package/dist/server/implementation/mutations/refresh.d.ts +0 -20
  684. package/dist/server/implementation/mutations/refresh.d.ts.map +0 -1
  685. package/dist/server/implementation/mutations/refresh.js +0 -85
  686. package/dist/server/implementation/mutations/refresh.js.map +0 -1
  687. package/dist/server/implementation/mutations/register.d.ts +0 -37
  688. package/dist/server/implementation/mutations/register.d.ts.map +0 -1
  689. package/dist/server/implementation/mutations/register.js +0 -65
  690. package/dist/server/implementation/mutations/register.js.map +0 -1
  691. package/dist/server/implementation/mutations/retrieve.d.ts +0 -31
  692. package/dist/server/implementation/mutations/retrieve.d.ts.map +0 -1
  693. package/dist/server/implementation/mutations/retrieve.js +0 -50
  694. package/dist/server/implementation/mutations/retrieve.js.map +0 -1
  695. package/dist/server/implementation/mutations/signature.d.ts +0 -19
  696. package/dist/server/implementation/mutations/signature.d.ts.map +0 -1
  697. package/dist/server/implementation/mutations/signature.js +0 -27
  698. package/dist/server/implementation/mutations/signature.js.map +0 -1
  699. package/dist/server/implementation/mutations/signin.d.ts +0 -21
  700. package/dist/server/implementation/mutations/signin.d.ts.map +0 -1
  701. package/dist/server/implementation/mutations/signin.js.map +0 -1
  702. package/dist/server/implementation/mutations/signout.d.ts +0 -14
  703. package/dist/server/implementation/mutations/signout.d.ts.map +0 -1
  704. package/dist/server/implementation/mutations/signout.js +0 -27
  705. package/dist/server/implementation/mutations/signout.js.map +0 -1
  706. package/dist/server/implementation/mutations/store.d.ts +0 -11
  707. package/dist/server/implementation/mutations/store.d.ts.map +0 -1
  708. package/dist/server/implementation/mutations/store.js +0 -12
  709. package/dist/server/implementation/mutations/store.js.map +0 -1
  710. package/dist/server/implementation/mutations/verifier.d.ts +0 -11
  711. package/dist/server/implementation/mutations/verifier.d.ts.map +0 -1
  712. package/dist/server/implementation/mutations/verifier.js +0 -16
  713. package/dist/server/implementation/mutations/verifier.js.map +0 -1
  714. package/dist/server/implementation/mutations/verify.d.ts +0 -25
  715. package/dist/server/implementation/mutations/verify.d.ts.map +0 -1
  716. package/dist/server/implementation/mutations/verify.js +0 -105
  717. package/dist/server/implementation/mutations/verify.js.map +0 -1
  718. package/dist/server/implementation/passkey.d.ts +0 -24
  719. package/dist/server/implementation/passkey.d.ts.map +0 -1
  720. package/dist/server/implementation/passkey.js +0 -307
  721. package/dist/server/implementation/passkey.js.map +0 -1
  722. package/dist/server/implementation/provider.d.ts +0 -10
  723. package/dist/server/implementation/provider.d.ts.map +0 -1
  724. package/dist/server/implementation/provider.js +0 -19
  725. package/dist/server/implementation/provider.js.map +0 -1
  726. package/dist/server/implementation/ratelimit.d.ts +0 -10
  727. package/dist/server/implementation/ratelimit.d.ts.map +0 -1
  728. package/dist/server/implementation/ratelimit.js +0 -48
  729. package/dist/server/implementation/ratelimit.js.map +0 -1
  730. package/dist/server/implementation/redirects.d.ts +0 -10
  731. package/dist/server/implementation/redirects.d.ts.map +0 -1
  732. package/dist/server/implementation/redirects.js.map +0 -1
  733. package/dist/server/implementation/refresh.d.ts +0 -37
  734. package/dist/server/implementation/refresh.d.ts.map +0 -1
  735. package/dist/server/implementation/refresh.js +0 -109
  736. package/dist/server/implementation/refresh.js.map +0 -1
  737. package/dist/server/implementation/sessions.d.ts +0 -29
  738. package/dist/server/implementation/sessions.d.ts.map +0 -1
  739. package/dist/server/implementation/sessions.js.map +0 -1
  740. package/dist/server/implementation/signin.d.ts +0 -55
  741. package/dist/server/implementation/signin.d.ts.map +0 -1
  742. package/dist/server/implementation/signin.js +0 -148
  743. package/dist/server/implementation/signin.js.map +0 -1
  744. package/dist/server/implementation/tokens.d.ts +0 -11
  745. package/dist/server/implementation/tokens.d.ts.map +0 -1
  746. package/dist/server/implementation/tokens.js +0 -15
  747. package/dist/server/implementation/tokens.js.map +0 -1
  748. package/dist/server/implementation/totp.d.ts +0 -31
  749. package/dist/server/implementation/totp.d.ts.map +0 -1
  750. package/dist/server/implementation/totp.js +0 -142
  751. package/dist/server/implementation/totp.js.map +0 -1
  752. package/dist/server/implementation/types.d.ts +0 -189
  753. package/dist/server/implementation/types.d.ts.map +0 -1
  754. package/dist/server/implementation/types.js +0 -97
  755. package/dist/server/implementation/types.js.map +0 -1
  756. package/dist/server/implementation/users.d.ts +0 -30
  757. package/dist/server/implementation/users.d.ts.map +0 -1
  758. package/dist/server/implementation/users.js.map +0 -1
  759. package/dist/server/implementation/utils.d.ts +0 -19
  760. package/dist/server/implementation/utils.d.ts.map +0 -1
  761. package/dist/server/implementation/utils.js +0 -56
  762. package/dist/server/implementation/utils.js.map +0 -1
  763. package/dist/server/index.d.ts.map +0 -1
  764. package/dist/server/index.js.map +0 -1
  765. package/dist/server/oauth.d.ts.map +0 -1
  766. package/dist/server/providers.d.ts +0 -72
  767. package/dist/server/providers.d.ts.map +0 -1
  768. package/dist/server/providers.js.map +0 -1
  769. package/dist/server/templates.d.ts.map +0 -1
  770. package/dist/server/utils.d.ts.map +0 -1
  771. package/dist/server/version.d.ts +0 -5
  772. package/dist/server/version.d.ts.map +0 -1
  773. package/dist/server/version.js +0 -6
  774. package/dist/server/version.js.map +0 -1
  775. package/src/cli/utils.ts +0 -248
  776. package/src/server/implementation/device.ts +0 -307
  777. package/src/server/implementation/index.ts +0 -1583
  778. package/src/server/implementation/mutations/account.ts +0 -50
  779. package/src/server/implementation/mutations/index.ts +0 -157
  780. package/src/server/implementation/mutations/invalidate.ts +0 -42
  781. package/src/server/implementation/mutations/oauth.ts +0 -73
  782. package/src/server/implementation/mutations/refresh.ts +0 -175
  783. package/src/server/implementation/mutations/register.ts +0 -100
  784. package/src/server/implementation/mutations/retrieve.ts +0 -79
  785. package/src/server/implementation/mutations/signature.ts +0 -39
  786. package/src/server/implementation/mutations/signout.ts +0 -35
  787. package/src/server/implementation/mutations/store.ts +0 -7
  788. package/src/server/implementation/mutations/verifier.ts +0 -24
  789. package/src/server/implementation/mutations/verify.ts +0 -194
  790. package/src/server/implementation/passkey.ts +0 -620
  791. package/src/server/implementation/provider.ts +0 -36
  792. package/src/server/implementation/ratelimit.ts +0 -79
  793. package/src/server/implementation/refresh.ts +0 -172
  794. package/src/server/implementation/signin.ts +0 -296
  795. package/src/server/implementation/totp.ts +0 -342
  796. package/src/server/implementation/types.ts +0 -444
  797. package/src/server/implementation/utils.ts +0 -91
  798. package/src/server/version.ts +0 -2
@@ -1,6 +1,39 @@
1
+ import { Fx } from "@robelest/fx";
1
2
  import { ConvexHttpClient } from "convex/browser";
2
3
  import { ConvexError, Value } from "convex/values";
3
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
+ import type {
18
+ AuthApiRefs,
19
+ AuthClient,
20
+ AuthFlowContext,
21
+ AuthHandshakeErrorCode,
22
+ AuthSession,
23
+ AuthState,
24
+ ClientOptions,
25
+ ConvexTransport,
26
+ DeviceClient,
27
+ DeviceCodeResult,
28
+ HandshakeWaiter,
29
+ PasskeyClient,
30
+ PendingInvite,
31
+ SignInActionResult,
32
+ SignInResult,
33
+ Storage,
34
+ TotpClient,
35
+ } from "./core/types";
36
+
4
37
  // Re-export error utilities so consumers can import from `@robelest/convex-auth/client`.
5
38
  export {
6
39
  isAuthError,
@@ -8,139 +41,29 @@ export {
8
41
  AUTH_ERRORS,
9
42
  type AuthErrorCode,
10
43
  } from "../server/errors";
11
-
12
- /**
13
- * Structural interface for any Convex client.
14
- * Satisfied by `ConvexClient` (`convex/browser`),
15
- * `ConvexReactClient` (`convex/react`), and similar transports.
16
- *
17
- * `clearAuth` is present on `ConvexReactClient` and `BaseConvexClient`
18
- * but not on the simplified `ConvexClient`. When available we call it
19
- * during sign-out for a clean deauthentication.
20
- */
21
- interface ConvexTransport {
22
- action(action: any, args: any): Promise<any>;
23
- setAuth(
24
- fetchToken: (args: {
25
- forceRefreshToken: boolean;
26
- }) => Promise<string | null | undefined>,
27
- onChange?: (isAuthenticated: boolean) => void,
28
- ): void;
29
- clearAuth?(): void;
30
- }
31
-
32
- /** Pluggable key-value storage (defaults to `localStorage`). */
33
- export interface Storage {
34
- getItem(
35
- key: string,
36
- ): string | null | undefined | Promise<string | null | undefined>;
37
- setItem(key: string, value: string): void | Promise<void>;
38
- removeItem(key: string): void | Promise<void>;
39
- }
40
-
41
- type AuthSession = {
42
- token: string;
43
- refreshToken: string;
44
- };
45
-
46
- /**
47
- * Device code response returned when signing in with the `"device"` provider.
48
- *
49
- * The device displays the `userCode` (or `verificationUriComplete`) and
50
- * polls via `auth.device.poll()` until the user authorizes.
51
- */
52
- export type DeviceCodeResult = {
53
- /** High-entropy device code used for polling (keep secret). */
54
- deviceCode: string;
55
- /** Short human-readable code the user enters (e.g. "WDJB-MJHT"). */
56
- userCode: string;
57
- /** Base verification URL (e.g. "https://myapp.com/device"). */
58
- verificationUri: string;
59
- /** Verification URL with user code pre-filled as `?code=XXXX-XXXX`. */
60
- verificationUriComplete: string;
61
- /** Lifetime of the codes in seconds. */
62
- expiresIn: number;
63
- /** Minimum polling interval in seconds. */
64
- interval: number;
65
- };
66
-
67
- /**
68
- * Result of a `signIn` call.
69
- *
70
- * - `signingIn: true` — credentials were accepted and the user is authenticated.
71
- * - `redirect` — OAuth flow initiated; redirect the user to `redirect.toString()`.
72
- * - `totpRequired` — credentials valid but 2FA is needed; call `auth.totp.verify()`.
73
- * - `deviceCode` — device flow initiated; display the code and poll via `auth.device.poll()`.
74
- * - `verifier` — opaque string for multi-step flows (TOTP, passkey).
75
- */
76
- export type SignInResult = {
77
- /** `true` when sign-in completed and the user is authenticated. */
78
- signingIn: boolean;
79
- /** OAuth redirect URL. Present when the provider requires a browser redirect. */
80
- redirect?: URL;
81
- /** `true` when the account has TOTP enabled and a code is required. */
82
- totpRequired?: boolean;
83
- /** Device code response for the device authorization flow (RFC 8628). */
84
- deviceCode?: DeviceCodeResult;
85
- /** Opaque verifier for multi-step flows (pass to `totp.verify` or passkey phase 2). */
86
- verifier?: string;
87
- };
88
-
89
- /** Reactive auth state snapshot returned by `auth.state` and `auth.onChange`. */
90
- export type AuthState = {
91
- /** `true` during initial hydration before the first token is resolved. */
92
- isLoading: boolean;
93
- /** `true` when a valid JWT exists (user is signed in). */
94
- isAuthenticated: boolean;
95
- /** The raw JWT string, or `null` when not authenticated. */
96
- token: string | null;
97
- };
98
-
99
- /** Options for {@link client}. */
100
- export type ClientOptions = {
101
- /** Any Convex client (`ConvexClient` or `ConvexReactClient`). */
102
- convex: ConvexTransport;
103
- /**
104
- * Convex deployment URL. Derived automatically from the client internals
105
- * when omitted — pass explicitly only if auto-detection fails.
106
- */
107
- url?: string;
108
- /**
109
- * Key-value storage for persisting tokens.
110
- *
111
- * - Defaults to `localStorage` in SPA mode.
112
- * - Defaults to `null` (in-memory only) when `proxy` is set,
113
- * since httpOnly cookies handle persistence.
114
- */
115
- storage?: Storage | null;
116
- /** Override how the URL bar is updated after OAuth code exchange. */
117
- replaceURL?: (relativeUrl: string) => void | Promise<void>;
118
- /**
119
- * SSR proxy endpoint (e.g. `"/api/auth"`).
120
- *
121
- * When set, `signIn`/`signOut`/token refresh POST to this URL
122
- * (with `credentials: "include"`) instead of calling Convex directly.
123
- * The server handles httpOnly cookies for token persistence.
124
- *
125
- * Pair with {@link ClientOptions.token} for flash-free SSR hydration.
126
- */
127
- proxy?: string;
128
- /**
129
- * JWT from server-side hydration.
130
- *
131
- * In proxy mode the server reads the JWT from an httpOnly cookie
132
- * and passes it to the client during SSR. This avoids a loading
133
- * flash on first render — the client is immediately authenticated.
134
- */
135
- token?: string | null;
136
- };
44
+ export type {
45
+ AuthApiRefs,
46
+ AuthClient,
47
+ AuthState,
48
+ ClientOptions,
49
+ DeviceClient,
50
+ DeviceCodeResult,
51
+ PasskeyClient,
52
+ PendingInvite,
53
+ SignInResult,
54
+ Storage,
55
+ TotpClient,
56
+ } from "./core/types";
137
57
 
138
58
  const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
139
59
  const JWT_STORAGE_KEY = "__convexAuthJWT";
140
60
  const REFRESH_TOKEN_STORAGE_KEY = "__convexAuthRefreshToken";
61
+ const INVITE_TOKEN_KEY = "__convexAuthPendingInvite";
62
+ const INVITE_EMAIL_KEY = "__convexAuthPendingInviteEmail";
141
63
 
142
- const RETRY_BACKOFF = [500, 2000];
143
- const RETRY_JITTER = 100;
64
+ const RETRY_BASE_MS = 500;
65
+ const RETRY_MAX_RETRIES = 2;
66
+ const AUTH_HANDSHAKE_TIMEOUT_MS = 5000;
144
67
 
145
68
  /**
146
69
  * Resolve the Convex deployment URL from the client.
@@ -169,9 +92,10 @@ function resolveUrl(convex: ConvexTransport, explicit?: string): string {
169
92
  * ```ts
170
93
  * import { ConvexClient } from 'convex/browser';
171
94
  * import { client } from '@robelest/convex-auth/client';
95
+ * import { api } from '../convex/_generated/api';
172
96
  *
173
97
  * const convex = new ConvexClient(CONVEX_URL);
174
- * const auth = client({ convex });
98
+ * const auth = client({ convex, api: api.auth });
175
99
  * ```
176
100
  *
177
101
  * ### SSR / proxy mode
@@ -179,8 +103,8 @@ function resolveUrl(convex: ConvexTransport, explicit?: string): string {
179
103
  * ```ts
180
104
  * const auth = client({
181
105
  * convex,
182
- * proxy: '/api/auth',
183
- * token: tokenFromServer, // JWT read from httpOnly cookie during SSR
106
+ * proxyPath: '/api/auth',
107
+ * tokenSeed: tokenFromServer, // JWT read from httpOnly cookie during SSR
184
108
  * });
185
109
  * ```
186
110
  *
@@ -189,10 +113,26 @@ function resolveUrl(convex: ConvexTransport, explicit?: string): string {
189
113
  * holds the JWT in memory only.
190
114
  *
191
115
  * @param options - Client configuration. See {@link ClientOptions}.
192
- * @returns Auth client with `signIn`, `signOut`, `onChange`, `state`, `passkey`, and `totp`.
116
+ * @typeParam Api - An AuthApiRefs type determining which factor helpers are available.
117
+ * @returns Auth client with conditional `passkey`, `totp`, and `device` helpers.
118
+ * @throws {Error} When the Convex deployment URL cannot be determined and `url` is not passed explicitly.
119
+ * @throws {Error} When `proxyPath` is not set and the `api` option is missing.
193
120
  */
194
- export function client(options: ClientOptions) {
195
- const { convex, proxy } = options;
121
+ export function client<
122
+ Api extends AuthApiRefs<boolean, boolean, boolean> = AuthApiRefs,
123
+ >(options: ClientOptions<Api>): AuthClient<Api> {
124
+ const { convex, proxyPath, api: apiRefs } = options;
125
+ const proxy = proxyPath;
126
+
127
+ function requireApiRefs() {
128
+ if (!apiRefs) {
129
+ throw new Error(
130
+ "The `api` option is required when `proxyPath` is not set. " +
131
+ "Pass { api: api.auth }.",
132
+ );
133
+ }
134
+ return apiRefs;
135
+ }
196
136
 
197
137
  // In proxy mode, default storage to null (cookies handle persistence).
198
138
  const storage =
@@ -204,20 +144,79 @@ export function client(options: ClientOptions) {
204
144
  ? null
205
145
  : window.localStorage;
206
146
 
207
- const replaceURL =
208
- options.replaceURL ??
147
+ const replaceUrl =
148
+ options.replaceUrl ??
209
149
  ((url: string) => {
210
150
  if (typeof window !== "undefined") {
211
151
  window.history.replaceState({}, "", url);
212
152
  }
213
153
  });
214
154
 
155
+ // ---------------------------------------------------------------------------
156
+ // Location — SSR-safe URL reading
157
+ // ---------------------------------------------------------------------------
158
+
159
+ function getLocation(): URL | null {
160
+ if (typeof options.location === "function") return options.location();
161
+ if (options.location instanceof URL) return options.location;
162
+ if (typeof window !== "undefined") return new URL(window.location.href);
163
+ return null;
164
+ }
165
+
166
+ /**
167
+ * SSR-safe URL parameter reader.
168
+ *
169
+ * Uses the `location` option if provided, otherwise falls back to
170
+ * `window.location` (returns `null` during SSR where `window` is unavailable).
171
+ *
172
+ * @param name - The query parameter name.
173
+ * @returns The parameter value, or `null` if not present or in SSR.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * const workspaceId = auth.param("workspace");
178
+ * const tab = auth.param("tab") ?? "issues";
179
+ * ```
180
+ */
181
+ function param(name: string): string | null {
182
+ const loc = getLocation();
183
+ return loc?.searchParams.get(name) ?? null;
184
+ }
185
+
186
+ function cleanUrlParams(params: string[]) {
187
+ const loc = getLocation();
188
+ if (!loc) return;
189
+ const searchParams = new URLSearchParams(loc.search);
190
+ let changed = false;
191
+ for (const p of params) {
192
+ if (searchParams.has(p)) {
193
+ searchParams.delete(p);
194
+ changed = true;
195
+ }
196
+ }
197
+ if (changed) {
198
+ const next = searchParams.toString()
199
+ ? `${loc.pathname}?${searchParams}`
200
+ : loc.pathname;
201
+ void replaceUrl(next);
202
+ }
203
+ }
204
+
215
205
  const url = proxy ? undefined : resolveUrl(convex, options.url);
216
206
  const escapedNamespace = proxy
217
207
  ? proxy.replace(/[^a-zA-Z0-9]/g, "")
218
208
  : url!.replace(/[^a-zA-Z0-9]/g, "");
219
209
  const key = (name: string) => `${name}_${escapedNamespace}`;
210
+ const {
211
+ get: storageGet,
212
+ set: storageSet,
213
+ remove: storageRemove,
214
+ } = createStorageHelpers({ storage, key });
215
+ const { isAbsoluteUrl, proxyFetch, resolveProxyUrl } = createProxyHelpers({
216
+ proxy,
217
+ });
220
218
  const subscribers = new Set<() => void>();
219
+ let disposeStorageListener: (() => void) | null = null;
221
220
 
222
221
  // Unauthenticated HTTP client for code verification & OAuth exchange.
223
222
  // Only needed in SPA mode — proxy mode routes everything through the proxy.
@@ -227,30 +226,173 @@ export function client(options: ClientOptions) {
227
226
  // State
228
227
  // ---------------------------------------------------------------------------
229
228
 
230
- // If a server-provided token was supplied (SSR hydration), start authenticated.
231
- const serverToken = options.token ?? null;
229
+ // If a server-provided token was supplied (SSR hydration), treat it as
230
+ // immediately authenticated to avoid a handshake-only loading screen.
231
+ const serverToken =
232
+ typeof options.tokenSeed === "string" && options.tokenSeed.trim().length > 0
233
+ ? options.tokenSeed
234
+ : null;
232
235
  const hasServerToken = serverToken !== null;
233
236
 
234
237
  let token: string | null = serverToken;
235
238
  let isLoading = !hasServerToken;
239
+ let authConfirmed = hasServerToken;
240
+ let handshakePending = false;
241
+ let authEpoch = 0;
242
+ let destroyed = false;
243
+ const handshakeWaiters = new Set<HandshakeWaiter>();
236
244
  let snapshot: AuthState = {
245
+ phase: hasServerToken
246
+ ? "authenticated"
247
+ : isLoading
248
+ ? "loading"
249
+ : "unauthenticated",
237
250
  isLoading,
238
251
  isAuthenticated: hasServerToken,
239
252
  token,
240
253
  };
241
254
  let handlingCodeFlow = false;
242
255
 
256
+ const createHandshakeError = (
257
+ code: AuthHandshakeErrorCode,
258
+ context: Record<string, unknown>,
259
+ ) => {
260
+ return new ConvexError({
261
+ code,
262
+ message: AUTH_ERRORS[code],
263
+ ...context,
264
+ } as Value);
265
+ };
266
+
267
+ const settleHandshakeWaiters = (
268
+ epoch: number,
269
+ outcome:
270
+ | { type: "resolve" }
271
+ | { type: "reject"; error: ConvexError<Value> },
272
+ ) => {
273
+ for (const waiter of Array.from(handshakeWaiters)) {
274
+ if (waiter.epoch !== epoch) {
275
+ continue;
276
+ }
277
+ clearTimeout(waiter.timeoutId);
278
+ handshakeWaiters.delete(waiter);
279
+ if (outcome.type === "resolve") {
280
+ waiter.resolve();
281
+ } else {
282
+ waiter.reject(outcome.error);
283
+ }
284
+ }
285
+ };
286
+
287
+ const rejectObsoleteHandshakeWaiters = (activeEpoch: number) => {
288
+ for (const waiter of Array.from(handshakeWaiters)) {
289
+ if (waiter.epoch >= activeEpoch) {
290
+ continue;
291
+ }
292
+ clearTimeout(waiter.timeoutId);
293
+ handshakeWaiters.delete(waiter);
294
+ waiter.reject(
295
+ createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
296
+ ...waiter.context,
297
+ reason: "token_changed",
298
+ }),
299
+ );
300
+ }
301
+ };
302
+
303
+ const waitForAuthHandshake = async (context: AuthFlowContext) => {
304
+ if (token === null) {
305
+ return;
306
+ }
307
+ if (authConfirmed && !handshakePending) {
308
+ return;
309
+ }
310
+ if (!handshakePending) {
311
+ throw createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
312
+ ...context,
313
+ reason: "auth_rejected",
314
+ });
315
+ }
316
+
317
+ const epoch = authEpoch;
318
+ await new Promise<void>((resolve, reject) => {
319
+ const waiterRef: { current: HandshakeWaiter | null } = { current: null };
320
+ const timeoutId = setTimeout(() => {
321
+ if (waiterRef.current !== null) {
322
+ handshakeWaiters.delete(waiterRef.current);
323
+ }
324
+ reject(
325
+ createHandshakeError("AUTH_HANDSHAKE_TIMEOUT", {
326
+ ...context,
327
+ timeoutMs: AUTH_HANDSHAKE_TIMEOUT_MS,
328
+ }),
329
+ );
330
+ }, AUTH_HANDSHAKE_TIMEOUT_MS);
331
+
332
+ const waiter: HandshakeWaiter = {
333
+ epoch,
334
+ context,
335
+ resolve,
336
+ reject,
337
+ timeoutId,
338
+ };
339
+ waiterRef.current = waiter;
340
+ handshakeWaiters.add(waiter);
341
+ });
342
+ };
343
+
344
+ const handleConvexAuthChange = (isAuthenticated: boolean) => {
345
+ if (destroyed) {
346
+ return;
347
+ }
348
+
349
+ if (isAuthenticated) {
350
+ authConfirmed = true;
351
+ handshakePending = false;
352
+ settleHandshakeWaiters(authEpoch, { type: "resolve" });
353
+ } else {
354
+ authConfirmed = false;
355
+ // Do not reject immediately while a handshake is pending.
356
+ // Convex can transiently emit `false` while reauth is still in flight,
357
+ // and a subsequent `true` confirms the same session.
358
+ }
359
+
360
+ if (updateSnapshot()) {
361
+ notify();
362
+ }
363
+ };
364
+
243
365
  const notify = () => {
244
366
  for (const cb of subscribers) cb();
245
367
  };
246
368
 
247
369
  const updateSnapshot = () => {
370
+ const phaseDispatch = {
371
+ tag:
372
+ token !== null && handshakePending
373
+ ? "handshake"
374
+ : isLoading
375
+ ? "loading"
376
+ : token !== null && authConfirmed
377
+ ? "authenticated"
378
+ : "unauthenticated",
379
+ } as const;
380
+
381
+ const phase = {
382
+ handshake: "handshake",
383
+ loading: "loading",
384
+ authenticated: "authenticated",
385
+ unauthenticated: "unauthenticated",
386
+ }[phaseDispatch.tag] as AuthState["phase"];
387
+
248
388
  const next: AuthState = {
249
- isLoading,
250
- isAuthenticated: token !== null,
389
+ phase,
390
+ isLoading: phase === "loading" || phase === "handshake",
391
+ isAuthenticated: phase === "authenticated",
251
392
  token,
252
393
  };
253
394
  if (
395
+ snapshot.phase === next.phase &&
254
396
  snapshot.isLoading === next.isLoading &&
255
397
  snapshot.isAuthenticated === next.isAuthenticated &&
256
398
  snapshot.token === next.token
@@ -261,28 +403,54 @@ export function client(options: ClientOptions) {
261
403
  return true;
262
404
  };
263
405
 
264
- // ---------------------------------------------------------------------------
265
- // Storage helpers (SPA mode only)
266
- // ---------------------------------------------------------------------------
267
-
268
- const storageGet = async (name: string) =>
269
- storage ? ((await storage.getItem(key(name))) ?? null) : null;
270
- const storageSet = async (name: string, value: string) => {
271
- if (storage) await storage.setItem(key(name), value);
272
- };
273
- const storageRemove = async (name: string) => {
274
- if (storage) await storage.removeItem(key(name));
406
+ const finalizeLoadingState = () => {
407
+ if (!isLoading) {
408
+ return;
409
+ }
410
+ isLoading = false;
411
+ if (updateSnapshot()) {
412
+ notify();
413
+ }
275
414
  };
276
415
 
416
+ const inviteManager = createInviteManager({
417
+ param,
418
+ storageGet,
419
+ storageSet,
420
+ storageRemove,
421
+ cleanUrlParams,
422
+ tokenKey: INVITE_TOKEN_KEY,
423
+ emailKey: INVITE_EMAIL_KEY,
424
+ });
425
+ const getPendingInvite = () => inviteManager.getPendingInvite();
426
+ const persistInvite = () => inviteManager.persistInvite();
427
+ const acceptInvite = () => inviteManager.acceptInvite();
428
+
277
429
  // ---------------------------------------------------------------------------
278
430
  // Token management
279
431
  // ---------------------------------------------------------------------------
280
432
 
433
+ const bindConvexAuth = () => {
434
+ convex.setAuth(fetchAccessToken, handleConvexAuthChange);
435
+ };
436
+
281
437
  const setToken = async (
282
438
  args:
283
- | { shouldStore: true; tokens: AuthSession | null }
284
- | { shouldStore: false; tokens: { token: string } | null },
439
+ | {
440
+ shouldStore: true;
441
+ tokens: AuthSession | null;
442
+ requireHandshake?: boolean;
443
+ resyncConvexAuth?: boolean;
444
+ }
445
+ | {
446
+ shouldStore: false;
447
+ tokens: { token: string } | null;
448
+ requireHandshake?: boolean;
449
+ resyncConvexAuth?: boolean;
450
+ },
285
451
  ) => {
452
+ const previousToken = token;
453
+
286
454
  if (args.tokens === null) {
287
455
  token = null;
288
456
  if (args.shouldStore) {
@@ -296,49 +464,72 @@ export function client(options: ClientOptions) {
296
464
  await storageSet(REFRESH_TOKEN_STORAGE_KEY, args.tokens.refreshToken);
297
465
  }
298
466
  }
467
+
468
+ if (token !== previousToken) {
469
+ authEpoch += 1;
470
+ rejectObsoleteHandshakeWaiters(authEpoch);
471
+ }
472
+
473
+ if (token === null) {
474
+ authConfirmed = false;
475
+ handshakePending = false;
476
+ settleHandshakeWaiters(authEpoch, {
477
+ type: "reject",
478
+ error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
479
+ reason: "token_cleared",
480
+ }),
481
+ });
482
+ } else {
483
+ const shouldEnterHandshake =
484
+ args.requireHandshake === true || !authConfirmed;
485
+ if (shouldEnterHandshake) {
486
+ authConfirmed = false;
487
+ handshakePending = true;
488
+ } else {
489
+ handshakePending = false;
490
+ }
491
+ }
492
+
299
493
  const hadPendingLoad = isLoading;
300
494
  isLoading = false;
301
495
  const changed = updateSnapshot();
496
+ if (args.resyncConvexAuth !== false) {
497
+ bindConvexAuth();
498
+ }
302
499
  if (hadPendingLoad || changed) {
303
- // Re-sync the Convex client so it picks up the new token immediately.
304
- // Without this, the initial convex.setAuth(fetchAccessToken) from
305
- // initialization never re-polls and queries run unauthenticated after
306
- // magic link code exchange.
307
- if (!proxy) {
308
- convex.setAuth(fetchAccessToken);
309
- }
310
500
  notify();
311
501
  }
312
502
  };
313
503
 
314
- // ---------------------------------------------------------------------------
315
- // Proxy fetch helper
316
- // ---------------------------------------------------------------------------
317
-
318
- const proxyFetch = async (body: Record<string, unknown>) => {
319
- const response = await fetch(proxy!, {
320
- method: "POST",
321
- headers: { "Content-Type": "application/json" },
322
- credentials: "include",
323
- body: JSON.stringify(body),
504
+ const setTokenAndMaybeWait = async (
505
+ args:
506
+ | {
507
+ shouldStore: true;
508
+ tokens: AuthSession | null;
509
+ waitForHandshake: boolean;
510
+ context: AuthFlowContext;
511
+ }
512
+ | {
513
+ shouldStore: false;
514
+ tokens: { token: string } | null;
515
+ waitForHandshake: boolean;
516
+ context: AuthFlowContext;
517
+ },
518
+ ): Promise<boolean> => {
519
+ const { waitForHandshake, context, ...tokenArgs } = args;
520
+ await setToken({
521
+ ...(tokenArgs as
522
+ | { shouldStore: true; tokens: AuthSession | null }
523
+ | { shouldStore: false; tokens: { token: string } | null }),
524
+ requireHandshake: waitForHandshake,
324
525
  });
325
- if (!response.ok) {
326
- const errorBody = await response.json().catch(() => ({} as Record<string, unknown>));
327
- // Reconstruct ConvexError when the proxy forwards structured auth error data.
328
- if (
329
- typeof errorBody === "object" &&
330
- errorBody !== null &&
331
- "authError" in errorBody &&
332
- typeof (errorBody as Record<string, unknown>).authError === "object"
333
- ) {
334
- throw new ConvexError((errorBody as Record<string, unknown>).authError as Value);
335
- }
336
- throw new Error(
337
- (errorBody as Record<string, unknown>).error as string ??
338
- `Proxy request failed: ${response.status}`,
339
- );
526
+ if (tokenArgs.tokens === null) {
527
+ return false;
528
+ }
529
+ if (waitForHandshake) {
530
+ await waitForAuthHandshake(context);
340
531
  }
341
- return response.json();
532
+ return true;
342
533
  };
343
534
 
344
535
  // ---------------------------------------------------------------------------
@@ -348,40 +539,58 @@ export function client(options: ClientOptions) {
348
539
  const verifyCode = async (
349
540
  args: { code: string; verifier?: string } | { refreshToken: string },
350
541
  ) => {
351
- let lastError: unknown;
352
- let retry = 0;
353
- while (retry < RETRY_BACKOFF.length) {
354
- try {
355
- return await httpClient!.action(
356
- "auth:signIn" as any,
357
- "code" in args
358
- ? { params: { code: args.code }, verifier: args.verifier }
359
- : args,
360
- );
361
- } catch (e) {
362
- lastError = e;
363
- const isNetworkError =
364
- e instanceof Error && /network/i.test(e.message || "");
365
- if (!isNetworkError) break;
366
- const wait = RETRY_BACKOFF[retry]! + RETRY_JITTER * Math.random();
367
- retry++;
368
- await new Promise((resolve) => setTimeout(resolve, wait));
369
- }
370
- }
371
- throw lastError;
542
+ const verifyCodeRetryPolicy = Fx.retry.while(
543
+ Fx.retry.compose(
544
+ Fx.retry.jittered(Fx.retry.exponential(RETRY_BASE_MS)),
545
+ Fx.retry.recurs(RETRY_MAX_RETRIES),
546
+ ),
547
+ (meta) => isTransientNetworkError(meta.input),
548
+ );
549
+
550
+ return Fx.run(
551
+ Fx.from({
552
+ ok: () =>
553
+ httpClient!.action(
554
+ requireApiRefs().signIn,
555
+ "code" in args
556
+ ? { params: { code: args.code }, verifier: args.verifier }
557
+ : args,
558
+ ),
559
+ err: (e) => e,
560
+ }).pipe(
561
+ Fx.retry(verifyCodeRetryPolicy),
562
+ Fx.recover((e) => Fx.fatal(e)),
563
+ ),
564
+ );
372
565
  };
373
566
 
374
567
  const verifyCodeAndSetToken = async (
375
568
  args: { code: string; verifier?: string } | { refreshToken: string },
569
+ opts?: { resyncConvexAuth?: boolean },
376
570
  ) => {
377
571
  const { tokens } = await verifyCode(args);
378
572
  await setToken({
379
573
  shouldStore: true,
380
574
  tokens: (tokens as AuthSession | null) ?? null,
575
+ resyncConvexAuth: opts?.resyncConvexAuth,
381
576
  });
382
577
  return tokens !== null;
383
578
  };
384
579
 
580
+ const normalizeDeviceCodeResult = (device_code: any): DeviceCodeResult => {
581
+ return {
582
+ deviceCode: device_code.deviceCode,
583
+ userCode: device_code.userCode,
584
+ verificationUri:
585
+ device_code.verification_uri ?? device_code.verificationUri,
586
+ verificationUriComplete:
587
+ device_code.verification_uri_complete ??
588
+ device_code.verificationUriComplete,
589
+ expiresIn: device_code.expiresIn,
590
+ interval: device_code.interval,
591
+ };
592
+ };
593
+
385
594
  // ---------------------------------------------------------------------------
386
595
  // signIn
387
596
  // ---------------------------------------------------------------------------
@@ -394,6 +603,7 @@ export function client(options: ClientOptions) {
394
603
  * @param args - Provider-specific arguments. Pass a `Record<string, Value>`
395
604
  * or `FormData`. Common fields: `email`, `password`, `code`, `redirectTo`.
396
605
  * @returns A {@link SignInResult} indicating the outcome.
606
+ * @throws {ConvexError} When the server action rejects the sign-in attempt (e.g. invalid credentials, provider error, or rate limiting).
397
607
  *
398
608
  * @example Email magic link
399
609
  * ```ts
@@ -403,8 +613,8 @@ export function client(options: ClientOptions) {
403
613
  * @example Password
404
614
  * ```ts
405
615
  * const result = await auth.signIn('password', { email, password, flow: 'signIn' });
406
- * if (result.totpRequired) {
407
- * await auth.totp.verify({ code: totpCode, verifier: result.verifier! });
616
+ * if (result.kind === 'totpRequired') {
617
+ * await auth.totp.verify({ code: totpCode, verifier: result.verifier });
408
618
  * }
409
619
  * ```
410
620
  *
@@ -417,80 +627,121 @@ export function client(options: ClientOptions) {
417
627
  provider?: string,
418
628
  args?: FormData | Record<string, Value>,
419
629
  ): Promise<SignInResult> => {
630
+ // Persist invite before potential OAuth redirect
631
+ await persistInvite();
632
+
420
633
  const params =
421
634
  args instanceof FormData
422
- ? Array.from(args.entries()).reduce(
423
- (acc, [k, v]) => {
424
- acc[k] = v as string;
425
- return acc;
426
- },
427
- {} as Record<string, string>,
428
- )
429
- : args ?? {};
635
+ ? (() => {
636
+ const formParams: Record<string, Value> = {};
637
+ args.forEach((value, key) => {
638
+ formParams[key] = typeof value === "string" ? value : value.name;
639
+ });
640
+ return formParams;
641
+ })()
642
+ : (args ?? {});
643
+ const flow =
644
+ typeof params.flow === "string" && params.flow.length > 0
645
+ ? params.flow
646
+ : "signIn";
647
+
648
+ const handleSignInActionResult = async (
649
+ result: SignInActionResult,
650
+ options: { shouldStore: boolean; persistVerifier: boolean },
651
+ ): Promise<SignInResult> =>
652
+ Fx.run(
653
+ Fx.match(result, result.kind, {
654
+ 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,
674
+ }),
675
+ totpRequired: (totpRequiredResult) =>
676
+ Fx.succeed({
677
+ kind: "totpRequired" as const,
678
+ verifier: totpRequiredResult.verifier,
679
+ }),
680
+ deviceCode: (deviceCodeResult) =>
681
+ Fx.succeed({
682
+ kind: "deviceCode" as const,
683
+ deviceCode: normalizeDeviceCodeResult(
684
+ deviceCodeResult.deviceCode,
685
+ ),
686
+ }),
687
+ 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,
713
+ }),
714
+ started: (_startedResult) => Fx.succeed({ kind: "started" as const }),
715
+ passkeyOptions: (_passkeyOptionsResult) =>
716
+ Fx.succeed({ kind: "started" as const }),
717
+ totpSetup: (_totpSetupResult) =>
718
+ Fx.succeed({ kind: "started" as const }),
719
+ }),
720
+ );
430
721
 
431
722
  if (proxy) {
432
- // Proxy mode: POST to the proxy endpoint.
433
- const result = await proxyFetch({
723
+ const result = (await proxyFetch({
434
724
  action: "auth:signIn",
435
725
  args: { provider, params },
726
+ })) as SignInActionResult;
727
+ return handleSignInActionResult(result, {
728
+ shouldStore: false,
729
+ persistVerifier: false,
436
730
  });
437
- if (result.redirect !== undefined) {
438
- const redirectUrl = new URL(result.redirect);
439
- // Verifier is stored server-side in an httpOnly cookie.
440
- if (typeof window !== "undefined") {
441
- window.location.href = redirectUrl.toString();
442
- }
443
- return { signingIn: false, redirect: redirectUrl };
444
- }
445
- if (result.totpRequired) {
446
- return { signingIn: false, totpRequired: true, verifier: result.verifier };
447
- }
448
- if (result.deviceCode !== undefined) {
449
- return { signingIn: false, deviceCode: result.deviceCode as DeviceCodeResult };
450
- }
451
- if (result.tokens !== undefined) {
452
- // Proxy returns { token, refreshToken: "dummy" }.
453
- // Store JWT in memory only — real refresh token is in httpOnly cookie.
454
- await setToken({
455
- shouldStore: false,
456
- tokens:
457
- result.tokens === null ? null : { token: result.tokens.token },
458
- });
459
- return { signingIn: result.tokens !== null };
460
- }
461
- return { signingIn: false };
462
731
  }
463
732
 
464
733
  // SPA mode: call Convex directly.
465
734
  const verifier = (await storageGet(VERIFIER_STORAGE_KEY)) ?? undefined;
466
735
  await storageRemove(VERIFIER_STORAGE_KEY);
467
- const result = await convex.action("auth:signIn" as any, {
736
+ const result = (await convex.action(requireApiRefs().signIn, {
468
737
  provider,
469
738
  params,
470
739
  verifier,
740
+ })) as SignInActionResult;
741
+ return handleSignInActionResult(result, {
742
+ shouldStore: true,
743
+ persistVerifier: true,
471
744
  });
472
- if (result.redirect !== undefined) {
473
- const redirectUrl = new URL(result.redirect);
474
- await storageSet(VERIFIER_STORAGE_KEY, result.verifier!);
475
- if (typeof window !== "undefined") {
476
- window.location.href = redirectUrl.toString();
477
- }
478
- return { signingIn: false, redirect: redirectUrl };
479
- }
480
- if (result.totpRequired) {
481
- return { signingIn: false, totpRequired: true, verifier: result.verifier };
482
- }
483
- if (result.deviceCode !== undefined) {
484
- return { signingIn: false, deviceCode: result.deviceCode as DeviceCodeResult };
485
- }
486
- if (result.tokens !== undefined) {
487
- await setToken({
488
- shouldStore: true,
489
- tokens: (result.tokens as AuthSession | null) ?? null,
490
- });
491
- return { signingIn: result.tokens !== null };
492
- }
493
- return { signingIn: false };
494
745
  };
495
746
 
496
747
  // ---------------------------------------------------------------------------
@@ -506,22 +757,24 @@ export function client(options: ClientOptions) {
506
757
  */
507
758
  const signOut = async () => {
508
759
  if (proxy) {
509
- try {
510
- await proxyFetch({ action: "auth:signOut", args: {} });
511
- } catch {
512
- // Already signed out is fine.
513
- }
760
+ await Fx.run(
761
+ Fx.from({
762
+ ok: () => proxyFetch({ action: "auth:signOut", args: {} }),
763
+ err: () => undefined,
764
+ }).pipe(Fx.recover(() => Fx.succeed(undefined))),
765
+ );
514
766
  await setToken({ shouldStore: false, tokens: null });
515
767
  if (convex.clearAuth) convex.clearAuth();
516
768
  return;
517
769
  }
518
770
 
519
771
  // SPA mode.
520
- try {
521
- await convex.action("auth:signOut" as any, {});
522
- } catch {
523
- // Already signed out is fine.
524
- }
772
+ await Fx.run(
773
+ Fx.from({
774
+ ok: () => convex.action(requireApiRefs().signOut, {}),
775
+ err: () => undefined,
776
+ }).pipe(Fx.recover(() => Fx.succeed(undefined))),
777
+ );
525
778
  await setToken({ shouldStore: true, tokens: null });
526
779
  if (convex.clearAuth) convex.clearAuth();
527
780
  };
@@ -540,26 +793,71 @@ export function client(options: ClientOptions) {
540
793
  if (proxy) {
541
794
  // Proxy mode: POST to the proxy to refresh.
542
795
  // The proxy reads the real refresh token from the httpOnly cookie.
796
+ const resolvedProxyUrl = await resolveProxyUrl();
797
+ if (
798
+ typeof window === "undefined" &&
799
+ !(await isAbsoluteUrl(resolvedProxyUrl))
800
+ ) {
801
+ finalizeLoadingState();
802
+ return token;
803
+ }
804
+
543
805
  const tokenBeforeRefresh = token;
544
806
  return await browserMutex("__convexAuthProxyRefresh", async () => {
545
807
  // Another tab/call may have already refreshed.
546
808
  if (token !== tokenBeforeRefresh) return token;
547
- try {
548
- const result = await proxyFetch({
549
- action: "auth:signIn",
550
- args: { refreshToken: true },
551
- });
552
- if (result.tokens) {
553
- await setToken({
554
- shouldStore: false,
555
- tokens: { token: result.tokens.token },
556
- });
557
- } else {
558
- await setToken({ shouldStore: false, tokens: null });
559
- }
560
- } catch {
561
- await setToken({ shouldStore: false, tokens: null });
562
- }
809
+
810
+ const proxyRefreshRetryPolicy = Fx.retry.while(
811
+ Fx.retry.compose(
812
+ Fx.retry.jittered(Fx.retry.exponential(RETRY_BASE_MS)),
813
+ Fx.retry.recurs(RETRY_MAX_RETRIES),
814
+ ),
815
+ (meta) => isRetriableProxyRefreshError(meta.input),
816
+ );
817
+
818
+ await Fx.run(
819
+ Fx.from({
820
+ ok: () =>
821
+ proxyFetch({
822
+ action: "auth:signIn",
823
+ args: { refreshToken: true },
824
+ }),
825
+ err: (e) => e,
826
+ }).pipe(
827
+ Fx.retry(proxyRefreshRetryPolicy),
828
+ Fx.chain((result: any) =>
829
+ Fx.from({
830
+ ok: async () => {
831
+ if (result.tokens) {
832
+ await setToken({
833
+ shouldStore: false,
834
+ tokens: { token: result.tokens.token },
835
+ resyncConvexAuth: false,
836
+ });
837
+ } else {
838
+ await setToken({
839
+ shouldStore: false,
840
+ tokens: null,
841
+ resyncConvexAuth: false,
842
+ });
843
+ }
844
+ },
845
+ err: (e) => e,
846
+ }),
847
+ ),
848
+ Fx.inspect((error) =>
849
+ Fx.sync(() =>
850
+ console.error("[convex-auth] Proxy refresh failed:", error),
851
+ ),
852
+ ),
853
+ Fx.recover(() => {
854
+ if (token === null) {
855
+ finalizeLoadingState();
856
+ }
857
+ return Fx.succeed(undefined);
858
+ }),
859
+ ),
860
+ );
563
861
  return token;
564
862
  });
565
863
  }
@@ -574,9 +872,13 @@ export function client(options: ClientOptions) {
574
872
  const refreshToken =
575
873
  (await storageGet(REFRESH_TOKEN_STORAGE_KEY)) ?? null;
576
874
  if (!refreshToken) {
875
+ finalizeLoadingState();
577
876
  return null;
578
877
  }
579
- await verifyCodeAndSetToken({ refreshToken });
878
+ await verifyCodeAndSetToken(
879
+ { refreshToken },
880
+ { resyncConvexAuth: false },
881
+ );
580
882
  return token;
581
883
  });
582
884
  };
@@ -591,14 +893,32 @@ export function client(options: ClientOptions) {
591
893
  const code = new URLSearchParams(window.location.search).get("code");
592
894
  if (!code) return;
593
895
  handlingCodeFlow = true;
594
- const codeUrl = new URL(window.location.href);
595
- codeUrl.searchParams.delete("code");
596
- try {
597
- await replaceURL(codeUrl.pathname + codeUrl.search + codeUrl.hash);
598
- await signIn(undefined, { code });
599
- } finally {
600
- handlingCodeFlow = false;
601
- }
896
+ await Fx.run(
897
+ Fx.from({
898
+ ok: async () => {
899
+ await signIn(undefined, { code });
900
+ const codeUrl = new URL(window.location.href);
901
+ codeUrl.searchParams.delete("code");
902
+ await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
903
+ },
904
+ err: (e) => e,
905
+ }).pipe(
906
+ Fx.recover(() => Fx.succeed(undefined)),
907
+ Fx.tap(() =>
908
+ Fx.sync(() => {
909
+ handlingCodeFlow = false;
910
+ }),
911
+ ),
912
+ Fx.inspect(() =>
913
+ Fx.sync(() => {
914
+ handlingCodeFlow = false;
915
+ }),
916
+ ),
917
+ ),
918
+ );
919
+ // The flag is always reset — Fx.recover above ensures success path,
920
+ // but reset defensively here too.
921
+ handlingCodeFlow = false;
602
922
  };
603
923
 
604
924
  // ---------------------------------------------------------------------------
@@ -643,636 +963,124 @@ export function client(options: ClientOptions) {
643
963
 
644
964
  // Cross-tab sync via storage events (SPA mode only).
645
965
  if (!proxy && typeof window !== "undefined") {
966
+ const registryKey = key(JWT_STORAGE_KEY);
967
+ const registry = getStorageListenerRegistry();
968
+ const existingListener = registry[registryKey];
969
+ if (existingListener !== undefined) {
970
+ window.removeEventListener("storage", existingListener);
971
+ }
972
+
646
973
  const onStorage = (event: StorageEvent) => {
647
- void (async () => {
974
+ Fx.detach(async () => {
648
975
  if (event.key !== key(JWT_STORAGE_KEY)) return;
649
976
  await setToken({
650
977
  shouldStore: false,
651
- tokens:
652
- event.newValue === null ? null : { token: event.newValue },
978
+ tokens: event.newValue === null ? null : { token: event.newValue },
653
979
  });
654
- })();
980
+ }, "[convex-auth] Storage event handler failed:");
655
981
  };
656
982
  window.addEventListener("storage", onStorage);
983
+ registry[registryKey] = onStorage;
984
+ disposeStorageListener = () => {
985
+ if (registry[registryKey] === onStorage) {
986
+ delete registry[registryKey];
987
+ }
988
+ window.removeEventListener("storage", onStorage);
989
+ };
657
990
  }
658
991
 
659
992
  // Auto-wire: feed our tokens into the Convex client so
660
993
  // queries and mutations are automatically authenticated.
661
- convex.setAuth(fetchAccessToken);
994
+ bindConvexAuth();
662
995
 
663
996
  // Auto-hydrate and handle code flow.
664
997
  if (typeof window !== "undefined") {
665
998
  if (proxy) {
666
- // Proxy mode: if no initialToken was provided, try a refresh
667
- // to pick up any existing session from httpOnly cookies.
999
+ // Proxy mode: eagerly resolve auth once on startup so routes that only
1000
+ // read auth state (and do not issue Convex queries yet) don't stay in
1001
+ // the initial loading phase.
668
1002
  if (!hasServerToken) {
669
- void fetchAccessToken({ forceRefreshToken: true });
670
- } else {
671
- // initialToken already set — mark loading as done.
672
- isLoading = false;
673
- updateSnapshot();
1003
+ Fx.detach(
1004
+ () => fetchAccessToken({ forceRefreshToken: true }),
1005
+ "[convex-auth] Proxy token refresh failed:",
1006
+ );
674
1007
  }
675
1008
  } else {
676
1009
  // SPA mode: hydrate from localStorage, then handle OAuth code flow.
677
- void hydrateFromStorage().then(() =>
678
- handleCodeFlow().catch((error: unknown) => {
679
- console.error("[convex-auth] Code exchange failed:", error);
680
- }),
681
- );
1010
+ Fx.detach(async () => {
1011
+ await Fx.run(
1012
+ Fx.from({
1013
+ ok: async () => {
1014
+ await hydrateFromStorage();
1015
+ await handleCodeFlow();
1016
+ },
1017
+ err: (e) => e,
1018
+ }).pipe(
1019
+ Fx.inspect((error) =>
1020
+ Fx.sync(() =>
1021
+ console.error(
1022
+ "[convex-auth] Client initialization failed:",
1023
+ error,
1024
+ ),
1025
+ ),
1026
+ ),
1027
+ Fx.recover((_error) =>
1028
+ Fx.from({
1029
+ ok: () => setToken({ shouldStore: false, tokens: null }),
1030
+ err: (e) => e,
1031
+ }).pipe(Fx.recover(() => Fx.succeed(undefined))),
1032
+ ),
1033
+ ),
1034
+ );
1035
+ }, "[convex-auth] SPA initialization failed:");
682
1036
  }
683
1037
  }
684
1038
 
685
1039
  // ---------------------------------------------------------------------------
686
- // Passkey helpers
1040
+ // Auth factor helpers
687
1041
  // ---------------------------------------------------------------------------
688
1042
 
689
- /**
690
- * Base64url encode/decode helpers for the WebAuthn credential API.
691
- * These run client-side only (browser context).
692
- */
693
- const base64urlEncode = (buffer: ArrayBuffer): string => {
694
- const bytes = new Uint8Array(buffer);
695
- let binary = "";
696
- for (let i = 0; i < bytes.byteLength; i++) {
697
- binary += String.fromCharCode(bytes[i]!);
698
- }
699
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
700
- };
701
-
702
- const base64urlDecode = (str: string): Uint8Array => {
703
- const padded = str.replace(/-/g, "+").replace(/_/g, "/");
704
- const binary = atob(padded);
705
- const bytes = new Uint8Array(binary.length);
706
- for (let i = 0; i < binary.length; i++) {
707
- bytes[i] = binary.charCodeAt(i);
708
- }
709
- return bytes;
710
- };
711
-
712
- const passkey = {
713
- /**
714
- * Check if WebAuthn passkeys are supported in the current environment.
715
- */
716
- isSupported: (): boolean => {
717
- return (
718
- typeof window !== "undefined" &&
719
- typeof window.PublicKeyCredential !== "undefined"
720
- );
721
- },
722
-
723
- /**
724
- * Check if conditional UI (autofill-assisted passkey sign-in) is supported.
725
- *
726
- * ```ts
727
- * if (await auth.passkey.isAutofillSupported()) {
728
- * auth.passkey.authenticate({ autofill: true });
729
- * }
730
- * ```
731
- */
732
- isAutofillSupported: async (): Promise<boolean> => {
733
- if (typeof window === "undefined") return false;
734
- if (typeof window.PublicKeyCredential === "undefined") return false;
735
- if (
736
- typeof (
737
- window.PublicKeyCredential as any
738
- ).isConditionalMediationAvailable !== "function"
739
- ) {
740
- return false;
741
- }
742
- return (
743
- window.PublicKeyCredential as any
744
- ).isConditionalMediationAvailable();
745
- },
746
-
747
- /**
748
- * Register a new passkey for the current or new user.
749
- *
750
- * Performs the full two-round-trip WebAuthn registration ceremony:
751
- * 1. Requests creation options from the server (challenge, RP info)
752
- * 2. Calls `navigator.credentials.create()` with the options
753
- * 3. Sends the attestation back to the server for verification
754
- * 4. Server creates user + account + passkey records and returns tokens
755
- *
756
- * Works in both SPA and proxy (SSR) modes.
757
- *
758
- * ```ts
759
- * await auth.passkey.register({ name: "MacBook Touch ID" });
760
- * ```
761
- *
762
- * @param opts.name - Friendly name for this passkey
763
- * @param opts.email - Email to associate with the new account
764
- * @param opts.userName - Username for the credential (defaults to email)
765
- * @param opts.userDisplayName - Display name for the credential
766
- * @returns `{ signingIn: true }` on success
767
- */
768
- register: async (
769
- opts?: {
770
- name?: string;
771
- email?: string;
772
- userName?: string;
773
- userDisplayName?: string;
774
- },
775
- ): Promise<SignInResult> => {
776
- const phase1Params = {
777
- flow: "register-options",
778
- email: opts?.email,
779
- userName: opts?.userName,
780
- userDisplayName: opts?.userDisplayName,
781
- };
782
-
783
- // Phase 1: Get registration options from server
784
- let phase1Result: any;
785
- if (proxy) {
786
- phase1Result = await proxyFetch({
787
- action: "auth:signIn",
788
- args: { provider: "passkey", params: phase1Params },
789
- });
790
- } else {
791
- phase1Result = await convex.action("auth:signIn" as any, {
792
- provider: "passkey",
793
- params: phase1Params,
794
- });
795
- }
796
-
797
- if (!phase1Result.options) {
798
- throw new Error("Server did not return passkey registration options");
799
- }
800
-
801
- const options = phase1Result.options;
802
-
803
- // Convert base64url strings to ArrayBuffers for the credential API
804
- const createOptions: CredentialCreationOptions = {
805
- publicKey: {
806
- rp: options.rp,
807
- user: {
808
- id: base64urlDecode(options.user.id).buffer as ArrayBuffer,
809
- name: options.user.name,
810
- displayName: options.user.displayName,
811
- },
812
- challenge: base64urlDecode(options.challenge).buffer as ArrayBuffer,
813
- pubKeyCredParams: options.pubKeyCredParams,
814
- timeout: options.timeout,
815
- attestation: options.attestation,
816
- authenticatorSelection: options.authenticatorSelection,
817
- excludeCredentials: (options.excludeCredentials ?? []).map(
818
- (cred: any) => ({
819
- type: cred.type ?? "public-key",
820
- id: base64urlDecode(cred.id).buffer as ArrayBuffer,
821
- transports: cred.transports,
822
- }),
823
- ),
824
- },
825
- };
826
-
827
- // Phase 2: Create credential via browser API
828
- const credential = (await navigator.credentials.create(
829
- createOptions,
830
- )) as PublicKeyCredential | null;
831
- if (!credential) {
832
- throw new Error("Passkey registration was cancelled");
833
- }
834
-
835
- const response =
836
- credential.response as AuthenticatorAttestationResponse;
837
-
838
- // Extract transports if available
839
- const transports =
840
- typeof response.getTransports === "function"
841
- ? response.getTransports()
842
- : undefined;
843
-
844
- const phase2Params = {
845
- flow: "register-verify",
846
- clientDataJSON: base64urlEncode(response.clientDataJSON),
847
- attestationObject: base64urlEncode(response.attestationObject),
848
- transports,
849
- passkeyName: opts?.name,
850
- email: opts?.email,
851
- };
852
-
853
- // Phase 3: Send attestation to server for verification
854
- let phase2Result: any;
855
- if (proxy) {
856
- // In proxy mode the verifier is stored in an httpOnly cookie by the proxy.
857
- // We pass it back explicitly so the proxy can forward it to Convex.
858
- phase2Result = await proxyFetch({
859
- action: "auth:signIn",
860
- args: {
861
- provider: "passkey",
862
- params: phase2Params,
863
- verifier: phase1Result.verifier,
864
- },
865
- });
866
- } else {
867
- phase2Result = await convex.action("auth:signIn" as any, {
868
- provider: "passkey",
869
- params: phase2Params,
870
- verifier: phase1Result.verifier,
871
- });
872
- }
873
-
874
- if (phase2Result.tokens) {
875
- if (proxy) {
876
- await setToken({
877
- shouldStore: false,
878
- tokens:
879
- phase2Result.tokens === null
880
- ? null
881
- : { token: phase2Result.tokens.token },
882
- });
883
- } else {
884
- await setToken({
885
- shouldStore: true,
886
- tokens: phase2Result.tokens as AuthSession,
887
- });
888
- }
889
- return { signingIn: true };
890
- }
891
- return { signingIn: false };
892
- },
893
-
894
- /**
895
- * Authenticate with an existing passkey.
896
- *
897
- * Performs the full two-round-trip WebAuthn authentication ceremony:
898
- * 1. Requests assertion options from the server (challenge, allowed credentials)
899
- * 2. Calls `navigator.credentials.get()` with the options
900
- * 3. Sends the assertion back to the server for signature verification
901
- * 4. Server verifies signature, updates counter, creates session, returns tokens
902
- *
903
- * Works in both SPA and proxy (SSR) modes.
904
- *
905
- * ```ts
906
- * // Discoverable credential (no email needed)
907
- * await auth.passkey.authenticate();
908
- *
909
- * // Scoped to a specific user's credentials
910
- * await auth.passkey.authenticate({ email: "user@example.com" });
911
- *
912
- * // Autofill-assisted (conditional UI)
913
- * await auth.passkey.authenticate({ autofill: true });
914
- * ```
915
- *
916
- * @param opts.email - Scope to credentials for this email's user
917
- * @param opts.autofill - Use conditional mediation (autofill UI)
918
- * @returns `{ signingIn: true }` on success
919
- */
920
- authenticate: async (
921
- opts?: { email?: string; autofill?: boolean },
922
- ): Promise<SignInResult> => {
923
- const phase1Params = {
924
- flow: "auth-options",
925
- email: opts?.email,
926
- };
927
-
928
- // Phase 1: Get assertion options from server
929
- let phase1Result: any;
930
- if (proxy) {
931
- phase1Result = await proxyFetch({
932
- action: "auth:signIn",
933
- args: { provider: "passkey", params: phase1Params },
934
- });
935
- } else {
936
- phase1Result = await convex.action("auth:signIn" as any, {
937
- provider: "passkey",
938
- params: phase1Params,
939
- });
940
- }
941
-
942
- if (!phase1Result.options) {
943
- throw new Error("Server did not return passkey authentication options");
944
- }
945
-
946
- const options = phase1Result.options;
947
-
948
- // Convert base64url strings to ArrayBuffers for the credential API
949
- const getOptions: CredentialRequestOptions = {
950
- publicKey: {
951
- challenge: base64urlDecode(options.challenge).buffer as ArrayBuffer,
952
- timeout: options.timeout,
953
- rpId: options.rpId,
954
- userVerification: options.userVerification,
955
- allowCredentials: (options.allowCredentials ?? []).map(
956
- (cred: any) => ({
957
- type: cred.type ?? "public-key",
958
- id: base64urlDecode(cred.id).buffer as ArrayBuffer,
959
- transports: cred.transports,
960
- }),
961
- ),
962
- },
963
- ...(opts?.autofill ? { mediation: "conditional" as any } : {}),
964
- };
965
-
966
- // Phase 2: Get credential via browser API
967
- const credential = (await navigator.credentials.get(
968
- getOptions,
969
- )) as PublicKeyCredential | null;
970
- if (!credential) {
971
- throw new Error("Passkey authentication was cancelled");
972
- }
973
-
974
- const response =
975
- credential.response as AuthenticatorAssertionResponse;
976
-
977
- const phase2Params = {
978
- flow: "auth-verify",
979
- credentialId: base64urlEncode(credential.rawId),
980
- clientDataJSON: base64urlEncode(response.clientDataJSON),
981
- authenticatorData: base64urlEncode(response.authenticatorData),
982
- signature: base64urlEncode(response.signature),
983
- };
984
-
985
- // Phase 3: Send assertion to server for verification
986
- let phase2Result: any;
987
- if (proxy) {
988
- phase2Result = await proxyFetch({
989
- action: "auth:signIn",
990
- args: {
991
- provider: "passkey",
992
- params: phase2Params,
993
- verifier: phase1Result.verifier,
994
- },
995
- });
996
- } else {
997
- phase2Result = await convex.action("auth:signIn" as any, {
998
- provider: "passkey",
999
- params: phase2Params,
1000
- verifier: phase1Result.verifier,
1001
- });
1002
- }
1003
-
1004
- if (phase2Result.tokens) {
1005
- if (proxy) {
1006
- await setToken({
1007
- shouldStore: false,
1008
- tokens:
1009
- phase2Result.tokens === null
1010
- ? null
1011
- : { token: phase2Result.tokens.token },
1012
- });
1013
- } else {
1014
- await setToken({
1015
- shouldStore: true,
1016
- tokens: phase2Result.tokens as AuthSession,
1017
- });
1018
- }
1019
- return { signingIn: true };
1020
- }
1021
- return { signingIn: false };
1022
- },
1023
- };
1024
-
1025
- const totp = {
1026
- /**
1027
- * Start TOTP enrollment. Must be authenticated.
1028
- *
1029
- * Returns a URI for QR code display and a base32 secret for manual entry.
1030
- *
1031
- * ```ts
1032
- * const setup = await auth.totp.setup();
1033
- * // Display QR code from setup.uri
1034
- * // Or show setup.secret for manual entry
1035
- * ```
1036
- */
1037
- setup: async (
1038
- opts?: { name?: string; accountName?: string },
1039
- ): Promise<{ uri: string; secret: string; verifier: string; totpId: string }> => {
1040
- const params: Record<string, any> = { flow: "setup" };
1041
- if (opts?.name) params.name = opts.name;
1042
- if (opts?.accountName) params.accountName = opts.accountName;
1043
-
1044
- if (proxy) {
1045
- const result = await proxyFetch({
1046
- action: "auth:signIn",
1047
- args: { provider: "totp", params },
1048
- });
1049
- return { uri: result.totpSetup.uri, secret: result.totpSetup.secret, verifier: result.verifier, totpId: result.totpSetup.totpId };
1050
- }
1051
-
1052
- const result = await convex.action("auth:signIn" as any, {
1053
- provider: "totp",
1054
- params,
1055
- });
1056
- return { uri: result.totpSetup.uri, secret: result.totpSetup.secret, verifier: result.verifier, totpId: result.totpSetup.totpId };
1057
- },
1058
-
1059
- /**
1060
- * Complete TOTP enrollment by verifying the first code from the authenticator app.
1061
- *
1062
- * ```ts
1063
- * await auth.totp.confirm({ code: "123456", verifier: setup.verifier, totpId: setup.totpId });
1064
- * ```
1065
- */
1066
- confirm: async (opts: {
1067
- code: string;
1068
- verifier: string;
1069
- totpId: string;
1070
- }): Promise<void> => {
1071
- const params: Record<string, any> = {
1072
- flow: "confirm",
1073
- code: opts.code,
1074
- totpId: opts.totpId,
1075
- };
1076
-
1077
- if (proxy) {
1078
- const result = await proxyFetch({
1079
- action: "auth:signIn",
1080
- args: { provider: "totp", params, verifier: opts.verifier },
1081
- });
1082
- if (result.tokens) {
1083
- await setToken({
1084
- shouldStore: false,
1085
- tokens: result.tokens === null ? null : { token: result.tokens.token },
1086
- });
1087
- }
1088
- return;
1089
- }
1090
-
1091
- const result = await convex.action("auth:signIn" as any, {
1092
- provider: "totp",
1093
- params,
1094
- verifier: opts.verifier,
1095
- });
1096
- if (result.tokens) {
1097
- await setToken({
1098
- shouldStore: true,
1099
- tokens: (result.tokens as AuthSession | null) ?? null,
1100
- });
1101
- }
1102
- },
1103
-
1104
- /**
1105
- * Complete 2FA verification during sign-in.
1106
- *
1107
- * Called after a credentials sign-in returns `totpRequired: true`.
1108
- *
1109
- * ```ts
1110
- * const result = await auth.signIn("password", { email, password });
1111
- * if (result.totpRequired) {
1112
- * await auth.totp.verify({ code: "123456", verifier: result.verifier! });
1113
- * }
1114
- * ```
1115
- */
1116
- verify: async (opts: { code: string; verifier: string }): Promise<void> => {
1117
- const params: Record<string, any> = {
1118
- flow: "verify",
1119
- code: opts.code,
1120
- };
1121
-
1122
- if (proxy) {
1123
- const result = await proxyFetch({
1124
- action: "auth:signIn",
1125
- args: { provider: "totp", params, verifier: opts.verifier },
1126
- });
1127
- if (result.tokens) {
1128
- await setToken({
1129
- shouldStore: false,
1130
- tokens: result.tokens === null ? null : { token: result.tokens.token },
1131
- });
1132
- }
1133
- return;
1134
- }
1135
-
1136
- const result = await convex.action("auth:signIn" as any, {
1137
- provider: "totp",
1138
- params,
1139
- verifier: opts.verifier,
1140
- });
1141
- if (result.tokens) {
1142
- await setToken({
1143
- shouldStore: true,
1144
- tokens: (result.tokens as AuthSession | null) ?? null,
1145
- });
1146
- }
1147
- },
1148
- };
1149
-
1150
- const device = {
1151
- /**
1152
- * Poll for device authorization status.
1153
- *
1154
- * The device calls this repeatedly (respecting `interval`) after
1155
- * initiating a device flow via `signIn("device")`. Returns when
1156
- * the user authorizes, or throws on timeout/denial.
1157
- *
1158
- * ```ts
1159
- * const result = await auth.signIn("device");
1160
- * const { deviceCode } = result;
1161
- * // Display deviceCode.userCode to the user, then poll:
1162
- * await auth.device.poll(deviceCode);
1163
- * // User is now signed in
1164
- * ```
1165
- *
1166
- * @param code - The {@link DeviceCodeResult} from `signIn("device")`.
1167
- * @returns Resolves when the device is authorized and tokens are stored.
1168
- * @throws When the code expires, is denied, or polling encounters an error.
1169
- */
1170
- poll: async (code: DeviceCodeResult): Promise<void> => {
1171
- const intervalMs = code.interval * 1000;
1172
- const expiresAt = Date.now() + code.expiresIn * 1000;
1173
-
1174
- while (Date.now() < expiresAt) {
1175
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
1176
-
1177
- try {
1178
- let result: any;
1179
- const params: Record<string, any> = {
1180
- flow: "poll",
1181
- deviceCode: code.deviceCode,
1182
- };
1183
-
1184
- if (proxy) {
1185
- result = await proxyFetch({
1186
- action: "auth:signIn",
1187
- args: { provider: "device", params },
1188
- });
1189
- } else {
1190
- result = await convex.action("auth:signIn" as any, {
1191
- provider: "device",
1192
- params,
1193
- });
1194
- }
1195
-
1196
- // Authorized — tokens received
1197
- if (result.tokens) {
1198
- if (proxy) {
1199
- await setToken({
1200
- shouldStore: false,
1201
- tokens:
1202
- result.tokens === null
1203
- ? null
1204
- : { token: result.tokens.token },
1205
- });
1206
- } else {
1207
- await setToken({
1208
- shouldStore: true,
1209
- tokens: (result.tokens as AuthSession | null) ?? null,
1210
- });
1211
- }
1212
- return;
1213
- }
1214
- } catch (e: unknown) {
1215
- // Handle expected polling errors
1216
- if (e instanceof ConvexError) {
1217
- const data = e.data as Record<string, unknown>;
1218
- const code_ = data?.code as string | undefined;
1219
- if (code_ === "DEVICE_AUTHORIZATION_PENDING") {
1220
- continue; // Keep polling
1221
- }
1222
- if (code_ === "DEVICE_SLOW_DOWN") {
1223
- // Back off by adding one interval
1224
- await new Promise((resolve) =>
1225
- setTimeout(resolve, intervalMs),
1226
- );
1227
- continue;
1228
- }
1229
- }
1230
- // Non-recoverable error — rethrow
1231
- throw e;
1232
- }
1233
- }
1234
-
1235
- throw new Error("Device authorization timed out.");
1236
- },
1043
+ const passkey = createPasskeyClient({
1044
+ proxy,
1045
+ convex,
1046
+ requireApiRefs,
1047
+ proxyFetch,
1048
+ setTokenAndMaybeWait,
1049
+ });
1237
1050
 
1238
- /**
1239
- * Authorize a device from the verification page.
1240
- *
1241
- * Called by an authenticated user on the verification page after
1242
- * they enter the user code displayed on the device.
1243
- *
1244
- * ```ts
1245
- * // On the /device verification page:
1246
- * await auth.device.verify(userCode);
1247
- * ```
1248
- *
1249
- * @param userCode - The user code entered by the user (e.g. "WDJB-MJHT").
1250
- */
1251
- verify: async (userCode: string): Promise<void> => {
1252
- const params: Record<string, any> = {
1253
- flow: "verify",
1254
- userCode,
1255
- };
1051
+ const totp = createTotpClient({
1052
+ proxy,
1053
+ convex,
1054
+ requireApiRefs,
1055
+ proxyFetch,
1056
+ setTokenAndMaybeWait,
1057
+ });
1256
1058
 
1257
- if (proxy) {
1258
- await proxyFetch({
1259
- action: "auth:signIn",
1260
- args: { provider: "device", params },
1261
- });
1262
- } else {
1263
- await convex.action("auth:signIn" as any, {
1264
- provider: "device",
1265
- params,
1266
- });
1267
- }
1268
- },
1269
- };
1059
+ const device = createDeviceClient({
1060
+ proxy,
1061
+ convex,
1062
+ requireApiRefs,
1063
+ proxyFetch,
1064
+ setTokenAndMaybeWait,
1065
+ });
1270
1066
 
1271
1067
  return {
1272
1068
  /** Current auth state snapshot. */
1273
1069
  get state(): AuthState {
1274
1070
  return snapshot;
1275
1071
  },
1072
+ /** SSR-safe URL param reader. */
1073
+ param,
1074
+ /** Pending invite from URL or recovered from storage. Null if none. */
1075
+ get invite(): PendingInvite | null {
1076
+ const pendingInvite = getPendingInvite();
1077
+ if (!pendingInvite) return null;
1078
+ return {
1079
+ token: pendingInvite.token,
1080
+ email: pendingInvite.email,
1081
+ accept: acceptInvite,
1082
+ };
1083
+ },
1276
1084
  /** Sign in with a provider. See {@link SignInResult} for return shape. */
1277
1085
  signIn,
1278
1086
  /** Sign out and clear all token state. */
@@ -1285,94 +1093,42 @@ export function client(options: ClientOptions) {
1285
1093
  totp,
1286
1094
  /** Device authorization (RFC 8628) helpers. */
1287
1095
  device,
1288
- };
1289
- }
1290
-
1291
- // ---------------------------------------------------------------------------
1292
- // Browser mutex ensures only one tab refreshes a token at a time.
1293
- // ---------------------------------------------------------------------------
1294
-
1295
- async function browserMutex<T>(
1296
- key: string,
1297
- callback: () => Promise<T>,
1298
- ): Promise<T> {
1299
- const lockManager = (globalThis as any)?.navigator?.locks;
1300
- return lockManager !== undefined
1301
- ? await lockManager.request(key, callback)
1302
- : await manualMutex(key, callback);
1303
- }
1304
-
1305
- function getMutexValue(key: string): {
1306
- currentlyRunning: Promise<void> | null;
1307
- waiting: Array<() => Promise<void>>;
1308
- } {
1309
- if ((globalThis as any).__convexAuthMutexes === undefined) {
1310
- (globalThis as any).__convexAuthMutexes = {} as Record<
1311
- string,
1312
- {
1313
- currentlyRunning: Promise<void> | null;
1314
- waiting: Array<() => Promise<void>>;
1315
- }
1316
- >;
1317
- }
1318
- let mutex = (globalThis as any).__convexAuthMutexes[key];
1319
- if (mutex === undefined) {
1320
- (globalThis as any).__convexAuthMutexes[key] = {
1321
- currentlyRunning: null,
1322
- waiting: [],
1323
- };
1324
- }
1325
- mutex = (globalThis as any).__convexAuthMutexes[key];
1326
- return mutex;
1327
- }
1328
-
1329
- function setMutexValue(
1330
- key: string,
1331
- value: {
1332
- currentlyRunning: Promise<void> | null;
1333
- waiting: Array<() => Promise<void>>;
1334
- },
1335
- ) {
1336
- (globalThis as any).__convexAuthMutexes[key] = value;
1337
- }
1338
-
1339
- async function enqueueCallbackForMutex(
1340
- key: string,
1341
- callback: () => Promise<void>,
1342
- ) {
1343
- const mutex = getMutexValue(key);
1344
- if (mutex.currentlyRunning === null) {
1345
- setMutexValue(key, {
1346
- currentlyRunning: callback().finally(() => {
1347
- const nextCb = getMutexValue(key).waiting.shift();
1348
- getMutexValue(key).currentlyRunning = null;
1349
- setMutexValue(key, {
1350
- ...getMutexValue(key),
1351
- currentlyRunning:
1352
- nextCb === undefined ? null : enqueueCallbackForMutex(key, nextCb),
1353
- });
1354
- }),
1355
- waiting: [],
1356
- });
1357
- } else {
1358
- setMutexValue(key, {
1359
- ...mutex,
1360
- waiting: [...mutex.waiting, callback],
1361
- });
1362
- }
1363
- }
1364
-
1365
- async function manualMutex<T>(
1366
- key: string,
1367
- callback: () => Promise<T>,
1368
- ): Promise<T> {
1369
- const outerPromise = new Promise<T>((resolve, reject) => {
1370
- const wrappedCallback: () => Promise<void> = () => {
1371
- return callback()
1372
- .then((v) => resolve(v))
1373
- .catch((e) => reject(e));
1374
- };
1375
- void enqueueCallbackForMutex(key, wrappedCallback);
1376
- });
1377
- return outerPromise;
1096
+ /**
1097
+ * Tear down this auth client instance.
1098
+ *
1099
+ * Removes the cross-tab `storage` event listener, clears all
1100
+ * `onChange` subscribers, and rejects any in-flight handshake
1101
+ * waiters. Call this when the client is no longer needed
1102
+ * (e.g. on SPA unmount or hot-module replacement) to prevent
1103
+ * memory leaks and stale callbacks.
1104
+ *
1105
+ * @example
1106
+ * ```ts
1107
+ * // SvelteKit onDestroy
1108
+ * import { onDestroy } from "svelte";
1109
+ * const auth = client({ convex, api: api.auth });
1110
+ * onDestroy(() => auth.destroy());
1111
+ * ```
1112
+ *
1113
+ * @example
1114
+ * ```ts
1115
+ * const unsubscribe = auth.onChange((state) => console.log(state.phase));
1116
+ *
1117
+ * // Later, during cleanup:
1118
+ * unsubscribe();
1119
+ * auth.destroy();
1120
+ * ```
1121
+ */
1122
+ destroy: () => {
1123
+ destroyed = true;
1124
+ settleHandshakeWaiters(authEpoch, {
1125
+ type: "reject",
1126
+ error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
1127
+ reason: "destroyed",
1128
+ }),
1129
+ });
1130
+ disposeStorageListener?.();
1131
+ subscribers.clear();
1132
+ },
1133
+ } as AuthClient<Api>;
1378
1134
  }