@robelest/convex-auth 0.0.4-preview.22 → 0.0.4-preview.24

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 (314) hide show
  1. package/README.md +10 -11
  2. package/dist/authorization/index.d.ts +1 -1
  3. package/dist/authorization/index.js +1 -1
  4. package/dist/authorization/index.js.map +1 -1
  5. package/dist/client/index.d.ts +1 -2
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +36 -39
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/component/client/index.d.ts +1 -2
  10. package/dist/component/index.js +2 -2
  11. package/dist/component/model.d.ts +9 -9
  12. package/dist/component/model.d.ts.map +1 -1
  13. package/dist/component/public/enterprise/audit.d.ts.map +1 -1
  14. package/dist/component/public/enterprise/audit.js.map +1 -1
  15. package/dist/component/public/enterprise/core.d.ts.map +1 -1
  16. package/dist/component/public/enterprise/core.js.map +1 -1
  17. package/dist/component/public/enterprise/domains.d.ts.map +1 -1
  18. package/dist/component/public/enterprise/domains.js.map +1 -1
  19. package/dist/component/public/enterprise/scim.d.ts.map +1 -1
  20. package/dist/component/public/enterprise/scim.js.map +1 -1
  21. package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
  22. package/dist/component/public/enterprise/secrets.js.map +1 -1
  23. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
  24. package/dist/component/public/enterprise/webhooks.js.map +1 -1
  25. package/dist/component/public/factors/devices.d.ts.map +1 -1
  26. package/dist/component/public/factors/devices.js.map +1 -1
  27. package/dist/component/public/factors/passkeys.d.ts.map +1 -1
  28. package/dist/component/public/factors/passkeys.js.map +1 -1
  29. package/dist/component/public/factors/totp.d.ts.map +1 -1
  30. package/dist/component/public/factors/totp.js.map +1 -1
  31. package/dist/component/public/groups/core.js.map +1 -1
  32. package/dist/component/public/groups/invites.d.ts.map +1 -1
  33. package/dist/component/public/groups/invites.js.map +1 -1
  34. package/dist/component/public/groups/members.d.ts.map +1 -1
  35. package/dist/component/public/groups/members.js.map +1 -1
  36. package/dist/component/public/identity/accounts.d.ts.map +1 -1
  37. package/dist/component/public/identity/accounts.js.map +1 -1
  38. package/dist/component/public/identity/codes.d.ts.map +1 -1
  39. package/dist/component/public/identity/codes.js.map +1 -1
  40. package/dist/component/public/identity/sessions.d.ts.map +1 -1
  41. package/dist/component/public/identity/sessions.js.map +1 -1
  42. package/dist/component/public/identity/tokens.d.ts.map +1 -1
  43. package/dist/component/public/identity/tokens.js.map +1 -1
  44. package/dist/component/public/identity/users.d.ts.map +1 -1
  45. package/dist/component/public/identity/users.js.map +1 -1
  46. package/dist/component/public/identity/verifiers.d.ts.map +1 -1
  47. package/dist/component/public/identity/verifiers.js.map +1 -1
  48. package/dist/component/public/security/keys.d.ts.map +1 -1
  49. package/dist/component/public/security/keys.js.map +1 -1
  50. package/dist/component/public/security/limits.d.ts.map +1 -1
  51. package/dist/component/public/security/limits.js.map +1 -1
  52. package/dist/component/schema.d.ts +41 -41
  53. package/dist/component/server/auth.d.ts +127 -130
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +100 -64
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/context.js +53 -0
  58. package/dist/component/server/context.js.map +1 -0
  59. package/dist/component/server/core.js +113 -250
  60. package/dist/component/server/core.js.map +1 -1
  61. package/dist/component/server/crypto.js +25 -7
  62. package/dist/component/server/crypto.js.map +1 -1
  63. package/dist/component/server/device.js +59 -16
  64. package/dist/component/server/device.js.map +1 -1
  65. package/dist/component/server/enterprise/domain.js +148 -59
  66. package/dist/component/server/enterprise/domain.js.map +1 -1
  67. package/dist/component/server/enterprise/http.js +36 -15
  68. package/dist/component/server/enterprise/http.js.map +1 -1
  69. package/dist/component/server/enterprise/oidc.js +1 -1
  70. package/dist/component/server/http.d.ts +85 -0
  71. package/dist/component/server/http.d.ts.map +1 -0
  72. package/dist/component/server/http.js +85 -22
  73. package/dist/component/server/http.js.map +1 -1
  74. package/dist/component/server/identity.js +5 -2
  75. package/dist/component/server/identity.js.map +1 -1
  76. package/dist/component/server/limits.js +21 -30
  77. package/dist/component/server/limits.js.map +1 -1
  78. package/dist/component/server/mutations/account.js +12 -10
  79. package/dist/component/server/mutations/account.js.map +1 -1
  80. package/dist/component/server/mutations/code.js +5 -2
  81. package/dist/component/server/mutations/code.js.map +1 -1
  82. package/dist/component/server/mutations/invalidate.js +1 -1
  83. package/dist/component/server/mutations/invalidate.js.map +1 -1
  84. package/dist/component/server/mutations/oauth.js +10 -4
  85. package/dist/component/server/mutations/oauth.js.map +1 -1
  86. package/dist/component/server/mutations/refresh.js +2 -2
  87. package/dist/component/server/mutations/refresh.js.map +1 -1
  88. package/dist/component/server/mutations/register.js +46 -42
  89. package/dist/component/server/mutations/register.js.map +1 -1
  90. package/dist/component/server/mutations/retrieve.js +21 -25
  91. package/dist/component/server/mutations/retrieve.js.map +1 -1
  92. package/dist/component/server/mutations/signature.js +10 -4
  93. package/dist/component/server/mutations/signature.js.map +1 -1
  94. package/dist/component/server/mutations/signout.js.map +1 -1
  95. package/dist/component/server/mutations/store.js +9 -24
  96. package/dist/component/server/mutations/store.js.map +1 -1
  97. package/dist/component/server/mutations/verifier.js.map +1 -1
  98. package/dist/component/server/mutations/verify.js +1 -1
  99. package/dist/component/server/mutations/verify.js.map +1 -1
  100. package/dist/component/server/oauth.js +53 -16
  101. package/dist/component/server/oauth.js.map +1 -1
  102. package/dist/component/server/passkey.js +115 -31
  103. package/dist/component/server/passkey.js.map +1 -1
  104. package/dist/component/server/redirects.js +9 -3
  105. package/dist/component/server/redirects.js.map +1 -1
  106. package/dist/component/server/refresh.js +10 -7
  107. package/dist/component/server/refresh.js.map +1 -1
  108. package/dist/component/server/runtime.d.ts +5 -5
  109. package/dist/component/server/runtime.js +156 -113
  110. package/dist/component/server/runtime.js.map +1 -1
  111. package/dist/component/server/signin.js +34 -10
  112. package/dist/component/server/signin.js.map +1 -1
  113. package/dist/component/server/totp.js +79 -19
  114. package/dist/component/server/totp.js.map +1 -1
  115. package/dist/component/server/types.d.ts +12 -20
  116. package/dist/component/server/types.d.ts.map +1 -1
  117. package/dist/component/server/types.js.map +1 -1
  118. package/dist/component/server/users.js +6 -3
  119. package/dist/component/server/users.js.map +1 -1
  120. package/dist/component/server/utils.js +10 -4
  121. package/dist/component/server/utils.js.map +1 -1
  122. package/dist/core/types.d.ts +14 -22
  123. package/dist/core/types.d.ts.map +1 -1
  124. package/dist/factors/device.js +8 -9
  125. package/dist/factors/device.js.map +1 -1
  126. package/dist/factors/passkey.js +18 -21
  127. package/dist/factors/passkey.js.map +1 -1
  128. package/dist/providers/password.js +66 -81
  129. package/dist/providers/password.js.map +1 -1
  130. package/dist/runtime/invite.js +2 -8
  131. package/dist/runtime/invite.js.map +1 -1
  132. package/dist/server/auth.d.ts +127 -130
  133. package/dist/server/auth.d.ts.map +1 -1
  134. package/dist/server/auth.js +100 -64
  135. package/dist/server/auth.js.map +1 -1
  136. package/dist/server/context.d.ts +1 -0
  137. package/dist/server/context.js +53 -0
  138. package/dist/server/context.js.map +1 -0
  139. package/dist/server/core.d.ts +74 -195
  140. package/dist/server/core.d.ts.map +1 -1
  141. package/dist/server/core.js +113 -250
  142. package/dist/server/core.js.map +1 -1
  143. package/dist/server/crypto.d.ts.map +1 -1
  144. package/dist/server/crypto.js +25 -7
  145. package/dist/server/crypto.js.map +1 -1
  146. package/dist/server/device.js +59 -16
  147. package/dist/server/device.js.map +1 -1
  148. package/dist/server/enterprise/domain.d.ts +0 -8
  149. package/dist/server/enterprise/domain.d.ts.map +1 -1
  150. package/dist/server/enterprise/domain.js +148 -59
  151. package/dist/server/enterprise/domain.js.map +1 -1
  152. package/dist/server/enterprise/http.d.ts.map +1 -1
  153. package/dist/server/enterprise/http.js +35 -14
  154. package/dist/server/enterprise/http.js.map +1 -1
  155. package/dist/server/http.d.ts +81 -3
  156. package/dist/server/http.d.ts.map +1 -1
  157. package/dist/server/http.js +84 -21
  158. package/dist/server/http.js.map +1 -1
  159. package/dist/server/identity.js +5 -2
  160. package/dist/server/identity.js.map +1 -1
  161. package/dist/server/index.d.ts +3 -2
  162. package/dist/server/index.js +2 -2
  163. package/dist/server/limits.js +21 -30
  164. package/dist/server/limits.js.map +1 -1
  165. package/dist/server/mounts.d.ts +25 -63
  166. package/dist/server/mounts.d.ts.map +1 -1
  167. package/dist/server/mounts.js +46 -107
  168. package/dist/server/mounts.js.map +1 -1
  169. package/dist/server/mutations/account.d.ts +8 -9
  170. package/dist/server/mutations/account.d.ts.map +1 -1
  171. package/dist/server/mutations/account.js +11 -9
  172. package/dist/server/mutations/account.js.map +1 -1
  173. package/dist/server/mutations/code.d.ts +12 -12
  174. package/dist/server/mutations/code.d.ts.map +1 -1
  175. package/dist/server/mutations/code.js +5 -2
  176. package/dist/server/mutations/code.js.map +1 -1
  177. package/dist/server/mutations/invalidate.d.ts +4 -4
  178. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  179. package/dist/server/mutations/invalidate.js.map +1 -1
  180. package/dist/server/mutations/oauth.d.ts +14 -12
  181. package/dist/server/mutations/oauth.d.ts.map +1 -1
  182. package/dist/server/mutations/oauth.js +9 -3
  183. package/dist/server/mutations/oauth.js.map +1 -1
  184. package/dist/server/mutations/refresh.d.ts +3 -3
  185. package/dist/server/mutations/refresh.d.ts.map +1 -1
  186. package/dist/server/mutations/refresh.js +1 -1
  187. package/dist/server/mutations/refresh.js.map +1 -1
  188. package/dist/server/mutations/register.d.ts +11 -11
  189. package/dist/server/mutations/register.d.ts.map +1 -1
  190. package/dist/server/mutations/register.js +45 -41
  191. package/dist/server/mutations/register.js.map +1 -1
  192. package/dist/server/mutations/retrieve.d.ts +6 -6
  193. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  194. package/dist/server/mutations/retrieve.js +20 -24
  195. package/dist/server/mutations/retrieve.js.map +1 -1
  196. package/dist/server/mutations/signature.d.ts +6 -7
  197. package/dist/server/mutations/signature.d.ts.map +1 -1
  198. package/dist/server/mutations/signature.js +9 -3
  199. package/dist/server/mutations/signature.js.map +1 -1
  200. package/dist/server/mutations/signin.d.ts +5 -5
  201. package/dist/server/mutations/signout.js.map +1 -1
  202. package/dist/server/mutations/store.d.ts +83 -83
  203. package/dist/server/mutations/store.js +8 -23
  204. package/dist/server/mutations/store.js.map +1 -1
  205. package/dist/server/mutations/verifier.js.map +1 -1
  206. package/dist/server/mutations/verify.d.ts +7 -7
  207. package/dist/server/mutations/verify.d.ts.map +1 -1
  208. package/dist/server/mutations/verify.js.map +1 -1
  209. package/dist/server/oauth.js +53 -16
  210. package/dist/server/oauth.js.map +1 -1
  211. package/dist/server/passkey.d.ts +2 -2
  212. package/dist/server/passkey.d.ts.map +1 -1
  213. package/dist/server/passkey.js +114 -30
  214. package/dist/server/passkey.js.map +1 -1
  215. package/dist/server/redirects.js +9 -3
  216. package/dist/server/redirects.js.map +1 -1
  217. package/dist/server/refresh.js +10 -7
  218. package/dist/server/refresh.js.map +1 -1
  219. package/dist/server/runtime.d.ts +11 -11
  220. package/dist/server/runtime.js +155 -112
  221. package/dist/server/runtime.js.map +1 -1
  222. package/dist/server/signin.js +34 -10
  223. package/dist/server/signin.js.map +1 -1
  224. package/dist/server/ssr.d.ts.map +1 -1
  225. package/dist/server/ssr.js +175 -184
  226. package/dist/server/ssr.js.map +1 -1
  227. package/dist/server/totp.js +78 -18
  228. package/dist/server/totp.js.map +1 -1
  229. package/dist/server/types.d.ts +13 -21
  230. package/dist/server/types.d.ts.map +1 -1
  231. package/dist/server/types.js.map +1 -1
  232. package/dist/server/users.js +6 -3
  233. package/dist/server/users.js.map +1 -1
  234. package/dist/server/utils.js +10 -4
  235. package/dist/server/utils.js.map +1 -1
  236. package/package.json +1 -5
  237. package/src/authorization/index.ts +1 -1
  238. package/src/client/core/types.ts +14 -14
  239. package/src/client/factors/device.ts +10 -12
  240. package/src/client/factors/passkey.ts +23 -26
  241. package/src/client/index.ts +54 -64
  242. package/src/client/runtime/invite.ts +5 -7
  243. package/src/component/index.ts +9 -3
  244. package/src/component/public/enterprise/audit.ts +6 -1
  245. package/src/component/public/enterprise/core.ts +1 -0
  246. package/src/component/public/enterprise/domains.ts +5 -1
  247. package/src/component/public/enterprise/scim.ts +1 -0
  248. package/src/component/public/enterprise/secrets.ts +1 -0
  249. package/src/component/public/enterprise/webhooks.ts +1 -0
  250. package/src/component/public/factors/devices.ts +1 -0
  251. package/src/component/public/factors/passkeys.ts +1 -0
  252. package/src/component/public/factors/totp.ts +1 -0
  253. package/src/component/public/groups/core.ts +1 -1
  254. package/src/component/public/groups/invites.ts +7 -1
  255. package/src/component/public/groups/members.ts +1 -0
  256. package/src/component/public/identity/accounts.ts +1 -0
  257. package/src/component/public/identity/codes.ts +1 -0
  258. package/src/component/public/identity/sessions.ts +1 -0
  259. package/src/component/public/identity/tokens.ts +1 -0
  260. package/src/component/public/identity/users.ts +1 -0
  261. package/src/component/public/identity/verifiers.ts +1 -0
  262. package/src/component/public/security/keys.ts +1 -0
  263. package/src/component/public/security/limits.ts +1 -0
  264. package/src/providers/password.ts +89 -110
  265. package/src/server/auth.ts +240 -182
  266. package/src/server/context.ts +90 -0
  267. package/src/server/core.ts +195 -286
  268. package/src/server/crypto.ts +31 -29
  269. package/src/server/device.ts +65 -32
  270. package/src/server/enterprise/domain.ts +158 -170
  271. package/src/server/enterprise/http.ts +46 -39
  272. package/src/server/http.ts +289 -30
  273. package/src/server/identity.ts +5 -5
  274. package/src/server/index.ts +9 -3
  275. package/src/server/limits.ts +53 -80
  276. package/src/server/mounts.ts +56 -80
  277. package/src/server/mutations/account.ts +22 -36
  278. package/src/server/mutations/code.ts +6 -6
  279. package/src/server/mutations/invalidate.ts +1 -1
  280. package/src/server/mutations/oauth.ts +14 -8
  281. package/src/server/mutations/refresh.ts +5 -4
  282. package/src/server/mutations/register.ts +87 -132
  283. package/src/server/mutations/retrieve.ts +44 -44
  284. package/src/server/mutations/signature.ts +13 -6
  285. package/src/server/mutations/signout.ts +1 -1
  286. package/src/server/mutations/store.ts +16 -31
  287. package/src/server/mutations/verifier.ts +1 -1
  288. package/src/server/mutations/verify.ts +3 -5
  289. package/src/server/oauth.ts +60 -69
  290. package/src/server/passkey.ts +567 -517
  291. package/src/server/redirects.ts +10 -6
  292. package/src/server/refresh.ts +14 -18
  293. package/src/server/runtime.ts +340 -302
  294. package/src/server/signin.ts +44 -37
  295. package/src/server/ssr.ts +390 -407
  296. package/src/server/totp.ts +85 -35
  297. package/src/server/types.ts +19 -22
  298. package/src/server/users.ts +7 -6
  299. package/src/server/utils.ts +10 -12
  300. package/dist/component/server/authError.js +0 -34
  301. package/dist/component/server/authError.js.map +0 -1
  302. package/dist/component/server/errors.d.ts +0 -1
  303. package/dist/component/server/errors.js +0 -137
  304. package/dist/component/server/errors.js.map +0 -1
  305. package/dist/server/authError.d.ts +0 -46
  306. package/dist/server/authError.d.ts.map +0 -1
  307. package/dist/server/authError.js +0 -34
  308. package/dist/server/authError.js.map +0 -1
  309. package/dist/server/errors.d.ts +0 -177
  310. package/dist/server/errors.d.ts.map +0 -1
  311. package/dist/server/errors.js +0 -212
  312. package/dist/server/errors.js.map +0 -1
  313. package/src/server/authError.ts +0 -44
  314. package/src/server/errors.ts +0 -290
