@robelest/convex-auth 0.0.4-preview.13 → 0.0.4-preview.16

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 (328) hide show
  1. package/README.md +140 -9
  2. package/dist/bin.cjs +5957 -5478
  3. package/dist/client/index.d.ts +3 -7
  4. package/dist/client/index.d.ts.map +1 -1
  5. package/dist/client/index.js +27 -26
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/component/_generated/api.d.ts +14 -0
  8. package/dist/component/_generated/api.d.ts.map +1 -1
  9. package/dist/component/_generated/api.js.map +1 -1
  10. package/dist/component/_generated/component.d.ts +1672 -24
  11. package/dist/component/_generated/component.d.ts.map +1 -1
  12. package/dist/component/convex.config.d.ts +2 -2
  13. package/dist/component/convex.config.d.ts.map +1 -1
  14. package/dist/component/index.d.ts +1 -1
  15. package/dist/component/index.js +2 -2
  16. package/dist/component/model.d.ts +153 -0
  17. package/dist/component/model.d.ts.map +1 -0
  18. package/dist/component/model.js +343 -0
  19. package/dist/component/model.js.map +1 -0
  20. package/dist/component/providers/sso.d.ts +1 -1
  21. package/dist/component/public/enterprise.d.ts +54 -0
  22. package/dist/component/public/enterprise.d.ts.map +1 -0
  23. package/dist/component/public/enterprise.js +515 -0
  24. package/dist/component/public/enterprise.js.map +1 -0
  25. package/dist/component/public/factors.d.ts +52 -0
  26. package/dist/component/public/factors.d.ts.map +1 -0
  27. package/dist/component/public/factors.js +285 -0
  28. package/dist/component/public/factors.js.map +1 -0
  29. package/dist/component/public/groups.d.ts +116 -0
  30. package/dist/component/public/groups.d.ts.map +1 -0
  31. package/dist/component/public/groups.js +596 -0
  32. package/dist/component/public/groups.js.map +1 -0
  33. package/dist/component/public/identity.d.ts +93 -0
  34. package/dist/component/public/identity.d.ts.map +1 -0
  35. package/dist/component/public/identity.js +426 -0
  36. package/dist/component/public/identity.js.map +1 -0
  37. package/dist/component/public/keys.d.ts +41 -0
  38. package/dist/component/public/keys.d.ts.map +1 -0
  39. package/dist/component/public/keys.js +157 -0
  40. package/dist/component/public/keys.js.map +1 -0
  41. package/dist/component/public/shared.d.ts +26 -0
  42. package/dist/component/public/shared.d.ts.map +1 -0
  43. package/dist/component/public/shared.js +32 -0
  44. package/dist/component/public/shared.js.map +1 -0
  45. package/dist/component/public.d.ts +9 -321
  46. package/dist/component/public.d.ts.map +1 -1
  47. package/dist/component/public.js +6 -2145
  48. package/dist/component/schema.d.ts +406 -260
  49. package/dist/component/schema.js +37 -32
  50. package/dist/component/schema.js.map +1 -1
  51. package/dist/component/server/auth.d.ts +161 -15
  52. package/dist/component/server/auth.d.ts.map +1 -1
  53. package/dist/component/server/auth.js +100 -7
  54. package/dist/component/server/auth.js.map +1 -1
  55. package/dist/component/server/cookies.js +3 -0
  56. package/dist/component/server/cookies.js.map +1 -1
  57. package/dist/component/server/db.js +1 -0
  58. package/dist/component/server/db.js.map +1 -1
  59. package/dist/component/server/device.js +3 -1
  60. package/dist/component/server/device.js.map +1 -1
  61. package/dist/component/server/domains/core.js +629 -0
  62. package/dist/component/server/domains/core.js.map +1 -0
  63. package/dist/component/server/domains/sso.js +884 -0
  64. package/dist/component/server/domains/sso.js.map +1 -0
  65. package/dist/component/server/factory.d.ts +136 -0
  66. package/dist/component/server/factory.d.ts.map +1 -0
  67. package/dist/component/server/factory.js +1134 -0
  68. package/dist/component/server/factory.js.map +1 -0
  69. package/dist/component/server/fx.js +2 -1
  70. package/dist/component/server/fx.js.map +1 -1
  71. package/dist/component/server/http.js +287 -0
  72. package/dist/component/server/http.js.map +1 -0
  73. package/dist/component/server/identity.js +13 -0
  74. package/dist/component/server/identity.js.map +1 -0
  75. package/dist/component/server/keys.js +4 -0
  76. package/dist/component/server/keys.js.map +1 -1
  77. package/dist/component/server/mutations/account.js +1 -1
  78. package/dist/component/server/mutations/index.js +2 -2
  79. package/dist/component/server/mutations/index.js.map +1 -1
  80. package/dist/component/server/mutations/invalidate.js +1 -1
  81. package/dist/component/server/mutations/oauth.js +10 -7
  82. package/dist/component/server/mutations/oauth.js.map +1 -1
  83. package/dist/component/server/mutations/refresh.js +1 -1
  84. package/dist/component/server/mutations/register.js +1 -1
  85. package/dist/component/server/mutations/retrieve.js +1 -1
  86. package/dist/component/server/mutations/signature.js +1 -1
  87. package/dist/component/server/mutations/store.js +6 -3
  88. package/dist/component/server/mutations/store.js.map +1 -1
  89. package/dist/component/server/mutations/verify.js +1 -1
  90. package/dist/component/server/oauth.js +3 -0
  91. package/dist/component/server/oauth.js.map +1 -1
  92. package/dist/component/server/passkey.js +3 -2
  93. package/dist/component/server/passkey.js.map +1 -1
  94. package/dist/component/server/provider.js +2 -0
  95. package/dist/component/server/provider.js.map +1 -1
  96. package/dist/component/server/providers.js +10 -0
  97. package/dist/component/server/providers.js.map +1 -1
  98. package/dist/component/server/ratelimit.js +3 -0
  99. package/dist/component/server/ratelimit.js.map +1 -1
  100. package/dist/component/server/redirects.js +2 -0
  101. package/dist/component/server/redirects.js.map +1 -1
  102. package/dist/component/server/refresh.js +5 -0
  103. package/dist/component/server/refresh.js.map +1 -1
  104. package/dist/component/server/sessions.js +5 -0
  105. package/dist/component/server/sessions.js.map +1 -1
  106. package/dist/component/server/signin.js +2 -1
  107. package/dist/component/server/signin.js.map +1 -1
  108. package/dist/component/server/sso.js +166 -19
  109. package/dist/component/server/sso.js.map +1 -1
  110. package/dist/component/server/tokens.js +1 -0
  111. package/dist/component/server/tokens.js.map +1 -1
  112. package/dist/component/server/totp.js +4 -2
  113. package/dist/component/server/totp.js.map +1 -1
  114. package/dist/component/server/types.d.ts +106 -38
  115. package/dist/component/server/types.d.ts.map +1 -1
  116. package/dist/component/server/types.js.map +1 -1
  117. package/dist/component/server/users.js +1 -0
  118. package/dist/component/server/users.js.map +1 -1
  119. package/dist/component/server/utils.js +44 -2
  120. package/dist/component/server/utils.js.map +1 -1
  121. package/dist/providers/anonymous.d.ts +1 -1
  122. package/dist/providers/credentials.d.ts +1 -1
  123. package/dist/providers/password.d.ts +1 -1
  124. package/dist/providers/sso.d.ts +1 -1
  125. package/dist/providers/sso.js.map +1 -1
  126. package/dist/server/auth.d.ts +163 -17
  127. package/dist/server/auth.d.ts.map +1 -1
  128. package/dist/server/auth.js +100 -7
  129. package/dist/server/auth.js.map +1 -1
  130. package/dist/server/cookies.d.ts +1 -38
  131. package/dist/server/cookies.js +3 -0
  132. package/dist/server/cookies.js.map +1 -1
  133. package/dist/server/db.d.ts +1 -125
  134. package/dist/server/db.js +1 -0
  135. package/dist/server/db.js.map +1 -1
  136. package/dist/server/device.d.ts +1 -24
  137. package/dist/server/device.js +3 -1
  138. package/dist/server/device.js.map +1 -1
  139. package/dist/server/domains/core.d.ts +434 -0
  140. package/dist/server/domains/core.d.ts.map +1 -0
  141. package/dist/server/domains/core.js +629 -0
  142. package/dist/server/domains/core.js.map +1 -0
  143. package/dist/server/domains/sso.d.ts +409 -0
  144. package/dist/server/domains/sso.d.ts.map +1 -0
  145. package/dist/server/domains/sso.js +884 -0
  146. package/dist/server/domains/sso.js.map +1 -0
  147. package/dist/server/enterpriseValidators.d.ts +1 -0
  148. package/dist/server/enterpriseValidators.js +60 -0
  149. package/dist/server/enterpriseValidators.js.map +1 -0
  150. package/dist/server/factory.d.ts +136 -0
  151. package/dist/server/factory.d.ts.map +1 -0
  152. package/dist/server/factory.js +1134 -0
  153. package/dist/server/factory.js.map +1 -0
  154. package/dist/server/fx.d.ts +1 -16
  155. package/dist/server/fx.d.ts.map +1 -1
  156. package/dist/server/fx.js +1 -0
  157. package/dist/server/fx.js.map +1 -1
  158. package/dist/server/http.d.ts +59 -0
  159. package/dist/server/http.d.ts.map +1 -0
  160. package/dist/server/http.js +287 -0
  161. package/dist/server/http.js.map +1 -0
  162. package/dist/server/identity.d.ts +1 -0
  163. package/dist/server/identity.js +13 -0
  164. package/dist/server/identity.js.map +1 -0
  165. package/dist/server/index.d.ts +468 -1
  166. package/dist/server/index.d.ts.map +1 -1
  167. package/dist/server/index.js +530 -36
  168. package/dist/server/index.js.map +1 -1
  169. package/dist/server/keys.d.ts +1 -57
  170. package/dist/server/keys.js +4 -0
  171. package/dist/server/keys.js.map +1 -1
  172. package/dist/server/mutations/account.d.ts +7 -7
  173. package/dist/server/mutations/account.d.ts.map +1 -1
  174. package/dist/server/mutations/code.d.ts +13 -13
  175. package/dist/server/mutations/code.d.ts.map +1 -1
  176. package/dist/server/mutations/index.d.ts +107 -107
  177. package/dist/server/mutations/index.d.ts.map +1 -1
  178. package/dist/server/mutations/index.js +1 -1
  179. package/dist/server/mutations/index.js.map +1 -1
  180. package/dist/server/mutations/invalidate.d.ts +5 -5
  181. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  182. package/dist/server/mutations/oauth.d.ts +10 -10
  183. package/dist/server/mutations/oauth.d.ts.map +1 -1
  184. package/dist/server/mutations/oauth.js +9 -6
  185. package/dist/server/mutations/oauth.js.map +1 -1
  186. package/dist/server/mutations/refresh.d.ts +4 -4
  187. package/dist/server/mutations/register.d.ts +12 -12
  188. package/dist/server/mutations/register.d.ts.map +1 -1
  189. package/dist/server/mutations/retrieve.d.ts +7 -7
  190. package/dist/server/mutations/signature.d.ts +5 -5
  191. package/dist/server/mutations/signin.d.ts +6 -6
  192. package/dist/server/mutations/signin.d.ts.map +1 -1
  193. package/dist/server/mutations/signout.d.ts +1 -1
  194. package/dist/server/mutations/store.d.ts +3 -2
  195. package/dist/server/mutations/store.d.ts.map +1 -1
  196. package/dist/server/mutations/store.js +6 -3
  197. package/dist/server/mutations/store.js.map +1 -1
  198. package/dist/server/mutations/verifier.d.ts +1 -1
  199. package/dist/server/mutations/verify.d.ts +11 -11
  200. package/dist/server/mutations/verify.d.ts.map +1 -1
  201. package/dist/server/oauth.d.ts +1 -59
  202. package/dist/server/oauth.js +3 -0
  203. package/dist/server/oauth.js.map +1 -1
  204. package/dist/server/passkey.d.ts.map +1 -1
  205. package/dist/server/passkey.js +3 -2
  206. package/dist/server/passkey.js.map +1 -1
  207. package/dist/server/provider.d.ts +1 -14
  208. package/dist/server/provider.d.ts.map +1 -1
  209. package/dist/server/provider.js +2 -0
  210. package/dist/server/provider.js.map +1 -1
  211. package/dist/server/providers.js +10 -0
  212. package/dist/server/providers.js.map +1 -1
  213. package/dist/server/ratelimit.d.ts +1 -22
  214. package/dist/server/ratelimit.js +3 -0
  215. package/dist/server/ratelimit.js.map +1 -1
  216. package/dist/server/redirects.d.ts +1 -10
  217. package/dist/server/redirects.js +2 -0
  218. package/dist/server/redirects.js.map +1 -1
  219. package/dist/server/refresh.d.ts +1 -37
  220. package/dist/server/refresh.js +5 -0
  221. package/dist/server/refresh.js.map +1 -1
  222. package/dist/server/sessions.d.ts +1 -28
  223. package/dist/server/sessions.js +5 -0
  224. package/dist/server/sessions.js.map +1 -1
  225. package/dist/server/signin.d.ts +1 -55
  226. package/dist/server/signin.js +2 -1
  227. package/dist/server/signin.js.map +1 -1
  228. package/dist/server/sso.d.ts +1 -348
  229. package/dist/server/sso.js +165 -18
  230. package/dist/server/sso.js.map +1 -1
  231. package/dist/server/templates.d.ts +1 -21
  232. package/dist/server/templates.js +1 -0
  233. package/dist/server/templates.js.map +1 -1
  234. package/dist/server/tokens.d.ts +1 -11
  235. package/dist/server/tokens.js +1 -0
  236. package/dist/server/tokens.js.map +1 -1
  237. package/dist/server/totp.d.ts +1 -23
  238. package/dist/server/totp.js +4 -2
  239. package/dist/server/totp.js.map +1 -1
  240. package/dist/server/types.d.ts +114 -77
  241. package/dist/server/types.d.ts.map +1 -1
  242. package/dist/server/types.js.map +1 -1
  243. package/dist/server/users.d.ts +1 -31
  244. package/dist/server/users.js +1 -0
  245. package/dist/server/users.js.map +1 -1
  246. package/dist/server/utils.d.ts +1 -27
  247. package/dist/server/utils.js +44 -2
  248. package/dist/server/utils.js.map +1 -1
  249. package/dist/server/version.d.ts +1 -1
  250. package/dist/server/version.js +1 -1
  251. package/dist/server/version.js.map +1 -1
  252. package/package.json +4 -5
  253. package/src/cli/bin.ts +5 -0
  254. package/src/cli/index.ts +22 -9
  255. package/src/cli/keys.ts +3 -0
  256. package/src/client/index.ts +36 -37
  257. package/src/component/_generated/api.ts +14 -0
  258. package/src/component/_generated/component.ts +2106 -9
  259. package/src/component/index.ts +3 -1
  260. package/src/component/model.ts +441 -0
  261. package/src/component/public/enterprise.ts +753 -0
  262. package/src/component/public/factors.ts +332 -0
  263. package/src/component/public/groups.ts +932 -0
  264. package/src/component/public/identity.ts +566 -0
  265. package/src/component/public/keys.ts +209 -0
  266. package/src/component/public/shared.ts +119 -0
  267. package/src/component/public.ts +5 -2965
  268. package/src/component/schema.ts +68 -63
  269. package/src/providers/sso.ts +1 -1
  270. package/src/server/auth.ts +413 -18
  271. package/src/server/cookies.ts +3 -0
  272. package/src/server/db.ts +3 -0
  273. package/src/server/device.ts +3 -1
  274. package/src/server/domains/core.ts +1071 -0
  275. package/src/server/domains/sso.ts +1749 -0
  276. package/src/server/enterpriseValidators.ts +93 -0
  277. package/src/server/factory.ts +2181 -0
  278. package/src/server/fx.ts +1 -0
  279. package/src/server/http.ts +529 -0
  280. package/src/server/identity.ts +18 -0
  281. package/src/server/index.ts +806 -40
  282. package/src/server/keys.ts +4 -0
  283. package/src/server/mutations/index.ts +1 -1
  284. package/src/server/mutations/oauth.ts +36 -8
  285. package/src/server/mutations/store.ts +6 -3
  286. package/src/server/oauth.ts +6 -0
  287. package/src/server/passkey.ts +3 -2
  288. package/src/server/provider.ts +2 -0
  289. package/src/server/providers.ts +20 -0
  290. package/src/server/ratelimit.ts +3 -0
  291. package/src/server/redirects.ts +2 -0
  292. package/src/server/refresh.ts +5 -0
  293. package/src/server/sessions.ts +5 -0
  294. package/src/server/signin.ts +1 -0
  295. package/src/server/sso.ts +259 -17
  296. package/src/server/templates.ts +1 -0
  297. package/src/server/tokens.ts +1 -0
  298. package/src/server/totp.ts +4 -2
  299. package/src/server/types.ts +178 -83
  300. package/src/server/users.ts +1 -0
  301. package/src/server/utils.ts +71 -1
  302. package/src/server/version.ts +1 -1
  303. package/dist/component/public.js.map +0 -1
  304. package/dist/component/server/implementation.d.ts +0 -1264
  305. package/dist/component/server/implementation.d.ts.map +0 -1
  306. package/dist/component/server/implementation.js +0 -2365
  307. package/dist/component/server/implementation.js.map +0 -1
  308. package/dist/server/cookies.d.ts.map +0 -1
  309. package/dist/server/db.d.ts.map +0 -1
  310. package/dist/server/device.d.ts.map +0 -1
  311. package/dist/server/implementation.d.ts +0 -1264
  312. package/dist/server/implementation.d.ts.map +0 -1
  313. package/dist/server/implementation.js +0 -2365
  314. package/dist/server/implementation.js.map +0 -1
  315. package/dist/server/keys.d.ts.map +0 -1
  316. package/dist/server/oauth.d.ts.map +0 -1
  317. package/dist/server/ratelimit.d.ts.map +0 -1
  318. package/dist/server/redirects.d.ts.map +0 -1
  319. package/dist/server/refresh.d.ts.map +0 -1
  320. package/dist/server/sessions.d.ts.map +0 -1
  321. package/dist/server/signin.d.ts.map +0 -1
  322. package/dist/server/sso.d.ts.map +0 -1
  323. package/dist/server/templates.d.ts.map +0 -1
  324. package/dist/server/tokens.d.ts.map +0 -1
  325. package/dist/server/totp.d.ts.map +0 -1
  326. package/dist/server/users.d.ts.map +0 -1
  327. package/dist/server/utils.d.ts.map +0 -1
  328. package/src/server/implementation.ts +0 -5336
