@robelest/convex-auth 0.0.4-preview.18 → 0.0.4-preview.19

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 (541) hide show
  1. package/README.md +6 -3
  2. package/dist/authorization/index.d.ts +46 -0
  3. package/dist/authorization/index.d.ts.map +1 -1
  4. package/dist/authorization/index.js +46 -0
  5. package/dist/authorization/index.js.map +1 -1
  6. package/dist/bin.cjs +21 -13
  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 -16
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +67 -565
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +40 -12
  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 +1306 -1344
  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 -2
  20. package/dist/component/model.d.ts +80 -80
  21. package/dist/component/model.d.ts.map +1 -1
  22. package/dist/component/model.js +5 -1
  23. package/dist/component/model.js.map +1 -1
  24. package/dist/component/providers/credentials.d.ts +3 -2
  25. package/dist/component/providers/credentials.d.ts.map +1 -1
  26. package/dist/component/providers/email.d.ts +30 -0
  27. package/dist/component/providers/email.d.ts.map +1 -1
  28. package/dist/component/providers/phone.d.ts +28 -0
  29. package/dist/component/providers/phone.d.ts.map +1 -1
  30. package/dist/component/public/enterprise/audit.d.ts +73 -0
  31. package/dist/component/public/enterprise/audit.d.ts.map +1 -0
  32. package/dist/component/public/enterprise/audit.js +108 -0
  33. package/dist/component/public/enterprise/audit.js.map +1 -0
  34. package/dist/component/public/enterprise/core.d.ts +176 -0
  35. package/dist/component/public/enterprise/core.d.ts.map +1 -0
  36. package/dist/component/public/enterprise/core.js +292 -0
  37. package/dist/component/public/enterprise/core.js.map +1 -0
  38. package/dist/component/public/enterprise/domains.d.ts +174 -0
  39. package/dist/component/public/enterprise/domains.d.ts.map +1 -0
  40. package/dist/component/public/enterprise/domains.js +271 -0
  41. package/dist/component/public/enterprise/domains.js.map +1 -0
  42. package/dist/component/public/enterprise/scim.d.ts +245 -0
  43. package/dist/component/public/enterprise/scim.d.ts.map +1 -0
  44. package/dist/component/public/enterprise/scim.js +344 -0
  45. package/dist/component/public/enterprise/scim.js.map +1 -0
  46. package/dist/component/public/enterprise/secrets.d.ts +78 -0
  47. package/dist/component/public/enterprise/secrets.d.ts.map +1 -0
  48. package/dist/component/public/enterprise/secrets.js +118 -0
  49. package/dist/component/public/enterprise/secrets.js.map +1 -0
  50. package/dist/component/public/enterprise/webhooks.d.ts +211 -0
  51. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -0
  52. package/dist/component/public/enterprise/webhooks.js +300 -0
  53. package/dist/component/public/enterprise/webhooks.js.map +1 -0
  54. package/dist/component/public/factors/devices.d.ts +157 -0
  55. package/dist/component/public/factors/devices.d.ts.map +1 -0
  56. package/dist/component/public/factors/devices.js +216 -0
  57. package/dist/component/public/factors/devices.js.map +1 -0
  58. package/dist/component/public/factors/passkeys.d.ts +175 -0
  59. package/dist/component/public/factors/passkeys.d.ts.map +1 -0
  60. package/dist/component/public/factors/passkeys.js +238 -0
  61. package/dist/component/public/factors/passkeys.js.map +1 -0
  62. package/dist/component/public/factors/totp.d.ts +189 -0
  63. package/dist/component/public/factors/totp.d.ts.map +1 -0
  64. package/dist/component/public/factors/totp.js +254 -0
  65. package/dist/component/public/factors/totp.js.map +1 -0
  66. package/dist/component/public/groups/core.d.ts +137 -0
  67. package/dist/component/public/groups/core.d.ts.map +1 -0
  68. package/dist/component/public/groups/core.js +321 -0
  69. package/dist/component/public/groups/core.js.map +1 -0
  70. package/dist/component/public/groups/invites.d.ts +217 -0
  71. package/dist/component/public/groups/invites.d.ts.map +1 -0
  72. package/dist/component/public/groups/invites.js +457 -0
  73. package/dist/component/public/groups/invites.js.map +1 -0
  74. package/dist/component/public/groups/members.d.ts +204 -0
  75. package/dist/component/public/groups/members.d.ts.map +1 -0
  76. package/dist/component/public/groups/members.js +355 -0
  77. package/dist/component/public/groups/members.js.map +1 -0
  78. package/dist/component/public/identity/accounts.d.ts +147 -0
  79. package/dist/component/public/identity/accounts.d.ts.map +1 -0
  80. package/dist/component/public/identity/accounts.js +200 -0
  81. package/dist/component/public/identity/accounts.js.map +1 -0
  82. package/dist/component/public/identity/codes.d.ts +104 -0
  83. package/dist/component/public/identity/codes.d.ts.map +1 -0
  84. package/dist/component/public/identity/codes.js +140 -0
  85. package/dist/component/public/identity/codes.js.map +1 -0
  86. package/dist/component/public/identity/sessions.d.ts +128 -0
  87. package/dist/component/public/identity/sessions.d.ts.map +1 -0
  88. package/dist/component/public/identity/sessions.js +192 -0
  89. package/dist/component/public/identity/sessions.js.map +1 -0
  90. package/dist/component/public/identity/tokens.d.ts +169 -0
  91. package/dist/component/public/identity/tokens.d.ts.map +1 -0
  92. package/dist/component/public/identity/tokens.js +227 -0
  93. package/dist/component/public/identity/tokens.js.map +1 -0
  94. package/dist/component/public/identity/users.d.ts +212 -0
  95. package/dist/component/public/identity/users.d.ts.map +1 -0
  96. package/dist/component/public/identity/users.js +311 -0
  97. package/dist/component/public/identity/users.js.map +1 -0
  98. package/dist/component/public/identity/verifiers.d.ts +116 -0
  99. package/dist/component/public/identity/verifiers.d.ts.map +1 -0
  100. package/dist/component/public/identity/verifiers.js +154 -0
  101. package/dist/component/public/identity/verifiers.js.map +1 -0
  102. package/dist/component/public/security/keys.d.ts +209 -0
  103. package/dist/component/public/security/keys.d.ts.map +1 -0
  104. package/dist/component/public/security/keys.js +319 -0
  105. package/dist/component/public/security/keys.js.map +1 -0
  106. package/dist/component/public/security/limits.d.ts +114 -0
  107. package/dist/component/public/security/limits.d.ts.map +1 -0
  108. package/dist/component/public/security/limits.js +169 -0
  109. package/dist/component/public/security/limits.js.map +1 -0
  110. package/dist/component/public.d.ts +22 -7
  111. package/dist/component/public.js +21 -6
  112. package/dist/component/schema.d.ts +288 -288
  113. package/dist/component/server/auth.d.ts +212 -25
  114. package/dist/component/server/auth.d.ts.map +1 -1
  115. package/dist/component/server/auth.js +68 -4
  116. package/dist/component/server/auth.js.map +1 -1
  117. package/dist/component/server/authError.js +34 -0
  118. package/dist/component/server/authError.js.map +1 -0
  119. package/dist/component/server/{providers.js → config.js} +2 -2
  120. package/dist/component/server/config.js.map +1 -0
  121. package/dist/component/server/{domains/core.js → core.js} +107 -88
  122. package/dist/component/server/core.js.map +1 -0
  123. package/dist/component/server/{provider.js → crypto.js} +3 -3
  124. package/dist/component/server/crypto.js.map +1 -0
  125. package/dist/component/server/device.js +1 -1
  126. package/dist/component/server/device.js.map +1 -1
  127. package/dist/component/server/enterprise/config.js +46 -0
  128. package/dist/component/server/enterprise/config.js.map +1 -0
  129. package/dist/component/server/{domains/sso.js → enterprise/domain.js} +6 -5
  130. package/dist/component/server/enterprise/domain.js.map +1 -0
  131. package/dist/component/server/enterprise/http.js +766 -0
  132. package/dist/component/server/enterprise/http.js.map +1 -0
  133. package/dist/component/server/enterprise/oidc.js +248 -0
  134. package/dist/component/server/enterprise/oidc.js.map +1 -0
  135. package/dist/component/server/enterprise/policy.js +85 -0
  136. package/dist/component/server/enterprise/policy.js.map +1 -0
  137. package/dist/component/server/enterprise/saml.js +338 -0
  138. package/dist/component/server/enterprise/saml.js.map +1 -0
  139. package/dist/component/server/enterprise/scim.js +97 -0
  140. package/dist/component/server/enterprise/scim.js.map +1 -0
  141. package/dist/component/server/enterprise/shared.js +51 -0
  142. package/dist/component/server/enterprise/shared.js.map +1 -0
  143. package/dist/component/server/errors.js.map +1 -1
  144. package/dist/component/server/http.js +2 -1
  145. package/dist/component/server/http.js.map +1 -1
  146. package/dist/component/server/identity.js +1 -1
  147. package/dist/component/server/identity.js.map +1 -1
  148. package/dist/component/server/{ratelimit.js → limits.js} +3 -3
  149. package/dist/component/server/limits.js.map +1 -0
  150. package/dist/component/server/mutations/account.js +3 -3
  151. package/dist/component/server/mutations/account.js.map +1 -1
  152. package/dist/component/server/mutations/code.js +2 -2
  153. package/dist/component/server/mutations/code.js.map +1 -1
  154. package/dist/component/server/mutations/invalidate.js +1 -1
  155. package/dist/component/server/mutations/invalidate.js.map +1 -1
  156. package/dist/component/server/mutations/oauth.js +5 -3
  157. package/dist/component/server/mutations/oauth.js.map +1 -1
  158. package/dist/component/server/mutations/refresh.js +1 -1
  159. package/dist/component/server/mutations/refresh.js.map +1 -1
  160. package/dist/component/server/mutations/register.js +3 -3
  161. package/dist/component/server/mutations/register.js.map +1 -1
  162. package/dist/component/server/mutations/retrieve.js +4 -4
  163. package/dist/component/server/mutations/retrieve.js.map +1 -1
  164. package/dist/component/server/mutations/signature.js +2 -2
  165. package/dist/component/server/mutations/signature.js.map +1 -1
  166. package/dist/component/server/mutations/signin.js +1 -1
  167. package/dist/component/server/mutations/signin.js.map +1 -1
  168. package/dist/component/server/mutations/signout.js +1 -1
  169. package/dist/component/server/mutations/signout.js.map +1 -1
  170. package/dist/component/server/mutations/store/refs.js +15 -0
  171. package/dist/component/server/mutations/store/refs.js.map +1 -0
  172. package/dist/component/server/mutations/store.js +80 -10
  173. package/dist/component/server/mutations/store.js.map +1 -1
  174. package/dist/component/server/mutations/verifier.js +1 -1
  175. package/dist/component/server/mutations/verifier.js.map +1 -1
  176. package/dist/component/server/mutations/verify.js +4 -3
  177. package/dist/component/server/mutations/verify.js.map +1 -1
  178. package/dist/component/server/oauth.js +1 -1
  179. package/dist/component/server/oauth.js.map +1 -1
  180. package/dist/component/server/passkey.js +2 -1
  181. package/dist/component/server/passkey.js.map +1 -1
  182. package/dist/component/server/redirects.js +1 -1
  183. package/dist/component/server/redirects.js.map +1 -1
  184. package/dist/component/server/refresh.js +1 -1
  185. package/dist/component/server/refresh.js.map +1 -1
  186. package/dist/component/server/{factory.d.ts → runtime.d.ts} +2 -2
  187. package/dist/component/server/runtime.d.ts.map +1 -0
  188. package/dist/component/server/runtime.js +413 -0
  189. package/dist/component/server/runtime.js.map +1 -0
  190. package/dist/component/server/signin.js +2 -1
  191. package/dist/component/server/signin.js.map +1 -1
  192. package/dist/component/server/totp.js +2 -1
  193. package/dist/component/server/totp.js.map +1 -1
  194. package/dist/component/server/types.d.ts +225 -41
  195. package/dist/component/server/types.d.ts.map +1 -1
  196. package/dist/component/server/types.js.map +1 -1
  197. package/dist/component/server/users.js +1 -1
  198. package/dist/component/server/users.js.map +1 -1
  199. package/dist/component/server/utils.js +1 -1
  200. package/dist/component/server/utils.js.map +1 -1
  201. package/dist/core/types.d.ts +369 -0
  202. package/dist/core/types.d.ts.map +1 -0
  203. package/dist/factors/device.js +105 -0
  204. package/dist/factors/device.js.map +1 -0
  205. package/dist/factors/passkey.js +181 -0
  206. package/dist/factors/passkey.js.map +1 -0
  207. package/dist/factors/totp.js +122 -0
  208. package/dist/factors/totp.js.map +1 -0
  209. package/dist/providers/credentials.d.ts +5 -2
  210. package/dist/providers/credentials.d.ts.map +1 -1
  211. package/dist/providers/credentials.js +2 -0
  212. package/dist/providers/credentials.js.map +1 -1
  213. package/dist/providers/email.d.ts +30 -0
  214. package/dist/providers/email.d.ts.map +1 -1
  215. package/dist/providers/email.js +24 -0
  216. package/dist/providers/email.js.map +1 -1
  217. package/dist/providers/phone.d.ts +28 -0
  218. package/dist/providers/phone.d.ts.map +1 -1
  219. package/dist/providers/phone.js +22 -0
  220. package/dist/providers/phone.js.map +1 -1
  221. package/dist/runtime/browser.js +68 -0
  222. package/dist/runtime/browser.js.map +1 -0
  223. package/dist/runtime/invite.js +51 -0
  224. package/dist/runtime/invite.js.map +1 -0
  225. package/dist/runtime/proxy.js +70 -0
  226. package/dist/runtime/proxy.js.map +1 -0
  227. package/dist/runtime/storage.js +37 -0
  228. package/dist/runtime/storage.js.map +1 -0
  229. package/dist/server/auth.d.ts +218 -28
  230. package/dist/server/auth.d.ts.map +1 -1
  231. package/dist/server/auth.js +68 -4
  232. package/dist/server/auth.js.map +1 -1
  233. package/dist/server/{fx.d.ts → authError.d.ts} +25 -15
  234. package/dist/server/authError.d.ts.map +1 -0
  235. package/dist/server/authError.js +34 -0
  236. package/dist/server/authError.js.map +1 -0
  237. package/dist/server/{providers.js → config.js} +2 -2
  238. package/dist/server/config.js.map +1 -0
  239. package/dist/server/core.d.ts +1436 -0
  240. package/dist/server/core.d.ts.map +1 -0
  241. package/dist/server/{domains/core.js → core.js} +107 -88
  242. package/dist/server/core.js.map +1 -0
  243. package/dist/server/{provider.d.ts → crypto.d.ts} +2 -2
  244. package/dist/server/crypto.d.ts.map +1 -0
  245. package/dist/server/{provider.js → crypto.js} +3 -3
  246. package/dist/{component/server/provider.js.map → server/crypto.js.map} +1 -1
  247. package/dist/server/device.js +1 -1
  248. package/dist/server/device.js.map +1 -1
  249. package/dist/server/enterprise/config.js +46 -0
  250. package/dist/server/enterprise/config.js.map +1 -0
  251. package/dist/server/{domains/sso.d.ts → enterprise/domain.d.ts} +4 -4
  252. package/dist/server/enterprise/domain.d.ts.map +1 -0
  253. package/dist/server/{domains/sso.js → enterprise/domain.js} +6 -5
  254. package/dist/server/enterprise/domain.js.map +1 -0
  255. package/dist/server/enterprise/http.d.ts +26 -0
  256. package/dist/server/enterprise/http.d.ts.map +1 -0
  257. package/dist/server/enterprise/http.js +766 -0
  258. package/dist/server/enterprise/http.js.map +1 -0
  259. package/dist/server/enterprise/oidc.js +248 -0
  260. package/dist/server/enterprise/oidc.js.map +1 -0
  261. package/dist/server/enterprise/policy.js +85 -0
  262. package/dist/server/enterprise/policy.js.map +1 -0
  263. package/dist/server/enterprise/saml.d.ts +1 -0
  264. package/dist/server/enterprise/saml.js +338 -0
  265. package/dist/server/enterprise/saml.js.map +1 -0
  266. package/dist/server/enterprise/scim.d.ts +1 -0
  267. package/dist/server/enterprise/scim.js +97 -0
  268. package/dist/server/enterprise/scim.js.map +1 -0
  269. package/dist/server/enterprise/shared.d.ts +5 -0
  270. package/dist/server/enterprise/shared.d.ts.map +1 -0
  271. package/dist/server/enterprise/shared.js +51 -0
  272. package/dist/server/enterprise/shared.js.map +1 -0
  273. package/dist/server/enterprise/validators.d.ts +1 -0
  274. package/dist/server/{enterpriseValidators.js → enterprise/validators.js} +2 -2
  275. package/dist/server/enterprise/validators.js.map +1 -0
  276. package/dist/server/errors.d.ts +2 -0
  277. package/dist/server/errors.d.ts.map +1 -1
  278. package/dist/server/errors.js +2 -0
  279. package/dist/server/errors.js.map +1 -1
  280. package/dist/server/http.d.ts +2 -2
  281. package/dist/server/http.d.ts.map +1 -1
  282. package/dist/server/http.js +2 -1
  283. package/dist/server/http.js.map +1 -1
  284. package/dist/server/identity.js +1 -1
  285. package/dist/server/identity.js.map +1 -1
  286. package/dist/server/index.d.ts +4 -705
  287. package/dist/server/index.js +4 -1367
  288. package/dist/server/limits.d.ts +1 -0
  289. package/dist/server/{ratelimit.js → limits.js} +3 -3
  290. package/dist/server/limits.js.map +1 -0
  291. package/dist/server/mounts.d.ts +647 -0
  292. package/dist/server/mounts.d.ts.map +1 -0
  293. package/dist/server/mounts.js +643 -0
  294. package/dist/server/mounts.js.map +1 -0
  295. package/dist/server/mutations/account.d.ts +8 -8
  296. package/dist/server/mutations/account.js +3 -3
  297. package/dist/server/mutations/account.js.map +1 -1
  298. package/dist/server/mutations/code.d.ts +13 -13
  299. package/dist/server/mutations/code.js +2 -2
  300. package/dist/server/mutations/code.js.map +1 -1
  301. package/dist/server/mutations/index.d.ts +8 -312
  302. package/dist/server/mutations/index.js +14 -84
  303. package/dist/server/mutations/invalidate.d.ts +5 -5
  304. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  305. package/dist/server/mutations/invalidate.js +1 -1
  306. package/dist/server/mutations/invalidate.js.map +1 -1
  307. package/dist/server/mutations/oauth.d.ts +9 -9
  308. package/dist/server/mutations/oauth.d.ts.map +1 -1
  309. package/dist/server/mutations/oauth.js +5 -3
  310. package/dist/server/mutations/oauth.js.map +1 -1
  311. package/dist/server/mutations/refresh.d.ts +4 -4
  312. package/dist/server/mutations/refresh.js +1 -1
  313. package/dist/server/mutations/refresh.js.map +1 -1
  314. package/dist/server/mutations/register.d.ts +10 -10
  315. package/dist/server/mutations/register.d.ts.map +1 -1
  316. package/dist/server/mutations/register.js +3 -3
  317. package/dist/server/mutations/register.js.map +1 -1
  318. package/dist/server/mutations/retrieve.d.ts +8 -10
  319. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  320. package/dist/server/mutations/retrieve.js +5 -7
  321. package/dist/server/mutations/retrieve.js.map +1 -1
  322. package/dist/server/mutations/signature.d.ts +6 -6
  323. package/dist/server/mutations/signature.d.ts.map +1 -1
  324. package/dist/server/mutations/signature.js +2 -2
  325. package/dist/server/mutations/signature.js.map +1 -1
  326. package/dist/server/mutations/signin.d.ts +1 -1
  327. package/dist/server/mutations/signin.js +1 -1
  328. package/dist/server/mutations/signin.js.map +1 -1
  329. package/dist/server/mutations/signout.d.ts +1 -1
  330. package/dist/server/mutations/signout.js +1 -1
  331. package/dist/server/mutations/signout.js.map +1 -1
  332. package/dist/server/mutations/store/refs.d.ts +12 -0
  333. package/dist/server/mutations/store/refs.d.ts.map +1 -0
  334. package/dist/server/mutations/store/refs.js +15 -0
  335. package/dist/server/mutations/store/refs.js.map +1 -0
  336. package/dist/server/mutations/store.d.ts +303 -9
  337. package/dist/server/mutations/store.d.ts.map +1 -1
  338. package/dist/server/mutations/store.js +80 -10
  339. package/dist/server/mutations/store.js.map +1 -1
  340. package/dist/server/mutations/verifier.d.ts +1 -1
  341. package/dist/server/mutations/verifier.js +1 -1
  342. package/dist/server/mutations/verifier.js.map +1 -1
  343. package/dist/server/mutations/verify.d.ts +8 -8
  344. package/dist/server/mutations/verify.d.ts.map +1 -1
  345. package/dist/server/mutations/verify.js +4 -3
  346. package/dist/server/mutations/verify.js.map +1 -1
  347. package/dist/server/oauth.js +1 -1
  348. package/dist/server/oauth.js.map +1 -1
  349. package/dist/server/passkey.d.ts +2 -2
  350. package/dist/server/passkey.d.ts.map +1 -1
  351. package/dist/server/passkey.js +3 -2
  352. package/dist/server/passkey.js.map +1 -1
  353. package/dist/server/redirects.js +1 -1
  354. package/dist/server/redirects.js.map +1 -1
  355. package/dist/server/refresh.js +1 -1
  356. package/dist/server/refresh.js.map +1 -1
  357. package/dist/server/{factory.d.ts → runtime.d.ts} +16 -16
  358. package/dist/server/runtime.d.ts.map +1 -0
  359. package/dist/server/runtime.js +413 -0
  360. package/dist/server/runtime.js.map +1 -0
  361. package/dist/server/signin.js +3 -2
  362. package/dist/server/signin.js.map +1 -1
  363. package/dist/server/ssr.d.ts +226 -0
  364. package/dist/server/ssr.d.ts.map +1 -0
  365. package/dist/server/ssr.js +786 -0
  366. package/dist/server/ssr.js.map +1 -0
  367. package/dist/server/totp.js +2 -1
  368. package/dist/server/totp.js.map +1 -1
  369. package/dist/server/types.d.ts +242 -42
  370. package/dist/server/types.d.ts.map +1 -1
  371. package/dist/server/types.js +12 -1
  372. package/dist/server/types.js.map +1 -1
  373. package/dist/server/users.js +1 -1
  374. package/dist/server/users.js.map +1 -1
  375. package/dist/server/utils.js +1 -1
  376. package/dist/server/utils.js.map +1 -1
  377. package/package.json +3 -4
  378. package/src/authorization/index.ts +46 -0
  379. package/src/cli/index.ts +38 -20
  380. package/src/client/core/types.ts +437 -0
  381. package/src/client/factors/device.ts +160 -0
  382. package/src/client/factors/passkey.ts +282 -0
  383. package/src/client/factors/totp.ts +150 -0
  384. package/src/client/index.ts +133 -1267
  385. package/src/client/runtime/browser.ts +112 -0
  386. package/src/client/runtime/invite.ts +65 -0
  387. package/src/client/runtime/proxy.ts +111 -0
  388. package/src/client/runtime/storage.ts +79 -0
  389. package/src/component/_generated/api.ts +40 -12
  390. package/src/component/_generated/component.ts +1772 -1798
  391. package/src/component/model.ts +6 -0
  392. package/src/component/public/enterprise/audit.ts +120 -0
  393. package/src/component/public/enterprise/core.ts +354 -0
  394. package/src/component/public/enterprise/domains.ts +323 -0
  395. package/src/component/public/enterprise/scim.ts +396 -0
  396. package/src/component/public/enterprise/secrets.ts +132 -0
  397. package/src/component/public/enterprise/webhooks.ts +306 -0
  398. package/src/component/public/factors/devices.ts +223 -0
  399. package/src/component/public/factors/passkeys.ts +242 -0
  400. package/src/component/public/factors/totp.ts +258 -0
  401. package/src/component/public/groups/core.ts +481 -0
  402. package/src/component/public/groups/invites.ts +602 -0
  403. package/src/component/public/groups/members.ts +409 -0
  404. package/src/component/public/identity/accounts.ts +206 -0
  405. package/src/component/public/identity/codes.ts +148 -0
  406. package/src/component/public/identity/sessions.ts +209 -0
  407. package/src/component/public/identity/tokens.ts +250 -0
  408. package/src/component/public/identity/users.ts +354 -0
  409. package/src/component/public/identity/verifiers.ts +157 -0
  410. package/src/component/public/security/keys.ts +365 -0
  411. package/src/component/public/security/limits.ts +173 -0
  412. package/src/component/public.ts +27 -5
  413. package/src/providers/credentials.ts +4 -5
  414. package/src/providers/email.ts +30 -0
  415. package/src/providers/phone.ts +28 -0
  416. package/src/server/auth.ts +282 -28
  417. package/src/server/authError.ts +44 -0
  418. package/src/server/core.ts +2095 -0
  419. package/src/server/{provider.ts → crypto.ts} +1 -1
  420. package/src/server/device.ts +1 -1
  421. package/src/server/enterprise/config.ts +51 -0
  422. package/src/server/{domains/sso.ts → enterprise/domain.ts} +4 -2
  423. package/src/server/enterprise/http.ts +1324 -0
  424. package/src/server/enterprise/oidc.ts +500 -0
  425. package/src/server/enterprise/policy.ts +128 -0
  426. package/src/server/enterprise/saml.ts +578 -0
  427. package/src/server/enterprise/scim.ts +135 -0
  428. package/src/server/enterprise/shared.ts +134 -0
  429. package/src/server/errors.ts +2 -0
  430. package/src/server/http.ts +3 -1
  431. package/src/server/identity.ts +1 -1
  432. package/src/server/index.ts +32 -2605
  433. package/src/server/{ratelimit.ts → limits.ts} +1 -1
  434. package/src/server/mounts.ts +948 -0
  435. package/src/server/mutations/account.ts +4 -4
  436. package/src/server/mutations/code.ts +3 -3
  437. package/src/server/mutations/index.ts +8 -148
  438. package/src/server/mutations/invalidate.ts +2 -2
  439. package/src/server/mutations/oauth.ts +8 -6
  440. package/src/server/mutations/refresh.ts +3 -3
  441. package/src/server/mutations/register.ts +3 -3
  442. package/src/server/mutations/retrieve.ts +4 -8
  443. package/src/server/mutations/signature.ts +3 -3
  444. package/src/server/mutations/signin.ts +2 -2
  445. package/src/server/mutations/signout.ts +2 -2
  446. package/src/server/mutations/store/refs.ts +10 -0
  447. package/src/server/mutations/store.ts +137 -9
  448. package/src/server/mutations/verifier.ts +2 -2
  449. package/src/server/mutations/verify.ts +5 -5
  450. package/src/server/oauth.ts +1 -1
  451. package/src/server/passkey.ts +3 -4
  452. package/src/server/redirects.ts +1 -1
  453. package/src/server/refresh.ts +1 -1
  454. package/src/server/runtime.ts +880 -0
  455. package/src/server/signin.ts +3 -1
  456. package/src/server/ssr.ts +1764 -0
  457. package/src/server/totp.ts +3 -1
  458. package/src/server/types.ts +254 -43
  459. package/src/server/users.ts +1 -1
  460. package/src/server/utils.ts +1 -1
  461. package/src/test.ts +25 -0
  462. package/dist/component/public/enterprise.d.ts +0 -54
  463. package/dist/component/public/enterprise.d.ts.map +0 -1
  464. package/dist/component/public/enterprise.js +0 -515
  465. package/dist/component/public/enterprise.js.map +0 -1
  466. package/dist/component/public/factors.d.ts +0 -52
  467. package/dist/component/public/factors.d.ts.map +0 -1
  468. package/dist/component/public/factors.js +0 -285
  469. package/dist/component/public/factors.js.map +0 -1
  470. package/dist/component/public/groups.d.ts +0 -124
  471. package/dist/component/public/groups.d.ts.map +0 -1
  472. package/dist/component/public/groups.js +0 -680
  473. package/dist/component/public/groups.js.map +0 -1
  474. package/dist/component/public/identity.d.ts +0 -93
  475. package/dist/component/public/identity.d.ts.map +0 -1
  476. package/dist/component/public/identity.js +0 -426
  477. package/dist/component/public/identity.js.map +0 -1
  478. package/dist/component/public/keys.d.ts +0 -41
  479. package/dist/component/public/keys.d.ts.map +0 -1
  480. package/dist/component/public/keys.js +0 -157
  481. package/dist/component/public/keys.js.map +0 -1
  482. package/dist/component/public/shared.d.ts +0 -26
  483. package/dist/component/public/shared.d.ts.map +0 -1
  484. package/dist/component/public/shared.js +0 -32
  485. package/dist/component/public/shared.js.map +0 -1
  486. package/dist/component/server/domains/core.js.map +0 -1
  487. package/dist/component/server/domains/sso.js.map +0 -1
  488. package/dist/component/server/factory.d.ts.map +0 -1
  489. package/dist/component/server/factory.js +0 -1134
  490. package/dist/component/server/factory.js.map +0 -1
  491. package/dist/component/server/fx.js +0 -66
  492. package/dist/component/server/fx.js.map +0 -1
  493. package/dist/component/server/mutations/index.js +0 -85
  494. package/dist/component/server/mutations/index.js.map +0 -1
  495. package/dist/component/server/providers.js.map +0 -1
  496. package/dist/component/server/ratelimit.js.map +0 -1
  497. package/dist/component/server/sso.js +0 -830
  498. package/dist/component/server/sso.js.map +0 -1
  499. package/dist/server/domains/core.d.ts +0 -449
  500. package/dist/server/domains/core.d.ts.map +0 -1
  501. package/dist/server/domains/core.js.map +0 -1
  502. package/dist/server/domains/sso.d.ts.map +0 -1
  503. package/dist/server/domains/sso.js.map +0 -1
  504. package/dist/server/enterpriseValidators.js.map +0 -1
  505. package/dist/server/factory.d.ts.map +0 -1
  506. package/dist/server/factory.js +0 -1134
  507. package/dist/server/factory.js.map +0 -1
  508. package/dist/server/fx.d.ts.map +0 -1
  509. package/dist/server/fx.js +0 -66
  510. package/dist/server/fx.js.map +0 -1
  511. package/dist/server/index.d.ts.map +0 -1
  512. package/dist/server/index.js.map +0 -1
  513. package/dist/server/mutations/index.d.ts.map +0 -1
  514. package/dist/server/mutations/index.js.map +0 -1
  515. package/dist/server/provider.d.ts.map +0 -1
  516. package/dist/server/provider.js.map +0 -1
  517. package/dist/server/providers.js.map +0 -1
  518. package/dist/server/ratelimit.js.map +0 -1
  519. package/dist/server/sso.js +0 -830
  520. package/dist/server/sso.js.map +0 -1
  521. package/dist/server/version.d.ts +0 -5
  522. package/dist/server/version.d.ts.map +0 -1
  523. package/dist/server/version.js +0 -6
  524. package/dist/server/version.js.map +0 -1
  525. package/src/component/public/enterprise.ts +0 -753
  526. package/src/component/public/factors.ts +0 -333
  527. package/src/component/public/groups.ts +0 -1066
  528. package/src/component/public/identity.ts +0 -569
  529. package/src/component/public/keys.ts +0 -209
  530. package/src/component/public/shared.ts +0 -119
  531. package/src/server/domains/core.ts +0 -1096
  532. package/src/server/factory.ts +0 -2181
  533. package/src/server/fx.ts +0 -152
  534. package/src/server/sso.ts +0 -1516
  535. package/src/server/version.ts +0 -2
  536. /package/dist/server/{enterpriseValidators.d.ts → config.d.ts} +0 -0
  537. /package/dist/server/{providers.d.ts → enterprise/config.d.ts} +0 -0
  538. /package/dist/server/{ratelimit.d.ts → enterprise/oidc.d.ts} +0 -0
  539. /package/dist/server/{sso.d.ts → enterprise/policy.d.ts} +0 -0
  540. /package/src/server/{providers.ts → config.ts} +0 -0
  541. /package/src/server/{enterpriseValidators.ts → enterprise/validators.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"signin.js","names":[],"sources":["../../../src/server/signin.ts"],"sourcesContent":["import type { Fx as FxType } from \"@robelest/fx\";\nimport { GenericId } from \"convex/values\";\n\nimport { handleDevice } from \"./device\";\nimport { AuthError, Fx } from \"./fx\";\nimport {\n callCreateVerificationCode,\n callRefreshSession,\n callSignIn,\n callVerifier,\n callVerifierSignature,\n callVerifyCodeAndSignIn,\n} from \"./mutations/index\";\nimport { handlePasskeyFx } from \"./passkey\";\nimport { redirectAbsoluteUrl, setURLSearchParam } from \"./redirects\";\nimport { handleTotp } from \"./totp\";\nimport {\n AuthProviderMaterializedConfig,\n ConvexCredentialsConfig,\n EmailConfig,\n GenericActionCtxWithAuthConfig,\n PhoneConfig,\n} from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n SessionInfoWithTokens,\n Tokens,\n queryTotpVerifiedByUserId,\n} from \"./types\";\nimport type { OAuthMaterializedConfig } from \"./types\";\nimport { generateRandomString } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\nconst DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 60 * 60 * 24; // 24 hours\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\ntype SignInResult =\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | { kind: \"refreshTokens\"; signedIn: { tokens: Tokens } }\n | { kind: \"started\"; started: true }\n | { kind: \"redirect\"; redirect: string; verifier: string }\n | { kind: \"passkeyOptions\"; options: Record<string, any>; verifier: string }\n | { kind: \"totpRequired\"; verifier: string }\n | {\n kind: \"totpSetup\";\n uri: string;\n secret: string;\n verifier: string;\n totpId: string;\n }\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n };\n\n/** @internal */\nexport async function signInImpl(\n ctx: EnrichedActionCtx,\n provider: AuthProviderMaterializedConfig | null,\n args: {\n accountId?: GenericId<\"Account\">;\n params?: Record<string, any>;\n verifier?: string;\n refreshToken?: string;\n calledBy?: string;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): Promise<SignInResult> {\n const fx = signInFx(ctx, provider, args, options);\n return Fx.run(\n fx.pipe(Fx.recover((e) => Fx.fatal((e as AuthError).toConvexError()))),\n );\n}\n\n/**\n * Core sign-in pipeline as an Fx generator.\n *\n * Handles: refresh tokens, verification codes, then dispatches by\n * provider type using a dispatch map (no if-chain).\n */\nfunction signInFx(\n ctx: EnrichedActionCtx,\n provider: AuthProviderMaterializedConfig | null,\n args: {\n accountId?: GenericId<\"Account\">;\n params?: Record<string, any>;\n verifier?: string;\n refreshToken?: string;\n calledBy?: string;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): FxType<SignInResult, AuthError> {\n return Fx.gen(function* () {\n // --- Refresh token (no provider) ---\n if (provider === null && args.refreshToken) {\n const tokens = yield* Fx.promise(() =>\n callRefreshSession(ctx, { refreshToken: args.refreshToken! }),\n );\n if (tokens === null) {\n return { kind: \"signedIn\" as const, signedIn: null };\n }\n return { kind: \"refreshTokens\" as const, signedIn: { tokens } };\n }\n\n // --- Verify code (no provider, code present) ---\n if (provider === null && args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n verifier: args.verifier,\n generateTokens: true,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n return { kind: \"signedIn\" as const, signedIn: result };\n }\n\n // --- Provider is required past this point ---\n const resolvedProvider = yield* provider != null\n ? Fx.succeed(provider)\n : Fx.fail(new AuthError(\"SIGN_IN_MISSING_PARAMS\"));\n\n // --- Dispatch by provider type ---\n return yield* Fx.match(resolvedProvider).on(\"type\", {\n email: (p) => handleEmailAndPhoneProviderFx(ctx, p, args, options),\n phone: (p) => handleEmailAndPhoneProviderFx(ctx, p, args, options),\n credentials: (p) => handleCredentialsFx(ctx, p, args, options),\n oauth: (p) => handleOAuthProviderFx(ctx, p, args, options),\n passkey: (p) => handlePasskeyFx(ctx, p, args),\n totp: (p) => handleTotp(ctx, p, args),\n device: (p) => handleDevice(ctx, p, args),\n sso: (_p) => handleSsoProviderFx(ctx, args),\n });\n });\n}\n\n// ============================================================================\n// Email / Phone\n// ============================================================================\n\nfunction handleEmailAndPhoneProviderFx(\n ctx: EnrichedActionCtx,\n provider: EmailConfig | PhoneConfig,\n args: {\n params?: Record<string, any>;\n accountId?: GenericId<\"Account\">;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): FxType<\n | { kind: \"started\"; started: true }\n | { kind: \"signedIn\"; signedIn: SessionInfoWithTokens },\n AuthError\n> {\n return Fx.gen(function* () {\n // --- Code verification path ---\n if (args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n provider: provider.id,\n generateTokens: options.generateTokens,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n const verified = yield* result != null\n ? Fx.succeed(result)\n : Fx.fail(new AuthError(\"INVALID_VERIFICATION_CODE\"));\n return {\n kind: \"signedIn\" as const,\n signedIn: verified as SessionInfoWithTokens,\n };\n }\n\n // --- Send verification code path ---\n const alphabet =\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n const code = provider.generateVerificationToken\n ? yield* Fx.from({\n ok: async () => provider.generateVerificationToken!(),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to generate verification token\",\n ),\n })\n : generateRandomString(32, alphabet);\n const expirationTime =\n Date.now() +\n (provider.maxAge ?? DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S) * 1000;\n\n const identifier = yield* Fx.promise(() =>\n callCreateVerificationCode(ctx, {\n provider: provider.id,\n accountId: args.accountId,\n email: args.params?.email,\n phone: args.params?.phone,\n code,\n expirationTime,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n const destination = yield* Fx.promise(() =>\n redirectAbsoluteUrl(\n ctx.auth.config,\n (args.params ?? {}) as { redirectTo: unknown },\n ),\n );\n const verificationArgs = {\n identifier,\n url: setURLSearchParam(destination, \"code\", code),\n token: code,\n expires: new Date(expirationTime),\n };\n yield* Fx.match(provider).on(\"type\", {\n email: (p) =>\n Fx.from({\n ok: async () =>\n p.sendVerificationRequest(\n {\n ...verificationArgs,\n provider: p,\n request: new Request(\"http://localhost\"),\n },\n ctx,\n ),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to send email code\"),\n }),\n phone: (p) =>\n Fx.from({\n ok: async () =>\n p.sendVerificationRequest(\n { ...verificationArgs, provider: p },\n ctx,\n ),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to send phone code\"),\n }),\n });\n return { kind: \"started\" as const, started: true as const };\n });\n}\n\n// ============================================================================\n// Credentials\n// ============================================================================\n\nfunction handleCredentialsFx(\n ctx: EnrichedActionCtx,\n provider: ConvexCredentialsConfig,\n args: {\n params?: Record<string, any>;\n },\n options: {\n generateTokens: boolean;\n },\n): FxType<\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | { kind: \"totpRequired\"; verifier: string },\n AuthError\n> {\n return Fx.gen(function* () {\n const result = yield* Fx.promise(() =>\n provider.authorize(args.params ?? {}, ctx),\n );\n if (result === null) {\n return { kind: \"signedIn\" as const, signedIn: null };\n }\n\n // Check if user has TOTP 2FA enrolled before issuing tokens\n const hasTotpEnrolled = yield* Fx.promise(async () => {\n const totpDoc = await queryTotpVerifiedByUserId(ctx, result.userId);\n return totpDoc !== null;\n });\n if (hasTotpEnrolled) {\n // Create session but withhold tokens — TOTP verification needed\n yield* Fx.promise(() =>\n callSignIn(ctx, {\n userId: result.userId,\n sessionId: result.sessionId,\n generateTokens: false,\n }),\n );\n // Store userId in verifier so the TOTP verify flow can complete sign-in\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n yield* Fx.promise(() =>\n callVerifierSignature(ctx, {\n verifier,\n signature: JSON.stringify({ userId: result.userId }),\n }),\n );\n return { kind: \"totpRequired\" as const, verifier };\n }\n\n const idsAndTokens = yield* Fx.promise(() =>\n callSignIn(ctx, {\n userId: result.userId,\n sessionId: result.sessionId,\n generateTokens: options.generateTokens,\n }),\n );\n return { kind: \"signedIn\" as const, signedIn: idsAndTokens };\n });\n}\n\n// ============================================================================\n// OAuth\n// ============================================================================\n\nfunction handleOAuthProviderFx(\n ctx: EnrichedActionCtx,\n provider: OAuthMaterializedConfig,\n args: {\n params?: Record<string, any>;\n verifier?: string;\n },\n options: {\n allowExtraProviders: boolean;\n },\n): FxType<\n | { kind: \"signedIn\"; signedIn: SessionInfoWithTokens | null }\n | { kind: \"redirect\"; redirect: string; verifier: string },\n AuthError\n> {\n return Fx.gen(function* () {\n // --- Code verification path ---\n if (args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n verifier: args.verifier,\n generateTokens: true,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n return {\n kind: \"signedIn\" as const,\n signedIn: result as SessionInfoWithTokens | null,\n };\n }\n\n // --- Build redirect URL ---\n const redirect = new URL(\n (process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv(\"CONVEX_SITE_URL\")) +\n `/api/auth/signin/${provider.id}`,\n );\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n redirect.searchParams.set(\"code\", verifier);\n\n if (args.params?.redirectTo !== undefined) {\n yield* Fx.guard(\n typeof args.params.redirectTo !== \"string\",\n Fx.fail(\n new AuthError(\n \"INVALID_REDIRECT\",\n `Expected \\`redirectTo\\` to be a string, got ${args.params.redirectTo}`,\n ),\n ),\n );\n redirect.searchParams.set(\"redirectTo\", args.params.redirectTo);\n }\n\n return {\n kind: \"redirect\" as const,\n redirect: redirect.toString(),\n verifier,\n };\n });\n}\n\n// ============================================================================\n// SSO (Enterprise OIDC / SAML)\n// ============================================================================\n\nfunction handleSsoProviderFx(\n ctx: EnrichedActionCtx,\n args: {\n params?: Record<string, any>;\n },\n): FxType<{ kind: \"redirect\"; redirect: string; verifier: string }, AuthError> {\n return Fx.gen(function* () {\n const enterpriseId = args.params?.enterpriseId;\n if (!enterpriseId || typeof enterpriseId !== \"string\") {\n return yield* Fx.fail(\n new AuthError(\n \"SIGN_IN_MISSING_PARAMS\",\n \"enterpriseId is required for SSO sign-in.\",\n ),\n );\n }\n\n const protocol: \"oidc\" | \"saml\" = args.params?.protocol ?? \"oidc\";\n if (protocol !== \"oidc\" && protocol !== \"saml\") {\n return yield* Fx.fail(\n new AuthError(\n \"SIGN_IN_MISSING_PARAMS\",\n `Invalid SSO protocol: ${protocol as string}. Expected \"oidc\" or \"saml\".`,\n ),\n );\n }\n\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n const siteUrl =\n process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv(\"CONVEX_SITE_URL\");\n const redirect = new URL(\n `${siteUrl}/api/auth/sso/${enterpriseId}/${protocol}/signin`,\n );\n redirect.searchParams.set(\"code\", verifier);\n\n if (typeof args.params?.redirectTo === \"string\") {\n redirect.searchParams.set(\"redirectTo\", args.params.redirectTo);\n }\n\n return {\n kind: \"redirect\" as const,\n redirect: redirect.toString(),\n verifier,\n };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"signin.js","names":[],"sources":["../../../src/server/signin.ts"],"sourcesContent":["import type { Fx as FxType } from \"@robelest/fx\";\nimport { GenericId } from \"convex/values\";\n\nimport { handleDevice } from \"./device\";\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./authError\";\nimport {\n callCreateVerificationCode,\n callRefreshSession,\n callSignIn,\n callVerifier,\n callVerifierSignature,\n callVerifyCodeAndSignIn,\n} from \"./mutations/index\";\nimport { handlePasskeyFx } from \"./passkey\";\nimport { redirectAbsoluteUrl, setURLSearchParam } from \"./redirects\";\nimport { handleTotp } from \"./totp\";\nimport {\n AuthProviderMaterializedConfig,\n ConvexCredentialsConfig,\n EmailConfig,\n GenericActionCtxWithAuthConfig,\n PhoneConfig,\n} from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n SessionInfoWithTokens,\n Tokens,\n queryTotpVerifiedByUserId,\n} from \"./types\";\nimport type { OAuthMaterializedConfig } from \"./types\";\nimport { generateRandomString } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\nconst DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 60 * 60 * 24; // 24 hours\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\ntype SignInResult =\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | { kind: \"refreshTokens\"; signedIn: { tokens: Tokens } }\n | { kind: \"started\"; started: true }\n | { kind: \"redirect\"; redirect: string; verifier: string }\n | { kind: \"passkeyOptions\"; options: Record<string, any>; verifier: string }\n | { kind: \"totpRequired\"; verifier: string }\n | {\n kind: \"totpSetup\";\n uri: string;\n secret: string;\n verifier: string;\n totpId: string;\n }\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n };\n\n/** @internal */\nexport async function signInImpl(\n ctx: EnrichedActionCtx,\n provider: AuthProviderMaterializedConfig | null,\n args: {\n accountId?: GenericId<\"Account\">;\n params?: Record<string, any>;\n verifier?: string;\n refreshToken?: string;\n calledBy?: string;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): Promise<SignInResult> {\n const fx = signInFx(ctx, provider, args, options);\n return Fx.run(\n fx.pipe(Fx.recover((e) => Fx.fatal((e as AuthError).toConvexError()))),\n );\n}\n\n/**\n * Core sign-in pipeline as an Fx generator.\n *\n * Handles: refresh tokens, verification codes, then dispatches by\n * provider type using a dispatch map (no if-chain).\n */\nfunction signInFx(\n ctx: EnrichedActionCtx,\n provider: AuthProviderMaterializedConfig | null,\n args: {\n accountId?: GenericId<\"Account\">;\n params?: Record<string, any>;\n verifier?: string;\n refreshToken?: string;\n calledBy?: string;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): FxType<SignInResult, AuthError> {\n return Fx.gen(function* () {\n // --- Refresh token (no provider) ---\n if (provider === null && args.refreshToken) {\n const tokens = yield* Fx.promise(() =>\n callRefreshSession(ctx, { refreshToken: args.refreshToken! }),\n );\n if (tokens === null) {\n return { kind: \"signedIn\" as const, signedIn: null };\n }\n return { kind: \"refreshTokens\" as const, signedIn: { tokens } };\n }\n\n // --- Verify code (no provider, code present) ---\n if (provider === null && args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n verifier: args.verifier,\n generateTokens: true,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n return { kind: \"signedIn\" as const, signedIn: result };\n }\n\n // --- Provider is required past this point ---\n const resolvedProvider = yield* provider != null\n ? Fx.succeed(provider)\n : Fx.fail(new AuthError(\"SIGN_IN_MISSING_PARAMS\"));\n\n // --- Dispatch by provider type ---\n return yield* Fx.match(resolvedProvider).on(\"type\", {\n email: (p) => handleEmailAndPhoneProviderFx(ctx, p, args, options),\n phone: (p) => handleEmailAndPhoneProviderFx(ctx, p, args, options),\n credentials: (p) => handleCredentialsFx(ctx, p, args, options),\n oauth: (p) => handleOAuthProviderFx(ctx, p, args, options),\n passkey: (p) => handlePasskeyFx(ctx, p, args),\n totp: (p) => handleTotp(ctx, p, args),\n device: (p) => handleDevice(ctx, p, args),\n sso: (_p) => handleSsoProviderFx(ctx, args),\n });\n });\n}\n\n// ============================================================================\n// Email / Phone\n// ============================================================================\n\nfunction handleEmailAndPhoneProviderFx(\n ctx: EnrichedActionCtx,\n provider: EmailConfig | PhoneConfig,\n args: {\n params?: Record<string, any>;\n accountId?: GenericId<\"Account\">;\n },\n options: {\n generateTokens: boolean;\n allowExtraProviders: boolean;\n },\n): FxType<\n | { kind: \"started\"; started: true }\n | { kind: \"signedIn\"; signedIn: SessionInfoWithTokens },\n AuthError\n> {\n return Fx.gen(function* () {\n // --- Code verification path ---\n if (args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n provider: provider.id,\n generateTokens: options.generateTokens,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n const verified = yield* result != null\n ? Fx.succeed(result)\n : Fx.fail(new AuthError(\"INVALID_VERIFICATION_CODE\"));\n return {\n kind: \"signedIn\" as const,\n signedIn: verified as SessionInfoWithTokens,\n };\n }\n\n // --- Send verification code path ---\n const alphabet =\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n const code = provider.generateVerificationToken\n ? yield* Fx.from({\n ok: async () => provider.generateVerificationToken!(),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to generate verification token\",\n ),\n })\n : generateRandomString(32, alphabet);\n const expirationTime =\n Date.now() +\n (provider.maxAge ?? DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S) * 1000;\n\n const identifier = yield* Fx.promise(() =>\n callCreateVerificationCode(ctx, {\n provider: provider.id,\n accountId: args.accountId,\n email: args.params?.email,\n phone: args.params?.phone,\n code,\n expirationTime,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n const destination = yield* Fx.promise(() =>\n redirectAbsoluteUrl(\n ctx.auth.config,\n (args.params ?? {}) as { redirectTo: unknown },\n ),\n );\n const verificationArgs = {\n identifier,\n url: setURLSearchParam(destination, \"code\", code),\n token: code,\n expires: new Date(expirationTime),\n };\n yield* Fx.match(provider).on(\"type\", {\n email: (p) =>\n Fx.from({\n ok: async () =>\n p.sendVerificationRequest(\n {\n ...verificationArgs,\n provider: p,\n request: new Request(\"http://localhost\"),\n },\n ctx,\n ),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to send email code\"),\n }),\n phone: (p) =>\n Fx.from({\n ok: async () =>\n p.sendVerificationRequest(\n { ...verificationArgs, provider: p },\n ctx,\n ),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to send phone code\"),\n }),\n });\n return { kind: \"started\" as const, started: true as const };\n });\n}\n\n// ============================================================================\n// Credentials\n// ============================================================================\n\nfunction handleCredentialsFx(\n ctx: EnrichedActionCtx,\n provider: ConvexCredentialsConfig,\n args: {\n params?: Record<string, any>;\n },\n options: {\n generateTokens: boolean;\n },\n): FxType<\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | { kind: \"totpRequired\"; verifier: string },\n AuthError\n> {\n return Fx.gen(function* () {\n const result = yield* Fx.promise(() =>\n provider.authorize(args.params ?? {}, ctx),\n );\n if (result === null) {\n return { kind: \"signedIn\" as const, signedIn: null };\n }\n\n // Check if user has TOTP 2FA enrolled before issuing tokens\n const hasTotpEnrolled = yield* Fx.promise(async () => {\n const totpDoc = await queryTotpVerifiedByUserId(ctx, result.userId);\n return totpDoc !== null;\n });\n if (hasTotpEnrolled) {\n // Create session but withhold tokens — TOTP verification needed\n yield* Fx.promise(() =>\n callSignIn(ctx, {\n userId: result.userId,\n sessionId: result.sessionId,\n generateTokens: false,\n }),\n );\n // Store userId in verifier so the TOTP verify flow can complete sign-in\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n yield* Fx.promise(() =>\n callVerifierSignature(ctx, {\n verifier,\n signature: JSON.stringify({ userId: result.userId }),\n }),\n );\n return { kind: \"totpRequired\" as const, verifier };\n }\n\n const idsAndTokens = yield* Fx.promise(() =>\n callSignIn(ctx, {\n userId: result.userId,\n sessionId: result.sessionId,\n generateTokens: options.generateTokens,\n }),\n );\n return { kind: \"signedIn\" as const, signedIn: idsAndTokens };\n });\n}\n\n// ============================================================================\n// OAuth\n// ============================================================================\n\nfunction handleOAuthProviderFx(\n ctx: EnrichedActionCtx,\n provider: OAuthMaterializedConfig,\n args: {\n params?: Record<string, any>;\n verifier?: string;\n },\n options: {\n allowExtraProviders: boolean;\n },\n): FxType<\n | { kind: \"signedIn\"; signedIn: SessionInfoWithTokens | null }\n | { kind: \"redirect\"; redirect: string; verifier: string },\n AuthError\n> {\n return Fx.gen(function* () {\n // --- Code verification path ---\n if (args.params?.code !== undefined) {\n const result = yield* Fx.promise(() =>\n callVerifyCodeAndSignIn(ctx, {\n params: args.params,\n verifier: args.verifier,\n generateTokens: true,\n allowExtraProviders: options.allowExtraProviders,\n }),\n );\n return {\n kind: \"signedIn\" as const,\n signedIn: result as SessionInfoWithTokens | null,\n };\n }\n\n // --- Build redirect URL ---\n const redirect = new URL(\n (process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv(\"CONVEX_SITE_URL\")) +\n `/api/auth/signin/${provider.id}`,\n );\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n redirect.searchParams.set(\"code\", verifier);\n\n if (args.params?.redirectTo !== undefined) {\n yield* Fx.guard(\n typeof args.params.redirectTo !== \"string\",\n Fx.fail(\n new AuthError(\n \"INVALID_REDIRECT\",\n `Expected \\`redirectTo\\` to be a string, got ${args.params.redirectTo}`,\n ),\n ),\n );\n redirect.searchParams.set(\"redirectTo\", args.params.redirectTo);\n }\n\n return {\n kind: \"redirect\" as const,\n redirect: redirect.toString(),\n verifier,\n };\n });\n}\n\n// ============================================================================\n// SSO (Enterprise OIDC / SAML)\n// ============================================================================\n\nfunction handleSsoProviderFx(\n ctx: EnrichedActionCtx,\n args: {\n params?: Record<string, any>;\n },\n): FxType<{ kind: \"redirect\"; redirect: string; verifier: string }, AuthError> {\n return Fx.gen(function* () {\n const enterpriseId = args.params?.enterpriseId;\n if (!enterpriseId || typeof enterpriseId !== \"string\") {\n return yield* Fx.fail(\n new AuthError(\n \"SIGN_IN_MISSING_PARAMS\",\n \"enterpriseId is required for SSO sign-in.\",\n ),\n );\n }\n\n const protocol: \"oidc\" | \"saml\" = args.params?.protocol ?? \"oidc\";\n if (protocol !== \"oidc\" && protocol !== \"saml\") {\n return yield* Fx.fail(\n new AuthError(\n \"SIGN_IN_MISSING_PARAMS\",\n `Invalid SSO protocol: ${protocol as string}. Expected \"oidc\" or \"saml\".`,\n ),\n );\n }\n\n const verifier = yield* Fx.promise(() => callVerifier(ctx));\n const siteUrl =\n process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv(\"CONVEX_SITE_URL\");\n const redirect = new URL(\n `${siteUrl}/api/auth/sso/${enterpriseId}/${protocol}/signin`,\n );\n redirect.searchParams.set(\"code\", verifier);\n\n if (typeof args.params?.redirectTo === \"string\") {\n redirect.searchParams.set(\"redirectTo\", args.params.redirectTo);\n }\n\n return {\n kind: \"redirect\" as const,\n redirect: redirect.toString(),\n verifier,\n };\n });\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
- import { AuthError, Fx } from "./fx.js";
1
+ import { AuthError } from "./authError.js";
2
2
  import { userIdFromIdentitySubject } from "./identity.js";
3
3
  import { callVerifierSignature } from "./mutations/signature.js";
4
4
  import { callSignIn } from "./mutations/signin.js";
5
5
  import { callVerifier } from "./mutations/verifier.js";
6
6
  import { mutateTotpInsert, mutateTotpMarkVerified, mutateTotpUpdateLastUsed, mutateVerifierDelete, queryTotpById, queryTotpVerifiedByUserId, queryUserById, queryVerifierById } from "./types.js";
7
7
  import { encodeBase32LowerCaseNoPadding } from "@oslojs/encoding";
8
+ import { Fx } from "@robelest/fx";
8
9
  import { createTOTPKeyURI, verifyTOTPWithGracePeriod } from "@oslojs/otp";
9
10
 
10
11
  //#region src/server/totp.ts
@@ -1 +1 @@
1
- {"version":3,"file":"totp.js","names":["code","verifier"],"sources":["../../../src/server/totp.ts"],"sourcesContent":["/**\n * Server-side TOTP ceremony logic for two-factor authentication.\n *\n * Handles the three phases of the TOTP flow:\n * 1. setup — generate a TOTP secret and `otpauth://` URI for enrollment\n * 2. confirm — verify the first code from the authenticator app\n * 3. verify — verify a TOTP code during sign-in (2FA challenge)\n */\n\nimport { encodeBase32LowerCaseNoPadding } from \"@oslojs/encoding\";\nimport { verifyTOTPWithGracePeriod, createTOTPKeyURI } from \"@oslojs/otp\";\nimport type { Fx as FxType } from \"@robelest/fx\";\n\nimport { AuthError, Fx } from \"./fx\";\nimport { userIdFromIdentitySubject } from \"./identity\";\nimport { callSignIn, callVerifier } from \"./mutations/index\";\nimport { callVerifierSignature } from \"./mutations/signature\";\nimport { TotpProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n queryUserById,\n queryTotpById,\n queryTotpVerifiedByUserId,\n queryVerifierById,\n mutateTotpInsert,\n mutateTotpMarkVerified,\n mutateTotpUpdateLastUsed,\n mutateVerifierDelete,\n} from \"./types\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Setup flow\n// ============================================================================\n\n// ============================================================================\n// Confirm flow\n// ============================================================================\n\n// ============================================================================\n// Verify flow (2FA during sign-in)\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype TotpResult =\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | {\n kind: \"totpSetup\";\n uri: string;\n secret: string;\n verifier: string;\n totpId: string;\n };\n\nconst TOTP_FLOWS = [\"setup\", \"confirm\", \"verify\"] as const;\n\ntype TotpFlow = (typeof TOTP_FLOWS)[number];\n\ntype TotpDispatch =\n | { flow: \"setup\"; params: Record<string, unknown> }\n | { flow: \"confirm\"; code: string; totpId: string; verifier: string }\n | { flow: \"verify\"; code: string; verifier: string };\n\nconst resolveTotpFlowFx = (\n params: Record<string, unknown>,\n): FxType<TotpFlow, AuthError> => {\n const flow = params.flow;\n return typeof flow === \"string\" && TOTP_FLOWS.includes(flow as never)\n ? Fx.succeed(flow as TotpFlow)\n : Fx.fail(\n new AuthError(\n \"TOTP_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: setup, confirm, verify\",\n ),\n );\n};\n\nconst requireTotpVerifierFx = (\n verifier: string | undefined,\n): FxType<string, AuthError> =>\n verifier != null\n ? Fx.succeed(verifier)\n : Fx.fail(new AuthError(\"TOTP_MISSING_VERIFIER\"));\n\nconst requireTotpCodeFx = (\n params: Record<string, unknown>,\n): FxType<string, AuthError> =>\n typeof params.code === \"string\"\n ? Fx.succeed(params.code)\n : Fx.fail(new AuthError(\"TOTP_MISSING_CODE\"));\n\nconst requireTotpIdFx = (\n params: Record<string, unknown>,\n): FxType<string, AuthError> =>\n typeof params.totpId === \"string\"\n ? Fx.succeed(params.totpId)\n : Fx.fail(new AuthError(\"TOTP_MISSING_ID\"));\n\nconst resolveTotpDispatchFx = (\n params: Record<string, unknown>,\n verifier: string | undefined,\n): FxType<TotpDispatch, AuthError> =>\n resolveTotpFlowFx(params).pipe(\n Fx.chain((flow) =>\n Fx.match({ flow }).on(\"flow\", {\n setup: () => Fx.succeed({ flow: \"setup\" as const, params }),\n confirm: () =>\n Fx.gen(function* () {\n const resolvedVerifier = yield* requireTotpVerifierFx(verifier);\n const code = yield* requireTotpCodeFx(params);\n const totpId = yield* requireTotpIdFx(params);\n return {\n flow: \"confirm\" as const,\n code,\n totpId,\n verifier: resolvedVerifier,\n };\n }),\n verify: () =>\n Fx.gen(function* () {\n const resolvedVerifier = yield* requireTotpVerifierFx(verifier);\n const code = yield* requireTotpCodeFx(params);\n return {\n flow: \"verify\" as const,\n code,\n verifier: resolvedVerifier,\n };\n }),\n }),\n ),\n );\n\n/** @internal */\nexport const handleTotp = (\n ctx: EnrichedActionCtx,\n provider: TotpProviderConfig,\n args: { params?: Record<string, any>; verifier?: string },\n): FxType<TotpResult, AuthError> => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n\n return resolveTotpDispatchFx(params, args.verifier).pipe(\n Fx.chain((dispatch) =>\n Fx.match(dispatch).on(\"flow\", {\n setup: ({ params }) =>\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }).pipe(\n Fx.chain((identity) =>\n identity === null\n ? Fx.fail(new AuthError(\"TOTP_AUTH_REQUIRED\"))\n : Fx.succeed(userIdFromIdentitySubject(identity.subject)),\n ),\n Fx.chain((userId) =>\n Fx.from({\n ok: async () => {\n const secret = new Uint8Array(20);\n crypto.getRandomValues(secret);\n\n let accountName: string = params.accountName as string;\n if (!accountName) {\n const user = await queryUserById(ctx, userId);\n accountName = user?.email ?? \"user\";\n }\n\n const uri = createTOTPKeyURI(\n provider.options.issuer,\n accountName,\n secret,\n provider.options.period,\n provider.options.digits,\n );\n const base32Secret = encodeBase32LowerCaseNoPadding(secret);\n\n const verifier = await callVerifier(ctx);\n await callVerifierSignature(ctx, {\n verifier,\n signature: JSON.stringify({\n secret: Array.from(secret),\n userId,\n digits: provider.options.digits,\n period: provider.options.period,\n }),\n });\n\n const totpId = await mutateTotpInsert(ctx, {\n userId,\n secret: secret.buffer.slice(\n secret.byteOffset,\n secret.byteOffset + secret.byteLength,\n ),\n digits: provider.options.digits,\n period: provider.options.period,\n verified: false,\n name:\n typeof params.name === \"string\" ? params.name : undefined,\n createdAt: Date.now(),\n });\n\n return {\n kind: \"totpSetup\" as const,\n uri,\n secret: base32Secret,\n verifier,\n totpId,\n };\n },\n err: (e) =>\n new AuthError(\n \"INTERNAL_ERROR\",\n `TOTP setup failed: ${String(e)}`,\n ),\n }),\n ),\n ),\n confirm: ({ code, totpId, verifier }) =>\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }).pipe(\n Fx.chain((identity) =>\n identity === null\n ? Fx.fail(new AuthError(\"TOTP_AUTH_REQUIRED\"))\n : Fx.succeed(userIdFromIdentitySubject(identity.subject)),\n ),\n Fx.chain((userId) =>\n Fx.from({\n ok: () => queryTotpById(ctx, totpId),\n err: () => new AuthError(\"TOTP_NOT_FOUND\"),\n })\n .pipe(\n Fx.chain((doc) =>\n doc === null\n ? Fx.fail(new AuthError(\"TOTP_NOT_FOUND\"))\n : Fx.succeed(doc),\n ),\n Fx.chain((totpDoc) =>\n totpDoc.verified\n ? Fx.fail(new AuthError(\"TOTP_ALREADY_VERIFIED\"))\n : Fx.succeed(totpDoc),\n ),\n )\n .pipe(\n Fx.chain((totpDoc) =>\n verifyTOTPWithGracePeriod(\n new Uint8Array(totpDoc.secret),\n provider.options.period,\n provider.options.digits,\n code,\n 30,\n )\n ? Fx.succeed(totpDoc)\n : Fx.fail(new AuthError(\"TOTP_INVALID_CODE\")),\n ),\n )\n .pipe(\n Fx.chain((_totpDoc) =>\n Fx.from({\n ok: async () => {\n await mutateTotpMarkVerified(ctx, totpId, Date.now());\n await mutateVerifierDelete(ctx, verifier);\n return callSignIn(ctx, {\n userId,\n generateTokens: true,\n });\n },\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }),\n ),\n )\n .pipe(\n Fx.map((signInResult) => ({\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n })),\n ),\n ),\n ),\n verify: ({ code, verifier }) =>\n Fx.from({\n ok: () => queryVerifierById(ctx, verifier),\n err: () => new AuthError(\"TOTP_INVALID_VERIFIER\"),\n }).pipe(\n Fx.chain((doc) =>\n doc === null\n ? Fx.fail(new AuthError(\"TOTP_INVALID_VERIFIER\"))\n : Fx.succeed(doc),\n ),\n Fx.map((doc) => {\n const data = JSON.parse(doc.signature!);\n return { userId: data.userId as string, code, verifier };\n }),\n Fx.chain(({ userId, code, verifier }) =>\n Fx.from({\n ok: () => queryTotpVerifiedByUserId(ctx, userId),\n err: () => new AuthError(\"TOTP_NO_ENROLLMENT\"),\n }).pipe(\n Fx.chain((totpDoc) =>\n totpDoc === null\n ? Fx.fail(new AuthError(\"TOTP_NO_ENROLLMENT\"))\n : Fx.succeed(totpDoc),\n ),\n Fx.chain((totpDoc) =>\n verifyTOTPWithGracePeriod(\n new Uint8Array(totpDoc.secret),\n totpDoc.period,\n totpDoc.digits,\n code,\n 30,\n )\n ? Fx.succeed(totpDoc)\n : Fx.fail(new AuthError(\"TOTP_INVALID_CODE\")),\n ),\n Fx.chain((totpDoc) =>\n Fx.from({\n ok: async () => {\n await mutateTotpUpdateLastUsed(\n ctx,\n totpDoc._id,\n Date.now(),\n );\n await mutateVerifierDelete(ctx, verifier);\n return callSignIn(ctx, { userId, generateTokens: true });\n },\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }),\n ),\n Fx.map((signInResult) => ({\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n })),\n ),\n ),\n ),\n }),\n ),\n );\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n"],"mappings":"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"}
1
+ {"version":3,"file":"totp.js","names":["code","verifier"],"sources":["../../../src/server/totp.ts"],"sourcesContent":["/**\n * Server-side TOTP ceremony logic for two-factor authentication.\n *\n * Handles the three phases of the TOTP flow:\n * 1. setup — generate a TOTP secret and `otpauth://` URI for enrollment\n * 2. confirm — verify the first code from the authenticator app\n * 3. verify — verify a TOTP code during sign-in (2FA challenge)\n */\n\nimport { encodeBase32LowerCaseNoPadding } from \"@oslojs/encoding\";\nimport { verifyTOTPWithGracePeriod, createTOTPKeyURI } from \"@oslojs/otp\";\nimport type { Fx as FxType } from \"@robelest/fx\";\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./authError\";\nimport { userIdFromIdentitySubject } from \"./identity\";\nimport { callSignIn, callVerifier } from \"./mutations/index\";\nimport { callVerifierSignature } from \"./mutations/signature\";\nimport { TotpProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n queryUserById,\n queryTotpById,\n queryTotpVerifiedByUserId,\n queryVerifierById,\n mutateTotpInsert,\n mutateTotpMarkVerified,\n mutateTotpUpdateLastUsed,\n mutateVerifierDelete,\n} from \"./types\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Setup flow\n// ============================================================================\n\n// ============================================================================\n// Confirm flow\n// ============================================================================\n\n// ============================================================================\n// Verify flow (2FA during sign-in)\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype TotpResult =\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | {\n kind: \"totpSetup\";\n uri: string;\n secret: string;\n verifier: string;\n totpId: string;\n };\n\nconst TOTP_FLOWS = [\"setup\", \"confirm\", \"verify\"] as const;\n\ntype TotpFlow = (typeof TOTP_FLOWS)[number];\n\ntype TotpDispatch =\n | { flow: \"setup\"; params: Record<string, unknown> }\n | { flow: \"confirm\"; code: string; totpId: string; verifier: string }\n | { flow: \"verify\"; code: string; verifier: string };\n\nconst resolveTotpFlowFx = (\n params: Record<string, unknown>,\n): FxType<TotpFlow, AuthError> => {\n const flow = params.flow;\n return typeof flow === \"string\" && TOTP_FLOWS.includes(flow as never)\n ? Fx.succeed(flow as TotpFlow)\n : Fx.fail(\n new AuthError(\n \"TOTP_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: setup, confirm, verify\",\n ),\n );\n};\n\nconst requireTotpVerifierFx = (\n verifier: string | undefined,\n): FxType<string, AuthError> =>\n verifier != null\n ? Fx.succeed(verifier)\n : Fx.fail(new AuthError(\"TOTP_MISSING_VERIFIER\"));\n\nconst requireTotpCodeFx = (\n params: Record<string, unknown>,\n): FxType<string, AuthError> =>\n typeof params.code === \"string\"\n ? Fx.succeed(params.code)\n : Fx.fail(new AuthError(\"TOTP_MISSING_CODE\"));\n\nconst requireTotpIdFx = (\n params: Record<string, unknown>,\n): FxType<string, AuthError> =>\n typeof params.totpId === \"string\"\n ? Fx.succeed(params.totpId)\n : Fx.fail(new AuthError(\"TOTP_MISSING_ID\"));\n\nconst resolveTotpDispatchFx = (\n params: Record<string, unknown>,\n verifier: string | undefined,\n): FxType<TotpDispatch, AuthError> =>\n resolveTotpFlowFx(params).pipe(\n Fx.chain((flow) =>\n Fx.match({ flow }).on(\"flow\", {\n setup: () => Fx.succeed({ flow: \"setup\" as const, params }),\n confirm: () =>\n Fx.gen(function* () {\n const resolvedVerifier = yield* requireTotpVerifierFx(verifier);\n const code = yield* requireTotpCodeFx(params);\n const totpId = yield* requireTotpIdFx(params);\n return {\n flow: \"confirm\" as const,\n code,\n totpId,\n verifier: resolvedVerifier,\n };\n }),\n verify: () =>\n Fx.gen(function* () {\n const resolvedVerifier = yield* requireTotpVerifierFx(verifier);\n const code = yield* requireTotpCodeFx(params);\n return {\n flow: \"verify\" as const,\n code,\n verifier: resolvedVerifier,\n };\n }),\n }),\n ),\n );\n\n/** @internal */\nexport const handleTotp = (\n ctx: EnrichedActionCtx,\n provider: TotpProviderConfig,\n args: { params?: Record<string, any>; verifier?: string },\n): FxType<TotpResult, AuthError> => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n\n return resolveTotpDispatchFx(params, args.verifier).pipe(\n Fx.chain((dispatch) =>\n Fx.match(dispatch).on(\"flow\", {\n setup: ({ params }) =>\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }).pipe(\n Fx.chain((identity) =>\n identity === null\n ? Fx.fail(new AuthError(\"TOTP_AUTH_REQUIRED\"))\n : Fx.succeed(userIdFromIdentitySubject(identity.subject)),\n ),\n Fx.chain((userId) =>\n Fx.from({\n ok: async () => {\n const secret = new Uint8Array(20);\n crypto.getRandomValues(secret);\n\n let accountName: string = params.accountName as string;\n if (!accountName) {\n const user = await queryUserById(ctx, userId);\n accountName = user?.email ?? \"user\";\n }\n\n const uri = createTOTPKeyURI(\n provider.options.issuer,\n accountName,\n secret,\n provider.options.period,\n provider.options.digits,\n );\n const base32Secret = encodeBase32LowerCaseNoPadding(secret);\n\n const verifier = await callVerifier(ctx);\n await callVerifierSignature(ctx, {\n verifier,\n signature: JSON.stringify({\n secret: Array.from(secret),\n userId,\n digits: provider.options.digits,\n period: provider.options.period,\n }),\n });\n\n const totpId = await mutateTotpInsert(ctx, {\n userId,\n secret: secret.buffer.slice(\n secret.byteOffset,\n secret.byteOffset + secret.byteLength,\n ),\n digits: provider.options.digits,\n period: provider.options.period,\n verified: false,\n name:\n typeof params.name === \"string\" ? params.name : undefined,\n createdAt: Date.now(),\n });\n\n return {\n kind: \"totpSetup\" as const,\n uri,\n secret: base32Secret,\n verifier,\n totpId,\n };\n },\n err: (e) =>\n new AuthError(\n \"INTERNAL_ERROR\",\n `TOTP setup failed: ${String(e)}`,\n ),\n }),\n ),\n ),\n confirm: ({ code, totpId, verifier }) =>\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }).pipe(\n Fx.chain((identity) =>\n identity === null\n ? Fx.fail(new AuthError(\"TOTP_AUTH_REQUIRED\"))\n : Fx.succeed(userIdFromIdentitySubject(identity.subject)),\n ),\n Fx.chain((userId) =>\n Fx.from({\n ok: () => queryTotpById(ctx, totpId),\n err: () => new AuthError(\"TOTP_NOT_FOUND\"),\n })\n .pipe(\n Fx.chain((doc) =>\n doc === null\n ? Fx.fail(new AuthError(\"TOTP_NOT_FOUND\"))\n : Fx.succeed(doc),\n ),\n Fx.chain((totpDoc) =>\n totpDoc.verified\n ? Fx.fail(new AuthError(\"TOTP_ALREADY_VERIFIED\"))\n : Fx.succeed(totpDoc),\n ),\n )\n .pipe(\n Fx.chain((totpDoc) =>\n verifyTOTPWithGracePeriod(\n new Uint8Array(totpDoc.secret),\n provider.options.period,\n provider.options.digits,\n code,\n 30,\n )\n ? Fx.succeed(totpDoc)\n : Fx.fail(new AuthError(\"TOTP_INVALID_CODE\")),\n ),\n )\n .pipe(\n Fx.chain((_totpDoc) =>\n Fx.from({\n ok: async () => {\n await mutateTotpMarkVerified(ctx, totpId, Date.now());\n await mutateVerifierDelete(ctx, verifier);\n return callSignIn(ctx, {\n userId,\n generateTokens: true,\n });\n },\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }),\n ),\n )\n .pipe(\n Fx.map((signInResult) => ({\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n })),\n ),\n ),\n ),\n verify: ({ code, verifier }) =>\n Fx.from({\n ok: () => queryVerifierById(ctx, verifier),\n err: () => new AuthError(\"TOTP_INVALID_VERIFIER\"),\n }).pipe(\n Fx.chain((doc) =>\n doc === null\n ? Fx.fail(new AuthError(\"TOTP_INVALID_VERIFIER\"))\n : Fx.succeed(doc),\n ),\n Fx.map((doc) => {\n const data = JSON.parse(doc.signature!);\n return { userId: data.userId as string, code, verifier };\n }),\n Fx.chain(({ userId, code, verifier }) =>\n Fx.from({\n ok: () => queryTotpVerifiedByUserId(ctx, userId),\n err: () => new AuthError(\"TOTP_NO_ENROLLMENT\"),\n }).pipe(\n Fx.chain((totpDoc) =>\n totpDoc === null\n ? Fx.fail(new AuthError(\"TOTP_NO_ENROLLMENT\"))\n : Fx.succeed(totpDoc),\n ),\n Fx.chain((totpDoc) =>\n verifyTOTPWithGracePeriod(\n new Uint8Array(totpDoc.secret),\n totpDoc.period,\n totpDoc.digits,\n code,\n 30,\n )\n ? Fx.succeed(totpDoc)\n : Fx.fail(new AuthError(\"TOTP_INVALID_CODE\")),\n ),\n Fx.chain((totpDoc) =>\n Fx.from({\n ok: async () => {\n await mutateTotpUpdateLastUsed(\n ctx,\n totpDoc._id,\n Date.now(),\n );\n await mutateVerifierDelete(ctx, verifier);\n return callSignIn(ctx, { userId, generateTokens: true });\n },\n err: (e) => new AuthError(\"INTERNAL_ERROR\", String(e)),\n }),\n ),\n Fx.map((signInResult) => ({\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n })),\n ),\n ),\n ),\n }),\n ),\n );\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,MAAM,aAAa;CAAC;CAAS;CAAW;CAAS;AASjD,MAAM,qBACJ,WACgC;CAChC,MAAM,OAAO,OAAO;AACpB,QAAO,OAAO,SAAS,YAAY,WAAW,SAAS,KAAc,GACjE,GAAG,QAAQ,KAAiB,GAC5B,GAAG,KACD,IAAI,UACF,qBACA,oEACD,CACF;;AAGP,MAAM,yBACJ,aAEA,YAAY,OACR,GAAG,QAAQ,SAAS,GACpB,GAAG,KAAK,IAAI,UAAU,wBAAwB,CAAC;AAErD,MAAM,qBACJ,WAEA,OAAO,OAAO,SAAS,WACnB,GAAG,QAAQ,OAAO,KAAK,GACvB,GAAG,KAAK,IAAI,UAAU,oBAAoB,CAAC;AAEjD,MAAM,mBACJ,WAEA,OAAO,OAAO,WAAW,WACrB,GAAG,QAAQ,OAAO,OAAO,GACzB,GAAG,KAAK,IAAI,UAAU,kBAAkB,CAAC;AAE/C,MAAM,yBACJ,QACA,aAEA,kBAAkB,OAAO,CAAC,KACxB,GAAG,OAAO,SACR,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ;CAC5B,aAAa,GAAG,QAAQ;EAAE,MAAM;EAAkB;EAAQ,CAAC;CAC3D,eACE,GAAG,IAAI,aAAa;EAClB,MAAM,mBAAmB,OAAO,sBAAsB,SAAS;AAG/D,SAAO;GACL,MAAM;GACN,MAJW,OAAO,kBAAkB,OAAO;GAK3C,QAJa,OAAO,gBAAgB,OAAO;GAK3C,UAAU;GACX;GACD;CACJ,cACE,GAAG,IAAI,aAAa;EAClB,MAAM,mBAAmB,OAAO,sBAAsB,SAAS;AAE/D,SAAO;GACL,MAAM;GACN,MAHW,OAAO,kBAAkB,OAAO;GAI3C,UAAU;GACX;GACD;CACL,CAAC,CACH,CACF;;AAGH,MAAa,cACX,KACA,UACA,SACkC;AAGlC,QAAO,sBAFS,KAAK,UAAU,EAAE,EAEI,KAAK,SAAS,CAAC,KAClD,GAAG,OAAO,aACR,GAAG,MAAM,SAAS,CAAC,GAAG,QAAQ;EAC5B,QAAQ,EAAE,aACR,GAAG,KAAK;GACN,UAAU,IAAI,KAAK,iBAAiB;GACpC,MAAM,MAAM,IAAI,UAAU,kBAAkB,OAAO,EAAE,CAAC;GACvD,CAAC,CAAC,KACD,GAAG,OAAO,aACR,aAAa,OACT,GAAG,KAAK,IAAI,UAAU,qBAAqB,CAAC,GAC5C,GAAG,QAAQ,0BAA0B,SAAS,QAAQ,CAAC,CAC5D,EACD,GAAG,OAAO,WACR,GAAG,KAAK;GACN,IAAI,YAAY;IACd,MAAM,SAAS,IAAI,WAAW,GAAG;AACjC,WAAO,gBAAgB,OAAO;IAE9B,IAAI,cAAsB,OAAO;AACjC,QAAI,CAAC,YAEH,gBADa,MAAM,cAAc,KAAK,OAAO,GACzB,SAAS;IAG/B,MAAM,MAAM,iBACV,SAAS,QAAQ,QACjB,aACA,QACA,SAAS,QAAQ,QACjB,SAAS,QAAQ,OAClB;IACD,MAAM,eAAe,+BAA+B,OAAO;IAE3D,MAAM,WAAW,MAAM,aAAa,IAAI;AACxC,UAAM,sBAAsB,KAAK;KAC/B;KACA,WAAW,KAAK,UAAU;MACxB,QAAQ,MAAM,KAAK,OAAO;MAC1B;MACA,QAAQ,SAAS,QAAQ;MACzB,QAAQ,SAAS,QAAQ;MAC1B,CAAC;KACH,CAAC;AAgBF,WAAO;KACL,MAAM;KACN;KACA,QAAQ;KACR;KACA,QAnBa,MAAM,iBAAiB,KAAK;MACzC;MACA,QAAQ,OAAO,OAAO,MACpB,OAAO,YACP,OAAO,aAAa,OAAO,WAC5B;MACD,QAAQ,SAAS,QAAQ;MACzB,QAAQ,SAAS,QAAQ;MACzB,UAAU;MACV,MACE,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;MAClD,WAAW,KAAK,KAAK;MACtB,CAAC;KAQD;;GAEH,MAAM,MACJ,IAAI,UACF,kBACA,sBAAsB,OAAO,EAAE,GAChC;GACJ,CAAC,CACH,CACF;EACH,UAAU,EAAE,MAAM,QAAQ,eACxB,GAAG,KAAK;GACN,UAAU,IAAI,KAAK,iBAAiB;GACpC,MAAM,MAAM,IAAI,UAAU,kBAAkB,OAAO,EAAE,CAAC;GACvD,CAAC,CAAC,KACD,GAAG,OAAO,aACR,aAAa,OACT,GAAG,KAAK,IAAI,UAAU,qBAAqB,CAAC,GAC5C,GAAG,QAAQ,0BAA0B,SAAS,QAAQ,CAAC,CAC5D,EACD,GAAG,OAAO,WACR,GAAG,KAAK;GACN,UAAU,cAAc,KAAK,OAAO;GACpC,WAAW,IAAI,UAAU,iBAAiB;GAC3C,CAAC,CACC,KACC,GAAG,OAAO,QACR,QAAQ,OACJ,GAAG,KAAK,IAAI,UAAU,iBAAiB,CAAC,GACxC,GAAG,QAAQ,IAAI,CACpB,EACD,GAAG,OAAO,YACR,QAAQ,WACJ,GAAG,KAAK,IAAI,UAAU,wBAAwB,CAAC,GAC/C,GAAG,QAAQ,QAAQ,CACxB,CACF,CACA,KACC,GAAG,OAAO,YACR,0BACE,IAAI,WAAW,QAAQ,OAAO,EAC9B,SAAS,QAAQ,QACjB,SAAS,QAAQ,QACjB,MACA,GACD,GACG,GAAG,QAAQ,QAAQ,GACnB,GAAG,KAAK,IAAI,UAAU,oBAAoB,CAAC,CAChD,CACF,CACA,KACC,GAAG,OAAO,aACR,GAAG,KAAK;GACN,IAAI,YAAY;AACd,UAAM,uBAAuB,KAAK,QAAQ,KAAK,KAAK,CAAC;AACrD,UAAM,qBAAqB,KAAK,SAAS;AACzC,WAAO,WAAW,KAAK;KACrB;KACA,gBAAgB;KACjB,CAAC;;GAEJ,MAAM,MAAM,IAAI,UAAU,kBAAkB,OAAO,EAAE,CAAC;GACvD,CAAC,CACH,CACF,CACA,KACC,GAAG,KAAK,kBAAkB;GACxB,MAAM;GACN,UAAU;GACX,EAAE,CACJ,CACJ,CACF;EACH,SAAS,EAAE,MAAM,eACf,GAAG,KAAK;GACN,UAAU,kBAAkB,KAAK,SAAS;GAC1C,WAAW,IAAI,UAAU,wBAAwB;GAClD,CAAC,CAAC,KACD,GAAG,OAAO,QACR,QAAQ,OACJ,GAAG,KAAK,IAAI,UAAU,wBAAwB,CAAC,GAC/C,GAAG,QAAQ,IAAI,CACpB,EACD,GAAG,KAAK,QAAQ;AAEd,UAAO;IAAE,QADI,KAAK,MAAM,IAAI,UAAW,CACjB;IAAkB;IAAM;IAAU;IACxD,EACF,GAAG,OAAO,EAAE,QAAQ,cAAM,2BACxB,GAAG,KAAK;GACN,UAAU,0BAA0B,KAAK,OAAO;GAChD,WAAW,IAAI,UAAU,qBAAqB;GAC/C,CAAC,CAAC,KACD,GAAG,OAAO,YACR,YAAY,OACR,GAAG,KAAK,IAAI,UAAU,qBAAqB,CAAC,GAC5C,GAAG,QAAQ,QAAQ,CACxB,EACD,GAAG,OAAO,YACR,0BACE,IAAI,WAAW,QAAQ,OAAO,EAC9B,QAAQ,QACR,QAAQ,QACRA,QACA,GACD,GACG,GAAG,QAAQ,QAAQ,GACnB,GAAG,KAAK,IAAI,UAAU,oBAAoB,CAAC,CAChD,EACD,GAAG,OAAO,YACR,GAAG,KAAK;GACN,IAAI,YAAY;AACd,UAAM,yBACJ,KACA,QAAQ,KACR,KAAK,KAAK,CACX;AACD,UAAM,qBAAqB,KAAKC,WAAS;AACzC,WAAO,WAAW,KAAK;KAAE;KAAQ,gBAAgB;KAAM,CAAC;;GAE1D,MAAM,MAAM,IAAI,UAAU,kBAAkB,OAAO,EAAE,CAAC;GACvD,CAAC,CACH,EACD,GAAG,KAAK,kBAAkB;GACxB,MAAM;GACN,UAAU;GACX,EAAE,CACJ,CACF,CACF;EACJ,CAAC,CACH,CACF"}
@@ -1,6 +1,6 @@
1
1
  import _default from "../schema.js";
2
2
  import { OAuthProviderInstance } from "../providers/oauth.js";
3
- import { CredentialsUserConfig } from "../providers/credentials.js";
3
+ import { CredentialsConfig } from "../providers/credentials.js";
4
4
  import { Password } from "../providers/password.js";
5
5
  import { Passkey } from "../providers/passkey.js";
6
6
  import { Totp } from "../providers/totp.js";
@@ -15,19 +15,59 @@ import { GenericId, Infer, Value } from "convex/values";
15
15
  import * as arctic0 from "arctic";
16
16
 
17
17
  //#region src/server/types.d.ts
18
- /** A value that is either `T` or a `PromiseLike<T>`. */
18
+ /**
19
+ * A value that is either `T` or a `PromiseLike<T>`.
20
+ *
21
+ * @typeParam T - The underlying value type.
22
+ */
19
23
  type Awaitable<T> = T | PromiseLike<T>;
24
+ /**
25
+ * A single role definition within the authorization config.
26
+ *
27
+ * Each role has an optional human-readable label and a list of grant strings
28
+ * that members with this role receive.
29
+ *
30
+ * @see {@link AuthAuthorizationConfig}
31
+ */
20
32
  type AuthRoleDefinition = {
21
- id?: string;
22
- label?: string;
33
+ /** Optional stable identifier (defaults to the record key). */id?: string; /** Human-readable label for admin UIs. */
34
+ label?: string; /** Permission grant strings conferred by this role. */
23
35
  grants: string[];
24
36
  };
37
+ /**
38
+ * Authorization configuration mapping role IDs to {@link AuthRoleDefinition}s.
39
+ *
40
+ * Passed as `authorization.roles` in {@link ConvexAuthConfig}.
41
+ *
42
+ * @see {@link AuthRoleDefinition}
43
+ * @see {@link ConvexAuthConfig}
44
+ */
25
45
  type AuthAuthorizationConfig = {
26
46
  roles: Record<string, AuthRoleDefinition>;
27
47
  };
48
+ /**
49
+ * Extracts the union of role ID strings from an authorization config.
50
+ *
51
+ * When `TAuthorization` is defined, this resolves to the literal key union
52
+ * of the `roles` record. Otherwise falls back to `string`.
53
+ *
54
+ * @typeParam TAuthorization - The authorization config type, or `undefined`.
55
+ *
56
+ * @see {@link AuthGrant}
57
+ */
28
58
  type AuthRoleId<TAuthorization extends AuthAuthorizationConfig | undefined> = TAuthorization extends {
29
59
  roles: infer TRoles extends Record<string, any>;
30
60
  } ? keyof TRoles & string : string;
61
+ /**
62
+ * Extracts the union of grant strings from all roles in an authorization config.
63
+ *
64
+ * When `TAuthorization` is defined, this resolves to the literal union
65
+ * of all `grants` array elements across every role. Otherwise falls back to `string`.
66
+ *
67
+ * @typeParam TAuthorization - The authorization config type, or `undefined`.
68
+ *
69
+ * @see {@link AuthRoleId}
70
+ */
31
71
  type AuthGrant<TAuthorization extends AuthAuthorizationConfig | undefined> = TAuthorization extends {
32
72
  roles: infer TRoles extends Record<string, {
33
73
  grants: readonly any[];
@@ -59,12 +99,16 @@ type ConvexAuthConfig = {
59
99
  * How long can a user session last without the user reauthenticating.
60
100
  *
61
101
  * Defaults to 30 days.
102
+ *
103
+ * @defaultValue 2_592_000_000
62
104
  */
63
105
  totalDurationMs?: number;
64
106
  /**
65
107
  * How long can a user session last without the user being active.
66
108
  *
67
109
  * Defaults to 30 days.
110
+ *
111
+ * @defaultValue 2_592_000_000
68
112
  */
69
113
  inactiveDurationMs?: number;
70
114
  };
@@ -76,6 +120,8 @@ type ConvexAuthConfig = {
76
120
  * How long is the JWT valid for after it is signed initially.
77
121
  *
78
122
  * Defaults to 1 hour.
123
+ *
124
+ * @defaultValue 3_600_000
79
125
  */
80
126
  durationMs?: number;
81
127
  };
@@ -89,6 +135,8 @@ type ConvexAuthConfig = {
89
135
  *
90
136
  * Defaults to 10 times per hour (that is 10 failed attempts, and then
91
137
  * allow another one every 6 minutes).
138
+ *
139
+ * @defaultValue 10
92
140
  */
93
141
  maxFailedAttemptsPerHour?: number;
94
142
  };
@@ -268,10 +316,43 @@ interface SSOProviderConfig {
268
316
  id: string;
269
317
  type: "sso";
270
318
  }
319
+ /**
320
+ * Account linking strategy for enterprise SSO sign-in.
321
+ *
322
+ * - `"verifiedEmail"` — link accounts when the IdP-provided email matches a verified email on an existing user.
323
+ * - `"none"` — never auto-link; always create a new account.
324
+ */
271
325
  type EnterpriseAccountLinkingPolicy = "verifiedEmail" | "none";
326
+ /**
327
+ * Policy for reusing existing users during SCIM provisioning.
328
+ *
329
+ * - `"externalId"` — match by the SCIM `externalId` to reuse a previously provisioned user.
330
+ * - `"none"` — always create a new user for each SCIM provision request.
331
+ */
272
332
  type EnterpriseScimReuseUserPolicy = "externalId" | "none";
333
+ /**
334
+ * Just-in-time provisioning mode for enterprise SSO.
335
+ *
336
+ * - `"off"` — no JIT provisioning; users must be pre-provisioned.
337
+ * - `"createUser"` — create a user record on first SSO sign-in.
338
+ * - `"createUserAndMembership"` — create a user and add them to the enterprise group on first SSO sign-in.
339
+ */
273
340
  type EnterpriseJitProvisioningMode = "off" | "createUser" | "createUserAndMembership";
341
+ /**
342
+ * Deprovisioning strategy when a SCIM user is deleted.
343
+ *
344
+ * - `"soft"` — mark the user as inactive but preserve the record.
345
+ * - `"hard"` — permanently delete the user and associated data.
346
+ */
274
347
  type EnterpriseDeprovisionMode = "soft" | "hard";
348
+ /**
349
+ * Effective enterprise policy document stored for an SSO/SCIM tenant.
350
+ *
351
+ * Controls account linking, JIT provisioning, SCIM reuse behavior,
352
+ * deprovisioning, and any app-defined extension metadata.
353
+ *
354
+ * @see {@link EnterprisePolicyPatch}
355
+ */
275
356
  interface EnterprisePolicy {
276
357
  version: 1;
277
358
  identity: {
@@ -294,6 +375,12 @@ interface EnterprisePolicy {
294
375
  };
295
376
  extend?: Record<string, unknown>;
296
377
  }
378
+ /**
379
+ * Partial update payload for {@link EnterprisePolicy}.
380
+ *
381
+ * Use this when patching only selected enterprise policy sections without
382
+ * replacing the entire stored policy document.
383
+ */
297
384
  interface EnterprisePolicyPatch {
298
385
  identity?: {
299
386
  accountLinking?: {
@@ -317,6 +404,8 @@ interface EnterprisePolicyPatch {
317
404
  }
318
405
  /**
319
406
  * Email provider config for magic link / OTP sign-in.
407
+ *
408
+ * @typeParam DataModel - The Convex data model for typed action contexts.
320
409
  */
321
410
  interface EmailConfig<DataModel extends GenericDataModel = GenericDataModel> {
322
411
  /** Provider identifier (e.g. `"email"`, `"resend"`). */
@@ -327,7 +416,11 @@ interface EmailConfig<DataModel extends GenericDataModel = GenericDataModel> {
327
416
  name?: string;
328
417
  /** Sender address (e.g. `"My App <noreply@example.com>"`). */
329
418
  from?: string;
330
- /** Token expiration in seconds. Defaults to 86 400 (24 hours). */
419
+ /**
420
+ * Token expiration in seconds. Defaults to 86 400 (24 hours).
421
+ *
422
+ * @defaultValue 86400
423
+ */
331
424
  maxAge?: number;
332
425
  /**
333
426
  * Send the verification token to the user.
@@ -365,18 +458,23 @@ interface EmailConfig<DataModel extends GenericDataModel = GenericDataModel> {
365
458
  /**
366
459
  * The values passed to the `signIn` function.
367
460
  */
368
-
369
461
  params: Record<string, Value | undefined>, account: GenericDoc<DataModel, "Account">) => Promise<void>;
370
462
  /** Raw user options before merging with defaults. */
371
463
  options: EmailUserConfig<DataModel>;
372
464
  }
373
465
  /**
374
- * Configurable options for an email provider config.
466
+ * User-facing configuration shape accepted by the email provider.
467
+ *
468
+ * Equivalent to `Partial<EmailConfig>` without internal runtime-only fields.
469
+ *
470
+ * @typeParam DataModel - The Convex data model.
375
471
  */
376
472
  type EmailUserConfig<DataModel extends GenericDataModel = GenericDataModel> = Omit<Partial<EmailConfig<DataModel>>, "options" | "type">;
377
473
  /**
378
474
  * Same as email provider config, but verifies
379
475
  * phone number instead of the email address.
476
+ *
477
+ * @typeParam DataModel - The Convex data model for typed action contexts.
380
478
  */
381
479
  interface PhoneConfig<DataModel extends GenericDataModel = GenericDataModel> {
382
480
  id: string;
@@ -425,18 +523,21 @@ interface PhoneConfig<DataModel extends GenericDataModel = GenericDataModel> {
425
523
  /**
426
524
  * The values passed to the `signIn` function.
427
525
  */
428
-
429
526
  params: Record<string, Value | undefined>, account: GenericDoc<DataModel, "Account">) => Promise<void>;
430
527
  options: PhoneUserConfig<DataModel>;
431
528
  }
432
529
  /**
433
- * Configurable options for a phone provider config.
530
+ * User-facing configuration shape accepted by the phone provider.
531
+ *
532
+ * Equivalent to `Partial<PhoneConfig>` without internal runtime-only fields.
533
+ *
534
+ * @typeParam DataModel - The Convex data model.
434
535
  */
435
536
  type PhoneUserConfig<DataModel extends GenericDataModel = GenericDataModel> = Omit<Partial<PhoneConfig<DataModel>>, "options" | "type">;
436
537
  /**
437
538
  * Credentials provider config used by Convex Auth.
438
539
  */
439
- type ConvexCredentialsConfig = CredentialsUserConfig<any> & {
540
+ type ConvexCredentialsConfig = CredentialsConfig<any> & {
440
541
  type: "credentials";
441
542
  id: string;
442
543
  };
@@ -449,12 +550,37 @@ interface PasskeyProviderConfig {
449
550
  options: {
450
551
  /** Relying Party display name. Defaults to SITE_URL hostname. */rpName?: string; /** Relying Party ID (hostname). Defaults to SITE_URL hostname. */
451
552
  rpId?: string; /** Allowed origins for credential verification. Defaults to SITE_URL. */
452
- origin?: string | string[]; /** Attestation conveyance preference. Defaults to "none". */
453
- attestation?: "none" | "direct"; /** User verification requirement. Defaults to "required". */
454
- userVerification?: "required" | "preferred" | "discouraged"; /** Resident key (discoverable credential) preference. Defaults to "preferred". */
553
+ origin?: string | string[];
554
+ /**
555
+ * Attestation conveyance preference. Defaults to "none".
556
+ *
557
+ * @defaultValue "none"
558
+ */
559
+ attestation?: "none" | "direct";
560
+ /**
561
+ * User verification requirement. Defaults to "required".
562
+ *
563
+ * @defaultValue "required"
564
+ */
565
+ userVerification?: "required" | "preferred" | "discouraged";
566
+ /**
567
+ * Resident key (discoverable credential) preference. Defaults to "preferred".
568
+ *
569
+ * @defaultValue "preferred"
570
+ */
455
571
  residentKey?: "required" | "preferred" | "discouraged"; /** Restrict to platform or cross-platform authenticators. */
456
- authenticatorAttachment?: "platform" | "cross-platform"; /** Supported COSE algorithms. Defaults to [-7 (ES256), -257 (RS256)]. */
457
- algorithms?: number[]; /** Challenge expiration in ms. Defaults to 300_000 (5 minutes). */
572
+ authenticatorAttachment?: "platform" | "cross-platform";
573
+ /**
574
+ * Supported COSE algorithms. Defaults to [-7 (ES256), -257 (RS256)].
575
+ *
576
+ * @defaultValue [-7, -257]
577
+ */
578
+ algorithms?: number[];
579
+ /**
580
+ * Challenge expiration in ms. Defaults to 300_000 (5 minutes).
581
+ *
582
+ * @defaultValue 300_000
583
+ */
458
584
  challengeExpirationMs?: number;
459
585
  };
460
586
  }
@@ -465,8 +591,18 @@ interface TotpProviderConfig {
465
591
  id: string;
466
592
  type: "totp";
467
593
  options: {
468
- /** Issuer name shown in authenticator apps (e.g. "My App"). */issuer: string; /** Number of digits in each code (default: 6). */
469
- digits: number; /** Time period in seconds for code rotation (default: 30). */
594
+ /** Issuer name shown in authenticator apps (e.g. "My App"). */issuer: string;
595
+ /**
596
+ * Number of digits in each code (default: 6).
597
+ *
598
+ * @defaultValue 6
599
+ */
600
+ digits: number;
601
+ /**
602
+ * Time period in seconds for code rotation (default: 30).
603
+ *
604
+ * @defaultValue 30
605
+ */
470
606
  period: number;
471
607
  };
472
608
  }
@@ -529,9 +665,50 @@ type AuthProviderSignInResult = {
529
665
  userId: GenericId<"User">;
530
666
  sessionId: GenericId<"Session">;
531
667
  } | null;
532
- /** Server-side auth helpers available on enriched action contexts. */
668
+ /** Arguments for `auth.member.resolve()`. */
669
+ type AuthMemberResolveArgs = {
670
+ userId: GenericId<"User">;
671
+ groupId: GenericId<"Group">;
672
+ ancestry?: boolean;
673
+ roleIds?: string[];
674
+ grants?: string[];
675
+ maxDepth?: number;
676
+ };
677
+ /** Result of `auth.member.resolve()` — membership check with role and grant details. */
678
+ type AuthMemberResolveResult = {
679
+ ok: boolean;
680
+ membership: GenericDoc<GenericDataModel, "GroupMember"> | null;
681
+ matchedGroupId: GenericId<"Group"> | null;
682
+ roleIds: string[];
683
+ grants: string[];
684
+ missingGrants: string[];
685
+ depth: number | null;
686
+ isDirect: boolean;
687
+ isInherited: boolean;
688
+ traversedGroupIds: GenericId<"Group">[];
689
+ code?: "INVALID_ROLE_IDS";
690
+ invalidRoleIds?: string[];
691
+ };
692
+ /**
693
+ * Server-side auth helper methods injected into `ctx.auth` within provider actions.
694
+ *
695
+ * Provides programmatic access to account management, session lifecycle,
696
+ * membership resolution, and provider sign-in from within Convex actions
697
+ * that use {@link GenericActionCtxWithAuthConfig}.
698
+ *
699
+ * @see {@link GenericActionCtxWithAuthConfig}
700
+ *
701
+ * @example
702
+ * ```ts
703
+ * // Inside a credentials provider's authorize callback:
704
+ * const { account, user } = await ctx.auth.account.get(ctx, {
705
+ * provider: "password",
706
+ * account: { id: email },
707
+ * });
708
+ * ```
709
+ */
533
710
  type AuthServerHelpers = {
534
- account: {
711
+ /** Account management: create, retrieve, and update provider-linked accounts. */account: {
535
712
  create: (ctx: GenericActionCtx<any>, args: AuthCreateAccountArgs) => Promise<{
536
713
  ok: true;
537
714
  account: GenericDoc<GenericDataModel, "Account">;
@@ -556,23 +733,8 @@ type AuthServerHelpers = {
556
733
  except: GenericId<"Session">[];
557
734
  }>;
558
735
  };
559
- access: {
560
- check: (ctx: GenericActionCtx<any>, args: {
561
- userId: GenericId<"User">;
562
- groupId: GenericId<"Group">;
563
- grants: string[];
564
- maxDepth?: number;
565
- }) => Promise<{
566
- ok: boolean;
567
- grants: string[];
568
- missingGrants: string[];
569
- roleIds: string[];
570
- matchedGroupId: GenericId<"Group"> | null;
571
- membership: GenericDoc<GenericDataModel, "GroupMember"> | null;
572
- isDirect: boolean;
573
- isInherited: boolean;
574
- depth: number | null;
575
- }>;
736
+ member: {
737
+ resolve: (ctx: GenericActionCtx<any>, args: AuthMemberResolveArgs) => Promise<AuthMemberResolveResult>;
576
738
  };
577
739
  provider: {
578
740
  signIn: (ctx: GenericActionCtx<any>, provider: AuthProviderConfig, args: AuthProviderSignInArgs) => Promise<AuthProviderSignInResult>;
@@ -581,6 +743,8 @@ type AuthServerHelpers = {
581
743
  /**
582
744
  * Your `ActionCtx` enriched with `ctx.auth.config` field with
583
745
  * the config passed to `createAuth`.
746
+ *
747
+ * @typeParam DataModel - The Convex data model.
584
748
  */
585
749
  type GenericActionCtxWithAuthConfig<DataModel extends GenericDataModel> = GenericActionCtx<DataModel> & {
586
750
  auth: GenericActionCtx<DataModel>["auth"] & {
@@ -600,18 +764,38 @@ type ConvexAuthMaterializedConfig = {
600
764
  * Materialized OAuth provider config (Arctic-based).
601
765
  *
602
766
  * Carries the Arctic provider instance along with scopes and profile config.
603
- * Produced by materializing an `OAuthProviderInstance` during `configDefaults`.
767
+ * Produced by materializing an `OAuthProviderInstance` during `configDefaults`.
604
768
  */
605
769
  interface OAuthMaterializedConfig {
770
+ /**
771
+ * Provider identifier (e.g. `"google"`, `"github"`).
772
+ * @readonly
773
+ */
606
774
  readonly id: string;
775
+ /**
776
+ * Discriminant for provider type routing.
777
+ * @readonly
778
+ */
607
779
  readonly type: "oauth";
608
- /** The Arctic provider instance. */
780
+ /**
781
+ * The Arctic provider instance.
782
+ * @readonly
783
+ */
609
784
  readonly provider: any;
610
- /** OAuth scopes to request. */
785
+ /**
786
+ * OAuth scopes to request.
787
+ * @readonly
788
+ */
611
789
  readonly scopes: string[];
612
- /** User-provided profile extraction callback. */
790
+ /**
791
+ * User-provided profile extraction callback.
792
+ * @readonly
793
+ */
613
794
  readonly profile?: (tokens: arctic0.OAuth2Tokens) => Promise<OAuthProfile>;
614
- /** Account-linking policy for OAuth identities. Defaults to verified email linking. */
795
+ /**
796
+ * Account-linking policy for OAuth identities. Defaults to verified email linking.
797
+ * @readonly
798
+ */
615
799
  readonly accountLinking?: "verifiedEmail" | "none";
616
800
  }
617
801
  /**