@@ -1,10 +1,9 @@
1
1
  import { Fx } from "@robelest/fx";
2
+ import { ConvexError } from "convex/values";
2
3
 
3
4
  import { authDb } from "./db";
4
- import { AuthError } from "./authError";
5
5
  import { Doc, MutationCtx } from "./types";
6
6
  import { ConvexAuthConfig } from "./types";
7
- import { errorMessage } from "./utils";
8
7
 
9
8
  const DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR = 10;
10
9
 
@@ -16,7 +15,7 @@ export const isSignInRateLimited = (
16
15
  ctx: MutationCtx,
17
16
  identifier: string,
18
17
  config: ConvexAuthConfig,
19
- ): Fx<boolean, AuthError> =>
18
+ ): Fx<boolean, ConvexError<any>> =>
20
19
  getRateLimitState(ctx, identifier, config).pipe(
21
20
  Fx.map((state) => state !== null && state.attemptsLeft < 1),
22
21
  );
@@ -31,40 +30,28 @@ export const recordFailedSignIn = (
31
30
  ctx: MutationCtx,
32
31
  identifier: string,
33
32
  config: ConvexAuthConfig,
34
- ): Fx<void, AuthError> =>
35
- getRateLimitState(ctx, identifier, config).pipe(
36
- Fx.chain((state) =>
37
- state !== null
38
- ? Fx.from({
39
- ok: () =>
40
- authDb(ctx, config).rateLimits.patch(state.limit._id, {
41
- attemptsLeft: state.attemptsLeft - 1,
42
- lastAttemptTime: Date.now(),
43
- }),
44
- err: (e) =>
45
- new AuthError(
46
- "INTERNAL_ERROR",
47
- `Failed to patch rate limit: ${errorMessage(e)}`,
48
- ),
49
- })
50
- : Fx.from({
51
- ok: () =>
52
- authDb(ctx, config).rateLimits.create({
53
- identifier,
54
- attemptsLeft:
55
- (config.signIn?.maxFailedAttemptsPerHour ??
56
- DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR) - 1,
57
- lastAttemptTime: Date.now(),
58
- }),
59
- err: (e) =>
60
- new AuthError(
61
- "INTERNAL_ERROR",
62
- `Failed to create rate limit: ${errorMessage(e)}`,
63
- ),
64
- }),
65
- ),
66
- Fx.map(() => undefined),
67
- );
33
+ ): Fx<void, ConvexError<any>> =>
34
+ Fx.gen(function* () {
35
+ const state = yield* getRateLimitState(ctx, identifier, config);
36
+ if (state !== null) {
37
+ yield* Fx.promise(() =>
38
+ authDb(ctx, config).rateLimits.patch(state.limit._id, {
39
+ attemptsLeft: state.attemptsLeft - 1,
40
+ lastAttemptTime: Date.now(),
41
+ }),
42
+ );
43
+ } else {
44
+ yield* Fx.promise(() =>
45
+ authDb(ctx, config).rateLimits.create({
46
+ identifier,
47
+ attemptsLeft:
48
+ (config.signIn?.maxFailedAttemptsPerHour ??
49
+ DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR) - 1,
50
+ lastAttemptTime: Date.now(),
51
+ }),
52
+ );
53
+ }
54
+ });
68
55
 
