@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
@@ -1,13 +1,13 @@
1
- import { Fx } from "./fx.js";
2
- import { Auth } from "./implementation.js";
1
+ import { AuthError, Fx } from "./fx.js";
2
+ import { Auth } from "./factory.js";
3
3
 
4
4
  //#region src/server/auth.ts
5
5
  /**
6
6
  * Create an auth API object.
7
7
  *
8
- * When `new SSO()` is included in providers, `auth.sso` is available
9
- * on the returned object. Without it, `auth.sso` is absent and
10
- * accessing it is a TypeScript compile error.
8
+ * When `new SSO()` is included in providers, `auth.sso` and `auth.scim`
9
+ * are available on the returned object. Without it, those namespaces are
10
+ * absent and accessing them is a TypeScript compile error.
11
11
  */
12
12
  function createAuth(component, config) {
13
13
  const authResult = Auth({
@@ -15,6 +15,86 @@ function createAuth(component, config) {
15
15
  component,
16
16
  providers: [...config.providers]
17
17
  });
18
+ const { domain: domainApi, scim: scimApi, connection: connectionApi, audit: auditApi, webhook: webhookApi, oidc: oidcApi, saml: samlApi, ...restSso } = authResult.auth.sso;
19
+ const setEnterpriseDomains = async (ctx, enterpriseId, domains) => {
20
+ const enterprise = await connectionApi.get(ctx, enterpriseId);
21
+ if (enterprise === null) throw new AuthError("INVALID_PARAMETERS", "Enterprise not found.").toConvexError();
22
+ const normalized = domains.map((entry) => ({
23
+ ...entry,
24
+ domain: entry.domain.trim().toLowerCase()
25
+ }));
26
+ const deduped = /* @__PURE__ */ new Map();
27
+ for (const entry of normalized) {
28
+ if (entry.domain.length === 0) throw new AuthError("INVALID_PARAMETERS", "Domain must not be empty.").toConvexError();
29
+ if (deduped.has(entry.domain)) throw new AuthError("INVALID_PARAMETERS", `Duplicate domain: ${entry.domain}`).toConvexError();
30
+ deduped.set(entry.domain, entry);
31
+ }
32
+ const nextDomains = [...deduped.values()];
33
+ const primaryCount = nextDomains.filter((entry) => entry.isPrimary).length;
34
+ if (primaryCount > 1) throw new AuthError("INVALID_PARAMETERS", "Only one primary domain may be set.").toConvexError();
35
+ if (nextDomains.length > 0 && primaryCount === 0) nextDomains[0] = {
36
+ ...nextDomains[0],
37
+ isPrimary: true
38
+ };
39
+ const currentDomains = await domainApi.list(ctx, enterpriseId);
40
+ const currentByDomain = new Map(currentDomains.map((entry) => [entry.domain.toLowerCase(), entry]));
41
+ for (const existing of currentDomains) if (!deduped.has(existing.domain.toLowerCase())) await domainApi.remove(ctx, existing._id);
42
+ for (const nextDomain of nextDomains) {
43
+ const current = currentByDomain.get(nextDomain.domain);
44
+ if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) continue;
45
+ if (current) await domainApi.remove(ctx, current._id);
46
+ const domainId = await domainApi.add(ctx, {
47
+ enterpriseId: enterprise._id,
48
+ groupId: enterprise.groupId,
49
+ domain: nextDomain.domain,
50
+ isPrimary: nextDomain.isPrimary
51
+ });
52
+ if (current?.verifiedAt !== void 0) await ctx.runMutation(component.public.enterpriseDomainVerify, {
53
+ domainId,
54
+ verifiedAt: current.verifiedAt
55
+ });
56
+ }
57
+ return {
58
+ ok: true,
59
+ enterpriseId,
60
+ domains: (await domainApi.list(ctx, enterpriseId)).map((domain) => ({
61
+ domainId: domain._id,
62
+ domain: domain.domain,
63
+ isPrimary: domain.isPrimary,
64
+ verified: domain.verifiedAt !== void 0,
65
+ verifiedAt: domain.verifiedAt ?? null
66
+ }))
67
+ };
68
+ };
69
+ const publicSso = {
70
+ admin: {
71
+ ...restSso,
72
+ oidc: { ...oidcApi },
73
+ saml: { ...samlApi },
74
+ connection: {
75
+ ...connectionApi,
76
+ domain: {
77
+ list: domainApi.list,
78
+ validate: domainApi.validate,
79
+ set: setEnterpriseDomains,
80
+ verification: {
81
+ request: domainApi.verification.request,
82
+ confirm: domainApi.verification.confirm
83
+ }
84
+ }
85
+ },
86
+ policy: restSso.policy,
87
+ audit: { list: auditApi.list },
88
+ webhook: {
89
+ endpoint: webhookApi.endpoint,
90
+ delivery: { list: webhookApi.delivery.list }
91
+ }
92
+ },
93
+ client: {
94
+ signIn: oidcApi.signIn,
95
+ metadata: samlApi.metadata
96
+ }
97
+ };
18
98
  return {
19
99
  signIn: authResult.signIn,
20
100
  signOut: authResult.signOut,
@@ -25,12 +105,25 @@ function createAuth(component, config) {
25
105
  account: authResult.auth.account,
26
106
  group: authResult.auth.group,
27
107
  member: authResult.auth.member,
108
+ access: authResult.auth.access,
28
109
  invite: authResult.auth.invite,
29
110
  key: authResult.auth.key,
30
- sso: authResult.auth.sso,
111
+ sso: publicSso,
112
+ scim: { admin: {
113
+ configure: scimApi.configure,
114
+ get: scimApi.get,
115
+ validate: scimApi.validate
116
+ } },
31
117
  http: authResult.auth.http
32
118
  };
33
119
  }
120
+ function defineRoles(roles) {
121
+ return Object.fromEntries(Object.entries(roles).map(([id, role]) => [id, {
122
+ id,
123
+ ...role.label ? { label: role.label } : {},
124
+ grants: [...role.grants]
125
+ }]));
126
+ }
34
127
  function AuthCtx(auth, config) {
35
128
  return {
36
129
  args: {},
@@ -77,5 +170,5 @@ function AuthCtx(auth, config) {
77
170
  }
78
171
 
79
172
  //#endregion
80
- export { AuthCtx, createAuth };
173
+ export { AuthCtx, createAuth, defineRoles };
81
174
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Fx } from \"./fx\";\nimport { AuthError } from \"./fx\";\nimport { Auth as AuthFactory } from \"./implementation\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthProviderConfig,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\n/** The base auth API surface, without conditional namespaces. */\nexport type AuthApiBase = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: ReturnType<typeof AuthFactory>[\"auth\"][\"member\"];\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n};\n\n/** Auth API with SSO namespace — present only when `new SSO()` is in providers. */\nexport type AuthApi = AuthApiBase & {\n sso: ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n};\n\n/**\n * The return type of `createAuth`. Conditional namespaces:\n * - `auth.sso` — only when `new SSO()` is in providers\n * - `auth.clientApi` — typed API refs for the client SDK with capabilities\n */\nexport type ConvexAuthResult<P extends AuthProviderConfig[]> =\n HasSSO<P> extends true ? AuthApi : AuthApiBase;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/component\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: { ... } });\n * ```\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` is available\n * on the returned object. Without it, `auth.sso` is absent and\n * accessing it is a TypeScript compile error.\n */\nexport function createAuth<P extends AuthProviderConfig[]>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\"> & { providers: P },\n): ConvexAuthResult<P> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: authResult.auth.sso,\n http: authResult.auth.http,\n } as ConvexAuthResult<P>;\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\nexport type UserDoc = Doc<\"User\">;\n\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n optional?: boolean;\n resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;\n};\n\n/** Overload: optional auth */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n } & TResolve;\n };\n args: {};\n }>;\n};\n/** Overload: required auth (default) */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\">;\n user: UserDoc;\n } & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const modeDispatch =\n config?.optional === true\n ? { mode: \"optional\" as const }\n : { mode: \"required\" as const };\n\n const userContext = await Fx.run(\n Fx.match(modeDispatch, modeDispatch.mode, {\n optional: async () => {\n const userId = await auth.user.current(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n required: async () => {\n const userId = await auth.user.require(ctx);\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n }),\n );\n\n if (userContext === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: null,\n user: null,\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, userContext.user)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: userContext.userId,\n user: userContext.user,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":";;;;;;;;;;;AAoGA,SAAgB,WACd,WACA,QACqB;CACrB,MAAM,aAAaA,KAAY;EAC7B,GAAG;EACH;EACA,WAAW,CAAC,GAAG,OAAO,UAAU;EACjC,CAAC;AAEF,QAAO;EACL,QAAQ,WAAW;EACnB,SAAS,WAAW;EACpB,OAAO,WAAW;EAClB,MAAM,WAAW,KAAK;EACtB,SAAS,WAAW,KAAK;EACzB,UAAU,WAAW,KAAK;EAC1B,SAAS,WAAW,KAAK;EACzB,OAAO,WAAW,KAAK;EACvB,QAAQ,WAAW,KAAK;EACxB,QAAQ,WAAW,KAAK;EACxB,KAAK,WAAW,KAAK;EACrB,KAAK,WAAW,KAAK;EACrB,MAAM,WAAW,KAAK;EACvB;;AA+DH,SAAgB,QAAQ,MAAgB,QAA6B;AACnE,QAAO;EACL,MAAM,EAAE;EACR,OAAO,OAAO,KAAU,OAAY,WAAiB;GACnD,MAAM,aAAa,IAAI;GACvB,MAAM,eACJ,QAAQ,aAAa,OACjB,EAAE,MAAM,YAAqB,GAC7B,EAAE,MAAM,YAAqB;GAEnC,MAAM,cAAc,MAAM,GAAG,IAC3B,GAAG,MAAM,cAAc,aAAa,MAAM;IACxC,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,IAAI;AAC3C,SAAI,CAAC,OACH,QAAO;AAGT,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAEzB,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,IAAI;AAE3C,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAE1B,CAAC,CACH;AAED,OAAI,gBAAgB,KAClB,QAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ;KACR,MAAM;KACP,EACF;IACD,MAAM,EAAE;IACT;GAGH,MAAM,QAAQ,QAAQ,UAClB,MAAM,OAAO,QAAQ,KAAK,YAAY,KAAK,GAC3C,EAAE;AAEN,UAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ,YAAY;KACpB,MAAM,YAAY;KAClB,GAAG;KACJ,EACF;IACD,MAAM,EAAE;IACT;;EAEJ"}
1
+ {"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Auth as AuthFactory } from \"./factory\";\nimport { Fx } from \"./fx\";\nimport { AuthError } from \"./fx\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthAuthorizationConfig,\n AuthGrant,\n AuthProviderConfig,\n AuthRoleDefinition,\n AuthRoleId,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\ntype MemberApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = Omit<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"],\n \"create\" | \"list\" | \"update\" | \"inherit\" | \"require\"\n> & {\n create: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"create\"]\n >[0],\n data: {\n groupId: string;\n userId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n status?: string;\n extend?: Record<string, unknown>;\n },\n ) => Promise<{ ok: true; memberId: string }>;\n list: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]\n >[0],\n opts?: {\n where?: {\n groupId?: string;\n userId?: string;\n roleId?: AuthRoleId<TAuthorization>;\n status?: string;\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]>;\n update: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"update\"]\n >[0],\n memberId: string,\n data: Record<string, unknown> & { roleIds?: AuthRoleId<TAuthorization>[] },\n ) => Promise<{ ok: true; memberId: string }>;\n inherit: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"inherit\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"inherit\"]>;\n require: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"require\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"require\"]>;\n};\n\ntype AccessApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = {\n check: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"check\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n grants: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"check\"]>;\n require: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"require\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n grants: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"require\"]>;\n};\n\n/** The base auth API surface, without conditional namespaces. */\nexport type AuthApiBase<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: MemberApiWithAuthorization<TAuthorization>;\n access: AccessApiWithAuthorization<TAuthorization>;\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n};\n\ntype InternalSsoApi = ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n\ntype PublicSsoAdminApi = {\n connection: InternalSsoApi[\"connection\"] & {\n domain: {\n list: InternalSsoApi[\"domain\"][\"list\"];\n validate: InternalSsoApi[\"domain\"][\"validate\"];\n set: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n enterpriseId: string,\n domains: Array<{\n domain: string;\n isPrimary?: boolean;\n }>,\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domains: Array<{\n domainId: string;\n domain: string;\n isPrimary: boolean;\n verified: boolean;\n verifiedAt: number | null;\n }>;\n }>;\n verification: {\n request: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domain: string;\n requestedAt: number;\n expiresAt: number;\n challenge: {\n recordType: \"TXT\";\n recordName: string;\n recordValue: string;\n };\n }>;\n confirm: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: boolean;\n enterpriseId: string;\n domain: string;\n verifiedAt?: number;\n checks: Array<{ name: string; ok: boolean; message?: string }>;\n }>;\n };\n };\n };\n oidc: Omit<InternalSsoApi[\"oidc\"], \"signIn\">;\n saml: Omit<InternalSsoApi[\"saml\"], \"metadata\">;\n policy: InternalSsoApi[\"policy\"];\n audit: {\n list: InternalSsoApi[\"audit\"][\"list\"];\n };\n webhook: {\n endpoint: InternalSsoApi[\"webhook\"][\"endpoint\"];\n delivery: {\n list: InternalSsoApi[\"webhook\"][\"delivery\"][\"list\"];\n };\n };\n};\n\ntype PublicSsoClientApi = {\n signIn: InternalSsoApi[\"oidc\"][\"signIn\"];\n metadata: InternalSsoApi[\"saml\"][\"metadata\"];\n};\n\ntype PublicSsoApi = {\n admin: PublicSsoAdminApi;\n client: PublicSsoClientApi;\n};\n\ntype PublicScimApi = {\n admin: Omit<InternalSsoApi[\"scim\"], \"getConfigByToken\" | \"identity\">;\n};\n\n/** Auth API with enterprise namespaces — present only when `new SSO()` is in providers. */\nexport type AuthApi<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = AuthApiBase<TAuthorization> & {\n sso: PublicSsoApi;\n scim: PublicScimApi;\n};\n\n/**\n * The return type of `createAuth`. Conditional namespaces:\n * - `auth.sso` and `auth.scim` — only when `new SSO()` is in providers\n * - `auth.clientApi` — typed API refs for the client SDK with capabilities\n */\nexport type ConvexAuthResult<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> =\n HasSSO<P> extends true\n ? AuthApi<TAuthorization>\n : AuthApiBase<TAuthorization>;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/component\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: api.auth });\n * ```\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` and `auth.scim`\n * are available on the returned object. Without it, those namespaces are\n * absent and accessing them is a TypeScript compile error.\n */\nexport function createAuth<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\" | \"authorization\"> & {\n providers: P;\n authorization?: TAuthorization;\n },\n): ConvexAuthResult<P, TAuthorization> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n const {\n domain: domainApi,\n scim: scimApi,\n connection: connectionApi,\n audit: auditApi,\n webhook: webhookApi,\n oidc: oidcApi,\n saml: samlApi,\n ...restSso\n } = authResult.auth.sso as InternalSsoApi;\n\n type SetEnterpriseDomains = PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"];\n type EnterpriseDomainInput = Array<{\n domain: string;\n isPrimary?: boolean;\n }>;\n const setEnterpriseDomains: PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"] =\n async (\n ctx: Parameters<SetEnterpriseDomains>[0],\n enterpriseId: Parameters<SetEnterpriseDomains>[1],\n domains: EnterpriseDomainInput,\n ) => {\n const enterprise = await connectionApi.get(ctx, enterpriseId);\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n\n const normalized = domains.map((entry: (typeof domains)[number]) => ({\n ...entry,\n domain: entry.domain.trim().toLowerCase(),\n }));\n const deduped = new Map<string, (typeof normalized)[number]>();\n for (const entry of normalized) {\n if (entry.domain.length === 0) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain must not be empty.\",\n ).toConvexError();\n }\n if (deduped.has(entry.domain)) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n `Duplicate domain: ${entry.domain}`,\n ).toConvexError();\n }\n deduped.set(entry.domain, entry);\n }\n\n const nextDomains = [...deduped.values()];\n const primaryCount = nextDomains.filter(\n (entry) => entry.isPrimary,\n ).length;\n if (primaryCount > 1) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Only one primary domain may be set.\",\n ).toConvexError();\n }\n if (nextDomains.length > 0 && primaryCount === 0) {\n nextDomains[0] = { ...nextDomains[0], isPrimary: true };\n }\n\n const currentDomains = await domainApi.list(ctx, enterpriseId);\n const currentByDomain = new Map<string, (typeof currentDomains)[number]>(\n currentDomains.map((entry: (typeof currentDomains)[number]) => [\n entry.domain.toLowerCase(),\n entry,\n ]),\n );\n\n for (const existing of currentDomains) {\n if (!deduped.has(existing.domain.toLowerCase())) {\n await domainApi.remove(ctx, existing._id);\n }\n }\n\n for (const nextDomain of nextDomains) {\n const current = currentByDomain.get(nextDomain.domain);\n if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) {\n continue;\n }\n if (current) {\n await domainApi.remove(ctx, current._id);\n }\n const domainId = await domainApi.add(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain: nextDomain.domain,\n isPrimary: nextDomain.isPrimary,\n });\n if (current?.verifiedAt !== undefined) {\n await (ctx as any).runMutation(\n component.public.enterpriseDomainVerify,\n {\n domainId,\n verifiedAt: current.verifiedAt,\n },\n );\n }\n }\n\n const updatedDomains = await domainApi.list(ctx, enterpriseId);\n return {\n ok: true as const,\n enterpriseId,\n domains: updatedDomains.map(\n (domain: (typeof updatedDomains)[number]) => ({\n domainId: domain._id,\n domain: domain.domain,\n isPrimary: domain.isPrimary,\n verified: domain.verifiedAt !== undefined,\n verifiedAt: domain.verifiedAt ?? null,\n }),\n ),\n };\n };\n\n const publicSso: PublicSsoApi = {\n admin: {\n ...restSso,\n oidc: {\n ...oidcApi,\n },\n saml: {\n ...samlApi,\n },\n connection: {\n ...connectionApi,\n domain: {\n list: domainApi.list,\n validate: domainApi.validate,\n set: setEnterpriseDomains,\n verification: {\n request: domainApi.verification.request,\n confirm: domainApi.verification.confirm,\n },\n },\n },\n policy: restSso.policy,\n audit: {\n list: auditApi.list,\n },\n webhook: {\n endpoint: webhookApi.endpoint,\n delivery: {\n list: webhookApi.delivery.list,\n },\n },\n },\n client: {\n signIn: oidcApi.signIn,\n metadata: samlApi.metadata,\n },\n };\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n access: authResult.auth.access,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: publicSso,\n scim: {\n admin: {\n configure: scimApi.configure,\n get: scimApi.get,\n validate: scimApi.validate,\n },\n },\n http: authResult.auth.http,\n } as unknown as ConvexAuthResult<P, TAuthorization>;\n}\n\nexport function defineRoles<\n const TRoles extends Record<\n string,\n { label?: string; grants: readonly string[] }\n >,\n>(\n roles: TRoles,\n): {\n [K in keyof TRoles]: {\n id: K & string;\n label?: TRoles[K][\"label\"];\n grants: Array<TRoles[K][\"grants\"][number] & string>;\n };\n} {\n return Object.fromEntries(\n Object.entries(roles).map(([id, role]) => [\n id,\n {\n id,\n ...(role.label ? { label: role.label } : {}),\n grants: [...role.grants],\n },\n ]),\n ) as {\n [K in keyof TRoles]: {\n id: K & string;\n label?: TRoles[K][\"label\"];\n grants: Array<TRoles[K][\"grants\"][number] & string>;\n };\n };\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\nexport type UserDoc = Doc<\"User\">;\n\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n optional?: boolean;\n resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;\n};\n\n/** Overload: optional auth */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n } & TResolve;\n };\n args: {};\n }>;\n};\n/** Overload: required auth (default) */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\">;\n user: UserDoc;\n } & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const modeDispatch =\n config?.optional === true\n ? { mode: \"optional\" as const }\n : { mode: \"required\" as const };\n\n const userContext = await Fx.run(\n Fx.match(modeDispatch, modeDispatch.mode, {\n optional: async () => {\n const userId = await auth.user.current(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n required: async () => {\n const userId = await auth.user.require(ctx);\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n }),\n );\n\n if (userContext === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: null,\n user: null,\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, userContext.user)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: userContext.userId,\n user: userContext.user,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { isLocalHost } from "./utils.js";
2
2
 