@@ -0,0 +1,332 @@
1
+ import {
2
+ mutation,
3
+ query,
4
+ v,
5
+ vDeviceCodeDoc,
6
+ vDeviceStatus,
7
+ vPasskeyDoc,
8
+ vRateLimitResult,
9
+ vTotpFactorDoc,
10
+ } from "./shared";
11
+
12
+ // ============================================================================
13
+ // Passkeys
14
+ // ============================================================================
15
+
16
+ /** Store a new passkey credential for a user. */
17
+ export const passkeyInsert = mutation({
18
+ args: {
19
+ userId: v.id("User"),
20
+ credentialId: v.string(),
21
+ publicKey: v.bytes(),
22
+ algorithm: v.number(),
23
+ counter: v.number(),
24
+ transports: v.optional(v.array(v.string())),
25
+ deviceType: v.string(),
26
+ backedUp: v.boolean(),
27
+ name: v.optional(v.string()),
28
+ createdAt: v.number(),
29
+ },
30
+ returns: v.id("Passkey"),
31
+ handler: async (ctx, args) => {
32
+ return await ctx.db.insert("Passkey", args);
33
+ },
34
+ });
35
+
36
+ /** Look up a passkey by its credential ID. */
37
+ export const passkeyGetByCredentialId = query({
38
+ args: { credentialId: v.string() },
39
+ returns: v.union(vPasskeyDoc, v.null()),
40
+ handler: async (ctx, { credentialId }) => {
41
+ return await ctx.db
42
+ .query("Passkey")
43
+ .withIndex("credential_id", (q) => q.eq("credentialId", credentialId))
44
+ .unique();
45
+ },
46
+ });
47
+
48
+ /** List all passkeys for a user. */
49
+ export const passkeyListByUserId = query({
50
+ args: { userId: v.id("User") },
51
+ returns: v.array(vPasskeyDoc),
52
+ handler: async (ctx, { userId }) => {
53
+ return await ctx.db
54
+ .query("Passkey")
55
+ .withIndex("user_id", (q) => q.eq("userId", userId))
56
+ .collect();
57
+ },
58
+ });
59
+
60
+ /** Update a passkey's counter and last used timestamp after authentication. */
61
+ export const passkeyUpdateCounter = mutation({
62
+ args: {
63
+ passkeyId: v.id("Passkey"),
64
+ counter: v.number(),
65
+ lastUsedAt: v.number(),
66
+ },
67
+ returns: v.null(),
68
+ handler: async (ctx, { passkeyId, counter, lastUsedAt }) => {
69
+ await ctx.db.patch("Passkey", passkeyId, { counter, lastUsedAt });
70
+ return null;
71
+ },
72
+ });
73
+
74
+ /** Update a passkey's metadata (name). */
75
+ export const passkeyUpdateMeta = mutation({
76
+ args: { passkeyId: v.id("Passkey"), data: v.any() },
77
+ returns: v.null(),
78
+ handler: async (ctx, { passkeyId, data }) => {
79
+ await ctx.db.patch("Passkey", passkeyId, data);
80
+ return null;
81
+ },
82
+ });
83
+
84
+ /** Delete a passkey credential. */
85
+ export const passkeyDelete = mutation({
86
+ args: { passkeyId: v.id("Passkey") },
87
+ returns: v.null(),
88
+ handler: async (ctx, { passkeyId }) => {
89
+ await ctx.db.delete("Passkey", passkeyId);
90
+ return null;
91
+ },
92
+ });
93
+
94
+ // ============================================================================
95
+ // TOTP Two-Factor Authentication
96
+ // ============================================================================
97
+
98
+ /** Store a new TOTP enrollment for a user. */
99
+ export const totpInsert = mutation({
100
+ args: {
101
+ userId: v.id("User"),
102
+ secret: v.bytes(),
103
+ digits: v.number(),
104
+ period: v.number(),
105
+ verified: v.boolean(),
106
+ name: v.optional(v.string()),
107
+ createdAt: v.number(),
108
+ },
109
+ returns: v.id("TotpFactor"),
110
+ handler: async (ctx, args) => {
111
+ return await ctx.db.insert("TotpFactor", args);
112
+ },
113
+ });
114
+
115
+ /** Get a verified TOTP enrollment for a user (returns first match). */
116
+ export const totpGetVerifiedByUserId = query({
117
+ args: { userId: v.id("User") },
118
+ returns: v.union(vTotpFactorDoc, v.null()),
119
+ handler: async (ctx, { userId }) => {
120
+ return await ctx.db
121
+ .query("TotpFactor")
122
+ .withIndex("user_id", (q) => q.eq("userId", userId))
123
+ .filter((q) => q.eq(q.field("verified"), true))
124
+ .first();
125
+ },
126
+ });
127
+
128
+ /** List all TOTP enrollments for a user. */
129
+ export const totpListByUserId = query({
130
+ args: { userId: v.id("User") },
131
+ returns: v.array(vTotpFactorDoc),
132
+ handler: async (ctx, { userId }) => {
133
+ return await ctx.db
134
+ .query("TotpFactor")
135
+ .withIndex("user_id", (q) => q.eq("userId", userId))
136
+ .collect();
137
+ },
138
+ });
139
+
140
+ /** Get a TOTP enrollment by its ID. */
141
+ export const totpGetById = query({
142
+ args: { totpId: v.id("TotpFactor") },
143
+ returns: v.union(vTotpFactorDoc, v.null()),
144
+ handler: async (ctx, { totpId }) => {
145
+ return await ctx.db.get("TotpFactor", totpId);
146
+ },
147
+ });
148
+
149
+ /** Mark a TOTP enrollment as verified (setup complete). */
150
+ export const totpMarkVerified = mutation({
151
+ args: { totpId: v.id("TotpFactor"), lastUsedAt: v.number() },
152
+ returns: v.null(),
153
+ handler: async (ctx, { totpId, lastUsedAt }) => {
154
+ await ctx.db.patch("TotpFactor", totpId, { verified: true, lastUsedAt });
155
+ return null;
156
+ },
157
+ });
158
+
159
+ /** Update a TOTP enrollment's last used timestamp. */
160
+ export const totpUpdateLastUsed = mutation({
161
+ args: { totpId: v.id("TotpFactor"), lastUsedAt: v.number() },
162
+ returns: v.null(),
163
+ handler: async (ctx, { totpId, lastUsedAt }) => {
164
+ await ctx.db.patch("TotpFactor", totpId, { lastUsedAt });
165
+ return null;
166
+ },
167
+ });
168
+
169
+ /** Delete a TOTP enrollment. */
170
+ export const totpDelete = mutation({
171
+ args: { totpId: v.id("TotpFactor") },
172
+ returns: v.null(),
173
+ handler: async (ctx, { totpId }) => {
174
+ await ctx.db.delete("TotpFactor", totpId);
175
+ return null;
176
+ },
177
+ });
178
+
179
+ // ============================================================================
180
+ // Rate Limits
181
+ // ============================================================================
182
+
183
+ /** Look up a rate limit entry by its identifier. */
184
+ export const rateLimitGet = query({
185
+ args: { identifier: v.string() },
186
+ returns: v.union(vRateLimitResult, v.null()),
187
+ handler: async (ctx, { identifier }) => {
188
+ const row = await ctx.db
189
+ .query("RateLimit")
190
+ .withIndex("by_identifier", (q) => q.eq("identifier", identifier))
191
+ .unique();
192
+ if (row === null) {
193
+ return null;
194
+ }
195
+ return {
196
+ ...row,
197
+ attemptsLeft: row.attempts_left,
198
+ lastAttemptTime: row.last_attempt_time,
199
+ };
200
+ },
201
+ });
202
+
203
+ /** Create a new rate limit entry. */
204
+ export const rateLimitCreate = mutation({
205
+ args: {
206
+ identifier: v.string(),
207
+ attemptsLeft: v.number(),
208
+ lastAttemptTime: v.number(),
209
+ },
210
+ returns: v.id("RateLimit"),
211
+ handler: async (ctx, { identifier, attemptsLeft, lastAttemptTime }) => {
212
+ return await ctx.db.insert("RateLimit", {
213
+ identifier,
214
+ attempts_left: attemptsLeft,
215
+ last_attempt_time: lastAttemptTime,
216
+ });
217
+ },
218
+ });
219
+
220
+ /** Patch a rate limit entry with partial data. */
221
+ export const rateLimitPatch = mutation({
222
+ args: { rateLimitId: v.id("RateLimit"), data: v.any() },
223
+ returns: v.null(),
224
+ handler: async (ctx, { rateLimitId, data }) => {
225
+ const nextData: Record<string, unknown> = { ...data };
226
+ if (nextData.attemptsLeft !== undefined) {
227
+ nextData.attempts_left = nextData.attemptsLeft;
228
+ delete nextData.attemptsLeft;
229
+ }
230
+ if (nextData.lastAttemptTime !== undefined) {
231
+ nextData.last_attempt_time = nextData.lastAttemptTime;
232
+ delete nextData.lastAttemptTime;
233
+ }
234
+ await ctx.db.patch("RateLimit", rateLimitId, nextData);
235
+ return null;
236
+ },
237
+ });
238
+
239
+ /** Delete a rate limit entry. */
240
+ export const rateLimitDelete = mutation({
241
+ args: { rateLimitId: v.id("RateLimit") },
242
+ returns: v.null(),
243
+ handler: async (ctx, { rateLimitId }) => {
244
+ await ctx.db.delete("RateLimit", rateLimitId);
245
+ return null;
246
+ },
247
+ });
248
+
249
+ // ============================================================================
250
+ // Device Authorization (RFC 8628)
251
+ // ============================================================================
252
+
253
+ /** Insert a new device authorization record. */
254
+ export const deviceInsert = mutation({
255
+ args: {
256
+ deviceCodeHash: v.string(),
257
+ userCode: v.string(),
258
+ expiresAt: v.number(),
259
+ interval: v.number(),
260
+ status: vDeviceStatus,
261
+ },
262
+ returns: v.id("DeviceCode"),
263
+ handler: async (ctx, args) => {
264
+ return await ctx.db.insert("DeviceCode", args);
265
+ },
266
+ });
267
+
268
+ /** Look up a device authorization by its hashed device code. */
269
+ export const deviceGetByCodeHash = query({
270
+ args: { deviceCodeHash: v.string() },
271
+ returns: v.union(vDeviceCodeDoc, v.null()),
272
+ handler: async (ctx, { deviceCodeHash }) => {
273
+ return await ctx.db
274
+ .query("DeviceCode")
275
+ .withIndex("device_code_hash", (q) =>
276
+ q.eq("deviceCodeHash", deviceCodeHash),
277
+ )
278
+ .first();
279
+ },
280
+ });
281
+
282
+ /** Look up a pending device authorization by its user code. */
283
+ export const deviceGetByUserCode = query({
284
+ args: { userCode: v.string() },
285
+ returns: v.union(vDeviceCodeDoc, v.null()),
286
+ handler: async (ctx, { userCode }) => {
287
+ return await ctx.db
288
+ .query("DeviceCode")
289
+ .withIndex("user_code_status", (q) =>
290
+ q.eq("userCode", userCode).eq("status", "pending"),
291
+ )
292
+ .first();
293
+ },
294
+ });
295
+
296
+ /** Authorize a device code — link it to a user and session. */
297
+ export const deviceAuthorize = mutation({
298
+ args: {
299
+ deviceId: v.id("DeviceCode"),
300
+ userId: v.id("User"),
301
+ sessionId: v.id("Session"),
302
+ },
303
+ returns: v.null(),
304
+ handler: async (ctx, { deviceId, userId, sessionId }) => {
305
+ await ctx.db.patch("DeviceCode", deviceId, {
306
+ status: "authorized",
307
+ userId,
308
+ sessionId,
309
+ });
310
+ return null;
311
+ },
312
+ });
313
+
314
+ /** Update the last-polled timestamp on a device authorization record. */
315
+ export const deviceUpdateLastPolled = mutation({
316
+ args: { deviceId: v.id("DeviceCode"), lastPolledAt: v.number() },
317
+ returns: v.null(),
318
+ handler: async (ctx, { deviceId, lastPolledAt }) => {
319
+ await ctx.db.patch("DeviceCode", deviceId, { lastPolledAt });
320
+ return null;
321
+ },
322
+ });
323
+
324
+ /** Delete a device authorization record (cleanup after use or expiry). */
325
+ export const deviceDelete = mutation({
326
+ args: { deviceId: v.id("DeviceCode") },
327
+ returns: v.null(),
328
+ handler: async (ctx, { deviceId }) => {
329
+ await ctx.db.delete("DeviceCode", deviceId);
330
+ return null;
331
+ },
332
+ });