69
56
  /**
70
57
  * Reset the rate limit for the given identifier (e.g. after successful sign-in).
@@ -74,21 +61,15 @@ export const resetSignInRateLimit = (
74
61
  ctx: MutationCtx,
75
62
  identifier: string,
76
63
  config: ConvexAuthConfig,
77
- ): Fx<void, AuthError> =>
78
- getRateLimitState(ctx, identifier, config).pipe(
79
- Fx.chain((state) =>
80
- state !== null
81
- ? Fx.from({
82
- ok: () => authDb(ctx, config).rateLimits.delete(state.limit._id),
83
- err: (e) =>
84
- new AuthError(
85
- "INTERNAL_ERROR",
86
- `Failed to delete rate limit: ${errorMessage(e)}`,
87
- ),
88
- })
89
- : Fx.unit,
90
- ),
91
- );
64
+ ): Fx<void, ConvexError<any>> =>
65
+ Fx.gen(function* () {
66
+ const state = yield* getRateLimitState(ctx, identifier, config);
67
+ if (state !== null) {
68
+ yield* Fx.promise(() =>
69
+ authDb(ctx, config).rateLimits.delete(state.limit._id),
70
+ );
71
+ }
72
+ });
92
73
 
93
74
  // ---------------------------------------------------------------------------
94
75
  // Internal
@@ -103,32 +84,24 @@ const getRateLimitState = (
103
84
  ctx: MutationCtx,
104
85
  identifier: string,
105
86
  config: ConvexAuthConfig,
106
- ): Fx<RateLimitState, AuthError> => {
107
- const now = Date.now();
108
- const maxAttemptsPerHour =
109
- config.signIn?.maxFailedAttemptsPerHour ??
110
- DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR;
87
+ ): Fx<RateLimitState, ConvexError<any>> =>
88
+ Fx.gen(function* () {
89
+ const now = Date.now();
90
+ const maxAttemptsPerHour =
91
+ config.signIn?.maxFailedAttemptsPerHour ??
92
+ DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR;
111
93
 
112
- return Fx.from({
113
- ok: () => authDb(ctx, config).rateLimits.get(identifier),
114
- err: (e) =>
115
- new AuthError(
116
- "INTERNAL_ERROR",
117
- `Failed to get rate limit: ${errorMessage(e)}`,
118
- ),
119
- }).pipe(
120
- Fx.map((raw) => {
121
- const limit = raw as
122
- | (Doc<"RateLimit"> & { attemptsLeft: number; lastAttemptTime: number })
123
- | null;
124
- if (limit === null) return null;
125
- const elapsed = now - limit.lastAttemptTime;
126
- const maxAttemptsPerMs = maxAttemptsPerHour / (60 * 60 * 1000);
127
- const attemptsLeft = Math.min(
128
- maxAttemptsPerHour,
129
- limit.attemptsLeft + elapsed * maxAttemptsPerMs,
130
- );
131
- return { limit, attemptsLeft };
132
- }),
133
- );
134
- };
94
+ const limit = (yield* Fx.promise(() =>
95
+ authDb(ctx, config).rateLimits.get(identifier),
96
+ )) as
97
+ | (Doc<"RateLimit"> & { attemptsLeft: number; lastAttemptTime: number })
98
+ | null;
99
+ if (limit === null) return null;
100
+ const elapsed = now - limit.lastAttemptTime;
101
+ const maxAttemptsPerMs = maxAttemptsPerHour / (60 * 60 * 1000);
102
+ const attemptsLeft = Math.min(
103
+ maxAttemptsPerHour,
104
+ limit.attemptsLeft + elapsed * maxAttemptsPerMs,
105
+ );
106
+ return { limit, attemptsLeft };
107
+ });
@@ -1,3 +1,4 @@
1
+ import { Cv } from "@robelest/fx/convex";
1
2
  import { actionGeneric, mutationGeneric, queryGeneric } from "convex/server";
2
3
  import { ConvexError, v } from "convex/values";
3
4
 
@@ -62,11 +63,11 @@ export type EnterpriseAdminAuthorizationInput = {
62
63
  /**
63
64
  * App-defined authorization hook for mounted enterprise admin APIs.
64
65
  *
65
- * Return `void` (or resolve) to allow the operation, or `{ ok: false }` to deny it.
66
+ * Return `void` (or resolve) to allow the operation, or throw to deny it.
66
67
  *
67
68
  * @param ctx - Convex context with `ctx.auth` for identity checks.
68
69
  * @param input - The {@link EnterpriseAdminAuthorizationInput} describing who is doing what.
69
- * @returns `void` to allow, `{ ok: false }` to deny.
70
+ * @returns `void` to allow; throw to deny.
70
71
  *
71
72
  * @example
72
73
  * ```ts
@@ -74,7 +75,7 @@ export type EnterpriseAdminAuthorizationInput = {
74
75
  *
75
76
  * const authorized: EnterpriseAuthorizer = async (ctx, input) => {
76
77
  * const identity = await ctx.auth.getUserIdentity();
77
- * if (!identity) return { ok: false };
78
+ * if (!identity) throw new Error("Forbidden");
78
79
  * // Allow all admin ops for the org owner
79
80
  * };
80
81
  * ```
@@ -82,7 +83,7 @@ export type EnterpriseAdminAuthorizationInput = {
82
83
  export type EnterpriseAuthorizer = (
83
84
  ctx: { auth: import("convex/server").Auth },
84
85
  input: EnterpriseAdminAuthorizationInput,
85
- ) => Promise<void | { ok: false }>;
86
+ ) => Promise<void>;
86
87
 
87
88
  type RoleRef<TRoleId extends string> = { id: TRoleId };
88
89
 
@@ -126,11 +127,11 @@ type MountedEnterpriseTarget = {
126
127
  domain?: string;
127
128
  };
128
129
 
129
- function requireSignedInUser(auth: Pick<AuthApi, "user">) {
130
+ function requireSignedInUser(auth: Pick<AuthApi, "context">) {
130
131
  return async (ctx: {
131
132
  auth: import("convex/server").Auth;
132
133
  }): Promise<string | null> => {
133
- return await auth.user.id(ctx as never);
134
+ return (await auth.context(ctx as never, { optional: true })).userId;
134
135
  };
135
136
  }
136
137
 
@@ -197,7 +198,7 @@ async function resolveMountedEnterpriseTarget(
197
198
  }
198
199
 
199
200
  function createMountedAdminAuthorizer(
200
- auth: Pick<AuthApi, "sso" | "user">,
201
+ auth: Pick<AuthApi, "context" | "sso">,
201
202
  options?: MountedEnterpriseOptions,
202
203
  ) {
203
204
  const requireUserId = requireSignedInUser(auth);
@@ -209,23 +210,26 @@ function createMountedAdminAuthorizer(
209
210
  ) => {
210
211
  const userId = await requireUserId(ctx);
211
212
  if (userId === null) {
212
- return { ok: false as const, code: "NOT_SIGNED_IN" as const };
213
+ throw Cv.error({
214
+ code: "NOT_SIGNED_IN",
215
+ message: "You must be signed in to perform this action.",
216
+ });
213
217
  }
214
218
  if (!options?.admin?.authorized) {
215
- return { ok: false as const, code: "FORBIDDEN" as const };
219
+ throw Cv.error({
220
+ code: "FORBIDDEN",
221
+ message: "Access denied.",
222
+ });
216
223
  }
217
224
  const resolved = await resolveMountedEnterpriseTarget(auth, ctx, target);
218
- const authResult = await options.admin.authorized(ctx, {
225
+ await options.admin.authorized(ctx, {
219
226
  userId,
220
227
  permission,
221
228
  enterpriseId: resolved.enterpriseId,
222
229
  groupId: resolved.groupId,
223
230
  resolvedGroupId: resolved.resolvedGroupId,
224
231
  });
225
- if (authResult && !authResult.ok) {
226
- return { ok: false as const, code: "FORBIDDEN" as const };
227
- }
228
- return { ok: true as const, userId, ...resolved };
232
+ return { userId, ...resolved };
229
233
  };
230
234
  }
231
235
 
@@ -265,7 +269,10 @@ function createMountedAdminAuthorizer(
265
269
  export function sso<
266
270
  TAuthorization extends AuthAuthorizationConfig | undefined = undefined,
267
271
  >(
268
- auth: Pick<AuthApi<TAuthorization>, "group" | "member" | "sso" | "user">,
272
+ auth: Pick<
273
+ AuthApi<TAuthorization>,
274
+ "context" | "group" | "member" | "sso"
275
+ >,
269
276
  options?: MountedEnterpriseOptions<AuthRoleId<TAuthorization>>,
270
277
  ) {
271
278
  const authorize = createMountedAdminAuthorizer(auth, options);
@@ -286,8 +293,6 @@ export function sso<
286
293
  const authResult = await authorize(ctx, "sso.connection.create", {
287
294
  groupId: args.groupId,
288
295
  });
289
- if (!authResult.ok)
290
- return { ok: false as const, code: authResult.code };
291
296
  const { userId } = authResult;
292
297
  const createsGroup = args.groupId === undefined;
293
298
  const groupId =
@@ -332,10 +337,9 @@ export function sso<
332
337
  get: queryGeneric({
333
338
  args: { enterpriseId: v.string() },
334
339
  handler: async (ctx, args) => {
335
- const _auth = await authorize(ctx, "sso.connection.read", {
340
+ await authorize(ctx, "sso.connection.read", {
336
341
  enterpriseId: args.enterpriseId,
337
342
  });
338
- if (!_auth.ok) return null;
339
343
  return await auth.sso.admin.connection.get(
340
344
  ctx as never,
341
345
  args.enterpriseId,
@@ -345,10 +349,9 @@ export function sso<
345
349
  getByGroup: queryGeneric({
346
350
  args: { groupId: v.string() },
347
351
  handler: async (ctx, args) => {
348
- const _auth = await authorize(ctx, "sso.connection.read", {
352
+ await authorize(ctx, "sso.connection.read", {
349
353
  groupId: args.groupId,
350
354
  });
351
- if (!_auth.ok) return null;
352
355
  return await auth.sso.admin.connection.getByGroup(
353
356
  ctx as never,
354
357
  args.groupId,
@@ -358,10 +361,9 @@ export function sso<
358
361
  getByDomain: queryGeneric({
359
362
  args: { domain: v.string() },
360
363
  handler: async (ctx, args) => {
361
- const _auth = await authorize(ctx, "sso.connection.read", {
364
+ await authorize(ctx, "sso.connection.read", {
362
365
  domain: args.domain,
363
366
  });
364
- if (!_auth.ok) return null;
365
367
  return await auth.sso.admin.connection.getByDomain(
366
368
  ctx as never,
367
369
  args.domain,
@@ -377,10 +379,9 @@ export function sso<
377
379
  order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
378
380
  },
379
381
  handler: async (ctx, args) => {
380
- const _auth = await authorize(ctx, "sso.connection.read", {
382
+ await authorize(ctx, "sso.connection.read", {
381
383
  groupId: args.where?.groupId,
382
384
  });
383
- if (!_auth.ok) return null;
384
385
  return await auth.sso.admin.connection.list(
385
386
  ctx as never,
386
387
  args as never,
@@ -397,25 +398,23 @@ export function sso<
397
398
  }),
398
399
  },
399
400
  handler: async (ctx, args) => {
400
- const _auth = await authorize(ctx, "sso.connection.manage", {
401
+ await authorize(ctx, "sso.connection.manage", {
401
402
  enterpriseId: args.enterpriseId,
402
403
  });
403
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
404
404
  await auth.sso.admin.connection.update(
405
405
  ctx as never,
406
406
  args.enterpriseId,
407
407
  args.data,
408
408
  );
409
- return { ok: true as const, enterpriseId: args.enterpriseId };
409
+ return { enterpriseId: args.enterpriseId };
410
410
  },
411
411
  }),
412
412
  delete: mutationGeneric({
413
413
  args: { enterpriseId: v.string() },
414
414
  handler: async (ctx, args) => {
415
- const _auth = await authorize(ctx, "sso.connection.manage", {
415
+ await authorize(ctx, "sso.connection.manage", {
416
416
  enterpriseId: args.enterpriseId,
417
417
  });
418
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
419
418
  return await auth.sso.admin.connection.delete(
420
419
  ctx as never,
421
420
  args.enterpriseId,
@@ -425,10 +424,9 @@ export function sso<
425
424
  status: queryGeneric({
426
425
  args: { enterpriseId: v.string() },
427
426
  handler: async (ctx, args) => {
428
- const _auth = await authorize(ctx, "sso.connection.read", {
427
+ await authorize(ctx, "sso.connection.read", {
429
428
  enterpriseId: args.enterpriseId,
430
429
  });
431
- if (!_auth.ok) return null;
432
430
  return await auth.sso.admin.connection.status(
433
431
  ctx as never,
434
432
  args.enterpriseId,
@@ -439,10 +437,9 @@ export function sso<
439
437
  list: queryGeneric({
440
438
  args: { enterpriseId: v.string() },
441
439
  handler: async (ctx, args) => {
442
- const _auth = await authorize(ctx, "sso.connection.read", {
440
+ await authorize(ctx, "sso.connection.read", {
443
441
  enterpriseId: args.enterpriseId,
444
442
  });
445
- if (!_auth.ok) return null;
446
443
  return await auth.sso.admin.connection.domain.list(
447
444
  ctx as never,
448
445
  args.enterpriseId,
@@ -452,10 +449,9 @@ export function sso<
452
449
  validate: queryGeneric({
453
450
  args: { enterpriseId: v.string() },
454
451
  handler: async (ctx, args) => {
455
- const _auth = await authorize(ctx, "sso.domain.manage", {
452
+ await authorize(ctx, "sso.domain.manage", {
456
453
  enterpriseId: args.enterpriseId,
457
454
  });
458
- if (!_auth.ok) return null;
459
455
  return await auth.sso.admin.connection.domain.validate(
460
456
  ctx as never,
461
457
  args.enterpriseId,
@@ -468,10 +464,9 @@ export function sso<
468
464
  domains: v.array(enterpriseDomainInputValidator),
469
465
  },
470
466
  handler: async (ctx, args) => {
471
- const _auth = await authorize(ctx, "sso.domain.manage", {
467
+ await authorize(ctx, "sso.domain.manage", {
472
468
  enterpriseId: args.enterpriseId,
473
469
  });
474
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
475
470
  return await auth.sso.admin.connection.domain.set(
476
471
  ctx as never,
477
472
  args.enterpriseId,
@@ -483,10 +478,9 @@ export function sso<
483
478
  request: mutationGeneric({
484
479
  args: enterpriseDomainVerificationInputValidator,
485
480
  handler: async (ctx, args) => {
486
- const _auth = await authorize(ctx, "sso.domain.manage", {
481
+ await authorize(ctx, "sso.domain.manage", {
487
482
  enterpriseId: args.enterpriseId,
488
483
  });
489
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
490
484
  return await auth.sso.admin.connection.domain.verification.request(
491
485
  ctx as never,
492
486
  args,
@@ -496,10 +490,9 @@ export function sso<
496
490
  confirm: actionGeneric({
497
491
  args: enterpriseDomainVerificationInputValidator,
498
492
  handler: async (ctx, args) => {
499
- const _auth = await authorize(ctx, "sso.domain.manage", {
493
+ await authorize(ctx, "sso.domain.manage", {
500
494
  enterpriseId: args.enterpriseId,
501
495
  });
502
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
503
496
  return await auth.sso.admin.connection.domain.verification.confirm(
504
497
  ctx as never,
505
498
  args,
@@ -524,20 +517,18 @@ export function sso<
524
517
  extraFields: v.optional(v.record(v.string(), v.string())),
525
518
  },
526
519
  handler: async (ctx, args) => {
527
- const _auth = await authorize(ctx, "sso.protocol.manage", {
520
+ await authorize(ctx, "sso.protocol.manage", {
528
521
  enterpriseId: args.enterpriseId,
529
522
  });
530
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
531
523
  return await auth.sso.admin.oidc.configure(ctx as never, args);
532
524
  },
533
525
  }),
534
526
  get: queryGeneric({
535
527
  args: { enterpriseId: v.string() },
536
528
  handler: async (ctx, args) => {
537
- const _auth = await authorize(ctx, "sso.connection.read", {
529
+ await authorize(ctx, "sso.connection.read", {
538
530
  enterpriseId: args.enterpriseId,
539
531
  });
540
- if (!_auth.ok) return null;
541
532
  return await auth.sso.admin.oidc.get(
542
533
  ctx as never,
543
534
  args.enterpriseId,
@@ -547,10 +538,9 @@ export function sso<
547
538
  validate: actionGeneric({
548
539
  args: { enterpriseId: v.string() },
549
540
  handler: async (ctx, args) => {
550
- const _auth = await authorize(ctx, "sso.protocol.manage", {
541
+ await authorize(ctx, "sso.protocol.manage", {
551
542
  enterpriseId: args.enterpriseId,
552
543
  });
553
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
554
544
  return await auth.sso.admin.oidc.validate(
555
545
  ctx as never,
556
546
  args.enterpriseId,
@@ -572,20 +562,18 @@ export function sso<
572
562
  sp: v.optional(enterpriseSamlSpValidator),
573
563
  },
574
564
  handler: async (ctx, args) => {
575
- const _auth = await authorize(ctx, "sso.protocol.manage", {
565
+ await authorize(ctx, "sso.protocol.manage", {
576
566
  enterpriseId: args.enterpriseId,
577
567
  });
578
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
579
568
  return await auth.sso.admin.saml.configure(ctx as never, args);
580
569
  },
581
570
  }),
582
571
  validate: queryGeneric({
583
572
  args: { enterpriseId: v.string() },
584
573
  handler: async (ctx, args) => {
585
- const _auth = await authorize(ctx, "sso.protocol.manage", {
574
+ await authorize(ctx, "sso.protocol.manage", {
586
575
  enterpriseId: args.enterpriseId,
587
576
  });
588
- if (!_auth.ok) return null;
589
577
  return await auth.sso.admin.saml.validate(
590
578
  ctx as never,
591
579
  args.enterpriseId,
@@ -597,10 +585,9 @@ export function sso<
597
585
  get: queryGeneric({
598
586
  args: { enterpriseId: v.string() },
599
587
  handler: async (ctx, args) => {
600
- const _auth = await authorize(ctx, "sso.connection.read", {
588
+ await authorize(ctx, "sso.connection.read", {
601
589
  enterpriseId: args.enterpriseId,
602
590
  });
603
- if (!_auth.ok) return null;
604
591
  return await auth.sso.admin.policy.get(
605
592
  ctx as never,
606
593
  args.enterpriseId,
@@ -613,10 +600,9 @@ export function sso<
613
600
  patch: enterprisePolicyPatchValidator,
614
601
  },
615
602
  handler: async (ctx, args) => {
616
- const _auth = await authorize(ctx, "sso.policy.manage", {
603
+ await authorize(ctx, "sso.policy.manage", {
617
604
  enterpriseId: args.enterpriseId,
618
605
  });
619
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
620
606
  return await auth.sso.admin.policy.update(
621
607
  ctx as never,
622
608
  args.enterpriseId,
@@ -627,10 +613,9 @@ export function sso<
627
613
  validate: queryGeneric({
628
614
  args: { enterpriseId: v.string() },
629
615
  handler: async (ctx, args) => {
630
- const _auth = await authorize(ctx, "sso.policy.manage", {
616
+ await authorize(ctx, "sso.policy.manage", {
631
617
  enterpriseId: args.enterpriseId,
632
618
  });
633
- if (!_auth.ok) return null;
634
619
  return await auth.sso.admin.policy.validate(
635
620
  ctx as never,
636
621
  args.enterpriseId,
@@ -646,11 +631,10 @@ export function sso<
646
631
  limit: v.optional(v.number()),
647
632
  },
648
633
  handler: async (ctx, args) => {
649
- const _auth = await authorize(ctx, "sso.audit.read", {
634
+ await authorize(ctx, "sso.audit.read", {
650
635
  enterpriseId: args.enterpriseId,
651
636
  groupId: args.groupId,
652
637
  });
653
- if (!_auth.ok) return null;
654
638
  return await auth.sso.admin.audit.list(ctx as never, args);
655
639
  },
656
640
  }),
@@ -663,10 +647,9 @@ export function sso<
663
647
  limit: v.optional(v.number()),
664
648
  },
665
649
  handler: async (ctx, args) => {
666
- const _auth = await authorize(ctx, "sso.webhook.manage", {
650
+ await authorize(ctx, "sso.webhook.manage", {
667
651
  enterpriseId: args.enterpriseId,
668
652
  });
669
- if (!_auth.ok) return null;
670
653
  return await (auth.sso.admin.webhook as any).delivery.list(
671
654
  ctx as never,
672
655
  args,
@@ -687,8 +670,6 @@ export function sso<
687
670
  const authResult = await authorize(ctx, "sso.webhook.manage", {
688
671
  enterpriseId: args.enterpriseId,
689
672
  });
690
- if (!authResult.ok)
691
- return { ok: false as const, code: authResult.code };
692
673
  const { userId } = authResult;
693
674
  const result = await auth.sso.admin.webhook.endpoint.create(
694
675
  ctx as never,
@@ -711,10 +692,9 @@ export function sso<
711
692
  list: queryGeneric({
712
693
  args: { enterpriseId: v.string() },
713
694
  handler: async (ctx, args) => {
714
- const _auth = await authorize(ctx, "sso.webhook.manage", {
695
+ await authorize(ctx, "sso.webhook.manage", {
715
696
  enterpriseId: args.enterpriseId,
716
697
  });
717
- if (!_auth.ok) return null;
718
698
  const endpoints = await auth.sso.admin.webhook.endpoint.list(
719
699
  ctx as never,
720
700
  args.enterpriseId,
@@ -733,16 +713,15 @@ export function sso<
733
713
  args.endpointId,
734
714
  );
735
715
  if (!endpoint) {
736
- return {
737
- ok: false as const,
738
- code: "INVALID_PARAMETERS" as const,
739
- };
716
+ throw Cv.error({
717
+ code: "INVALID_PARAMETERS",
718
+ message: "Webhook endpoint not found.",
719
+ });
740
720
  }
741
- const _auth = await authorize(ctx, "sso.webhook.manage", {
721
+ await authorize(ctx, "sso.webhook.manage", {
742
722
  enterpriseId: endpoint.enterpriseId,
743
723
  groupId: endpoint.groupId,
744
724
  });
745
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
746
725
  return await auth.sso.admin.webhook.endpoint.disable(
747
726
  ctx as never,
748
727
  args.endpointId,
@@ -811,7 +790,7 @@ export function sso<
811
790
  export function scim<
812
791
  TAuthorization extends AuthAuthorizationConfig | undefined = undefined,
813
792
  >(
814
- auth: Pick<AuthApi<TAuthorization>, "scim" | "sso" | "user">,
793
+ auth: Pick<AuthApi<TAuthorization>, "context" | "scim" | "sso">,
815
794
  options?: MountedEnterpriseOptions<AuthRoleId<TAuthorization>>,
816
795
  ) {
817
796
  const authorize = createMountedAdminAuthorizer(auth, options);
@@ -825,30 +804,27 @@ export function scim<
825
804
  status: v.optional(enterpriseStatusValidator),
826
805
  },
827
806
  handler: async (ctx, args) => {
828
- const _auth = await authorize(ctx, "scim.manage", {
807
+ await authorize(ctx, "scim.manage", {
829
808
  enterpriseId: args.enterpriseId,
830
809
  });
831
- if (!_auth.ok) return { ok: false as const, code: _auth.code };
832
810
  return await auth.scim.admin.configure(ctx as never, args);
833
811
  },
834
812
  }),
835
813
  get: queryGeneric({
836
814
  args: { enterpriseId: v.string() },
837
815
  handler: async (ctx, args) => {
838
- const _auth = await authorize(ctx, "scim.manage", {
816
+ await authorize(ctx, "scim.manage", {
839
817
  enterpriseId: args.enterpriseId,
840
818
  });
841
- if (!_auth.ok) return null;
842
819
  return await auth.scim.admin.get(ctx as never, args.enterpriseId);
843
820
  },
844
821
  }),
845
822
  validate: queryGeneric({
846
823
  args: { enterpriseId: v.string() },
847
824
  handler: async (ctx, args) => {
848
- const _auth = await authorize(ctx, "scim.manage", {
825
+ await authorize(ctx, "scim.manage", {
849
826
  enterpriseId: args.enterpriseId,
850
827
  });
851
- if (!_auth.ok) return null;
852
828
  return await auth.scim.admin.validate(
853
829
  ctx as never,
854
830
  args.enterpriseId,
@@ -899,7 +875,7 @@ export function enterprise<
899
875
  >(
900
876
  auth: Pick<
901
877
  AuthApi<TAuthorization>,
902
- "group" | "member" | "scim" | "sso" | "user"
878
+ "context" | "group" | "member" | "scim" | "sso"
903
879
  >,
904
880
  options: EnterpriseMountOptions<AuthRoleId<TAuthorization>>,
905
881
  ) {