3
3
  //#region src/server/cookies.ts
4
+ /** @internal */
4
5
  const SHARED_COOKIE_OPTIONS = {
5
6
  httpOnly: true,
6
7
  sameSite: "none",
@@ -9,6 +10,7 @@ const SHARED_COOKIE_OPTIONS = {
9
10
  partitioned: true
10
11
  };
11
12
  const REDIRECT_MAX_AGE = 900;
13
+ /** @internal */
12
14
  function redirectToParamCookie(providerId, redirectTo) {
13
15
  return {
14
16
  name: redirectToParamCookieName(providerId),
@@ -19,6 +21,7 @@ function redirectToParamCookie(providerId, redirectTo) {
19
21
  }
20
22
  };
21
23
  }
24
+ /** @internal */
22
25
  function useRedirectToParam(providerId, cookies) {
23
26
  const cookieName = redirectToParamCookieName(providerId);
24
27
  const redirectTo = cookies[cookieName];
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.js","names":[],"sources":["../../../src/server/cookies.ts"],"sourcesContent":["import { isLocalHost } from \"./utils\";\n\nexport const SHARED_COOKIE_OPTIONS = {\n httpOnly: true,\n sameSite: \"none\" as const,\n secure: true,\n path: \"/\",\n partitioned: true,\n};\n\nconst REDIRECT_MAX_AGE = 60 * 15; // 15 minutes in seconds\nexport function redirectToParamCookie(providerId: string, redirectTo: string) {\n return {\n name: redirectToParamCookieName(providerId),\n value: redirectTo,\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: REDIRECT_MAX_AGE },\n };\n}\n\nexport function useRedirectToParam(\n providerId: string,\n cookies: Record<string, string | undefined>,\n) {\n const cookieName = redirectToParamCookieName(providerId);\n const redirectTo = cookies[cookieName];\n if (redirectTo === undefined) {\n return null;\n }\n\n // Clear the cookie\n const updatedCookie = {\n name: cookieName,\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n\n return { redirectTo, updatedCookie };\n}\n\nfunction redirectToParamCookieName(providerId: string) {\n return (\n (!isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\") +\n providerId +\n \"RedirectTo\"\n );\n}\n"],"mappings":";;;AAEA,MAAa,wBAAwB;CACnC,UAAU;CACV,UAAU;CACV,QAAQ;CACR,MAAM;CACN,aAAa;CACd;AAED,MAAM,mBAAmB;AACzB,SAAgB,sBAAsB,YAAoB,YAAoB;AAC5E,QAAO;EACL,MAAM,0BAA0B,WAAW;EAC3C,OAAO;EACP,SAAS;GAAE,GAAG;GAAuB,QAAQ;GAAkB;EAChE;;AAGH,SAAgB,mBACd,YACA,SACA;CACA,MAAM,aAAa,0BAA0B,WAAW;CACxD,MAAM,aAAa,QAAQ;AAC3B,KAAI,eAAe,OACjB,QAAO;AAUT,QAAO;EAAE;EAAY,eANC;GACpB,MAAM;GACN,OAAO;GACP,SAAS;IAAE,GAAG;IAAuB,QAAQ;IAAG;GACjD;EAEmC;;AAGtC,SAAS,0BAA0B,YAAoB;AACrD,SACG,CAAC,YAAY,QAAQ,IAAI,gBAAgB,GAAG,YAAY,MACzD,aACA"}
1
+ {"version":3,"file":"cookies.js","names":[],"sources":["../../../src/server/cookies.ts"],"sourcesContent":["import { isLocalHost } from \"./utils\";\n\n/** @internal */\nexport const SHARED_COOKIE_OPTIONS = {\n httpOnly: true,\n sameSite: \"none\" as const,\n secure: true,\n path: \"/\",\n partitioned: true,\n};\n\nconst REDIRECT_MAX_AGE = 60 * 15; // 15 minutes in seconds\n/** @internal */\nexport function redirectToParamCookie(providerId: string, redirectTo: string) {\n return {\n name: redirectToParamCookieName(providerId),\n value: redirectTo,\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: REDIRECT_MAX_AGE },\n };\n}\n\n/** @internal */\nexport function useRedirectToParam(\n providerId: string,\n cookies: Record<string, string | undefined>,\n) {\n const cookieName = redirectToParamCookieName(providerId);\n const redirectTo = cookies[cookieName];\n if (redirectTo === undefined) {\n return null;\n }\n\n // Clear the cookie\n const updatedCookie = {\n name: cookieName,\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n\n return { redirectTo, updatedCookie };\n}\n\nfunction redirectToParamCookieName(providerId: string) {\n return (\n (!isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\") +\n providerId +\n \"RedirectTo\"\n );\n}\n"],"mappings":";;;;AAGA,MAAa,wBAAwB;CACnC,UAAU;CACV,UAAU;CACV,QAAQ;CACR,MAAM;CACN,aAAa;CACd;AAED,MAAM,mBAAmB;;AAEzB,SAAgB,sBAAsB,YAAoB,YAAoB;AAC5E,QAAO;EACL,MAAM,0BAA0B,WAAW;EAC3C,OAAO;EACP,SAAS;GAAE,GAAG;GAAuB,QAAQ;GAAkB;EAChE;;;AAIH,SAAgB,mBACd,YACA,SACA;CACA,MAAM,aAAa,0BAA0B,WAAW;CACxD,MAAM,aAAa,QAAQ;AAC3B,KAAI,eAAe,OACjB,QAAO;AAUT,QAAO;EAAE;EAAY,eANC;GACpB,MAAM;GACN,OAAO;GACP,SAAS;IAAE,GAAG;IAAuB,QAAQ;IAAG;GACjD;EAEmC;;AAGtC,SAAS,0BAA0B,YAAoB;AACrD,SACG,CAAC,YAAY,QAAQ,IAAI,gBAAgB,GAAG,YAAY,MACzD,aACA"}
@@ -1,4 +1,5 @@
1
1
  //#region src/server/db.ts
