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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (798) hide show
  1. package/README.md +67 -26
  2. package/dist/authorization/index.d.ts +63 -0
  3. package/dist/authorization/index.d.ts.map +1 -0
  4. package/dist/authorization/index.js +63 -0
  5. package/dist/authorization/index.js.map +1 -0
  6. package/dist/bin.js +6185 -0
  7. package/dist/client/core/types.d.ts +20 -0
  8. package/dist/client/core/types.d.ts.map +1 -0
  9. package/dist/client/index.d.ts +2 -299
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +407 -534
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +42 -0
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts +2546 -90
  17. package/dist/component/_generated/component.d.ts.map +1 -1
  18. package/dist/component/client/core/types.d.ts +2 -0
  19. package/dist/component/client/index.d.ts +2 -0
  20. package/dist/component/convex.config.d.ts +2 -2
  21. package/dist/component/functions.d.ts +11 -9
  22. package/dist/component/functions.d.ts.map +1 -1
  23. package/dist/component/functions.js.map +1 -1
  24. package/dist/component/index.d.ts +7 -11
  25. package/dist/component/index.js +2 -3
  26. package/dist/component/model.d.ts +153 -0
  27. package/dist/component/model.d.ts.map +1 -0
  28. package/dist/component/model.js +349 -0
  29. package/dist/component/model.js.map +1 -0
  30. package/dist/component/providers/anonymous.d.ts +54 -0
  31. package/dist/component/providers/anonymous.d.ts.map +1 -0
  32. package/dist/component/providers/credentials.d.ts +5 -5
  33. package/dist/component/providers/credentials.d.ts.map +1 -1
  34. package/dist/component/providers/device.d.ts +67 -0
  35. package/dist/component/providers/device.d.ts.map +1 -0
  36. package/dist/component/providers/email.d.ts +62 -0
  37. package/dist/component/providers/email.d.ts.map +1 -0
  38. package/dist/component/providers/oauth.d.ts.map +1 -1
  39. package/dist/component/providers/oauth.js.map +1 -1
  40. package/dist/component/providers/passkey.d.ts +57 -0
  41. package/dist/component/providers/passkey.d.ts.map +1 -0
  42. package/dist/component/providers/password.d.ts +88 -0
  43. package/dist/component/providers/password.d.ts.map +1 -0
  44. package/dist/component/providers/phone.d.ts +48 -0
  45. package/dist/component/providers/phone.d.ts.map +1 -0
  46. package/dist/component/providers/sso.d.ts +50 -0
  47. package/dist/component/providers/sso.d.ts.map +1 -0
  48. package/dist/component/providers/totp.d.ts +45 -0
  49. package/dist/component/providers/totp.d.ts.map +1 -0
  50. package/dist/component/public/enterprise/audit.d.ts +73 -0
  51. package/dist/component/public/enterprise/audit.d.ts.map +1 -0
  52. package/dist/component/public/enterprise/audit.js +108 -0
  53. package/dist/component/public/enterprise/audit.js.map +1 -0
  54. package/dist/component/public/enterprise/core.d.ts +176 -0
  55. package/dist/component/public/enterprise/core.d.ts.map +1 -0
  56. package/dist/component/public/enterprise/core.js +292 -0
  57. package/dist/component/public/enterprise/core.js.map +1 -0
  58. package/dist/component/public/enterprise/domains.d.ts +174 -0
  59. package/dist/component/public/enterprise/domains.d.ts.map +1 -0
  60. package/dist/component/public/enterprise/domains.js +271 -0
  61. package/dist/component/public/enterprise/domains.js.map +1 -0
  62. package/dist/component/public/enterprise/scim.d.ts +245 -0
  63. package/dist/component/public/enterprise/scim.d.ts.map +1 -0
  64. package/dist/component/public/enterprise/scim.js +344 -0
  65. package/dist/component/public/enterprise/scim.js.map +1 -0
  66. package/dist/component/public/enterprise/secrets.d.ts +78 -0
  67. package/dist/component/public/enterprise/secrets.d.ts.map +1 -0
  68. package/dist/component/public/enterprise/secrets.js +118 -0
  69. package/dist/component/public/enterprise/secrets.js.map +1 -0
  70. package/dist/component/public/enterprise/webhooks.d.ts +211 -0
  71. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -0
  72. package/dist/component/public/enterprise/webhooks.js +300 -0
  73. package/dist/component/public/enterprise/webhooks.js.map +1 -0
  74. package/dist/component/public/factors/devices.d.ts +157 -0
  75. package/dist/component/public/factors/devices.d.ts.map +1 -0
  76. package/dist/component/public/factors/devices.js +216 -0
  77. package/dist/component/public/factors/devices.js.map +1 -0
  78. package/dist/component/public/factors/passkeys.d.ts +175 -0
  79. package/dist/component/public/factors/passkeys.d.ts.map +1 -0
  80. package/dist/component/public/factors/passkeys.js +238 -0
  81. package/dist/component/public/factors/passkeys.js.map +1 -0
  82. package/dist/component/public/factors/totp.d.ts +189 -0
  83. package/dist/component/public/factors/totp.d.ts.map +1 -0
  84. package/dist/component/public/factors/totp.js +254 -0
  85. package/dist/component/public/factors/totp.js.map +1 -0
  86. package/dist/component/public/groups/core.d.ts +137 -0
  87. package/dist/component/public/groups/core.d.ts.map +1 -0
  88. package/dist/component/public/groups/core.js +321 -0
  89. package/dist/component/public/groups/core.js.map +1 -0
  90. package/dist/component/public/groups/invites.d.ts +217 -0
  91. package/dist/component/public/groups/invites.d.ts.map +1 -0
  92. package/dist/component/public/groups/invites.js +457 -0
  93. package/dist/component/public/groups/invites.js.map +1 -0
  94. package/dist/component/public/groups/members.d.ts +204 -0
  95. package/dist/component/public/groups/members.d.ts.map +1 -0
  96. package/dist/component/public/groups/members.js +355 -0
  97. package/dist/component/public/groups/members.js.map +1 -0
  98. package/dist/component/public/identity/accounts.d.ts +147 -0
  99. package/dist/component/public/identity/accounts.d.ts.map +1 -0
  100. package/dist/component/public/identity/accounts.js +200 -0
  101. package/dist/component/public/identity/accounts.js.map +1 -0
  102. package/dist/component/public/identity/codes.d.ts +104 -0
  103. package/dist/component/public/identity/codes.d.ts.map +1 -0
  104. package/dist/component/public/identity/codes.js +140 -0
  105. package/dist/component/public/identity/codes.js.map +1 -0
  106. package/dist/component/public/identity/sessions.d.ts +128 -0
  107. package/dist/component/public/identity/sessions.d.ts.map +1 -0
  108. package/dist/component/public/identity/sessions.js +192 -0
  109. package/dist/component/public/identity/sessions.js.map +1 -0
  110. package/dist/component/public/identity/tokens.d.ts +169 -0
  111. package/dist/component/public/identity/tokens.d.ts.map +1 -0
  112. package/dist/component/public/identity/tokens.js +227 -0
  113. package/dist/component/public/identity/tokens.js.map +1 -0
  114. package/dist/component/public/identity/users.d.ts +212 -0
  115. package/dist/component/public/identity/users.d.ts.map +1 -0
  116. package/dist/component/public/identity/users.js +311 -0
  117. package/dist/component/public/identity/users.js.map +1 -0
  118. package/dist/component/public/identity/verifiers.d.ts +116 -0
  119. package/dist/component/public/identity/verifiers.d.ts.map +1 -0
  120. package/dist/component/public/identity/verifiers.js +154 -0
  121. package/dist/component/public/identity/verifiers.js.map +1 -0
  122. package/dist/component/public/security/keys.d.ts +209 -0
  123. package/dist/component/public/security/keys.d.ts.map +1 -0
  124. package/dist/component/public/security/keys.js +319 -0
  125. package/dist/component/public/security/keys.js.map +1 -0
  126. package/dist/component/public/security/limits.d.ts +114 -0
  127. package/dist/component/public/security/limits.d.ts.map +1 -0
  128. package/dist/component/public/security/limits.js +169 -0
  129. package/dist/component/public/security/limits.js.map +1 -0
  130. package/dist/component/public.d.ts +24 -271
  131. package/dist/component/public.d.ts.map +1 -1
  132. package/dist/component/public.js +21 -1229
  133. package/dist/component/schema.d.ts +473 -110
  134. package/dist/component/schema.js +162 -73
  135. package/dist/component/schema.js.map +1 -1
  136. package/dist/component/server/auth.d.ts +318 -373
  137. package/dist/component/server/auth.d.ts.map +1 -1
  138. package/dist/component/server/auth.js +204 -123
  139. package/dist/component/server/auth.js.map +1 -1
  140. package/dist/component/server/authError.js +34 -0
  141. package/dist/component/server/authError.js.map +1 -0
  142. package/dist/component/server/{providers.js → config.js} +43 -12
  143. package/dist/component/server/config.js.map +1 -0
  144. package/dist/component/server/cookies.js +3 -0
  145. package/dist/component/server/cookies.js.map +1 -1
  146. package/dist/component/server/core.js +713 -0
  147. package/dist/component/server/core.js.map +1 -0
  148. package/dist/component/server/crypto.js +38 -0
  149. package/dist/component/server/crypto.js.map +1 -0
  150. package/dist/component/server/{implementation/db.js → db.js} +2 -1
  151. package/dist/component/server/db.js.map +1 -0
  152. package/dist/component/server/device.js +109 -0
  153. package/dist/component/server/device.js.map +1 -0
  154. package/dist/component/server/enterprise/config.js +46 -0
  155. package/dist/component/server/enterprise/config.js.map +1 -0
  156. package/dist/component/server/enterprise/domain.js +885 -0
  157. package/dist/component/server/enterprise/domain.js.map +1 -0
  158. package/dist/component/server/enterprise/http.js +766 -0
  159. package/dist/component/server/enterprise/http.js.map +1 -0
  160. package/dist/component/server/enterprise/oidc.js +248 -0
  161. package/dist/component/server/enterprise/oidc.js.map +1 -0
  162. package/dist/component/server/enterprise/policy.js +85 -0
  163. package/dist/component/server/enterprise/policy.js.map +1 -0
  164. package/dist/component/server/enterprise/saml.js +338 -0
  165. package/dist/component/server/enterprise/saml.js.map +1 -0
  166. package/dist/component/server/enterprise/scim.js +97 -0
  167. package/dist/component/server/enterprise/scim.js.map +1 -0
  168. package/dist/component/server/enterprise/shared.js +51 -0
  169. package/dist/component/server/enterprise/shared.js.map +1 -0
  170. package/dist/component/server/errors.d.ts +1 -0
  171. package/dist/component/server/errors.js +24 -16
  172. package/dist/component/server/errors.js.map +1 -1
  173. package/dist/component/server/http.js +288 -0
  174. package/dist/component/server/http.js.map +1 -0
  175. package/dist/component/server/identity.js +13 -0
  176. package/dist/component/server/identity.js.map +1 -0
  177. package/dist/{server/implementation → component/server}/keys.js +9 -31
  178. package/dist/component/server/keys.js.map +1 -0
  179. package/dist/component/server/limits.js +61 -0
  180. package/dist/component/server/limits.js.map +1 -0
  181. package/dist/component/server/mutations/account.js +44 -0
  182. package/dist/component/server/mutations/account.js.map +1 -0
  183. package/dist/component/server/{implementation/mutations → mutations}/code.js +7 -4
  184. package/dist/component/server/mutations/code.js.map +1 -0
  185. package/dist/component/server/mutations/invalidate.js +32 -0
  186. package/dist/component/server/mutations/invalidate.js.map +1 -0
  187. package/dist/component/server/mutations/oauth.js +110 -0
  188. package/dist/component/server/mutations/oauth.js.map +1 -0
  189. package/dist/component/server/mutations/refresh.js +119 -0
  190. package/dist/component/server/mutations/refresh.js.map +1 -0
  191. package/dist/component/server/mutations/register.js +83 -0
  192. package/dist/component/server/mutations/register.js.map +1 -0
  193. package/dist/component/server/mutations/retrieve.js +65 -0
  194. package/dist/component/server/mutations/retrieve.js.map +1 -0
  195. package/dist/component/server/mutations/signature.js +32 -0
  196. package/dist/component/server/mutations/signature.js.map +1 -0
  197. package/dist/component/server/{implementation/mutations → mutations}/signin.js +2 -2
  198. package/dist/component/server/mutations/signin.js.map +1 -0
  199. package/dist/component/server/mutations/signout.js +27 -0
  200. package/dist/component/server/mutations/signout.js.map +1 -0
  201. package/dist/component/server/mutations/store/refs.js +15 -0
  202. package/dist/component/server/mutations/store/refs.js.map +1 -0
  203. package/dist/component/server/mutations/store.js +85 -0
  204. package/dist/component/server/mutations/store.js.map +1 -0
  205. package/dist/component/server/mutations/verifier.js +18 -0
  206. package/dist/component/server/mutations/verifier.js.map +1 -0
  207. package/dist/component/server/mutations/verify.js +98 -0
  208. package/dist/component/server/mutations/verify.js.map +1 -0
  209. package/dist/component/server/oauth.js +106 -60
  210. package/dist/component/server/oauth.js.map +1 -1
  211. package/dist/component/server/passkey.js +328 -0
  212. package/dist/component/server/passkey.js.map +1 -0
  213. package/dist/{server/implementation → component/server}/redirects.js +13 -11
  214. package/dist/component/server/redirects.js.map +1 -0
  215. package/dist/component/server/refresh.js +96 -0
  216. package/dist/component/server/refresh.js.map +1 -0
  217. package/dist/component/server/runtime.d.ts +136 -0
  218. package/dist/component/server/runtime.d.ts.map +1 -0
  219. package/dist/component/server/runtime.js +413 -0
  220. package/dist/component/server/runtime.js.map +1 -0
  221. package/dist/{server/implementation → component/server}/sessions.js +14 -8
  222. package/dist/component/server/sessions.js.map +1 -0
  223. package/dist/component/server/signin.js +201 -0
  224. package/dist/component/server/signin.js.map +1 -0
  225. package/dist/component/server/tokens.js +17 -0
  226. package/dist/component/server/tokens.js.map +1 -0
  227. package/dist/component/server/totp.js +148 -0
  228. package/dist/component/server/totp.js.map +1 -0
  229. package/dist/component/server/types.d.ts +387 -298
  230. package/dist/component/server/types.d.ts.map +1 -1
  231. package/dist/component/server/{implementation/types.js → types.js} +1 -1
  232. package/dist/component/server/types.js.map +1 -0
  233. package/dist/component/server/{implementation/users.js → users.js} +54 -35
  234. package/dist/component/server/users.js.map +1 -0
  235. package/dist/component/server/utils.js +110 -4
  236. package/dist/component/server/utils.js.map +1 -1
  237. package/dist/core/types.d.ts +369 -0
  238. package/dist/core/types.d.ts.map +1 -0
  239. package/dist/factors/device.js +105 -0
  240. package/dist/factors/device.js.map +1 -0
  241. package/dist/factors/passkey.js +181 -0
  242. package/dist/factors/passkey.js.map +1 -0
  243. package/dist/factors/totp.js +122 -0
  244. package/dist/factors/totp.js.map +1 -0
  245. package/dist/providers/anonymous.d.ts +3 -9
  246. package/dist/providers/anonymous.d.ts.map +1 -1
  247. package/dist/providers/anonymous.js +1 -18
  248. package/dist/providers/anonymous.js.map +1 -1
  249. package/dist/providers/credentials.d.ts +8 -10
  250. package/dist/providers/credentials.d.ts.map +1 -1
  251. package/dist/providers/credentials.js +3 -5
  252. package/dist/providers/credentials.js.map +1 -1
  253. package/dist/providers/device.d.ts +18 -10
  254. package/dist/providers/device.d.ts.map +1 -1
  255. package/dist/providers/device.js +4 -8
  256. package/dist/providers/device.js.map +1 -1
  257. package/dist/providers/email.d.ts +50 -23
  258. package/dist/providers/email.d.ts.map +1 -1
  259. package/dist/providers/email.js +58 -34
  260. package/dist/providers/email.js.map +1 -1
  261. package/dist/providers/index.d.ts +7 -3
  262. package/dist/providers/index.js +4 -1
  263. package/dist/providers/oauth.d.ts.map +1 -1
  264. package/dist/providers/oauth.js.map +1 -1
  265. package/dist/providers/passkey.d.ts +12 -9
  266. package/dist/providers/passkey.d.ts.map +1 -1
  267. package/dist/providers/passkey.js +1 -7
  268. package/dist/providers/passkey.js.map +1 -1
  269. package/dist/providers/password.d.ts +6 -12
  270. package/dist/providers/password.d.ts.map +1 -1
  271. package/dist/providers/password.js +189 -89
  272. package/dist/providers/password.js.map +1 -1
  273. package/dist/providers/phone.d.ts +40 -11
  274. package/dist/providers/phone.d.ts.map +1 -1
  275. package/dist/providers/phone.js +52 -21
  276. package/dist/providers/phone.js.map +1 -1
  277. package/dist/providers/sso.d.ts +50 -0
  278. package/dist/providers/sso.d.ts.map +1 -0
  279. package/dist/providers/sso.js +34 -0
  280. package/dist/providers/sso.js.map +1 -0
  281. package/dist/providers/totp.d.ts +12 -9
  282. package/dist/providers/totp.d.ts.map +1 -1
  283. package/dist/providers/totp.js +1 -7
  284. package/dist/providers/totp.js.map +1 -1
  285. package/dist/runtime/browser.js +68 -0
  286. package/dist/runtime/browser.js.map +1 -0
  287. package/dist/runtime/invite.js +51 -0
  288. package/dist/runtime/invite.js.map +1 -0
  289. package/dist/runtime/proxy.js +70 -0
  290. package/dist/runtime/proxy.js.map +1 -0
  291. package/dist/runtime/storage.js +37 -0
  292. package/dist/runtime/storage.js.map +1 -0
  293. package/dist/server/auth.d.ts +335 -370
  294. package/dist/server/auth.d.ts.map +1 -1
  295. package/dist/server/auth.js +204 -123
  296. package/dist/server/auth.js.map +1 -1
  297. package/dist/server/authError.d.ts +46 -0
  298. package/dist/server/authError.d.ts.map +1 -0
  299. package/dist/server/authError.js +34 -0
  300. package/dist/server/authError.js.map +1 -0
  301. package/dist/server/config.d.ts +1 -0
  302. package/dist/server/{providers.js → config.js} +43 -12
  303. package/dist/server/config.js.map +1 -0
  304. package/dist/server/cookies.d.ts +1 -38
  305. package/dist/server/cookies.js +3 -0
  306. package/dist/server/cookies.js.map +1 -1
  307. package/dist/server/core.d.ts +1436 -0
  308. package/dist/server/core.d.ts.map +1 -0
  309. package/dist/server/core.js +713 -0
  310. package/dist/server/core.js.map +1 -0
  311. package/dist/server/crypto.d.ts +8 -0
  312. package/dist/server/crypto.d.ts.map +1 -0
  313. package/dist/server/crypto.js +38 -0
  314. package/dist/server/crypto.js.map +1 -0
  315. package/dist/server/db.d.ts +1 -0
  316. package/dist/server/{implementation/db.js → db.js} +2 -1
  317. package/dist/server/db.js.map +1 -0
  318. package/dist/server/device.d.ts +1 -0
  319. package/dist/server/device.js +109 -0
  320. package/dist/server/device.js.map +1 -0
  321. package/dist/server/enterprise/config.d.ts +1 -0
  322. package/dist/server/enterprise/config.js +46 -0
  323. package/dist/server/enterprise/config.js.map +1 -0
  324. package/dist/server/enterprise/domain.d.ts +409 -0
  325. package/dist/server/enterprise/domain.d.ts.map +1 -0
  326. package/dist/server/enterprise/domain.js +885 -0
  327. package/dist/server/enterprise/domain.js.map +1 -0
  328. package/dist/server/enterprise/http.d.ts +26 -0
  329. package/dist/server/enterprise/http.d.ts.map +1 -0
  330. package/dist/server/enterprise/http.js +766 -0
  331. package/dist/server/enterprise/http.js.map +1 -0
  332. package/dist/server/enterprise/oidc.d.ts +1 -0
  333. package/dist/server/enterprise/oidc.js +248 -0
  334. package/dist/server/enterprise/oidc.js.map +1 -0
  335. package/dist/server/enterprise/policy.d.ts +1 -0
  336. package/dist/server/enterprise/policy.js +85 -0
  337. package/dist/server/enterprise/policy.js.map +1 -0
  338. package/dist/server/enterprise/saml.d.ts +1 -0
  339. package/dist/server/enterprise/saml.js +338 -0
  340. package/dist/server/enterprise/saml.js.map +1 -0
  341. package/dist/server/enterprise/scim.d.ts +1 -0
  342. package/dist/server/enterprise/scim.js +97 -0
  343. package/dist/server/enterprise/scim.js.map +1 -0
  344. package/dist/server/enterprise/shared.d.ts +5 -0
  345. package/dist/server/enterprise/shared.d.ts.map +1 -0
  346. package/dist/server/enterprise/shared.js +51 -0
  347. package/dist/server/enterprise/shared.js.map +1 -0
  348. package/dist/server/enterprise/validators.d.ts +1 -0
  349. package/dist/server/enterprise/validators.js +60 -0
  350. package/dist/server/enterprise/validators.js.map +1 -0
  351. package/dist/server/errors.d.ts +33 -1
  352. package/dist/server/errors.d.ts.map +1 -1
  353. package/dist/server/errors.js +44 -1
  354. package/dist/server/errors.js.map +1 -1
  355. package/dist/server/http.d.ts +59 -0
  356. package/dist/server/http.d.ts.map +1 -0
  357. package/dist/server/http.js +288 -0
  358. package/dist/server/http.js.map +1 -0
  359. package/dist/server/identity.d.ts +1 -0
  360. package/dist/server/identity.js +13 -0
  361. package/dist/server/identity.js.map +1 -0
  362. package/dist/server/index.d.ts +4 -182
  363. package/dist/server/index.js +4 -376
  364. package/dist/server/keys.d.ts +1 -0
  365. package/dist/{component/server/implementation → server}/keys.js +9 -31
  366. package/dist/server/keys.js.map +1 -0
  367. package/dist/server/limits.d.ts +1 -0
  368. package/dist/server/limits.js +61 -0
  369. package/dist/server/limits.js.map +1 -0
  370. package/dist/server/mounts.d.ts +647 -0
  371. package/dist/server/mounts.d.ts.map +1 -0
  372. package/dist/server/mounts.js +643 -0
  373. package/dist/server/mounts.js.map +1 -0
  374. package/dist/server/mutations/account.d.ts +30 -0
  375. package/dist/server/mutations/account.d.ts.map +1 -0
  376. package/dist/server/mutations/account.js +44 -0
  377. package/dist/server/mutations/account.js.map +1 -0
  378. package/dist/server/mutations/code.d.ts +30 -0
  379. package/dist/server/mutations/code.d.ts.map +1 -0
  380. package/dist/server/{implementation/mutations → mutations}/code.js +7 -4
  381. package/dist/server/mutations/code.js.map +1 -0
  382. package/dist/server/mutations/index.d.ts +14 -0
  383. package/dist/server/mutations/index.js +15 -0
  384. package/dist/server/mutations/invalidate.d.ts +20 -0
  385. package/dist/server/mutations/invalidate.d.ts.map +1 -0
  386. package/dist/server/mutations/invalidate.js +32 -0
  387. package/dist/server/mutations/invalidate.js.map +1 -0
  388. package/dist/server/mutations/oauth.d.ts +28 -0
  389. package/dist/server/mutations/oauth.d.ts.map +1 -0
  390. package/dist/server/mutations/oauth.js +110 -0
  391. package/dist/server/mutations/oauth.js.map +1 -0
  392. package/dist/server/mutations/refresh.d.ts +21 -0
  393. package/dist/server/mutations/refresh.d.ts.map +1 -0
  394. package/dist/server/mutations/refresh.js +119 -0
  395. package/dist/server/mutations/refresh.js.map +1 -0
  396. package/dist/server/mutations/register.d.ts +38 -0
  397. package/dist/server/mutations/register.d.ts.map +1 -0
  398. package/dist/server/mutations/register.js +83 -0
  399. package/dist/server/mutations/register.js.map +1 -0
  400. package/dist/server/mutations/retrieve.d.ts +33 -0
  401. package/dist/server/mutations/retrieve.d.ts.map +1 -0
  402. package/dist/server/mutations/retrieve.js +65 -0
  403. package/dist/server/mutations/retrieve.js.map +1 -0
  404. package/dist/server/mutations/signature.d.ts +22 -0
  405. package/dist/server/mutations/signature.d.ts.map +1 -0
  406. package/dist/server/mutations/signature.js +32 -0
  407. package/dist/server/mutations/signature.js.map +1 -0
  408. package/dist/server/mutations/signin.d.ts +22 -0
  409. package/dist/server/mutations/signin.d.ts.map +1 -0
  410. package/dist/server/{implementation/mutations → mutations}/signin.js +2 -2
  411. package/dist/server/mutations/signin.js.map +1 -0
  412. package/dist/server/mutations/signout.d.ts +16 -0
  413. package/dist/server/mutations/signout.d.ts.map +1 -0
  414. package/dist/server/mutations/signout.js +27 -0
  415. package/dist/server/mutations/signout.js.map +1 -0
  416. package/dist/server/mutations/store/refs.d.ts +12 -0
  417. package/dist/server/mutations/store/refs.d.ts.map +1 -0
  418. package/dist/server/mutations/store/refs.js +15 -0
  419. package/dist/server/mutations/store/refs.js.map +1 -0
  420. package/dist/server/mutations/store.d.ts +306 -0
  421. package/dist/server/mutations/store.d.ts.map +1 -0
  422. package/dist/server/mutations/store.js +85 -0
  423. package/dist/server/mutations/store.js.map +1 -0
  424. package/dist/server/mutations/verifier.d.ts +13 -0
  425. package/dist/server/mutations/verifier.d.ts.map +1 -0
  426. package/dist/server/mutations/verifier.js +18 -0
  427. package/dist/server/mutations/verifier.js.map +1 -0
  428. package/dist/server/mutations/verify.d.ts +26 -0
  429. package/dist/server/mutations/verify.d.ts.map +1 -0
  430. package/dist/server/mutations/verify.js +98 -0
  431. package/dist/server/mutations/verify.js.map +1 -0
  432. package/dist/server/oauth.d.ts +1 -48
  433. package/dist/server/oauth.js +107 -64
  434. package/dist/server/oauth.js.map +1 -1
  435. package/dist/server/passkey.d.ts +27 -0
  436. package/dist/server/passkey.d.ts.map +1 -0
  437. package/dist/server/passkey.js +328 -0
  438. package/dist/server/passkey.js.map +1 -0
  439. package/dist/server/redirects.d.ts +1 -0
  440. package/dist/{component/server/implementation → server}/redirects.js +13 -11
  441. package/dist/server/redirects.js.map +1 -0
  442. package/dist/server/refresh.d.ts +1 -0
  443. package/dist/server/refresh.js +96 -0
  444. package/dist/server/refresh.js.map +1 -0
  445. package/dist/server/runtime.d.ts +136 -0
  446. package/dist/server/runtime.d.ts.map +1 -0
  447. package/dist/server/runtime.js +413 -0
  448. package/dist/server/runtime.js.map +1 -0
  449. package/dist/server/sessions.d.ts +1 -0
  450. package/dist/{component/server/implementation → server}/sessions.js +14 -8
  451. package/dist/server/sessions.js.map +1 -0
  452. package/dist/server/signin.d.ts +1 -0
  453. package/dist/server/signin.js +201 -0
  454. package/dist/server/signin.js.map +1 -0
  455. package/dist/server/ssr.d.ts +226 -0
  456. package/dist/server/ssr.d.ts.map +1 -0
  457. package/dist/server/ssr.js +786 -0
  458. package/dist/server/ssr.js.map +1 -0
  459. package/dist/server/templates.d.ts +1 -21
  460. package/dist/server/templates.js +2 -1
  461. package/dist/server/templates.js.map +1 -1
  462. package/dist/server/tokens.d.ts +1 -0
  463. package/dist/server/tokens.js +17 -0
  464. package/dist/server/tokens.js.map +1 -0
  465. package/dist/server/totp.d.ts +1 -0
  466. package/dist/server/totp.js +148 -0
  467. package/dist/server/totp.js.map +1 -0
  468. package/dist/server/types.d.ts +498 -306
  469. package/dist/server/types.d.ts.map +1 -1
  470. package/dist/server/types.js +108 -1
  471. package/dist/server/types.js.map +1 -0
  472. package/dist/server/users.d.ts +1 -0
  473. package/dist/server/{implementation/users.js → users.js} +54 -35
  474. package/dist/server/users.js.map +1 -0
  475. package/dist/server/utils.d.ts +1 -6
  476. package/dist/server/utils.js +110 -4
  477. package/dist/server/utils.js.map +1 -1
  478. package/package.json +49 -46
  479. package/src/authorization/index.ts +83 -0
  480. package/src/cli/bin.ts +5 -0
  481. package/src/cli/command.ts +6 -5
  482. package/src/cli/index.ts +456 -248
  483. package/src/cli/keys.ts +3 -0
  484. package/src/client/core/types.ts +437 -0
  485. package/src/client/factors/device.ts +160 -0
  486. package/src/client/factors/passkey.ts +282 -0
  487. package/src/client/factors/totp.ts +150 -0
  488. package/src/client/index.ts +745 -989
  489. package/src/client/runtime/browser.ts +112 -0
  490. package/src/client/runtime/invite.ts +65 -0
  491. package/src/client/runtime/proxy.ts +111 -0
  492. package/src/client/runtime/storage.ts +79 -0
  493. package/src/component/_generated/api.ts +42 -0
  494. package/src/component/_generated/component.ts +3123 -102
  495. package/src/component/functions.ts +38 -22
  496. package/src/component/index.ts +10 -20
  497. package/src/component/model.ts +449 -0
  498. package/src/component/public/enterprise/audit.ts +120 -0
  499. package/src/component/public/enterprise/core.ts +354 -0
  500. package/src/component/public/enterprise/domains.ts +323 -0
  501. package/src/component/public/enterprise/scim.ts +396 -0
  502. package/src/component/public/enterprise/secrets.ts +132 -0
  503. package/src/component/public/enterprise/webhooks.ts +306 -0
  504. package/src/component/public/factors/devices.ts +223 -0
  505. package/src/component/public/factors/passkeys.ts +242 -0
  506. package/src/component/public/factors/totp.ts +258 -0
  507. package/src/component/public/groups/core.ts +481 -0
  508. package/src/component/public/groups/invites.ts +602 -0
  509. package/src/component/public/groups/members.ts +409 -0
  510. package/src/component/public/identity/accounts.ts +206 -0
  511. package/src/component/public/identity/codes.ts +148 -0
  512. package/src/component/public/identity/sessions.ts +209 -0
  513. package/src/component/public/identity/tokens.ts +250 -0
  514. package/src/component/public/identity/users.ts +354 -0
  515. package/src/component/public/identity/verifiers.ts +157 -0
  516. package/src/component/public/security/keys.ts +365 -0
  517. package/src/component/public/security/limits.ts +173 -0
  518. package/src/component/public.ts +26 -1766
  519. package/src/component/schema.ts +273 -100
  520. package/src/providers/anonymous.ts +10 -20
  521. package/src/providers/credentials.ts +14 -22
  522. package/src/providers/device.ts +3 -14
  523. package/src/providers/email.ts +83 -47
  524. package/src/providers/index.ts +7 -0
  525. package/src/providers/oauth.ts +5 -3
  526. package/src/providers/passkey.ts +0 -13
  527. package/src/providers/password.ts +307 -130
  528. package/src/providers/phone.ts +81 -37
  529. package/src/providers/sso.ts +54 -0
  530. package/src/providers/totp.ts +0 -13
  531. package/src/samlify.d.ts +53 -0
  532. package/src/server/auth.ts +701 -247
  533. package/src/server/authError.ts +44 -0
  534. package/src/server/{providers.ts → config.ts} +84 -15
  535. package/src/server/cookies.ts +8 -1
  536. package/src/server/core.ts +2095 -0
  537. package/src/server/crypto.ts +88 -0
  538. package/src/server/{implementation/db.ts → db.ts} +90 -15
  539. package/src/server/device.ts +221 -0
  540. package/src/server/enterprise/config.ts +51 -0
  541. package/src/server/enterprise/domain.ts +1751 -0
  542. package/src/server/enterprise/http.ts +1324 -0
  543. package/src/server/enterprise/oidc.ts +500 -0
  544. package/src/server/enterprise/policy.ts +128 -0
  545. package/src/server/enterprise/saml.ts +578 -0
  546. package/src/server/enterprise/scim.ts +135 -0
  547. package/src/server/enterprise/shared.ts +134 -0
  548. package/src/server/enterprise/validators.ts +93 -0
  549. package/src/server/errors.ts +130 -119
  550. package/src/server/http.ts +531 -0
  551. package/src/server/identity.ts +18 -0
  552. package/src/server/index.ts +32 -650
  553. package/src/server/{implementation/keys.ts → keys.ts} +16 -44
  554. package/src/server/limits.ts +134 -0
  555. package/src/server/mounts.ts +948 -0
  556. package/src/server/mutations/account.ts +76 -0
  557. package/src/server/{implementation/mutations → mutations}/code.ts +22 -11
  558. package/src/server/mutations/index.ts +13 -0
  559. package/src/server/mutations/invalidate.ts +50 -0
  560. package/src/server/mutations/oauth.ts +237 -0
  561. package/src/server/mutations/refresh.ts +298 -0
  562. package/src/server/mutations/register.ts +200 -0
  563. package/src/server/mutations/retrieve.ts +109 -0
  564. package/src/server/mutations/signature.ts +50 -0
  565. package/src/server/{implementation/mutations → mutations}/signin.ts +9 -7
  566. package/src/server/mutations/signout.ts +43 -0
  567. package/src/server/mutations/store/refs.ts +10 -0
  568. package/src/server/mutations/store.ts +138 -0
  569. package/src/server/mutations/verifier.ts +34 -0
  570. package/src/server/mutations/verify.ts +202 -0
  571. package/src/server/oauth.ts +243 -131
  572. package/src/server/passkey.ts +784 -0
  573. package/src/server/{implementation/redirects.ts → redirects.ts} +21 -16
  574. package/src/server/refresh.ts +222 -0
  575. package/src/server/runtime.ts +880 -0
  576. package/src/server/{implementation/sessions.ts → sessions.ts} +33 -25
  577. package/src/server/signin.ts +438 -0
  578. package/src/server/ssr.ts +1764 -0
  579. package/src/server/templates.ts +8 -3
  580. package/src/server/{implementation/tokens.ts → tokens.ts} +11 -5
  581. package/src/server/totp.ts +349 -0
  582. package/src/server/types.ts +972 -207
  583. package/src/server/{implementation/users.ts → users.ts} +129 -75
  584. package/src/server/utils.ts +192 -5
  585. package/src/test.ts +28 -4
  586. package/dist/bin.cjs +0 -27757
  587. package/dist/component/providers/email.js +0 -47
  588. package/dist/component/providers/email.js.map +0 -1
  589. package/dist/component/public.js.map +0 -1
  590. package/dist/component/server/implementation/db.js.map +0 -1
  591. package/dist/component/server/implementation/device.js +0 -135
  592. package/dist/component/server/implementation/device.js.map +0 -1
  593. package/dist/component/server/implementation/index.d.ts +0 -870
  594. package/dist/component/server/implementation/index.d.ts.map +0 -1
  595. package/dist/component/server/implementation/index.js +0 -610
  596. package/dist/component/server/implementation/index.js.map +0 -1
  597. package/dist/component/server/implementation/keys.js.map +0 -1
  598. package/dist/component/server/implementation/mutations/account.js +0 -39
  599. package/dist/component/server/implementation/mutations/account.js.map +0 -1
  600. package/dist/component/server/implementation/mutations/code.js.map +0 -1
  601. package/dist/component/server/implementation/mutations/index.js +0 -70
  602. package/dist/component/server/implementation/mutations/index.js.map +0 -1
  603. package/dist/component/server/implementation/mutations/invalidate.js +0 -29
  604. package/dist/component/server/implementation/mutations/invalidate.js.map +0 -1
  605. package/dist/component/server/implementation/mutations/oauth.js +0 -51
  606. package/dist/component/server/implementation/mutations/oauth.js.map +0 -1
  607. package/dist/component/server/implementation/mutations/refresh.js +0 -85
  608. package/dist/component/server/implementation/mutations/refresh.js.map +0 -1
  609. package/dist/component/server/implementation/mutations/register.js +0 -65
  610. package/dist/component/server/implementation/mutations/register.js.map +0 -1
  611. package/dist/component/server/implementation/mutations/retrieve.js +0 -50
  612. package/dist/component/server/implementation/mutations/retrieve.js.map +0 -1
  613. package/dist/component/server/implementation/mutations/signature.js +0 -27
  614. package/dist/component/server/implementation/mutations/signature.js.map +0 -1
  615. package/dist/component/server/implementation/mutations/signin.js.map +0 -1
  616. package/dist/component/server/implementation/mutations/signout.js +0 -27
  617. package/dist/component/server/implementation/mutations/signout.js.map +0 -1
  618. package/dist/component/server/implementation/mutations/store.js +0 -12
  619. package/dist/component/server/implementation/mutations/store.js.map +0 -1
  620. package/dist/component/server/implementation/mutations/verifier.js +0 -16
  621. package/dist/component/server/implementation/mutations/verifier.js.map +0 -1
  622. package/dist/component/server/implementation/mutations/verify.js +0 -105
  623. package/dist/component/server/implementation/mutations/verify.js.map +0 -1
  624. package/dist/component/server/implementation/passkey.js +0 -307
  625. package/dist/component/server/implementation/passkey.js.map +0 -1
  626. package/dist/component/server/implementation/provider.js +0 -19
  627. package/dist/component/server/implementation/provider.js.map +0 -1
  628. package/dist/component/server/implementation/ratelimit.js +0 -48
  629. package/dist/component/server/implementation/ratelimit.js.map +0 -1
  630. package/dist/component/server/implementation/redirects.js.map +0 -1
  631. package/dist/component/server/implementation/refresh.js +0 -109
  632. package/dist/component/server/implementation/refresh.js.map +0 -1
  633. package/dist/component/server/implementation/sessions.js.map +0 -1
  634. package/dist/component/server/implementation/signin.js +0 -148
  635. package/dist/component/server/implementation/signin.js.map +0 -1
  636. package/dist/component/server/implementation/tokens.js +0 -15
  637. package/dist/component/server/implementation/tokens.js.map +0 -1
  638. package/dist/component/server/implementation/totp.js +0 -142
  639. package/dist/component/server/implementation/totp.js.map +0 -1
  640. package/dist/component/server/implementation/types.d.ts +0 -42
  641. package/dist/component/server/implementation/types.d.ts.map +0 -1
  642. package/dist/component/server/implementation/types.js.map +0 -1
  643. package/dist/component/server/implementation/users.js.map +0 -1
  644. package/dist/component/server/implementation/utils.js +0 -56
  645. package/dist/component/server/implementation/utils.js.map +0 -1
  646. package/dist/component/server/providers.js.map +0 -1
  647. package/dist/component/server/templates.js +0 -84
  648. package/dist/component/server/templates.js.map +0 -1
  649. package/dist/server/cookies.d.ts.map +0 -1
  650. package/dist/server/implementation/db.d.ts +0 -86
  651. package/dist/server/implementation/db.d.ts.map +0 -1
  652. package/dist/server/implementation/db.js.map +0 -1
  653. package/dist/server/implementation/device.d.ts +0 -30
  654. package/dist/server/implementation/device.d.ts.map +0 -1
  655. package/dist/server/implementation/device.js +0 -135
  656. package/dist/server/implementation/device.js.map +0 -1
  657. package/dist/server/implementation/index.d.ts +0 -870
  658. package/dist/server/implementation/index.d.ts.map +0 -1
  659. package/dist/server/implementation/index.js +0 -610
  660. package/dist/server/implementation/index.js.map +0 -1
  661. package/dist/server/implementation/keys.d.ts +0 -66
  662. package/dist/server/implementation/keys.d.ts.map +0 -1
  663. package/dist/server/implementation/keys.js.map +0 -1
  664. package/dist/server/implementation/mutations/account.d.ts +0 -27
  665. package/dist/server/implementation/mutations/account.d.ts.map +0 -1
  666. package/dist/server/implementation/mutations/account.js +0 -39
  667. package/dist/server/implementation/mutations/account.js.map +0 -1
  668. package/dist/server/implementation/mutations/code.d.ts +0 -29
  669. package/dist/server/implementation/mutations/code.d.ts.map +0 -1
  670. package/dist/server/implementation/mutations/code.js.map +0 -1
  671. package/dist/server/implementation/mutations/index.d.ts +0 -310
  672. package/dist/server/implementation/mutations/index.d.ts.map +0 -1
  673. package/dist/server/implementation/mutations/index.js +0 -70
  674. package/dist/server/implementation/mutations/index.js.map +0 -1
  675. package/dist/server/implementation/mutations/invalidate.d.ts +0 -18
  676. package/dist/server/implementation/mutations/invalidate.d.ts.map +0 -1
  677. package/dist/server/implementation/mutations/invalidate.js +0 -29
  678. package/dist/server/implementation/mutations/invalidate.js.map +0 -1
  679. package/dist/server/implementation/mutations/oauth.d.ts +0 -23
  680. package/dist/server/implementation/mutations/oauth.d.ts.map +0 -1
  681. package/dist/server/implementation/mutations/oauth.js +0 -51
  682. package/dist/server/implementation/mutations/oauth.js.map +0 -1
  683. package/dist/server/implementation/mutations/refresh.d.ts +0 -20
  684. package/dist/server/implementation/mutations/refresh.d.ts.map +0 -1
  685. package/dist/server/implementation/mutations/refresh.js +0 -85
  686. package/dist/server/implementation/mutations/refresh.js.map +0 -1
  687. package/dist/server/implementation/mutations/register.d.ts +0 -37
  688. package/dist/server/implementation/mutations/register.d.ts.map +0 -1
  689. package/dist/server/implementation/mutations/register.js +0 -65
  690. package/dist/server/implementation/mutations/register.js.map +0 -1
  691. package/dist/server/implementation/mutations/retrieve.d.ts +0 -31
  692. package/dist/server/implementation/mutations/retrieve.d.ts.map +0 -1
  693. package/dist/server/implementation/mutations/retrieve.js +0 -50
  694. package/dist/server/implementation/mutations/retrieve.js.map +0 -1
  695. package/dist/server/implementation/mutations/signature.d.ts +0 -19
  696. package/dist/server/implementation/mutations/signature.d.ts.map +0 -1
  697. package/dist/server/implementation/mutations/signature.js +0 -27
  698. package/dist/server/implementation/mutations/signature.js.map +0 -1
  699. package/dist/server/implementation/mutations/signin.d.ts +0 -21
  700. package/dist/server/implementation/mutations/signin.d.ts.map +0 -1
  701. package/dist/server/implementation/mutations/signin.js.map +0 -1
  702. package/dist/server/implementation/mutations/signout.d.ts +0 -14
  703. package/dist/server/implementation/mutations/signout.d.ts.map +0 -1
  704. package/dist/server/implementation/mutations/signout.js +0 -27
  705. package/dist/server/implementation/mutations/signout.js.map +0 -1
  706. package/dist/server/implementation/mutations/store.d.ts +0 -11
  707. package/dist/server/implementation/mutations/store.d.ts.map +0 -1
  708. package/dist/server/implementation/mutations/store.js +0 -12
  709. package/dist/server/implementation/mutations/store.js.map +0 -1
  710. package/dist/server/implementation/mutations/verifier.d.ts +0 -11
  711. package/dist/server/implementation/mutations/verifier.d.ts.map +0 -1
  712. package/dist/server/implementation/mutations/verifier.js +0 -16
  713. package/dist/server/implementation/mutations/verifier.js.map +0 -1
  714. package/dist/server/implementation/mutations/verify.d.ts +0 -25
  715. package/dist/server/implementation/mutations/verify.d.ts.map +0 -1
  716. package/dist/server/implementation/mutations/verify.js +0 -105
  717. package/dist/server/implementation/mutations/verify.js.map +0 -1
  718. package/dist/server/implementation/passkey.d.ts +0 -24
  719. package/dist/server/implementation/passkey.d.ts.map +0 -1
  720. package/dist/server/implementation/passkey.js +0 -307
  721. package/dist/server/implementation/passkey.js.map +0 -1
  722. package/dist/server/implementation/provider.d.ts +0 -10
  723. package/dist/server/implementation/provider.d.ts.map +0 -1
  724. package/dist/server/implementation/provider.js +0 -19
  725. package/dist/server/implementation/provider.js.map +0 -1
  726. package/dist/server/implementation/ratelimit.d.ts +0 -10
  727. package/dist/server/implementation/ratelimit.d.ts.map +0 -1
  728. package/dist/server/implementation/ratelimit.js +0 -48
  729. package/dist/server/implementation/ratelimit.js.map +0 -1
  730. package/dist/server/implementation/redirects.d.ts +0 -10
  731. package/dist/server/implementation/redirects.d.ts.map +0 -1
  732. package/dist/server/implementation/redirects.js.map +0 -1
  733. package/dist/server/implementation/refresh.d.ts +0 -37
  734. package/dist/server/implementation/refresh.d.ts.map +0 -1
  735. package/dist/server/implementation/refresh.js +0 -109
  736. package/dist/server/implementation/refresh.js.map +0 -1
  737. package/dist/server/implementation/sessions.d.ts +0 -29
  738. package/dist/server/implementation/sessions.d.ts.map +0 -1
  739. package/dist/server/implementation/sessions.js.map +0 -1
  740. package/dist/server/implementation/signin.d.ts +0 -55
  741. package/dist/server/implementation/signin.d.ts.map +0 -1
  742. package/dist/server/implementation/signin.js +0 -148
  743. package/dist/server/implementation/signin.js.map +0 -1
  744. package/dist/server/implementation/tokens.d.ts +0 -11
  745. package/dist/server/implementation/tokens.d.ts.map +0 -1
  746. package/dist/server/implementation/tokens.js +0 -15
  747. package/dist/server/implementation/tokens.js.map +0 -1
  748. package/dist/server/implementation/totp.d.ts +0 -31
  749. package/dist/server/implementation/totp.d.ts.map +0 -1
  750. package/dist/server/implementation/totp.js +0 -142
  751. package/dist/server/implementation/totp.js.map +0 -1
  752. package/dist/server/implementation/types.d.ts +0 -189
  753. package/dist/server/implementation/types.d.ts.map +0 -1
  754. package/dist/server/implementation/types.js +0 -97
  755. package/dist/server/implementation/types.js.map +0 -1
  756. package/dist/server/implementation/users.d.ts +0 -30
  757. package/dist/server/implementation/users.d.ts.map +0 -1
  758. package/dist/server/implementation/users.js.map +0 -1
  759. package/dist/server/implementation/utils.d.ts +0 -19
  760. package/dist/server/implementation/utils.d.ts.map +0 -1
  761. package/dist/server/implementation/utils.js +0 -56
  762. package/dist/server/implementation/utils.js.map +0 -1
  763. package/dist/server/index.d.ts.map +0 -1
  764. package/dist/server/index.js.map +0 -1
  765. package/dist/server/oauth.d.ts.map +0 -1
  766. package/dist/server/providers.d.ts +0 -72
  767. package/dist/server/providers.d.ts.map +0 -1
  768. package/dist/server/providers.js.map +0 -1
  769. package/dist/server/templates.d.ts.map +0 -1
  770. package/dist/server/utils.d.ts.map +0 -1
  771. package/dist/server/version.d.ts +0 -5
  772. package/dist/server/version.d.ts.map +0 -1
  773. package/dist/server/version.js +0 -6
  774. package/dist/server/version.js.map +0 -1
  775. package/src/cli/utils.ts +0 -248
  776. package/src/server/implementation/device.ts +0 -307
  777. package/src/server/implementation/index.ts +0 -1583
  778. package/src/server/implementation/mutations/account.ts +0 -50
  779. package/src/server/implementation/mutations/index.ts +0 -157
  780. package/src/server/implementation/mutations/invalidate.ts +0 -42
  781. package/src/server/implementation/mutations/oauth.ts +0 -73
  782. package/src/server/implementation/mutations/refresh.ts +0 -175
  783. package/src/server/implementation/mutations/register.ts +0 -100
  784. package/src/server/implementation/mutations/retrieve.ts +0 -79
  785. package/src/server/implementation/mutations/signature.ts +0 -39
  786. package/src/server/implementation/mutations/signout.ts +0 -35
  787. package/src/server/implementation/mutations/store.ts +0 -7
  788. package/src/server/implementation/mutations/verifier.ts +0 -24
  789. package/src/server/implementation/mutations/verify.ts +0 -194
  790. package/src/server/implementation/passkey.ts +0 -620
  791. package/src/server/implementation/provider.ts +0 -36
  792. package/src/server/implementation/ratelimit.ts +0 -79
  793. package/src/server/implementation/refresh.ts +0 -172
  794. package/src/server/implementation/signin.ts +0 -296
  795. package/src/server/implementation/totp.ts +0 -342
  796. package/src/server/implementation/types.ts +0 -444
  797. package/src/server/implementation/utils.ts +0 -91
  798. package/src/server/version.ts +0 -2