2
+ /** @internal */
2
3
  function authDb(ctx, config) {
3
4
  const component = config.component;
4
5
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","names":[],"sources":["../../../src/server/db.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n GenericMutationCtx,\n FunctionReference,\n} from \"convex/server\";\n\ntype MutationCtxLike = Pick<\n GenericMutationCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ActionCtxLike = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\" | \"runAction\"\n>;\n\ntype CtxLike = MutationCtxLike | ActionCtxLike;\n\ntype AuthComponentApiLike = {\n public: {\n userGetById: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedEmail: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedPhone: FunctionReference<\"query\", \"internal\">;\n userInsert: FunctionReference<\"mutation\", \"internal\">;\n userPatch: FunctionReference<\"mutation\", \"internal\">;\n userUpsert: FunctionReference<\"mutation\", \"internal\">;\n accountGet: FunctionReference<\"query\", \"internal\">;\n accountGetById: FunctionReference<\"query\", \"internal\">;\n accountInsert: FunctionReference<\"mutation\", \"internal\">;\n accountPatch: FunctionReference<\"mutation\", \"internal\">;\n accountDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionCreate: FunctionReference<\"mutation\", \"internal\">;\n sessionGetById: FunctionReference<\"query\", \"internal\">;\n sessionDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionListByUser: FunctionReference<\"query\", \"internal\">;\n verifierCreate: FunctionReference<\"mutation\", \"internal\">;\n verifierGetById: FunctionReference<\"query\", \"internal\">;\n verifierGetBySignature: FunctionReference<\"query\", \"internal\">;\n verifierPatch: FunctionReference<\"mutation\", \"internal\">;\n verifierDelete: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeGetByAccountId: FunctionReference<\"query\", \"internal\">;\n verificationCodeGetByCode: FunctionReference<\"query\", \"internal\">;\n verificationCodeCreate: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeDelete: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenCreate: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetById: FunctionReference<\"query\", \"internal\">;\n refreshTokenPatch: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetChildren: FunctionReference<\"query\", \"internal\">;\n refreshTokenListBySession: FunctionReference<\"query\", \"internal\">;\n refreshTokenDeleteAll: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetActive: FunctionReference<\"query\", \"internal\">;\n rateLimitGet: FunctionReference<\"query\", \"internal\">;\n rateLimitCreate: FunctionReference<\"mutation\", \"internal\">;\n rateLimitPatch: FunctionReference<\"mutation\", \"internal\">;\n rateLimitDelete: FunctionReference<\"mutation\", \"internal\">;\n };\n};\n\nexport type AuthDbConfig = { component: AuthComponentApiLike };\n\nexport type AuthDb = ReturnType<typeof authDb>;\n\nexport function authDb(ctx: CtxLike, config: AuthDbConfig) {\n const component = config.component;\n return {\n users: {\n getById: (userId: string) =>\n ctx.runQuery(component.public.userGetById, { userId }),\n findByVerifiedEmail: (email: string) =>\n ctx.runQuery(component.public.userFindByVerifiedEmail, { email }),\n findByVerifiedPhone: (phone: string) =>\n ctx.runQuery(component.public.userFindByVerifiedPhone, { phone }),\n insert: (data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userInsert, {\n data,\n }) as Promise<string>,\n patch: (userId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userPatch, { userId, data }),\n upsert: (userId: string | undefined, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userUpsert, {\n userId,\n data,\n }) as Promise<string>,\n },\n accounts: {\n get: (provider: string, providerAccountId: string) =>\n ctx.runQuery(component.public.accountGet, {\n provider,\n providerAccountId,\n }),\n getById: (accountId: string) =>\n ctx.runQuery(component.public.accountGetById, { accountId }),\n create: (args: {\n userId: string;\n provider: string;\n providerAccountId: string;\n secret?: string;\n extend?: Record<string, unknown>;\n }) =>\n ctx.runMutation(\n component.public.accountInsert,\n args,\n ) as Promise<string>,\n patch: (accountId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.accountPatch, { accountId, data }),\n delete: (accountId: string) =>\n ctx.runMutation(component.public.accountDelete, { accountId }),\n },\n sessions: {\n create: (userId: string, expirationTime: number) =>\n ctx.runMutation(component.public.sessionCreate, {\n userId,\n expirationTime,\n }) as Promise<string>,\n getById: (sessionId: string) =>\n ctx.runQuery(component.public.sessionGetById, { sessionId }),\n delete: (sessionId: string) =>\n ctx.runMutation(component.public.sessionDelete, { sessionId }),\n listByUser: (userId: string) =>\n ctx.runQuery(component.public.sessionListByUser, { userId }),\n },\n verifiers: {\n create: (sessionId?: string) =>\n ctx.runMutation(component.public.verifierCreate, {\n sessionId,\n }) as Promise<string>,\n getById: (verifierId: string) =>\n ctx.runQuery(component.public.verifierGetById, { verifierId }),\n getBySignature: (signature: string) =>\n ctx.runQuery(component.public.verifierGetBySignature, { signature }),\n patch: (verifierId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.verifierPatch, { verifierId, data }),\n delete: (verifierId: string) =>\n ctx.runMutation(component.public.verifierDelete, { verifierId }),\n },\n verificationCodes: {\n getByAccountId: (accountId: string) =>\n ctx.runQuery(component.public.verificationCodeGetByAccountId, {\n accountId,\n }),\n getByCode: (code: string) =>\n ctx.runQuery(component.public.verificationCodeGetByCode, { code }),\n create: (args: {\n accountId: string;\n provider: string;\n code: string;\n expirationTime: number;\n verifier?: string;\n emailVerified?: string;\n phoneVerified?: string;\n }) => ctx.runMutation(component.public.verificationCodeCreate, args),\n delete: (verificationCodeId: string) =>\n ctx.runMutation(component.public.verificationCodeDelete, {\n verificationCodeId,\n }),\n },\n refreshTokens: {\n create: (args: {\n sessionId: string;\n expirationTime: number;\n parentRefreshTokenId?: string;\n }) =>\n ctx.runMutation(\n component.public.refreshTokenCreate,\n args,\n ) as Promise<string>,\n getById: (refreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetById, { refreshTokenId }),\n patch: (refreshTokenId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.refreshTokenPatch, {\n refreshTokenId,\n data,\n }),\n getChildren: (sessionId: string, parentRefreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetChildren, {\n sessionId,\n parentRefreshTokenId,\n }),\n listBySession: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenListBySession, { sessionId }),\n deleteAll: (sessionId: string) =>\n ctx.runMutation(component.public.refreshTokenDeleteAll, { sessionId }),\n getActive: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenGetActive, { sessionId }),\n },\n rateLimits: {\n get: (identifier: string) =>\n ctx.runQuery(component.public.rateLimitGet, { identifier }),\n create: (args: {\n identifier: string;\n attemptsLeft: number;\n lastAttemptTime: number;\n }) => ctx.runMutation(component.public.rateLimitCreate, args),\n patch: (rateLimitId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.rateLimitPatch, { rateLimitId, data }),\n delete: (rateLimitId: string) =>\n ctx.runMutation(component.public.rateLimitDelete, { rateLimitId }),\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"db.js","names":[],"sources":["../../../src/server/db.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n GenericMutationCtx,\n FunctionReference,\n} from \"convex/server\";\n\ntype MutationCtxLike = Pick<\n GenericMutationCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ActionCtxLike = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\" | \"runAction\"\n>;\n\ntype CtxLike = MutationCtxLike | ActionCtxLike;\n\ntype AuthComponentApiLike = {\n public: {\n userGetById: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedEmail: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedPhone: FunctionReference<\"query\", \"internal\">;\n userInsert: FunctionReference<\"mutation\", \"internal\">;\n userPatch: FunctionReference<\"mutation\", \"internal\">;\n userUpsert: FunctionReference<\"mutation\", \"internal\">;\n accountGet: FunctionReference<\"query\", \"internal\">;\n accountGetById: FunctionReference<\"query\", \"internal\">;\n accountInsert: FunctionReference<\"mutation\", \"internal\">;\n accountPatch: FunctionReference<\"mutation\", \"internal\">;\n accountDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionCreate: FunctionReference<\"mutation\", \"internal\">;\n sessionGetById: FunctionReference<\"query\", \"internal\">;\n sessionDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionListByUser: FunctionReference<\"query\", \"internal\">;\n verifierCreate: FunctionReference<\"mutation\", \"internal\">;\n verifierGetById: FunctionReference<\"query\", \"internal\">;\n verifierGetBySignature: FunctionReference<\"query\", \"internal\">;\n verifierPatch: FunctionReference<\"mutation\", \"internal\">;\n verifierDelete: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeGetByAccountId: FunctionReference<\"query\", \"internal\">;\n verificationCodeGetByCode: FunctionReference<\"query\", \"internal\">;\n verificationCodeCreate: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeDelete: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenCreate: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetById: FunctionReference<\"query\", \"internal\">;\n refreshTokenPatch: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetChildren: FunctionReference<\"query\", \"internal\">;\n refreshTokenListBySession: FunctionReference<\"query\", \"internal\">;\n refreshTokenDeleteAll: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetActive: FunctionReference<\"query\", \"internal\">;\n rateLimitGet: FunctionReference<\"query\", \"internal\">;\n rateLimitCreate: FunctionReference<\"mutation\", \"internal\">;\n rateLimitPatch: FunctionReference<\"mutation\", \"internal\">;\n rateLimitDelete: FunctionReference<\"mutation\", \"internal\">;\n };\n};\n\n/** @internal */\nexport type AuthDbConfig = { component: AuthComponentApiLike };\n\n/** @internal */\nexport type AuthDb = ReturnType<typeof authDb>;\n\n/** @internal */\nexport function authDb(ctx: CtxLike, config: AuthDbConfig) {\n const component = config.component;\n return {\n users: {\n getById: (userId: string) =>\n ctx.runQuery(component.public.userGetById, { userId }),\n findByVerifiedEmail: (email: string) =>\n ctx.runQuery(component.public.userFindByVerifiedEmail, { email }),\n findByVerifiedPhone: (phone: string) =>\n ctx.runQuery(component.public.userFindByVerifiedPhone, { phone }),\n insert: (data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userInsert, {\n data,\n }) as Promise<string>,\n patch: (userId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userPatch, { userId, data }),\n upsert: (userId: string | undefined, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userUpsert, {\n userId,\n data,\n }) as Promise<string>,\n },\n accounts: {\n get: (provider: string, providerAccountId: string) =>\n ctx.runQuery(component.public.accountGet, {\n provider,\n providerAccountId,\n }),\n getById: (accountId: string) =>\n ctx.runQuery(component.public.accountGetById, { accountId }),\n create: (args: {\n userId: string;\n provider: string;\n providerAccountId: string;\n secret?: string;\n extend?: Record<string, unknown>;\n }) =>\n ctx.runMutation(\n component.public.accountInsert,\n args,\n ) as Promise<string>,\n patch: (accountId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.accountPatch, { accountId, data }),\n delete: (accountId: string) =>\n ctx.runMutation(component.public.accountDelete, { accountId }),\n },\n sessions: {\n create: (userId: string, expirationTime: number) =>\n ctx.runMutation(component.public.sessionCreate, {\n userId,\n expirationTime,\n }) as Promise<string>,\n getById: (sessionId: string) =>\n ctx.runQuery(component.public.sessionGetById, { sessionId }),\n delete: (sessionId: string) =>\n ctx.runMutation(component.public.sessionDelete, { sessionId }),\n listByUser: (userId: string) =>\n ctx.runQuery(component.public.sessionListByUser, { userId }),\n },\n verifiers: {\n create: (sessionId?: string) =>\n ctx.runMutation(component.public.verifierCreate, {\n sessionId,\n }) as Promise<string>,\n getById: (verifierId: string) =>\n ctx.runQuery(component.public.verifierGetById, { verifierId }),\n getBySignature: (signature: string) =>\n ctx.runQuery(component.public.verifierGetBySignature, { signature }),\n patch: (verifierId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.verifierPatch, { verifierId, data }),\n delete: (verifierId: string) =>\n ctx.runMutation(component.public.verifierDelete, { verifierId }),\n },\n verificationCodes: {\n getByAccountId: (accountId: string) =>\n ctx.runQuery(component.public.verificationCodeGetByAccountId, {\n accountId,\n }),\n getByCode: (code: string) =>\n ctx.runQuery(component.public.verificationCodeGetByCode, { code }),\n create: (args: {\n accountId: string;\n provider: string;\n code: string;\n expirationTime: number;\n verifier?: string;\n emailVerified?: string;\n phoneVerified?: string;\n }) => ctx.runMutation(component.public.verificationCodeCreate, args),\n delete: (verificationCodeId: string) =>\n ctx.runMutation(component.public.verificationCodeDelete, {\n verificationCodeId,\n }),\n },\n refreshTokens: {\n create: (args: {\n sessionId: string;\n expirationTime: number;\n parentRefreshTokenId?: string;\n }) =>\n ctx.runMutation(\n component.public.refreshTokenCreate,\n args,\n ) as Promise<string>,\n getById: (refreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetById, { refreshTokenId }),\n patch: (refreshTokenId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.refreshTokenPatch, {\n refreshTokenId,\n data,\n }),\n getChildren: (sessionId: string, parentRefreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetChildren, {\n sessionId,\n parentRefreshTokenId,\n }),\n listBySession: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenListBySession, { sessionId }),\n deleteAll: (sessionId: string) =>\n ctx.runMutation(component.public.refreshTokenDeleteAll, { sessionId }),\n getActive: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenGetActive, { sessionId }),\n },\n rateLimits: {\n get: (identifier: string) =>\n ctx.runQuery(component.public.rateLimitGet, { identifier }),\n create: (args: {\n identifier: string;\n attemptsLeft: number;\n lastAttemptTime: number;\n }) => ctx.runMutation(component.public.rateLimitCreate, args),\n patch: (rateLimitId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.rateLimitPatch, { rateLimitId, data }),\n delete: (rateLimitId: string) =>\n ctx.runMutation(component.public.rateLimitDelete, { rateLimitId }),\n },\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { AuthError } from "./fx.js";
2
2
  import { generateRandomString, requireEnv, sha256 } from "./utils.js";
3
+ import { userIdFromIdentitySubject } from "./identity.js";
3
4
  import { callSignIn } from "./mutations/signin.js";
4
5
  import { mutateDeviceAuthorize, mutateDeviceDelete, mutateDeviceInsert, mutateDeviceUpdateLastPolled, queryDeviceByCodeHash, queryDeviceByUserCode } from "./types.js";
5
6
  import { Fx } from "@robelest/fx";
@@ -23,6 +24,7 @@ const DEVICE_FLOWS = [
23
24
  "poll",
24
25
  "verify"
25
26
  ];
27
+ /** @internal */
26
28
  const handleDevice = (ctx, provider, args) => Fx.from({
27
29
  ok: async () => {
28
30
  const params = args.params ?? {};
@@ -81,7 +83,7 @@ const handleDevice = (ctx, provider, args) => Fx.from({
81
83
  if (typeof params.userCode !== "string") throw new AuthError("DEVICE_INVALID_USER_CODE", "Missing `userCode` parameter for verify flow.");
82
84
  const identity = await ctx.auth.getUserIdentity();
83
85
  if (identity === null) throw new AuthError("NOT_SIGNED_IN", "You must be signed in to authorize a device.");
84
- const userId = identity.subject.split("|")[0];
86
+ const userId = userIdFromIdentitySubject(identity.subject);
85
87
  const doc = await queryDeviceByUserCode(ctx, params.userCode);
86
88
  if (doc === null) throw new AuthError("DEVICE_INVALID_USER_CODE");
87
89
  if (Date.now() > doc.expiresAt) {
@@ -1 +1 @@
1
- {"version":3,"file":"device.js","names":["doc"],"sources":["../../../src/server/device.ts"],"sourcesContent":["/**\n * Server-side device authorization flow logic (RFC 8628).\n *\n * Handles the three phases of the device flow:\n * 1. (default) — Generate a device code + user code pair\n * 2. poll — Device checks whether the user has authorized yet\n * 3. verify — Authenticated user links a user code to their session\n *\n * Uses `@oslojs/crypto/random` for code generation and\n * `@oslojs/crypto/sha2` for hashing device codes before storage.\n */\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./fx\";\nimport { callSignIn } from \"./mutations/index\";\nimport { DeviceProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n mutateDeviceInsert,\n queryDeviceByCodeHash,\n queryDeviceByUserCode,\n mutateDeviceAuthorize,\n mutateDeviceUpdateLastPolled,\n mutateDeviceDelete,\n} from \"./types\";\nimport { generateRandomString, sha256 } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEVICE_CODE_ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst DEVICE_CODE_LENGTH = 40;\nconst DEVICE_FLOWS = [\"create\", \"poll\", \"verify\"] as const;\n\n// ============================================================================\n// Create flow\n// ============================================================================\n\n// ============================================================================\n// Poll flow — pipeline of validations + status dispatch\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype DeviceResult =\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n }\n | { kind: \"signedIn\"; signedIn: SessionInfo | null };\n\nexport const handleDevice = (\n ctx: EnrichedActionCtx,\n provider: DeviceProviderConfig,\n args: { params?: Record<string, any> },\n): Fx<DeviceResult, AuthError> =>\n Fx.from({\n ok: async () => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n const flow = (typeof params.flow === \"string\" ? params.flow : \"create\") as\n | \"create\"\n | \"poll\"\n | \"verify\";\n\n if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: create, poll, verify\",\n );\n }\n\n if (flow === \"create\") {\n const deviceCode = generateRandomString(\n DEVICE_CODE_LENGTH,\n DEVICE_CODE_ALPHABET,\n );\n const deviceCodeHash = await sha256(deviceCode);\n\n const rawUserCode = generateRandomString(\n provider.userCodeLength,\n provider.charset,\n );\n const mid = Math.floor(rawUserCode.length / 2);\n const userCode =\n rawUserCode.slice(0, mid) + \"-\" + rawUserCode.slice(mid);\n\n const expiresAt = Date.now() + provider.expiresIn * 1000;\n await mutateDeviceInsert(ctx, {\n deviceCodeHash,\n userCode,\n expiresAt,\n interval: provider.interval,\n status: \"pending\",\n });\n\n const verificationUri =\n provider.verificationUri ??\n `${process.env.SITE_URL ?? requireEnv(\"SITE_URL\")}/device`;\n\n return {\n kind: \"deviceCode\" as const,\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`,\n expiresIn: provider.expiresIn,\n interval: provider.interval,\n };\n }\n\n if (flow === \"poll\") {\n if (typeof params.deviceCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `deviceCode` parameter for poll flow.\",\n );\n }\n\n const hash = await sha256(params.deviceCode);\n const doc = await queryDeviceByCodeHash(ctx, hash);\n if (doc === null) {\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (\n doc.lastPolledAt !== undefined &&\n (Date.now() - doc.lastPolledAt) / 1000 < doc.interval\n ) {\n throw new AuthError(\"DEVICE_SLOW_DOWN\");\n }\n\n await mutateDeviceUpdateLastPolled(ctx, doc._id, Date.now());\n\n if (doc.status === \"pending\") {\n throw new AuthError(\"DEVICE_AUTHORIZATION_PENDING\");\n }\n if (doc.status === \"denied\") {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_DENIED\");\n }\n\n if (!doc.userId || !doc.sessionId) {\n throw new AuthError(\n \"INTERNAL_ERROR\",\n \"Authorized device code missing userId or sessionId\",\n );\n }\n\n await mutateDeviceDelete(ctx, doc._id);\n const signInResult = await callSignIn(ctx, {\n userId: doc.userId,\n sessionId: doc.sessionId,\n generateTokens: true,\n });\n return { kind: \"signedIn\" as const, signedIn: signInResult };\n }\n\n if (typeof params.userCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_INVALID_USER_CODE\",\n \"Missing `userCode` parameter for verify flow.\",\n );\n }\n\n const identity = await ctx.auth.getUserIdentity();\n if (identity === null) {\n throw new AuthError(\n \"NOT_SIGNED_IN\",\n \"You must be signed in to authorize a device.\",\n );\n }\n\n const userId = identity.subject.split(\"|\")[0]!;\n const doc = await queryDeviceByUserCode(ctx, params.userCode);\n if (doc === null) {\n throw new AuthError(\"DEVICE_INVALID_USER_CODE\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (doc.status !== \"pending\") {\n throw new AuthError(\"DEVICE_ALREADY_AUTHORIZED\");\n }\n\n const signInResult = await callSignIn(ctx, {\n userId,\n generateTokens: false,\n });\n await mutateDeviceAuthorize(\n ctx,\n doc._id,\n signInResult.userId,\n signInResult.sessionId,\n );\n return { kind: \"signedIn\" as const, signedIn: null };\n },\n err: (e) =>\n e instanceof AuthError\n ? e\n : new AuthError(\"INTERNAL_ERROR\", `Device flow failed: ${String(e)}`),\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"device.js","names":["doc"],"sources":["../../../src/server/device.ts"],"sourcesContent":["/**\n * Server-side device authorization flow logic (RFC 8628).\n *\n * Handles the three phases of the device flow:\n * 1. (default) — Generate a device code + user code pair\n * 2. poll — Device checks whether the user has authorized yet\n * 3. verify — Authenticated user links a user code to their session\n *\n * Uses `@oslojs/crypto/random` for code generation and\n * `@oslojs/crypto/sha2` for hashing device codes before storage.\n */\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./fx\";\nimport { userIdFromIdentitySubject } from \"./identity\";\nimport { callSignIn } from \"./mutations/index\";\nimport { DeviceProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n mutateDeviceInsert,\n queryDeviceByCodeHash,\n queryDeviceByUserCode,\n mutateDeviceAuthorize,\n mutateDeviceUpdateLastPolled,\n mutateDeviceDelete,\n} from \"./types\";\nimport { generateRandomString, sha256 } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEVICE_CODE_ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst DEVICE_CODE_LENGTH = 40;\nconst DEVICE_FLOWS = [\"create\", \"poll\", \"verify\"] as const;\n\n// ============================================================================\n// Create flow\n// ============================================================================\n\n// ============================================================================\n// Poll flow — pipeline of validations + status dispatch\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype DeviceResult =\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n }\n | { kind: \"signedIn\"; signedIn: SessionInfo | null };\n\n/** @internal */\nexport const handleDevice = (\n ctx: EnrichedActionCtx,\n provider: DeviceProviderConfig,\n args: { params?: Record<string, any> },\n): Fx<DeviceResult, AuthError> =>\n Fx.from({\n ok: async () => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n const flow = (typeof params.flow === \"string\" ? params.flow : \"create\") as\n | \"create\"\n | \"poll\"\n | \"verify\";\n\n if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: create, poll, verify\",\n );\n }\n\n if (flow === \"create\") {\n const deviceCode = generateRandomString(\n DEVICE_CODE_LENGTH,\n DEVICE_CODE_ALPHABET,\n );\n const deviceCodeHash = await sha256(deviceCode);\n\n const rawUserCode = generateRandomString(\n provider.userCodeLength,\n provider.charset,\n );\n const mid = Math.floor(rawUserCode.length / 2);\n const userCode =\n rawUserCode.slice(0, mid) + \"-\" + rawUserCode.slice(mid);\n\n const expiresAt = Date.now() + provider.expiresIn * 1000;\n await mutateDeviceInsert(ctx, {\n deviceCodeHash,\n userCode,\n expiresAt,\n interval: provider.interval,\n status: \"pending\",\n });\n\n const verificationUri =\n provider.verificationUri ??\n `${process.env.SITE_URL ?? requireEnv(\"SITE_URL\")}/device`;\n\n return {\n kind: \"deviceCode\" as const,\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`,\n expiresIn: provider.expiresIn,\n interval: provider.interval,\n };\n }\n\n if (flow === \"poll\") {\n if (typeof params.deviceCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `deviceCode` parameter for poll flow.\",\n );\n }\n\n const hash = await sha256(params.deviceCode);\n const doc = await queryDeviceByCodeHash(ctx, hash);\n if (doc === null) {\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (\n doc.lastPolledAt !== undefined &&\n (Date.now() - doc.lastPolledAt) / 1000 < doc.interval\n ) {\n throw new AuthError(\"DEVICE_SLOW_DOWN\");\n }\n\n await mutateDeviceUpdateLastPolled(ctx, doc._id, Date.now());\n\n if (doc.status === \"pending\") {\n throw new AuthError(\"DEVICE_AUTHORIZATION_PENDING\");\n }\n if (doc.status === \"denied\") {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_DENIED\");\n }\n\n if (!doc.userId || !doc.sessionId) {\n throw new AuthError(\n \"INTERNAL_ERROR\",\n \"Authorized device code missing userId or sessionId\",\n );\n }\n\n await mutateDeviceDelete(ctx, doc._id);\n const signInResult = await callSignIn(ctx, {\n userId: doc.userId,\n sessionId: doc.sessionId,\n generateTokens: true,\n });\n return { kind: \"signedIn\" as const, signedIn: signInResult };\n }\n\n if (typeof params.userCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_INVALID_USER_CODE\",\n \"Missing `userCode` parameter for verify flow.\",\n );\n }\n\n const identity = await ctx.auth.getUserIdentity();\n if (identity === null) {\n throw new AuthError(\n \"NOT_SIGNED_IN\",\n \"You must be signed in to authorize a device.\",\n );\n }\n\n const userId = userIdFromIdentitySubject(identity.subject);\n const doc = await queryDeviceByUserCode(ctx, params.userCode);\n if (doc === null) {\n throw new AuthError(\"DEVICE_INVALID_USER_CODE\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (doc.status !== \"pending\") {\n throw new AuthError(\"DEVICE_ALREADY_AUTHORIZED\");\n }\n\n const signInResult = await callSignIn(ctx, {\n userId,\n generateTokens: false,\n });\n await mutateDeviceAuthorize(\n ctx,\n doc._id,\n signInResult.userId,\n signInResult.sessionId,\n );\n return { kind: \"signedIn\" as const, signedIn: null };\n },\n err: (e) =>\n e instanceof AuthError\n ? e\n : new AuthError(\"INTERNAL_ERROR\", `Device flow failed: ${String(e)}`),\n });\n"],"mappings":"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"}