@@ -0,0 +1,2095 @@
1
+ import { Auth, GenericActionCtx, GenericDataModel } from "convex/server";
2
+ import { GenericId } from "convex/values";
3
+
4
+ import {
5
+ buildScopeChecker,
6
+ checkKeyRateLimit,
7
+ generateApiKey,
8
+ hashApiKey,
9
+ } from "./keys";
10
+ import { materializeProvider } from "./config";
11
+ import { signInImpl } from "./signin";
12
+ import type {
13
+ AuthProviderConfig,
14
+ KeyDoc,
15
+ KeyScope,
16
+ ScopeChecker,
17
+ UserOrderBy,
18
+ UserWhere,
19
+ } from "./types";
20
+ import {
21
+ generateRandomString,
22
+ sha256,
23
+ TOKEN_SUB_CLAIM_DIVIDER,
24
+ } from "./utils";
25
+
26
+ type ComponentCtx = Pick<
27
+ GenericActionCtx<GenericDataModel>,
28
+ "runQuery" | "runMutation"
29
+ >;
30
+ type ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery">;
31
+ type ComponentAuthReadCtx = ComponentReadCtx & { auth: Auth };
32
+ type AccountCredentials = { id: string; secret?: string };
33
+ type CreateAccountArgs = {
34
+ provider: string;
35
+ account: AccountCredentials;
36
+ profile: Record<string, unknown>;
37
+ shouldLinkViaEmail?: boolean;
38
+ shouldLinkViaPhone?: boolean;
39
+ };
40
+ type RetrieveAccountArgs = { provider: string; account: AccountCredentials };
41
+ type UpdateAccountCredentialsArgs = {
42
+ provider: string;
43
+ account: { id: string; secret: string };
44
+ };
45
+
46
+ type CoreDeps = {
47
+ config: any;
48
+ getAuth: () => any;
49
+ callInvalidateSessions: <DataModel extends GenericDataModel>(
50
+ ctx: GenericActionCtx<DataModel>,
51
+ args: { userId: GenericId<"User">; except?: GenericId<"Session">[] },
52
+ ) => Promise<void>;
53
+ callCreateAccountFromCredentials: <DataModel extends GenericDataModel>(
54
+ ctx: GenericActionCtx<DataModel>,
55
+ args: CreateAccountArgs,
56
+ ) => Promise<any>;
57
+ callRetrieveAccountWithCredentials: <DataModel extends GenericDataModel>(
58
+ ctx: GenericActionCtx<DataModel>,
59
+ args: RetrieveAccountArgs,
60
+ ) => Promise<any>;
61
+ callModifyAccount: <DataModel extends GenericDataModel>(
62
+ ctx: GenericActionCtx<DataModel>,
63
+ args: UpdateAccountCredentialsArgs,
64
+ ) => Promise<void>;
65
+ getEnrichCtx: () => <DataModel extends GenericDataModel>(
66
+ ctx: GenericActionCtx<DataModel>,
67
+ ) => any;
68
+ inviteTokenAlphabet: string;
69
+ inviteTokenLength: number;
70
+ };
71
+
72
+ /**
73
+ * Build the core auth domains that back the canonical app API surface.
74
+ *
75
+ * Creates the grouped `user`, `session`, `account`, `provider`, `group`,
76
+ * `member`, `invite`, and `key` APIs used by the higher-level auth
77
+ * factory. Each namespace wraps the underlying Convex component functions with
78
+ * application-friendly helpers, result shaping, and documentation-friendly
79
+ * method names.
80
+ *
81
+ * @param deps - Internal component wiring, provider config, and helper
82
+ * functions needed to construct the domain API surface.
83
+ * @returns The core domain namespaces consumed by the auth factory.
84
+ */
85
+ export function createCoreDomains(deps: CoreDeps) {
86
+ const {
87
+ config,
88
+ getAuth,
89
+ callInvalidateSessions,
90
+ callCreateAccountFromCredentials,
91
+ callRetrieveAccountWithCredentials,
92
+ callModifyAccount,
93
+ getEnrichCtx,
94
+ inviteTokenAlphabet,
95
+ inviteTokenLength,
96
+ } = deps;
97
+
98
+ const roleDefinitions = config.authorization.roles as Record<
99
+ string,
100
+ { label?: string; grants: string[] }
101
+ >;
102
+
103
+ const getRoleDefinition = (roleId: string) => {
104
+ return roleDefinitions[roleId] ?? null;
105
+ };
106
+
107
+ const normalizeRoleIds = (
108
+ roleIds?: string[],
109
+ ):
110
+ | { ok: true; roleIds: string[] }
111
+ | { ok: false; invalidRoleIds: string[] } => {
112
+ const normalized = Array.from(new Set(roleIds ?? []));
113
+ const invalid = normalized.filter((id) => getRoleDefinition(id) === null);
114
+ if (invalid.length > 0) {
115
+ return { ok: false, invalidRoleIds: invalid };
116
+ }
117
+ return { ok: true, roleIds: normalized };
118
+ };
119
+
120
+ const listAllKeysByUser = async (ctx: ComponentCtx, userId: string) => {
121
+ const items: Array<{ _id: string }> = [];
122
+ let cursor: string | null = null;
123
+ do {
124
+ const page = (await ctx.runQuery(config.component.public.keyList, {
125
+ where: { userId },
126
+ limit: 100,
127
+ cursor,
128
+ })) as {
129
+ items: Array<{ _id: string }>;
130
+ nextCursor: string | null;
131
+ };
132
+ items.push(...page.items);
133
+ cursor = page.nextCursor;
134
+ } while (cursor !== null);
135
+ return items;
136
+ };
137
+
138
+ const listAllMembersByUser = async (ctx: ComponentCtx, userId: string) => {
139
+ const items: Array<{ _id: string }> = [];
140
+ let cursor: string | null = null;
141
+ do {
142
+ const page = (await ctx.runQuery(config.component.public.memberList, {
143
+ where: { userId },
144
+ limit: 100,
145
+ cursor,
146
+ })) as {
147
+ items: Array<{ _id: string }>;
148
+ nextCursor: string | null;
149
+ };
150
+ items.push(...page.items);
151
+ cursor = page.nextCursor;
152
+ } while (cursor !== null);
153
+ return items;
154
+ };
155
+
156
+ const resolveGrantedPermissions = (roleIds?: string[]) => {
157
+ const grants = new Set<string>();
158
+ for (const roleId of roleIds ?? []) {
159
+ const role = getRoleDefinition(roleId);
160
+ if (role === null) continue;
161
+ for (const grant of role.grants) {
162
+ grants.add(grant);
163
+ }
164
+ }
165
+ return Array.from(grants).sort();
166
+ };
167
+
168
+ // Per-execution cache — attached to ctx so each function invocation has its own.
169
+ // Eliminates redundant cross-component RPCs for the same entity within a handler.
170
+ type CtxCache = {
171
+ users: Map<string, any>;
172
+ groups: Map<string, any>;
173
+ };
174
+ const AUTH_CACHE = Symbol("__convexAuthCache");
175
+ function cache(ctx: any): CtxCache {
176
+ if (!ctx[AUTH_CACHE]) {
177
+ ctx[AUTH_CACHE] = {
178
+ users: new Map(),
179
+ groups: new Map(),
180
+ } satisfies CtxCache;
181
+ }
182
+ return ctx[AUTH_CACHE];
183
+ }
184
+
185
+ const user = {
186
+ /**
187
+ * Resolve the current user's ID.
188
+ *
189
+ * Checks two sources in order:
190
+ *
191
+ * 1. **Session JWT** — extracts the `userId` from `ctx.auth.getUserIdentity()`.
192
+ * This is the standard path for browser sessions and costs zero DB reads.
193
+ * 2. **API key** — if a `request` is provided and contains a
194
+ * `Bearer sk_*` Authorization header, the key is verified against the
195
+ * database and the owning `userId` is returned.
196
+ *
197
+ * Returns `null` when neither source produces a valid identity.
198
+ *
199
+ * @param ctx - Convex query, mutation, or action context.
200
+ * @param request - Optional incoming `Request` to check for API key auth.
201
+ * Only needed in HTTP actions or server-side handlers.
202
+ * @returns The user's document ID, or `null` if unauthenticated.
203
+ *
204
+ * @example Session auth (queries, mutations)
205
+ * ```ts
206
+ * const userId = await auth.user.id(ctx);
207
+ * if (!userId) return { ok: false, code: "NOT_SIGNED_IN" };
208
+ * ```
209
+ *
210
+ * @example API key auth (HTTP actions)
211
+ * ```ts
212
+ * const userId = await auth.user.id(ctx, request);
213
+ * ```
214
+ */
215
+ id: async (
216
+ ctx: { auth: Auth } & Partial<ComponentCtx>,
217
+ request?: Request,
218
+ ): Promise<string | null> => {
219
+ const identity = await ctx.auth.getUserIdentity();
220
+ if (identity !== null) {
221
+ const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
222
+ return userId;
223
+ }
224
+ if (request !== undefined && "runMutation" in ctx && ctx.runMutation) {
225
+ const authHeader = request.headers.get("Authorization");
226
+ if (authHeader?.startsWith("Bearer sk_")) {
227
+ const rawKey = authHeader.slice(7);
228
+ const result = await getAuth().key.verify(
229
+ ctx as ComponentCtx,
230
+ rawKey,
231
+ );
232
+ if (result.ok) {
233
+ return result.userId;
234
+ }
235
+ return null;
236
+ }
237
+ }
238
+ return null;
239
+ },
240
+ /**
241
+ * Fetch a user document by ID.
242
+ *
243
+ * Results are **cached per-execution** — calling `auth.user.get(ctx, id)`
244
+ * multiple times within the same query or mutation handler for the same
245
+ * `userId` returns the cached result without an additional component read.
246
+ *
247
+ * @param ctx - Convex query or mutation context.
248
+ * @param userId - The user's document ID.
249
+ * @returns The user document, or `null` if not found.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const user = await auth.user.get(ctx, userId);
254
+ * const name = user?.name ?? user?.email ?? "Unknown";
255
+ * ```
256
+ */
257
+ get: async (ctx: ComponentReadCtx, userId: string) => {
258
+ const c = cache(ctx);
259
+ if (c.users.has(userId)) return c.users.get(userId);
260
+ const result = await ctx.runQuery(config.component.public.userGetById, {
261
+ userId,
262
+ });
263
+ c.users.set(userId, result);
264
+ return result;
265
+ },
266
+ /**
267
+ * List users with optional filtering, pagination, and ordering.
268
+ *
269
+ * Supports filtering by `email`, `phone`, `name`, and `isAnonymous`.
270
+ * Results are paginated — pass `cursor` from a previous response to
271
+ * load the next page.
272
+ *
273
+ * @param ctx - Convex query or mutation context.
274
+ * @param opts.where - Filter criteria (all optional, combined with AND).
275
+ * @param opts.limit - Max items per page (default 50, max 100).
276
+ * @param opts.cursor - Cursor from a previous `nextCursor` for pagination.
277
+ * @param opts.orderBy - Sort field: `"_creationTime"` (default), `"email"`, etc.
278
+ * @param opts.order - Sort direction: `"asc"` or `"desc"` (default `"desc"`).
279
+ * @returns `{ items, nextCursor }` — `nextCursor` is `null` when no more pages.
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * const { items, nextCursor } = await auth.user.list(ctx, {
284
+ * where: { email: "alice@example.com" },
285
+ * limit: 10,
286
+ * });
287
+ * ```
288
+ */
289
+ list: async (
290
+ ctx: ComponentReadCtx,
291
+ opts: {
292
+ where?: UserWhere;
293
+ limit?: number;
294
+ cursor?: string | null;
295
+ orderBy?: UserOrderBy;
296
+ order?: "asc" | "desc";
297
+ } = {},
298
+ ) => {
299
+ return await ctx.runQuery(config.component.public.userList, opts);
300
+ },
301
+ /**
302
+ * Convenience method: resolve the current user ID from the session
303
+ * and fetch their full document in one call. Returns `null` if
304
+ * unauthenticated. Equivalent to `auth.user.id(ctx)` then `auth.user.get(ctx, id)`.
305
+ *
306
+ * @param ctx - Convex query or mutation context with `auth` for session lookup.
307
+ * @returns The authenticated user's document, or `null` if unauthenticated.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * const viewer = await auth.user.viewer(ctx);
312
+ * if (!viewer) throw new Error("Not signed in");
313
+ * console.log(viewer.name, viewer.email);
314
+ * ```
315
+ */
316
+ viewer: async (ctx: ComponentAuthReadCtx) => {
317
+ const userId = await user.id(ctx);
318
+ if (userId === null) return null;
319
+ return await user.get(ctx, userId);
320
+ },
321
+ /**
322
+ * Patch a user document. Accepts any fields defined on the User schema
323
+ * (e.g. `name`, `image`, `email`, `extend`).
324
+ *
325
+ * @param ctx - Convex mutation context.
326
+ * @param userId - The user's document ID.
327
+ * @param data - Fields to merge into the user document.
328
+ * @returns `{ ok: true, userId }`.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * await auth.user.update(ctx, userId, {
333
+ * name: "Alice Smith",
334
+ * image: "https://example.com/avatar.png",
335
+ * });
336
+ * ```
337
+ */
338
+ update: async (
339
+ ctx: ComponentCtx,
340
+ userId: string,
341
+ data: Record<string, unknown>,
342
+ ) => {
343
+ await ctx.runMutation(config.component.public.userPatch, {
344
+ userId,
345
+ data,
346
+ });
347
+ return { ok: true as const, userId };
348
+ },
349
+ /**
350
+ * Set the user's active group. Stored in `user.extend.lastActiveGroup`.
351
+ * Pass `groupId: null` to clear. Useful for multi-workspace apps
352
+ * where the UI needs to remember which workspace is selected.
353
+ *
354
+ * @param ctx - Convex mutation context.
355
+ * @param opts.userId - The user's document ID.
356
+ * @param opts.groupId - Group ID to set as active, or `null` to clear.
357
+ * @returns `{ ok: true, userId, groupId }` confirming the active group was set (or cleared).
358
+ *
359
+ * @example
360
+ * ```ts
361
+ * // Switch to a workspace
362
+ * await auth.user.setActiveGroup(ctx, { userId, groupId: workspaceId });
363
+ *
364
+ * // Clear the active workspace
365
+ * await auth.user.setActiveGroup(ctx, { userId, groupId: null });
366
+ * ```
367
+ */
368
+ setActiveGroup: async (
369
+ ctx: ComponentCtx,
370
+ opts: { userId: string; groupId: string | null },
371
+ ) => {
372
+ const doc = await user.get(ctx, opts.userId);
373
+ const existingExtend =
374
+ doc !== null &&
375
+ doc.extend !== null &&
376
+ typeof doc.extend === "object" &&
377
+ !Array.isArray(doc.extend)
378
+ ? { ...(doc.extend as Record<string, unknown>) }
379
+ : {};
380
+ if (opts.groupId === null) {
381
+ const { lastActiveGroup: _omit, ...rest } = existingExtend;
382
+ await user.update(ctx, opts.userId, { extend: rest });
383
+ return { ok: true as const, userId: opts.userId, groupId: null };
384
+ }
385
+ await user.update(ctx, opts.userId, {
386
+ extend: { ...existingExtend, lastActiveGroup: opts.groupId },
387
+ });
388
+ return { ok: true as const, userId: opts.userId, groupId: opts.groupId };
389
+ },
390
+ /**
391
+ * Read the user's active group ID from `user.extend.lastActiveGroup`.
392
+ * Returns `null` if no active group is set.
393
+ *
394
+ * @param ctx - Convex query or mutation context.
395
+ * @param opts.userId - The user's document ID.
396
+ * @returns The active group's document ID, or `null` if none is set.
397
+ *
398
+ * @example
399
+ * ```ts
400
+ * const activeGroupId = await auth.user.getActiveGroup(ctx, { userId });
401
+ * if (activeGroupId) {
402
+ * const group = await auth.group.get(ctx, activeGroupId);
403
+ * }
404
+ * ```
405
+ */
406
+ getActiveGroup: async (
407
+ ctx: ComponentReadCtx,
408
+ opts: { userId: string },
409
+ ): Promise<string | null> => {
410
+ const doc = await user.get(ctx, opts.userId);
411
+ if (
412
+ doc !== null &&
413
+ doc.extend !== null &&
414
+ typeof doc.extend === "object" &&
415
+ !Array.isArray(doc.extend)
416
+ ) {
417
+ const val = (doc.extend as Record<string, unknown>).lastActiveGroup;
418
+ if (typeof val === "string") return val;
419
+ }
420
+ return null;
421
+ },
422
+ /**
423
+ * Delete a user and all associated data.
424
+ *
425
+ * By default (`cascade: true`) deletes the user's sessions, accounts,
426
+ * API keys, group memberships, passkey credentials, and TOTP factors.
427
+ * Pass `{ cascade: false }` to delete only the user document itself.
428
+ *
429
+ * @param ctx - Convex mutation context.
430
+ * @param userId - The user's document ID.
431
+ * @param opts.cascade - Whether to delete related records (default `true`).
432
+ * @returns `{ ok: true, userId }`.
433
+ */
434
+ delete: async (
435
+ ctx: ComponentCtx,
436
+ userId: string,
437
+ opts?: { cascade?: boolean },
438
+ ) => {
439
+ const cascade = opts?.cascade !== false;
440
+ const [sessions, accounts, keys, members, passkeys, totps] =
441
+ await Promise.all([
442
+ ctx.runQuery(config.component.public.sessionListByUser, {
443
+ userId,
444
+ }) as Promise<Array<{ _id: string }>>,
445
+ ctx.runQuery(config.component.public.accountListByUser, {
446
+ userId,
447
+ }) as Promise<Array<{ _id: string }>>,
448
+ listAllKeysByUser(ctx, userId),
449
+ listAllMembersByUser(ctx, userId),
450
+ ctx.runQuery(config.component.public.passkeyListByUserId, {
451
+ userId,
452
+ }) as Promise<Array<{ _id: string }>>,
453
+ ctx.runQuery(config.component.public.totpListByUserId, {
454
+ userId,
455
+ }) as Promise<Array<{ _id: string }>>,
456
+ ]);
457
+ const totalLinked =
458
+ sessions.length +
459
+ accounts.length +
460
+ keys.length +
461
+ members.length +
462
+ passkeys.length +
463
+ totps.length;
464
+ if (!cascade && totalLinked > 0) {
465
+ return { ok: false as const, code: "INVALID_PARAMETERS" as const };
466
+ }
467
+ const deletions: Promise<unknown>[] = [];
468
+ for (const s of sessions)
469
+ deletions.push(
470
+ ctx.runMutation(config.component.public.sessionDelete, {
471
+ sessionId: s._id,
472
+ }),
473
+ );
474
+ for (const a of accounts)
475
+ deletions.push(
476
+ ctx.runMutation(config.component.public.accountDelete, {
477
+ accountId: a._id,
478
+ }),
479
+ );
480
+ for (const k of keys)
481
+ deletions.push(
482
+ ctx.runMutation(config.component.public.keyDelete, { keyId: k._id }),
483
+ );
484
+ for (const m of members)
485
+ deletions.push(
486
+ ctx.runMutation(config.component.public.memberRemove, {
487
+ memberId: m._id,
488
+ }),
489
+ );
490
+ for (const p of passkeys)
491
+ deletions.push(
492
+ ctx.runMutation(config.component.public.passkeyDelete, {
493
+ passkeyId: p._id,
494
+ }),
495
+ );
496
+ for (const t of totps)
497
+ deletions.push(
498
+ ctx.runMutation(config.component.public.totpDelete, {
499
+ totpId: t._id,
500
+ }),
501
+ );
502
+ await Promise.all(deletions);
503
+ await ctx.runMutation(config.component.public.userDelete, { userId });
504
+ return { ok: true as const, userId };
505
+ },
506
+ };
507
+
508
+ const session = {
509
+ /**
510
+ * Resolve the current session's ID from the JWT.
511
+ *
512
+ * Extracts the `sessionId` portion of the `subject` claim in the
513
+ * identity token returned by `ctx.auth.getUserIdentity()`. The subject
514
+ * is encoded as `userId<divider>sessionId`, so this splits on the
515
+ * divider and returns the second segment.
516
+ *
517
+ * Returns `null` when there is no authenticated identity (i.e. no
518
+ * valid session JWT).
519
+ *
520
+ * @param ctx - Convex query, mutation, or action context (must include `auth`).
521
+ * @returns The current session's document ID, or `null` if unauthenticated.
522
+ *
523
+ * @example
524
+ * ```ts
525
+ * const sessionId = await auth.session.current(ctx);
526
+ * if (!sessionId) throw new Error("Not signed in");
527
+ * ```
528
+ */
529
+ current: async (ctx: { auth: Auth }) => {
530
+ const identity = await ctx.auth.getUserIdentity();
531
+ if (identity === null) return null;
532
+ const [, sessionId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
533
+ return sessionId as GenericId<"Session">;
534
+ },
535
+ /**
536
+ * Invalidate (sign out) all sessions for a given user.
537
+ *
538
+ * Marks every session belonging to `userId` as invalid so that
539
+ * subsequent requests using those session JWTs will fail authentication.
540
+ * Optionally, one or more sessions can be excluded — this is useful
541
+ * when you want to sign out all *other* devices while keeping the
542
+ * current session alive.
543
+ *
544
+ * This method delegates to the component's internal session
545
+ * invalidation RPC.
546
+ *
547
+ * @param ctx - Convex action context.
548
+ * @param args.userId - The user whose sessions should be invalidated.
549
+ * @param args.except - Optional array of session IDs to keep valid.
550
+ * @returns `{ ok: true, userId, except }` confirming the operation.
551
+ *
552
+ * @example Sign out everywhere except the current session
553
+ * ```ts
554
+ * const sessionId = await auth.session.current(ctx);
555
+ * await auth.session.invalidate(ctx, {
556
+ * userId,
557
+ * except: sessionId ? [sessionId] : [],
558
+ * });
559
+ * ```
560
+ */
561
+ invalidate: async <DataModel extends GenericDataModel>(
562
+ ctx: GenericActionCtx<DataModel>,
563
+ args: { userId: GenericId<"User">; except?: GenericId<"Session">[] },
564
+ ) => {
565
+ await callInvalidateSessions(ctx, args);
566
+ return {
567
+ ok: true as const,
568
+ userId: args.userId,
569
+ except: args.except ?? [],
570
+ };
571
+ },
572
+ /**
573
+ * Fetch a session document by ID.
574
+ *
575
+ * Returns the full session document from the component database, or
576
+ * `null` if no session with the given ID exists. Useful for inspecting
577
+ * session metadata such as creation time or associated device info.
578
+ *
579
+ * @param ctx - Convex query or mutation context.
580
+ * @param sessionId - The session's document ID.
581
+ * @returns The session document, or `null` if not found.
582
+ *
583
+ * @example
584
+ * ```ts
585
+ * const session = await auth.session.get(ctx, sessionId);
586
+ * if (!session) throw new Error("Session not found");
587
+ * ```
588
+ */
589
+ get: async (ctx: ComponentReadCtx, sessionId: string) => {
590
+ return await ctx.runQuery(config.component.public.sessionGetById, {
591
+ sessionId,
592
+ });
593
+ },
594
+ /**
595
+ * List all sessions belonging to a user.
596
+ *
597
+ * Returns every session document associated with the given `userId`,
598
+ * including both active and expired sessions. This is useful for
599
+ * building "active sessions" UIs or auditing sign-in history.
600
+ *
601
+ * @param ctx - Convex query or mutation context.
602
+ * @param opts.userId - The user whose sessions to list.
603
+ * @returns An array of session documents.
604
+ *
605
+ * @example
606
+ * ```ts
607
+ * const sessions = await auth.session.list(ctx, { userId });
608
+ * console.log(`User has ${sessions.length} sessions`);
609
+ * ```
610
+ */
611
+ list: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
612
+ return await ctx.runQuery(config.component.public.sessionListByUser, {
613
+ userId: opts.userId,
614
+ });
615
+ },
616
+ };
617
+
618
+ const account = {
619
+ /**
620
+ * Create a new auth account linked to a user.
621
+ *
622
+ * Creates a credentials-based account record for a given provider. If
623
+ * the user does not yet exist, one is created from the supplied
624
+ * `profile`. If `shouldLinkViaEmail` or `shouldLinkViaPhone` is set,
625
+ * the account may be linked to an existing user whose email or phone
626
+ * matches the profile.
627
+ *
628
+ * The `account.secret` (e.g. a hashed password) is optional and
629
+ * depends on the provider type.
630
+ *
631
+ * @param ctx - Convex action context.
632
+ * @param args.provider - The provider ID (e.g. `"password"`, `"credentials"`).
633
+ * @param args.account.id - Provider-specific account identifier (e.g. email address).
634
+ * @param args.account.secret - Optional credential secret (e.g. hashed password).
635
+ * @param args.profile - Profile data used to create or update the user document.
636
+ * @param args.shouldLinkViaEmail - If `true`, link to an existing user by email match.
637
+ * @param args.shouldLinkViaPhone - If `true`, link to an existing user by phone match.
638
+ * @returns `{ ok: true, ...created }` with the created account and user information.
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * const result = await auth.account.create(ctx, {
643
+ * provider: "password",
644
+ * account: { id: "alice@example.com", secret: hashedPassword },
645
+ * profile: { email: "alice@example.com", name: "Alice" },
646
+ * });
647
+ * ```
648
+ */
649
+ create: async <DataModel extends GenericDataModel>(
650
+ ctx: GenericActionCtx<DataModel>,
651
+ args: CreateAccountArgs,
652
+ ) => {
653
+ const created = await callCreateAccountFromCredentials(ctx, args);
654
+ return { ok: true as const, ...created };
655
+ },
656
+ /**
657
+ * Retrieve an auth account by provider and credentials.
658
+ *
659
+ * Looks up an account matching the given provider and account ID,
660
+ * optionally verifying the secret (e.g. password). If the account
661
+ * exists and the credentials are valid, the full account document is
662
+ * returned. Returns `null` if no matching account is found or if the
663
+ * credential verification fails (indicated by a string error from the
664
+ * underlying RPC).
665
+ *
666
+ * @param ctx - Convex action context.
667
+ * @param args.provider - The provider ID (e.g. `"password"`).
668
+ * @param args.account.id - Provider-specific account identifier.
669
+ * @param args.account.secret - Optional credential secret to verify.
670
+ * @returns The account document, or `null` if not found or verification failed.
671
+ *
672
+ * @example
673
+ * ```ts
674
+ * const acct = await auth.account.get(ctx, {
675
+ * provider: "password",
676
+ * account: { id: "alice@example.com", secret: plainTextPassword },
677
+ * });
678
+ * if (!acct) throw new Error("Invalid credentials");
679
+ * ```
680
+ */
681
+ get: async <DataModel extends GenericDataModel>(
682
+ ctx: GenericActionCtx<DataModel>,
683
+ args: RetrieveAccountArgs,
684
+ ) => {
685
+ const result = await callRetrieveAccountWithCredentials(ctx, args);
686
+ if (typeof result === "string") {
687
+ return null;
688
+ }
689
+ return result;
690
+ },
691
+ /**
692
+ * Update the credentials (secret) for an existing auth account.
693
+ *
694
+ * Replaces the stored secret for the account identified by `provider`
695
+ * and `account.id`. This is the standard path for password changes
696
+ * and password resets — the new secret is typically a freshly hashed
697
+ * password.
698
+ *
699
+ * @param ctx - Convex action context.
700
+ * @param args.provider - The provider ID (e.g. `"password"`).
701
+ * @param args.account.id - Provider-specific account identifier.
702
+ * @param args.account.secret - The new credential secret to store.
703
+ * @returns `{ ok: true, accountId }` confirming the update.
704
+ *
705
+ * @example Password reset
706
+ * ```ts
707
+ * await auth.account.update(ctx, {
708
+ * provider: "password",
709
+ * account: { id: "alice@example.com", secret: newHashedPassword },
710
+ * });
711
+ * ```
712
+ */
713
+ update: async <DataModel extends GenericDataModel>(
714
+ ctx: GenericActionCtx<DataModel>,
715
+ args: UpdateAccountCredentialsArgs,
716
+ ) => {
717
+ await callModifyAccount(ctx, args);
718
+ return { ok: true as const, accountId: args.account.id };
719
+ },
720
+ /**
721
+ * Delete an auth account by ID.
722
+ *
723
+ * Removes the account record from the database. As a safety measure,
724
+ * deletion is **refused** if this is the user's only remaining account
725
+ * — the user must always have at least one linked account. If the
726
+ * account is not found, returns an error result instead of throwing.
727
+ *
728
+ * @param ctx - Convex mutation context.
729
+ * @param accountId - The account's document ID.
730
+ * @returns `{ ok: true, accountId }` on success, or
731
+ * `{ ok: false, code: "ACCOUNT_NOT_FOUND" }` if the account does not exist, or
732
+ * `{ ok: false, code: "INVALID_PARAMETERS" }` if it is the user's last account.
733
+ *
734
+ * @example
735
+ * ```ts
736
+ * const result = await auth.account.delete(ctx, accountId);
737
+ * if (!result.ok) {
738
+ * console.error("Cannot delete account:", result.code);
739
+ * }
740
+ * ```
741
+ */
742
+ delete: async (ctx: ComponentCtx, accountId: string) => {
743
+ const doc = await ctx.runQuery(config.component.public.accountGetById, {
744
+ accountId,
745
+ });
746
+ if (doc === null) {
747
+ return { ok: false as const, code: "ACCOUNT_NOT_FOUND" as const };
748
+ }
749
+ const allAccounts = (await ctx.runQuery(
750
+ config.component.public.accountListByUser,
751
+ { userId: (doc as any).userId },
752
+ )) as Array<{ _id: string }>;
753
+ if (allAccounts.length <= 1) {
754
+ return { ok: false as const, code: "INVALID_PARAMETERS" as const };
755
+ }
756
+ await ctx.runMutation(config.component.public.accountDelete, {
757
+ accountId,
758
+ });
759
+ return { ok: true as const, accountId };
760
+ },
761
+ /**
762
+ * List all passkey credentials registered for a user.
763
+ *
764
+ * Returns every WebAuthn passkey credential associated with the given
765
+ * `userId`. Each document includes the credential's public key,
766
+ * metadata (such as a human-readable name), and counters used for
767
+ * replay protection.
768
+ *
769
+ * @param ctx - Convex query or mutation context.
770
+ * @param opts.userId - The user whose passkeys to list.
771
+ * @returns An array of passkey credential documents.
772
+ *
773
+ * @example
774
+ * ```ts
775
+ * const passkeys = await auth.account.listPasskeys(ctx, { userId });
776
+ * for (const pk of passkeys) {
777
+ * console.log(pk.name ?? "Unnamed passkey", pk._id);
778
+ * }
779
+ * ```
780
+ */
781
+ listPasskeys: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
782
+ return await ctx.runQuery(
783
+ config.component.public.passkeyListByUserId,
784
+ opts,
785
+ );
786
+ },
787
+ /**
788
+ * Rename a passkey credential.
789
+ *
790
+ * Updates the human-readable `name` metadata on a passkey document.
791
+ * Useful for letting users label their passkeys (e.g. "MacBook Pro",
792
+ * "YubiKey 5").
793
+ *
794
+ * @param ctx - Convex mutation context.
795
+ * @param passkeyId - The passkey credential's document ID.
796
+ * @param name - The new display name for the passkey.
797
+ * @returns `{ ok: true, passkeyId }` confirming the rename.
798
+ *
799
+ * @example
800
+ * ```ts
801
+ * await auth.account.renamePasskey(ctx, passkeyId, "Work laptop");
802
+ * ```
803
+ */
804
+ renamePasskey: async (
805
+ ctx: ComponentCtx,
806
+ passkeyId: string,
807
+ name: string,
808
+ ) => {
809
+ await ctx.runMutation(config.component.public.passkeyUpdateMeta, {
810
+ passkeyId,
811
+ data: { name },
812
+ });
813
+ return { ok: true as const, passkeyId };
814
+ },
815
+ /**
816
+ * Delete a passkey credential.
817
+ *
818
+ * Permanently removes a WebAuthn passkey credential from the database.
819
+ * After deletion, the physical authenticator associated with this
820
+ * credential can no longer be used to sign in.
821
+ *
822
+ * @param ctx - Convex mutation context.
823
+ * @param passkeyId - The passkey credential's document ID.
824
+ * @returns `{ ok: true, passkeyId }` confirming the deletion.
825
+ *
826
+ * @example
827
+ * ```ts
828
+ * await auth.account.deletePasskey(ctx, passkeyId);
829
+ * ```
830
+ */
831
+ deletePasskey: async (ctx: ComponentCtx, passkeyId: string) => {
832
+ await ctx.runMutation(config.component.public.passkeyDelete, {
833
+ passkeyId,
834
+ });
835
+ return { ok: true as const, passkeyId };
836
+ },
837
+ /**
838
+ * List all TOTP (time-based one-time password) factors for a user.
839
+ *
840
+ * Returns every TOTP authenticator factor registered for the given
841
+ * `userId`. Each document includes the secret, issuer, and verification
842
+ * metadata needed for two-factor authentication management UIs.
843
+ *
844
+ * @param ctx - Convex query or mutation context.
845
+ * @param opts.userId - The user whose TOTP factors to list.
846
+ * @returns An array of TOTP factor documents.
847
+ *
848
+ * @example
849
+ * ```ts
850
+ * const totps = await auth.account.listTotps(ctx, { userId });
851
+ * const has2FA = totps.length > 0;
852
+ * ```
853
+ */
854
+ listTotps: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
855
+ return await ctx.runQuery(config.component.public.totpListByUserId, opts);
856
+ },
857
+ /**
858
+ * Delete a TOTP factor.
859
+ *
860
+ * Permanently removes a TOTP authenticator factor from the database.
861
+ * After deletion, codes generated by the corresponding authenticator
862
+ * app will no longer be accepted for two-factor authentication.
863
+ *
864
+ * @param ctx - Convex mutation context.
865
+ * @param totpId - The TOTP factor's document ID.
866
+ * @returns `{ ok: true, totpId }` confirming the deletion.
867
+ *
868
+ * @example
869
+ * ```ts
870
+ * await auth.account.deleteTotp(ctx, totpId);
871
+ * ```
872
+ */
873
+ deleteTotp: async (ctx: ComponentCtx, totpId: string) => {
874
+ await ctx.runMutation(config.component.public.totpDelete, { totpId });
875
+ return { ok: true as const, totpId };
876
+ },
877
+ };
878
+
879
+ const provider = {
880
+ /**
881
+ * Sign in through a specific provider from server-side code.
882
+ *
883
+ * Materializes the supplied provider config, runs the standard sign-in
884
+ * flow, and returns the resulting `userId` and `sessionId` when the
885
+ * provider completes authentication immediately. Returns `null` for
886
+ * providers that require additional client-side steps (for example
887
+ * redirects, email verification, or other non-immediate flows).
888
+ *
889
+ * This helper is useful for trusted server flows where you already know
890
+ * which provider should handle the sign-in and want the same behavior as
891
+ * the public auth API without generating tokens for the client.
892
+ *
893
+ * @param ctx - Convex action context.
894
+ * @param providerConfig - Provider configuration object to materialize and use.
895
+ * @param args.accountId - Optional account document ID to sign in with directly.
896
+ * @param args.params - Optional provider-specific parameters forwarded to the sign-in flow.
897
+ * @returns `{ userId, sessionId }` when sign-in succeeds immediately, or `null`
898
+ * when the provider does not produce an immediate signed-in result.
899
+ *
900
+ * @example
901
+ * ```ts
902
+ * const signedIn = await auth.provider.signIn(ctx, passwordProvider, {
903
+ * params: { email: "alice@example.com", password: "secret" },
904
+ * });
905
+ *
906
+ * if (!signedIn) {
907
+ * throw new Error("Provider requires another auth step");
908
+ * }
909
+ * ```
910
+ */
911
+ signIn: async <DataModel extends GenericDataModel>(
912
+ ctx: GenericActionCtx<DataModel>,
913
+ providerConfig: AuthProviderConfig,
914
+ args: {
915
+ accountId?: GenericId<"Account">;
916
+ params?: Record<string, unknown>;
917
+ },
918
+ ) => {
919
+ const result = await signInImpl(
920
+ getEnrichCtx()(ctx),
921
+ materializeProvider(providerConfig),
922
+ args as {
923
+ accountId?: GenericId<"Account">;
924
+ params?: Record<string, any>;
925
+ },
926
+ { generateTokens: false, allowExtraProviders: true },
927
+ );
928
+ return result.kind === "signedIn"
929
+ ? result.signedIn !== null
930
+ ? {
931
+ userId: result.signedIn.userId,
932
+ sessionId: result.signedIn.sessionId,
933
+ }
934
+ : null
935
+ : null;
936
+ },
937
+ };
938
+
939
+ const group = {
940
+ /**
941
+ * Create a new group (organization, workspace, team, etc.).
942
+ *
943
+ * Groups are hierarchical — set `parentGroupId` to nest under an existing
944
+ * group, or omit it to create a root-level group. Two denormalized fields
945
+ * are maintained automatically:
946
+ *
947
+ * - `rootGroupId` — the root ancestor (self-referencing for root groups).
948
+ * - `isRoot` — `true` when the group has no parent.
949
+ *
950
+ * @param ctx - Convex mutation context.
951
+ * @param data.name - Display name for the group.
952
+ * @param data.slug - URL-safe slug (optional).
953
+ * @param data.type - App-defined type string (e.g. `"workspace"`, `"team"`).
954
+ * @param data.parentGroupId - Nest under this group. Omit for a root group.
955
+ * @param data.tags - Faceted classification tags (normalized at write time).
956
+ * @param data.extend - Arbitrary app-specific metadata.
957
+ * @returns `{ ok: true, groupId }`.
958
+ *
959
+ * @example Root group
960
+ * ```ts
961
+ * const { groupId } = await auth.group.create(ctx, {
962
+ * name: "Acme Corp", type: "workspace",
963
+ * });
964
+ * ```
965
+ *
966
+ * @example Nested team
967
+ * ```ts
968
+ * const { groupId } = await auth.group.create(ctx, {
969
+ * name: "Engineering", parentGroupId: orgId, type: "team",
970
+ * });
971
+ * ```
972
+ */
973
+ create: async (
974
+ ctx: ComponentCtx,
975
+ data: {
976
+ name: string;
977
+ slug?: string;
978
+ type?: string;
979
+ parentGroupId?: string;
980
+ tags?: Array<{ key: string; value: string }>;
981
+ extend?: Record<string, unknown>;
982
+ },
983
+ ): Promise<{ ok: true; groupId: string }> => {
984
+ const groupId = (await ctx.runMutation(
985
+ config.component.public.groupCreate,
986
+ data,
987
+ )) as string;
988
+ return { ok: true, groupId };
989
+ },
990
+ /**
991
+ * Fetch a group document by ID.
992
+ *
993
+ * Results are **cached per-execution** — calling `auth.group.get(ctx, id)`
994
+ * multiple times within the same handler for the same `groupId` returns
995
+ * the cached result without an additional component read.
996
+ *
997
+ * @param ctx - Convex query or mutation context.
998
+ * @param groupId - The group's document ID.
999
+ * @returns The group document (including `rootGroupId`, `isRoot`), or `null`.
1000
+ */
1001
+ get: async (ctx: ComponentReadCtx, groupId: string) => {
1002
+ const c = cache(ctx);
1003
+ if (c.groups.has(groupId)) return c.groups.get(groupId);
1004
+ const result = await ctx.runQuery(config.component.public.groupGet, {
1005
+ groupId,
1006
+ });
1007
+ c.groups.set(groupId, result);
1008
+ return result;
1009
+ },
1010
+ /**
1011
+ * List groups with optional filtering, pagination, and ordering.
1012
+ *
1013
+ * Supports filtering by `slug`, `type`, `parentGroupId`, `name`,
1014
+ * `isRoot`, and tags (`tagsAll`, `tagsAny`). The `isRoot` and
1015
+ * `parentGroupId` filters use dedicated indexes for efficient queries.
1016
+ *
1017
+ * @param ctx - Convex query or mutation context.
1018
+ * @param opts.where - Filter criteria (all optional, combined with AND).
1019
+ * @param opts.where.isRoot - `true` to find root groups, `false` for nested.
1020
+ * @param opts.where.parentGroupId - List direct children of this group.
1021
+ * @param opts.limit - Max items per page (default 50, max 100).
1022
+ * @param opts.cursor - Cursor from a previous `nextCursor`.
1023
+ * @param opts.orderBy - Sort field: `"_creationTime"`, `"name"`, `"slug"`, `"type"`.
1024
+ * @param opts.order - Sort direction: `"asc"` or `"desc"`.
1025
+ * @returns `{ items, nextCursor }`.
1026
+ *
1027
+ * @example List root workspaces
1028
+ * ```ts
1029
+ * const { items } = await auth.group.list(ctx, {
1030
+ * where: { isRoot: true },
1031
+ * orderBy: "name", order: "asc",
1032
+ * });
1033
+ * ```
1034
+ *
1035
+ * @example List children of a group
1036
+ * ```ts
1037
+ * const { items } = await auth.group.list(ctx, {
1038
+ * where: { parentGroupId: orgId },
1039
+ * });
1040
+ * ```
1041
+ */
1042
+ list: async (
1043
+ ctx: ComponentReadCtx,
1044
+ opts?: {
1045
+ where?: {
1046
+ slug?: string;
1047
+ type?: string;
1048
+ parentGroupId?: string;
1049
+ name?: string;
1050
+ isRoot?: boolean;
1051
+ tagsAll?: Array<{ key: string; value: string }>;
1052
+ tagsAny?: Array<{ key: string; value: string }>;
1053
+ };
1054
+ limit?: number;
1055
+ cursor?: string | null;
1056
+ orderBy?: "_creationTime" | "name" | "slug" | "type";
1057
+ order?: "asc" | "desc";
1058
+ },
1059
+ ) => {
1060
+ return await ctx.runQuery(config.component.public.groupList, {
1061
+ where: opts?.where,
1062
+ limit: opts?.limit,
1063
+ cursor: opts?.cursor,
1064
+ orderBy: opts?.orderBy,
1065
+ order: opts?.order,
1066
+ });
1067
+ },
1068
+ /**
1069
+ * Patch a group document.
1070
+ *
1071
+ * If `parentGroupId` is changed, the group's `rootGroupId` and `isRoot`
1072
+ * fields are recomputed automatically and cascaded to all descendants.
1073
+ *
1074
+ * @param ctx - Convex mutation context.
1075
+ * @param groupId - The group's document ID.
1076
+ * @param data - Fields to merge (e.g. `name`, `slug`, `tags`, `parentGroupId`).
1077
+ * @returns `{ ok: true, groupId }`.
1078
+ *
1079
+ * @example
1080
+ * ```ts
1081
+ * await auth.group.update(ctx, groupId, {
1082
+ * name: "Acme Corp (renamed)",
1083
+ * slug: "acme-corp",
1084
+ * });
1085
+ * ```
1086
+ */
1087
+ update: async (
1088
+ ctx: ComponentCtx,
1089
+ groupId: string,
1090
+ data: Record<string, unknown>,
1091
+ ) => {
1092
+ await ctx.runMutation(config.component.public.groupUpdate, {
1093
+ groupId,
1094
+ data,
1095
+ });
1096
+ return { ok: true as const, groupId };
1097
+ },
1098
+ /**
1099
+ * Delete a group and recursively cascade to all descendant groups,
1100
+ * their members, invites, and tags.
1101
+ *
1102
+ * @param ctx - Convex mutation context.
1103
+ * @param groupId - The group's document ID.
1104
+ * @returns `{ ok: true, groupId }`.
1105
+ *
1106
+ * @example
1107
+ * ```ts
1108
+ * await auth.group.delete(ctx, groupId);
1109
+ * ```
1110
+ */
1111
+ delete: async (ctx: ComponentCtx, groupId: string) => {
1112
+ await ctx.runMutation(config.component.public.groupDelete, { groupId });
1113
+ return { ok: true as const, groupId };
1114
+ },
1115
+ /**
1116
+ * Walk up the group hierarchy from `groupId` and return all ancestor
1117
+ * groups in order from immediate parent to root. Detects cycles and
1118
+ * respects `maxDepth` (default 32).
1119
+ *
1120
+ * @param ctx - Convex query or mutation context.
1121
+ * @param opts.groupId - Starting group ID.
1122
+ * @param opts.maxDepth - Max levels to traverse (default 32).
1123
+ * @param opts.includeSelf - Include the starting group in the result.
1124
+ * @returns `{ ancestors, cycleDetected, maxDepthReached }`.
1125
+ *
1126
+ * @example
1127
+ * ```ts
1128
+ * const { ancestors } = await auth.group.ancestors(ctx, {
1129
+ * groupId: teamId,
1130
+ * includeSelf: true,
1131
+ * });
1132
+ * const rootOrg = ancestors[ancestors.length - 1];
1133
+ * ```
1134
+ */
1135
+ ancestors: async (
1136
+ ctx: ComponentReadCtx,
1137
+ opts: { groupId: string; maxDepth?: number; includeSelf?: boolean },
1138
+ ) => {
1139
+ const maxDepth = Math.max(0, Math.floor(opts.maxDepth ?? 32));
1140
+ const visited = new Set<string>();
1141
+ const ancestors: any[] = [];
1142
+ let cycleDetected = false;
1143
+ let maxDepthReached = false;
1144
+ let currentGroupId: string | undefined = opts.groupId;
1145
+ let depth = 0;
1146
+ let isFirst = true;
1147
+ while (currentGroupId !== undefined) {
1148
+ if (depth > maxDepth) {
1149
+ maxDepthReached = true;
1150
+ break;
1151
+ }
1152
+ if (visited.has(currentGroupId)) {
1153
+ cycleDetected = true;
1154
+ break;
1155
+ }
1156
+ visited.add(currentGroupId);
1157
+ const doc = await group.get(ctx, currentGroupId);
1158
+ if (doc === null) break;
1159
+ if (isFirst) {
1160
+ isFirst = false;
1161
+ if (opts.includeSelf) ancestors.push(doc);
1162
+ currentGroupId = doc.parentGroupId;
1163
+ depth += 1;
1164
+ continue;
1165
+ }
1166
+ ancestors.push(doc);
1167
+ currentGroupId = doc.parentGroupId;
1168
+ depth += 1;
1169
+ }
1170
+ return { ancestors, cycleDetected, maxDepthReached };
1171
+ },
1172
+ };
1173
+
1174
+ const member = {
1175
+ /**
1176
+ * Add a user to a group with optional role IDs.
1177
+ *
1178
+ * Role IDs are validated against the roles defined in `defineRoles()` —
1179
+ * invalid IDs return `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1180
+ * Throws `DUPLICATE_MEMBERSHIP` if the user is already a member.
1181
+ *
1182
+ * @param ctx - Convex mutation context.
1183
+ * @param data.groupId - The group to add the user to.
1184
+ * @param data.userId - The user's document ID.
1185
+ * @param data.roleIds - Role IDs from `defineRoles()` (optional).
1186
+ * @param data.status - Membership status string (optional, app-defined).
1187
+ * @param data.extend - Arbitrary app-specific metadata.
1188
+ * @returns `{ ok: true, memberId }` or `{ ok: false, code, invalidRoleIds }`.
1189
+ *
1190
+ * @example
1191
+ * ```ts
1192
+ * const { memberId } = await auth.member.create(ctx, {
1193
+ * groupId: orgId,
1194
+ * userId,
1195
+ * roleIds: [roles.orgAdmin.id],
1196
+ * });
1197
+ * ```
1198
+ */
1199
+ create: async (
1200
+ ctx: ComponentCtx,
1201
+ data: {
1202
+ groupId: string;
1203
+ userId: string;
1204
+ roleIds?: string[];
1205
+ status?: string;
1206
+ extend?: Record<string, unknown>;
1207
+ },
1208
+ ) => {
1209
+ const normalized = normalizeRoleIds(data.roleIds);
1210
+ if (!normalized.ok)
1211
+ return {
1212
+ ok: false as const,
1213
+ code: "INVALID_ROLE_IDS" as const,
1214
+ invalidRoleIds: normalized.invalidRoleIds,
1215
+ };
1216
+ const memberId = (await ctx.runMutation(
1217
+ config.component.public.memberAdd,
1218
+ { ...data, roleIds: normalized.roleIds },
1219
+ )) as string;
1220
+ return { ok: true as const, memberId };
1221
+ },
1222
+ /**
1223
+ * Fetch a membership document by its document ID.
1224
+ *
1225
+ * @param ctx - Convex query or mutation context.
1226
+ * @param memberId - The membership document ID.
1227
+ * @returns The membership document, or `null` if not found.
1228
+ *
1229
+ * @example
1230
+ * ```ts
1231
+ * const membership = await auth.member.get(ctx, memberId);
1232
+ * if (!membership) throw new Error("Membership not found");
1233
+ * console.log(membership.roleIds, membership.groupId);
1234
+ * ```
1235
+ */
1236
+ get: async (ctx: ComponentReadCtx, memberId: string) => {
1237
+ return await ctx.runQuery(config.component.public.memberGet, {
1238
+ memberId,
1239
+ });
1240
+ },
1241
+ /**
1242
+ * List memberships with optional filtering and pagination.
1243
+ *
1244
+ * Supports filtering by `groupId`, `userId`, `roleId`, and `status`.
1245
+ * When `groupId` and `status` are both provided, a compound index
1246
+ * is used for efficient queries.
1247
+ *
1248
+ * @param ctx - Convex query or mutation context.
1249
+ * @param opts.where - Filter criteria (all optional).
1250
+ * @param opts.limit - Max items per page (default 50, max 100).
1251
+ * @param opts.cursor - Cursor for pagination.
1252
+ * @param opts.orderBy - Sort field: `"_creationTime"` or `"status"`.
1253
+ * @param opts.order - Sort direction: `"asc"` or `"desc"`.
1254
+ * @returns `{ items, nextCursor }`.
1255
+ *
1256
+ * @example
1257
+ * ```ts
1258
+ * const { items } = await auth.member.list(ctx, {
1259
+ * where: { groupId: orgId },
1260
+ * limit: 20,
1261
+ * orderBy: "_creationTime",
1262
+ * order: "asc",
1263
+ * });
1264
+ * ```
1265
+ */
1266
+ list: async (
1267
+ ctx: ComponentReadCtx,
1268
+ opts?: {
1269
+ where?: {
1270
+ groupId?: string;
1271
+ userId?: string;
1272
+ roleId?: string;
1273
+ status?: string;
1274
+ };
1275
+ limit?: number;
1276
+ cursor?: string | null;
1277
+ orderBy?: "_creationTime" | "status";
1278
+ order?: "asc" | "desc";
1279
+ },
1280
+ ) => {
1281
+ return await ctx.runQuery(config.component.public.memberList, {
1282
+ where: opts?.where,
1283
+ limit: opts?.limit,
1284
+ cursor: opts?.cursor,
1285
+ orderBy: opts?.orderBy,
1286
+ order: opts?.order,
1287
+ });
1288
+ },
1289
+ /**
1290
+ * Remove a membership by its document ID.
1291
+ *
1292
+ * @param ctx - Convex mutation context.
1293
+ * @param memberId - The membership document ID.
1294
+ * @returns `{ ok: true, memberId }`.
1295
+ *
1296
+ * @example
1297
+ * ```ts
1298
+ * await auth.member.delete(ctx, memberId);
1299
+ * ```
1300
+ */
1301
+ delete: async (ctx: ComponentCtx, memberId: string) => {
1302
+ await ctx.runMutation(config.component.public.memberRemove, { memberId });
1303
+ return { ok: true as const, memberId };
1304
+ },
1305
+ /**
1306
+ * Patch a membership's `roleIds`, `status`, or `extend` fields.
1307
+ * Role IDs are validated against `defineRoles()`.
1308
+ *
1309
+ * @param ctx - Convex mutation context.
1310
+ * @param memberId - The membership document ID.
1311
+ * @param data - Fields to merge. `roleIds` are validated.
1312
+ * @returns `{ ok: true, memberId }` or `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1313
+ *
1314
+ * @example
1315
+ * ```ts
1316
+ * await auth.member.update(ctx, memberId, {
1317
+ * roleIds: [roles.orgAdmin.id],
1318
+ * status: "active",
1319
+ * });
1320
+ * ```
1321
+ */
1322
+ update: async (
1323
+ ctx: ComponentCtx,
1324
+ memberId: string,
1325
+ data: Record<string, unknown>,
1326
+ ) => {
1327
+ const nextData = { ...data };
1328
+ if ("roleIds" in nextData) {
1329
+ const normalized = normalizeRoleIds(
1330
+ Array.isArray(nextData.roleIds)
1331
+ ? (nextData.roleIds as string[])
1332
+ : undefined,
1333
+ );
1334
+ if (!normalized.ok)
1335
+ return {
1336
+ ok: false as const,
1337
+ code: "INVALID_ROLE_IDS" as const,
1338
+ invalidRoleIds: normalized.invalidRoleIds,
1339
+ };
1340
+ nextData.roleIds = normalized.roleIds;
1341
+ }
1342
+ await ctx.runMutation(config.component.public.memberUpdate, {
1343
+ memberId,
1344
+ data: nextData,
1345
+ });
1346
+ return { ok: true as const, memberId };
1347
+ },
1348
+ /**
1349
+ * Resolve a user's membership in a group, optionally walking the
1350
+ * hierarchy and checking grants.
1351
+ *
1352
+ * **Default (no flags):** Direct lookup at `groupId` only — fast,
1353
+ * 1 component read. Replaces the old `getByUserAndGroup`.
1354
+ *
1355
+ * **`ancestry: true`:** Walk the group hierarchy from `groupId` up
1356
+ * to root, returning the first matching membership. Includes
1357
+ * `traversedGroupIds` in the result. Expensive (N reads).
1358
+ *
1359
+ * **`grants: [...]`:** Check if the user has all specified grants.
1360
+ * Works at the direct level by default. Combine with
1361
+ * `ancestry: true` to check inherited grants.
1362
+ *
1363
+ * @param ctx - Convex query or mutation context.
1364
+ * @param opts.userId - The user's document ID.
1365
+ * @param opts.groupId - The group to check membership in.
1366
+ * @param opts.ancestry - Walk the hierarchy (default `false`).
1367
+ * @param opts.grants - Grant strings to check (optional).
1368
+ * @param opts.roleIds - Role IDs to filter by (optional).
1369
+ * @param opts.maxDepth - Max hierarchy levels (default 32, only with ancestry).
1370
+ * @returns `{ ok, membership, roleIds, grants, missingGrants, ... }`.
1371
+ * `ok` is `true` when membership exists and all requested grants are satisfied.
1372
+ *
1373
+ * @example Direct lookup
1374
+ * ```ts
1375
+ * const result = await auth.member.resolve(ctx, { userId, groupId });
1376
+ * if (!result.membership) return { ok: false, code: "NOT_A_MEMBER" };
1377
+ * ```
1378
+ *
1379
+ * @example Check grants (no hierarchy walk)
1380
+ * ```ts
1381
+ * const result = await auth.member.resolve(ctx, {
1382
+ * userId, groupId, grants: ["issues.create"],
1383
+ * });
1384
+ * if (!result.ok) return { ok: false, code: "FORBIDDEN" };
1385
+ * ```
1386
+ *
1387
+ * @example Walk hierarchy + check grants
1388
+ * ```ts
1389
+ * const result = await auth.member.resolve(ctx, {
1390
+ * userId, groupId: teamId, ancestry: true, grants: ["issues.create"],
1391
+ * });
1392
+ * ```
1393
+ */
1394
+ resolve: async (
1395
+ ctx: ComponentReadCtx,
1396
+ opts: {
1397
+ userId: string;
1398
+ groupId: string;
1399
+ ancestry?: boolean;
1400
+ roleIds?: string[];
1401
+ grants?: string[];
1402
+ maxDepth?: number;
1403
+ },
1404
+ ) => {
1405
+ const normalized = normalizeRoleIds(opts.roleIds);
1406
+ if (!normalized.ok)
1407
+ return {
1408
+ ok: false as const,
1409
+ membership: null,
1410
+ matchedGroupId: null,
1411
+ roleIds: [] as string[],
1412
+ grants: [] as string[],
1413
+ missingGrants: Array.from(new Set(opts.grants ?? [])),
1414
+ depth: null,
1415
+ isDirect: false,
1416
+ isInherited: false,
1417
+ traversedGroupIds: [] as string[],
1418
+ code: "INVALID_ROLE_IDS" as const,
1419
+ invalidRoleIds: normalized.invalidRoleIds,
1420
+ };
1421
+ const requestedRoleIds = normalized.roleIds;
1422
+ const roleFilter =
1423
+ requestedRoleIds.length > 0 ? new Set(requestedRoleIds) : null;
1424
+ const requiredGrants = Array.from(new Set(opts.grants ?? []));
1425
+ const useAncestry = opts.ancestry === true;
1426
+
1427
+ let membership: any = null;
1428
+ let matchedGroupId: string | null = null;
1429
+ let depth: number | null = null;
1430
+ let isDirect = false;
1431
+ let isInherited = false;
1432
+ let traversedGroupIds: string[] = [];
1433
+
1434
+ if (useAncestry) {
1435
+ // Hierarchy walk — single component RPC
1436
+ const maxDepth = Math.max(0, Math.floor(opts.maxDepth ?? 32));
1437
+ const result = await ctx.runQuery(
1438
+ config.component.public.memberResolve,
1439
+ {
1440
+ userId: opts.userId,
1441
+ groupId: opts.groupId,
1442
+ maxDepth,
1443
+ ancestry: true,
1444
+ },
1445
+ );
1446
+ membership = result.membership;
1447
+ matchedGroupId = result.matchedGroupId;
1448
+ depth = result.depth;
1449
+ isDirect = result.isDirect;
1450
+ isInherited = result.isInherited;
1451
+ traversedGroupIds = result.traversedGroupIds ?? [];
1452
+ } else {
1453
+ // Fast path — direct lookup, 1 read
1454
+ const doc = await ctx.runQuery(
1455
+ config.component.public.memberGetByGroupAndUser,
1456
+ { userId: opts.userId, groupId: opts.groupId },
1457
+ );
1458
+ membership = doc;
1459
+ matchedGroupId = doc ? opts.groupId : null;
1460
+ depth = doc ? 0 : null;
1461
+ isDirect = doc !== null;
1462
+ }
1463
+
1464
+ if (membership === null) {
1465
+ return {
1466
+ ok: false as const,
1467
+ membership: null,
1468
+ matchedGroupId: null,
1469
+ roleIds: [] as string[],
1470
+ grants: [] as string[],
1471
+ missingGrants: requiredGrants,
1472
+ depth: null,
1473
+ isDirect: false,
1474
+ isInherited: false,
1475
+ traversedGroupIds,
1476
+ };
1477
+ }
1478
+
1479
+ const membershipRoleIds = membership.roleIds ?? [];
1480
+ const membershipGrants = resolveGrantedPermissions(membershipRoleIds);
1481
+
1482
+ // Check role filter
1483
+ if (
1484
+ roleFilter !== null &&
1485
+ !membershipRoleIds.some((roleId: string) => roleFilter.has(roleId))
1486
+ ) {
1487
+ return {
1488
+ ok: false as const,
1489
+ membership: null,
1490
+ matchedGroupId: null,
1491
+ roleIds: [] as string[],
1492
+ grants: [] as string[],
1493
+ missingGrants: requiredGrants,
1494
+ depth: null,
1495
+ isDirect: false,
1496
+ isInherited: false,
1497
+ traversedGroupIds,
1498
+ };
1499
+ }
1500
+
1501
+ const missingGrants = requiredGrants.filter(
1502
+ (grant) => !membershipGrants.includes(grant),
1503
+ );
1504
+
1505
+ return {
1506
+ ok: missingGrants.length === 0,
1507
+ membership,
1508
+ matchedGroupId,
1509
+ roleIds: membershipRoleIds,
1510
+ grants: membershipGrants,
1511
+ missingGrants,
1512
+ depth,
1513
+ isDirect,
1514
+ isInherited,
1515
+ traversedGroupIds,
1516
+ };
1517
+ },
1518
+ };
1519
+
1520
+ const invite = {
1521
+ /**
1522
+ * Create a pending invite. Returns a one-time `token` the recipient
1523
+ * uses to accept. Optionally scoped to a group with role IDs.
1524
+ *
1525
+ * @param ctx - Convex mutation context.
1526
+ * @param data.groupId - The group to invite the user to (optional).
1527
+ * @param data.invitedByUserId - The user who created this invite (optional).
1528
+ * @param data.email - The invitee's email address (optional).
1529
+ * @param data.roleIds - Role IDs from `defineRoles()` to assign on acceptance (optional).
1530
+ * @param data.expiresTime - Expiration timestamp in ms since epoch (optional).
1531
+ * @param data.extend - Arbitrary app-specific metadata (optional).
1532
+ * @returns `{ ok: true, inviteId, token }` or `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1533
+ *
1534
+ * @example
1535
+ * ```ts
1536
+ * const { token } = await auth.invite.create(ctx, {
1537
+ * groupId, email: "alice@example.com", roleIds: [roles.member.id],
1538
+ * });
1539
+ * ```
1540
+ */
1541
+ create: async (
1542
+ ctx: ComponentCtx,
1543
+ data: {
1544
+ groupId?: string;
1545
+ invitedByUserId?: string;
1546
+ email?: string;
1547
+ roleIds?: string[];
1548
+ expiresTime?: number;
1549
+ extend?: Record<string, unknown>;
1550
+ },
1551
+ ) => {
1552
+ const normalized = normalizeRoleIds(data.roleIds);
1553
+ if (!normalized.ok)
1554
+ return {
1555
+ ok: false as const,
1556
+ code: "INVALID_ROLE_IDS" as const,
1557
+ invalidRoleIds: normalized.invalidRoleIds,
1558
+ };
1559
+ const token = generateRandomString(
1560
+ inviteTokenLength,
1561
+ inviteTokenAlphabet,
1562
+ );
1563
+ const tokenHash = await sha256(token);
1564
+ const inviteId = (await ctx.runMutation(
1565
+ config.component.public.inviteCreate,
1566
+ { ...data, roleIds: normalized.roleIds, tokenHash, status: "pending" },
1567
+ )) as string;
1568
+ return { ok: true as const, inviteId, token };
1569
+ },
1570
+ /**
1571
+ * Fetch an invite document by ID.
1572
+ *
1573
+ * Returns the full invite document including its status, email,
1574
+ * group, role IDs, and token hash. Useful for displaying invite
1575
+ * details or checking status before performing actions.
1576
+ *
1577
+ * @param ctx - Convex query or mutation context.
1578
+ * @param inviteId - The invite's document ID.
1579
+ * @returns The invite document, or `null` if not found.
1580
+ *
1581
+ * @example
1582
+ * ```ts
1583
+ * const invite = await auth.invite.get(ctx, inviteId);
1584
+ * if (invite?.status === "pending") {
1585
+ * // show invite details
1586
+ * }
1587
+ * ```
1588
+ */
1589
+ get: async (ctx: ComponentReadCtx, inviteId: string) => {
1590
+ return await ctx.runQuery(config.component.public.inviteGet, {
1591
+ inviteId,
1592
+ });
1593
+ },
1594
+ token: {
1595
+ /**
1596
+ * Look up an invite by its raw token string.
1597
+ *
1598
+ * Hashes the raw token and queries the database for a matching
1599
+ * invite. This is the standard path for invite-link landing pages
1600
+ * where the token is extracted from the URL.
1601
+ *
1602
+ * @param ctx - Convex query or mutation context.
1603
+ * @param token - The raw invite token string from the invite link.
1604
+ * @returns The invite document, or `null` if no matching invite exists.
1605
+ *
1606
+ * @example
1607
+ * ```ts
1608
+ * const invite = await auth.invite.token.get(ctx, tokenFromUrl);
1609
+ * if (!invite || invite.status !== "pending") {
1610
+ * throw new Error("Invalid or expired invite");
1611
+ * }
1612
+ * ```
1613
+ */
1614
+ get: async (ctx: ComponentReadCtx, token: string) => {
1615
+ const tokenHash = await sha256(token);
1616
+ return await ctx.runQuery(
1617
+ config.component.public.inviteGetByTokenHash,
1618
+ { tokenHash },
1619
+ );
1620
+ },
1621
+ /**
1622
+ * Accept an invite by token. Creates a membership and marks the invite as accepted.
1623
+ *
1624
+ * Hashes the raw token, finds the matching invite, creates a group
1625
+ * membership with the invite's role IDs, and transitions the invite
1626
+ * status to `"accepted"`.
1627
+ *
1628
+ * @param ctx - Convex mutation context.
1629
+ * @param args.token - The raw invite token string.
1630
+ * @param args.acceptedByUserId - The user accepting the invite.
1631
+ * @returns `{ ok: true, ...result }` with the created membership details.
1632
+ *
1633
+ * @example
1634
+ * ```ts
1635
+ * const result = await auth.invite.token.accept(ctx, {
1636
+ * token: tokenFromUrl,
1637
+ * acceptedByUserId: userId,
1638
+ * });
1639
+ * ```
1640
+ */
1641
+ accept: async (
1642
+ ctx: ComponentCtx,
1643
+ args: { token: string; acceptedByUserId: string },
1644
+ ) => {
1645
+ const tokenHash = await sha256(args.token);
1646
+ const result = await ctx.runMutation(
1647
+ config.component.public.inviteAcceptByToken,
1648
+ { tokenHash, acceptedByUserId: args.acceptedByUserId },
1649
+ );
1650
+ return { ok: true as const, ...result };
1651
+ },
1652
+ },
1653
+ /**
1654
+ * List invites with optional filtering by group, status, email, etc.
1655
+ * Results are paginated.
1656
+ *
1657
+ * @param ctx - Convex query or mutation context.
1658
+ * @param opts.where - Filter criteria (all optional).
1659
+ * @param opts.where.status - `"pending"`, `"accepted"`, `"revoked"`, or `"expired"`.
1660
+ * @param opts.limit - Max items per page (default 50, max 100).
1661
+ * @param opts.cursor - Cursor from a previous `nextCursor` for pagination.
1662
+ * @param opts.orderBy - Sort field: `"_creationTime"`, `"status"`, `"email"`,
1663
+ * `"expiresTime"`, or `"acceptedTime"`.
1664
+ * @param opts.order - Sort direction: `"asc"` or `"desc"`.
1665
+ * @returns `{ items, nextCursor }` — `nextCursor` is `null` when there are no more pages.
1666
+ *
1667
+ * @example
1668
+ * ```ts
1669
+ * const { items } = await auth.invite.list(ctx, {
1670
+ * where: { groupId, status: "pending" },
1671
+ * orderBy: "_creationTime",
1672
+ * order: "desc",
1673
+ * });
1674
+ * ```
1675
+ */
1676
+ list: async (
1677
+ ctx: ComponentReadCtx,
1678
+ opts?: {
1679
+ where?: {
1680
+ tokenHash?: string;
1681
+ groupId?: string;
1682
+ status?: "pending" | "accepted" | "revoked" | "expired";
1683
+ email?: string;
1684
+ invitedByUserId?: string;
1685
+ roleId?: string;
1686
+ acceptedByUserId?: string;
1687
+ };
1688
+ limit?: number;
1689
+ cursor?: string | null;
1690
+ orderBy?:
1691
+ | "_creationTime"
1692
+ | "status"
1693
+ | "email"
1694
+ | "expiresTime"
1695
+ | "acceptedTime";
1696
+ order?: "asc" | "desc";
1697
+ },
1698
+ ) => {
1699
+ return await ctx.runQuery(config.component.public.inviteList, {
1700
+ where: opts?.where,
1701
+ limit: opts?.limit,
1702
+ cursor: opts?.cursor,
1703
+ orderBy: opts?.orderBy,
1704
+ order: opts?.order,
1705
+ });
1706
+ },
1707
+ /**
1708
+ * Accept an invite by ID. Optionally specify who accepted it.
1709
+ *
1710
+ * Transitions the invite's status to `"accepted"` and optionally
1711
+ * records the accepting user. Unlike `invite.token.accept`, this
1712
+ * method does not automatically create a group membership — use it
1713
+ * for admin-driven invite acceptance flows.
1714
+ *
1715
+ * @param ctx - Convex mutation context.
1716
+ * @param inviteId - The invite's document ID.
1717
+ * @param acceptedByUserId - The user who accepted the invite (optional).
1718
+ * @returns `{ ok: true, inviteId, acceptedByUserId }`.
1719
+ *
1720
+ * @example
1721
+ * ```ts
1722
+ * await auth.invite.accept(ctx, inviteId, userId);
1723
+ * ```
1724
+ */
1725
+ accept: async (
1726
+ ctx: ComponentCtx,
1727
+ inviteId: string,
1728
+ acceptedByUserId?: string,
1729
+ ) => {
1730
+ await ctx.runMutation(config.component.public.inviteAccept, {
1731
+ inviteId,
1732
+ ...(acceptedByUserId ? { acceptedByUserId } : {}),
1733
+ });
1734
+ return {
1735
+ ok: true as const,
1736
+ inviteId,
1737
+ acceptedByUserId: acceptedByUserId ?? null,
1738
+ };
1739
+ },
1740
+ /**
1741
+ * Revoke a pending invite. Sets its status to `"revoked"`.
1742
+ *
1743
+ * Once revoked, the invite's token can no longer be used to accept
1744
+ * the invitation. This is a permanent status change.
1745
+ *
1746
+ * @param ctx - Convex mutation context.
1747
+ * @param inviteId - The invite's document ID.
1748
+ * @returns `{ ok: true, inviteId }`.
1749
+ *
1750
+ * @example
1751
+ * ```ts
1752
+ * await auth.invite.revoke(ctx, inviteId);
1753
+ * ```
1754
+ */
1755
+ revoke: async (ctx: ComponentCtx, inviteId: string) => {
1756
+ await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
1757
+ return { ok: true as const, inviteId };
1758
+ },
1759
+ };
1760
+
1761
+ const key = {
1762
+ /**
1763
+ * Create an API key for programmatic access. The returned `secret`
1764
+ * (prefixed `sk_`) is shown only once — it is stored as a hash.
1765
+ *
1766
+ * @param ctx - Convex mutation context.
1767
+ * @param opts.userId - Owner of the key.
1768
+ * @param opts.name - Human-readable name (e.g. `"CI Pipeline"`).
1769
+ * @param opts.scopes - Array of `{ resource, actions }` permission scopes.
1770
+ * @param opts.rateLimit - Optional per-key rate limit `{ maxRequests, windowMs }`.
1771
+ * @param opts.expiresAt - Optional expiration timestamp (ms since epoch).
1772
+ * @param opts.metadata - Arbitrary app-specific metadata.
1773
+ * @returns `{ ok: true, keyId, secret }`. Store `secret` securely — it cannot be retrieved later.
1774
+ *
1775
+ * @example
1776
+ * ```ts
1777
+ * const { secret } = await auth.key.create(ctx, {
1778
+ * userId,
1779
+ * name: "CI Pipeline",
1780
+ * scopes: [{ resource: "data", actions: ["read"] }],
1781
+ * });
1782
+ * ```
1783
+ */
1784
+ create: async (
1785
+ ctx: ComponentCtx,
1786
+ opts: {
1787
+ userId: string;
1788
+ name: string;
1789
+ scopes: KeyScope[];
1790
+ rateLimit?: { maxRequests: number; windowMs: number };
1791
+ expiresAt?: number;
1792
+ metadata?: Record<string, unknown>;
1793
+ },
1794
+ ): Promise<{ ok: true; keyId: string; secret: string }> => {
1795
+ const { raw, hashedKey, displayPrefix } = await generateApiKey("sk_");
1796
+ const keyId = (await ctx.runMutation(config.component.public.keyInsert, {
1797
+ userId: opts.userId,
1798
+ prefix: displayPrefix,
1799
+ hashedKey,
1800
+ name: opts.name,
1801
+ scopes: opts.scopes,
1802
+ rateLimit: opts.rateLimit,
1803
+ expiresAt: opts.expiresAt,
1804
+ metadata: opts.metadata,
1805
+ })) as string;
1806
+ return { ok: true, keyId, secret: raw };
1807
+ },
1808
+ /**
1809
+ * Verify an API key and return the owner's identity and scopes.
1810
+ *
1811
+ * Checks the key against the database, enforces expiration and rate
1812
+ * limits, and returns a `ScopeChecker` for permission evaluation.
1813
+ *
1814
+ * @param ctx - Convex mutation context (updates `lastUsedAt` and rate limit state).
1815
+ * @param rawKey - The raw `sk_*` key string.
1816
+ * @returns On success: `{ ok: true, userId, keyId, scopes }` where `scopes.can(resource, action)` checks permissions.
1817
+ * On failure: `{ ok: false, code }` with one of:
1818
+ * - `"INVALID_API_KEY"` — key not found.
1819
+ * - `"API_KEY_REVOKED"` — key was revoked.
1820
+ * - `"API_KEY_EXPIRED"` — key past its `expiresAt`.
1821
+ * - `"API_KEY_RATE_LIMITED"` — rate limit exceeded.
1822
+ *
1823
+ * @example
1824
+ * ```ts
1825
+ * const result = await auth.key.verify(ctx, rawKey);
1826
+ * if (!result.ok) return { ok: false, code: result.code };
1827
+ * const canRead = result.scopes.can("data", "read");
1828
+ * ```
1829
+ */
1830
+ verify: async (
1831
+ ctx: ComponentCtx,
1832
+ rawKey: string,
1833
+ ): Promise<
1834
+ | { ok: true; userId: string; keyId: string; scopes: ScopeChecker }
1835
+ | {
1836
+ ok: false;
1837
+ code:
1838
+ | "INVALID_API_KEY"
1839
+ | "API_KEY_REVOKED"
1840
+ | "API_KEY_EXPIRED"
1841
+ | "API_KEY_RATE_LIMITED";
1842
+ }
1843
+ > => {
1844
+ const hashedKey = await hashApiKey(rawKey);
1845
+ const doc = (await ctx.runQuery(
1846
+ config.component.public.keyGetByHashedKey,
1847
+ { hashedKey },
1848
+ )) as KeyDoc | null;
1849
+ if (!doc) {
1850
+ return { ok: false as const, code: "INVALID_API_KEY" as const };
1851
+ }
1852
+ const k = doc;
1853
+ if (k.revoked) {
1854
+ return { ok: false as const, code: "API_KEY_REVOKED" as const };
1855
+ }
1856
+ if (k.expiresAt && k.expiresAt < Date.now()) {
1857
+ return { ok: false as const, code: "API_KEY_EXPIRED" as const };
1858
+ }
1859
+ const patchData: Record<string, unknown> = { lastUsedAt: Date.now() };
1860
+ if (k.rateLimit) {
1861
+ const { limited, newState } = checkKeyRateLimit(
1862
+ k.rateLimit,
1863
+ k.rateLimitState ?? undefined,
1864
+ );
1865
+ if (limited) {
1866
+ return { ok: false as const, code: "API_KEY_RATE_LIMITED" as const };
1867
+ }
1868
+ patchData.rateLimitState = newState;
1869
+ }
1870
+ await ctx.runMutation(config.component.public.keyPatch, {
1871
+ keyId: k._id,
1872
+ data: patchData,
1873
+ });
1874
+ return {
1875
+ ok: true as const,
1876
+ userId: k.userId,
1877
+ keyId: k._id,
1878
+ scopes: buildScopeChecker(k.scopes),
1879
+ };
1880
+ },
1881
+ /**
1882
+ * List API keys with optional filtering by user, revocation status, name,
1883
+ * or prefix. Results are paginated. Does not expose raw key secrets.
1884
+ *
1885
+ * @param ctx - Convex query or mutation context.
1886
+ * @param opts.where - Filter criteria (all optional, combined with AND).
1887
+ * @param opts.limit - Max items per page (default 50, max 100).
1888
+ * @param opts.cursor - Cursor from a previous `nextCursor` for pagination.
1889
+ * @param opts.orderBy - Sort field: `"_creationTime"`, `"name"`, `"lastUsedAt"`, `"expiresAt"`, or `"revoked"`.
1890
+ * @param opts.order - Sort direction: `"asc"` or `"desc"`.
1891
+ * @returns `{ items, nextCursor }` — `nextCursor` is `null` when no more pages.
1892
+ *
1893
+ * @example
1894
+ * ```ts
1895
+ * const { items } = await auth.key.list(ctx, {
1896
+ * where: { userId, revoked: false },
1897
+ * orderBy: "lastUsedAt",
1898
+ * order: "desc",
1899
+ * });
1900
+ * ```
1901
+ */
1902
+ list: async (
1903
+ ctx: ComponentReadCtx,
1904
+ opts?: {
1905
+ where?: {
1906
+ userId?: string;
1907
+ revoked?: boolean;
1908
+ name?: string;
1909
+ prefix?: string;
1910
+ };
1911
+ limit?: number;
1912
+ cursor?: string | null;
1913
+ orderBy?:
1914
+ | "_creationTime"
1915
+ | "name"
1916
+ | "lastUsedAt"
1917
+ | "expiresAt"
1918
+ | "revoked";
1919
+ order?: "asc" | "desc";
1920
+ },
1921
+ ) => {
1922
+ return await ctx.runQuery(config.component.public.keyList, {
1923
+ where: opts?.where,
1924
+ limit: opts?.limit,
1925
+ cursor: opts?.cursor,
1926
+ orderBy: opts?.orderBy,
1927
+ order: opts?.order,
1928
+ });
1929
+ },
1930
+ /**
1931
+ * Fetch an API key record by ID. Does not expose the raw key secret.
1932
+ *
1933
+ * Returns the key document including metadata, scopes, rate limit
1934
+ * configuration, and revocation status. The raw secret is never
1935
+ * stored or returned — only the hashed key and display prefix.
1936
+ *
1937
+ * @param ctx - Convex query or mutation context.
1938
+ * @param keyId - The API key's document ID.
1939
+ * @returns `{ ok: true, key }` with the key document, or `{ ok: false }` if not found.
1940
+ *
1941
+ * @example
1942
+ * ```ts
1943
+ * const result = await auth.key.get(ctx, keyId);
1944
+ * if (!result.ok) throw new Error("Key not found");
1945
+ * console.log(result.key.name, result.key.prefix);
1946
+ * ```
1947
+ */
1948
+ get: async (
1949
+ ctx: ComponentReadCtx,
1950
+ keyId: string,
1951
+ ): Promise<{ ok: true; key: KeyDoc } | { ok: false }> => {
1952
+ const doc = (await ctx.runQuery(config.component.public.keyGetById, {
1953
+ keyId,
1954
+ })) as KeyDoc | null;
1955
+ if (!doc) return { ok: false as const };
1956
+ return { ok: true as const, key: doc };
1957
+ },
1958
+ /**
1959
+ * Update a key's name, scopes, or rate limit.
1960
+ *
1961
+ * Patches the specified fields on the API key document. Only the
1962
+ * provided fields are changed — omitted fields remain unchanged.
1963
+ *
1964
+ * @param ctx - Convex mutation context.
1965
+ * @param keyId - The API key's document ID.
1966
+ * @param data - Fields to merge into the key document.
1967
+ * @returns `{ ok: true, keyId }`.
1968
+ *
1969
+ * @example
1970
+ * ```ts
1971
+ * await auth.key.update(ctx, keyId, {
1972
+ * name: "CI Pipeline (updated)",
1973
+ * scopes: [{ resource: "data", actions: ["read", "write"] }],
1974
+ * });
1975
+ * ```
1976
+ */
1977
+ update: async (
1978
+ ctx: ComponentCtx,
1979
+ keyId: string,
1980
+ data: {
1981
+ name?: string;
1982
+ scopes?: KeyScope[];
1983
+ rateLimit?: { maxRequests: number; windowMs: number };
1984
+ },
1985
+ ) => {
1986
+ await ctx.runMutation(config.component.public.keyPatch, { keyId, data });
1987
+ return { ok: true as const, keyId };
1988
+ },
1989
+ /**
1990
+ * Soft-delete: set `revoked: true`. The key can no longer be verified.
1991
+ *
1992
+ * After revocation, any subsequent calls to `auth.key.verify` with
1993
+ * this key will return `{ ok: false, code: "API_KEY_REVOKED" }`.
1994
+ * The key record is preserved for audit purposes.
1995
+ *
1996
+ * @param ctx - Convex mutation context.
1997
+ * @param keyId - The API key's document ID.
1998
+ * @returns `{ ok: true, keyId }`.
1999
+ *
2000
+ * @example
2001
+ * ```ts
2002
+ * await auth.key.revoke(ctx, keyId);
2003
+ * ```
2004
+ */
2005
+ revoke: async (ctx: ComponentCtx, keyId: string) => {
2006
+ await ctx.runMutation(config.component.public.keyPatch, {
2007
+ keyId,
2008
+ data: { revoked: true },
2009
+ });
2010
+ return { ok: true as const, keyId };
2011
+ },
2012
+ /**
2013
+ * Hard-delete: permanently remove the key record.
2014
+ *
2015
+ * Unlike `revoke`, this permanently removes the key document from
2016
+ * the database. Use this when you need to fully clean up a key
2017
+ * rather than preserving it for audit history.
2018
+ *
2019
+ * @param ctx - Convex mutation context.
2020
+ * @param keyId - The API key's document ID.
2021
+ * @returns `{ ok: true, keyId }`.
2022
+ *
2023
+ * @example
2024
+ * ```ts
2025
+ * await auth.key.delete(ctx, keyId);
2026
+ * ```
2027
+ */
2028
+ delete: async (ctx: ComponentCtx, keyId: string) => {
2029
+ await ctx.runMutation(config.component.public.keyDelete, { keyId });
2030
+ return { ok: true as const, keyId };
2031
+ },
2032
+ /**
2033
+ * Rotate a key: revokes the old key and creates a new one with the
2034
+ * same user, scopes, and rate limit. Returns the new `keyId` and `secret`.
2035
+ * Fails with `{ ok: false }` if the key is already revoked.
2036
+ *
2037
+ * @param ctx - Convex mutation context.
2038
+ * @param keyId - The existing API key's document ID to rotate.
2039
+ * @param opts.name - Optional new name for the rotated key (defaults to the old name).
2040
+ * @param opts.expiresAt - Optional new expiration timestamp in ms since epoch.
2041
+ * @returns `{ ok: true, keyId, secret }` with the new key, or `{ ok: false, code }` on failure.
2042
+ *
2043
+ * @example
2044
+ * ```ts
2045
+ * const result = await auth.key.rotate(ctx, oldKeyId, {
2046
+ * expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days
2047
+ * });
2048
+ * if (result.ok) {
2049
+ * // Store result.secret securely — shown only once
2050
+ * }
2051
+ * ```
2052
+ */
2053
+ rotate: async (
2054
+ ctx: ComponentCtx,
2055
+ keyId: string,
2056
+ opts?: { name?: string; expiresAt?: number },
2057
+ ): Promise<
2058
+ | { ok: true; keyId: string; secret: string }
2059
+ | { ok: false; code: "INVALID_PARAMETERS" | "API_KEY_REVOKED" }
2060
+ > => {
2061
+ const existing = await ctx.runQuery(config.component.public.keyGetById, {
2062
+ keyId,
2063
+ });
2064
+ if (!existing) {
2065
+ return { ok: false as const, code: "INVALID_PARAMETERS" as const };
2066
+ }
2067
+ if ((existing as any).revoked === true) {
2068
+ return { ok: false as const, code: "API_KEY_REVOKED" as const };
2069
+ }
2070
+ await ctx.runMutation(config.component.public.keyPatch, {
2071
+ keyId,
2072
+ data: { revoked: true },
2073
+ });
2074
+ return await key.create(ctx, {
2075
+ userId: (existing as any).userId,
2076
+ name: opts?.name ?? (existing as any).name,
2077
+ scopes: (existing as any).scopes ?? [],
2078
+ rateLimit: (existing as any).rateLimit,
2079
+ expiresAt: opts?.expiresAt,
2080
+ metadata: (existing as any).metadata,
2081
+ });
2082
+ },
2083
+ };
2084
+
2085
+ return {
2086
+ user,
2087
+ session,
2088
+ account,
2089
+ provider,
2090
+ group,
2091
+ member,
2092
+ invite,
2093
+ key,
2094
+ };
2095
+ }