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

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 (310) hide show
  1. package/dist/authorization/index.d.ts +1 -1
  2. package/dist/authorization/index.js +1 -1
  3. package/dist/authorization/index.js.map +1 -1
  4. package/dist/client/index.d.ts +1 -2
  5. package/dist/client/index.d.ts.map +1 -1
  6. package/dist/client/index.js +36 -39
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/component/client/index.d.ts +1 -2
  9. package/dist/component/convex.config.d.ts +2 -2
  10. package/dist/component/convex.config.d.ts.map +1 -1
  11. package/dist/component/model.d.ts +5 -5
  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 +39 -39
  53. package/dist/component/server/auth.d.ts +95 -52
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +63 -43
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/core.js +116 -235
  58. package/dist/component/server/core.js.map +1 -1
  59. package/dist/component/server/crypto.js +25 -7
  60. package/dist/component/server/crypto.js.map +1 -1
  61. package/dist/component/server/device.js +58 -15
  62. package/dist/component/server/device.js.map +1 -1
  63. package/dist/component/server/enterprise/domain.js +148 -59
  64. package/dist/component/server/enterprise/domain.js.map +1 -1
  65. package/dist/component/server/enterprise/http.js +36 -15
  66. package/dist/component/server/enterprise/http.js.map +1 -1
  67. package/dist/component/server/enterprise/oidc.js +1 -1
  68. package/dist/component/server/http.js +26 -21
  69. package/dist/component/server/http.js.map +1 -1
  70. package/dist/component/server/identity.js +5 -2
  71. package/dist/component/server/identity.js.map +1 -1
  72. package/dist/component/server/limits.js +21 -30
  73. package/dist/component/server/limits.js.map +1 -1
  74. package/dist/component/server/mutations/account.js +12 -10
  75. package/dist/component/server/mutations/account.js.map +1 -1
  76. package/dist/component/server/mutations/code.js +5 -2
  77. package/dist/component/server/mutations/code.js.map +1 -1
  78. package/dist/component/server/mutations/invalidate.js +1 -1
  79. package/dist/component/server/mutations/invalidate.js.map +1 -1
  80. package/dist/component/server/mutations/oauth.js +10 -4
  81. package/dist/component/server/mutations/oauth.js.map +1 -1
  82. package/dist/component/server/mutations/refresh.js +2 -2
  83. package/dist/component/server/mutations/refresh.js.map +1 -1
  84. package/dist/component/server/mutations/register.js +46 -42
  85. package/dist/component/server/mutations/register.js.map +1 -1
  86. package/dist/component/server/mutations/retrieve.js +21 -25
  87. package/dist/component/server/mutations/retrieve.js.map +1 -1
  88. package/dist/component/server/mutations/signature.js +10 -4
  89. package/dist/component/server/mutations/signature.js.map +1 -1
  90. package/dist/component/server/mutations/signout.js.map +1 -1
  91. package/dist/component/server/mutations/store.js +9 -24
  92. package/dist/component/server/mutations/store.js.map +1 -1
  93. package/dist/component/server/mutations/verifier.js.map +1 -1
  94. package/dist/component/server/mutations/verify.js +1 -1
  95. package/dist/component/server/mutations/verify.js.map +1 -1
  96. package/dist/component/server/oauth.js +53 -16
  97. package/dist/component/server/oauth.js.map +1 -1
  98. package/dist/component/server/passkey.js +115 -31
  99. package/dist/component/server/passkey.js.map +1 -1
  100. package/dist/component/server/redirects.js +9 -3
  101. package/dist/component/server/redirects.js.map +1 -1
  102. package/dist/component/server/refresh.js +10 -7
  103. package/dist/component/server/refresh.js.map +1 -1
  104. package/dist/component/server/runtime.d.ts +3 -3
  105. package/dist/component/server/runtime.d.ts.map +1 -1
  106. package/dist/component/server/runtime.js +62 -20
  107. package/dist/component/server/runtime.js.map +1 -1
  108. package/dist/component/server/signin.js +34 -10
  109. package/dist/component/server/signin.js.map +1 -1
  110. package/dist/component/server/totp.js +79 -19
  111. package/dist/component/server/totp.js.map +1 -1
  112. package/dist/component/server/types.d.ts +12 -20
  113. package/dist/component/server/types.d.ts.map +1 -1
  114. package/dist/component/server/types.js.map +1 -1
  115. package/dist/component/server/users.js +6 -3
  116. package/dist/component/server/users.js.map +1 -1
  117. package/dist/component/server/utils.js +10 -4
  118. package/dist/component/server/utils.js.map +1 -1
  119. package/dist/core/types.d.ts +14 -22
  120. package/dist/core/types.d.ts.map +1 -1
  121. package/dist/factors/device.js +8 -9
  122. package/dist/factors/device.js.map +1 -1
  123. package/dist/factors/passkey.js +18 -21
  124. package/dist/factors/passkey.js.map +1 -1
  125. package/dist/providers/password.js +66 -81
  126. package/dist/providers/password.js.map +1 -1
  127. package/dist/runtime/invite.js +2 -8
  128. package/dist/runtime/invite.js.map +1 -1
  129. package/dist/server/auth.d.ts +95 -52
  130. package/dist/server/auth.d.ts.map +1 -1
  131. package/dist/server/auth.js +63 -43
  132. package/dist/server/auth.js.map +1 -1
  133. package/dist/server/core.d.ts +71 -159
  134. package/dist/server/core.d.ts.map +1 -1
  135. package/dist/server/core.js +116 -235
  136. package/dist/server/core.js.map +1 -1
  137. package/dist/server/crypto.d.ts.map +1 -1
  138. package/dist/server/crypto.js +25 -7
  139. package/dist/server/crypto.js.map +1 -1
  140. package/dist/server/device.js +58 -15
  141. package/dist/server/device.js.map +1 -1
  142. package/dist/server/enterprise/domain.d.ts +0 -8
  143. package/dist/server/enterprise/domain.d.ts.map +1 -1
  144. package/dist/server/enterprise/domain.js +148 -59
  145. package/dist/server/enterprise/domain.js.map +1 -1
  146. package/dist/server/enterprise/http.d.ts.map +1 -1
  147. package/dist/server/enterprise/http.js +35 -14
  148. package/dist/server/enterprise/http.js.map +1 -1
  149. package/dist/server/http.d.ts +2 -2
  150. package/dist/server/http.d.ts.map +1 -1
  151. package/dist/server/http.js +25 -20
  152. package/dist/server/http.js.map +1 -1
  153. package/dist/server/identity.js +5 -2
  154. package/dist/server/identity.js.map +1 -1
  155. package/dist/server/index.d.ts +2 -2
  156. package/dist/server/limits.js +21 -30
  157. package/dist/server/limits.js.map +1 -1
  158. package/dist/server/mounts.d.ts +26 -64
  159. package/dist/server/mounts.d.ts.map +1 -1
  160. package/dist/server/mounts.js +45 -106
  161. package/dist/server/mounts.js.map +1 -1
  162. package/dist/server/mutations/account.d.ts +8 -9
  163. package/dist/server/mutations/account.d.ts.map +1 -1
  164. package/dist/server/mutations/account.js +11 -9
  165. package/dist/server/mutations/account.js.map +1 -1
  166. package/dist/server/mutations/code.d.ts +13 -13
  167. package/dist/server/mutations/code.d.ts.map +1 -1
  168. package/dist/server/mutations/code.js +5 -2
  169. package/dist/server/mutations/code.js.map +1 -1
  170. package/dist/server/mutations/invalidate.d.ts +4 -4
  171. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  172. package/dist/server/mutations/invalidate.js.map +1 -1
  173. package/dist/server/mutations/oauth.d.ts +12 -10
  174. package/dist/server/mutations/oauth.d.ts.map +1 -1
  175. package/dist/server/mutations/oauth.js +9 -3
  176. package/dist/server/mutations/oauth.js.map +1 -1
  177. package/dist/server/mutations/refresh.d.ts +3 -3
  178. package/dist/server/mutations/refresh.d.ts.map +1 -1
  179. package/dist/server/mutations/refresh.js +1 -1
  180. package/dist/server/mutations/refresh.js.map +1 -1
  181. package/dist/server/mutations/register.d.ts +11 -11
  182. package/dist/server/mutations/register.d.ts.map +1 -1
  183. package/dist/server/mutations/register.js +45 -41
  184. package/dist/server/mutations/register.js.map +1 -1
  185. package/dist/server/mutations/retrieve.d.ts +6 -6
  186. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  187. package/dist/server/mutations/retrieve.js +20 -24
  188. package/dist/server/mutations/retrieve.js.map +1 -1
  189. package/dist/server/mutations/signature.d.ts +6 -7
  190. package/dist/server/mutations/signature.d.ts.map +1 -1
  191. package/dist/server/mutations/signature.js +9 -3
  192. package/dist/server/mutations/signature.js.map +1 -1
  193. package/dist/server/mutations/signin.d.ts +5 -5
  194. package/dist/server/mutations/signin.d.ts.map +1 -1
  195. package/dist/server/mutations/signout.js.map +1 -1
  196. package/dist/server/mutations/store.d.ts +97 -97
  197. package/dist/server/mutations/store.d.ts.map +1 -1
  198. package/dist/server/mutations/store.js +8 -23
  199. package/dist/server/mutations/store.js.map +1 -1
  200. package/dist/server/mutations/verifier.js.map +1 -1
  201. package/dist/server/mutations/verify.d.ts +10 -10
  202. package/dist/server/mutations/verify.d.ts.map +1 -1
  203. package/dist/server/mutations/verify.js.map +1 -1
  204. package/dist/server/oauth.js +53 -16
  205. package/dist/server/oauth.js.map +1 -1
  206. package/dist/server/passkey.d.ts +2 -2
  207. package/dist/server/passkey.d.ts.map +1 -1
  208. package/dist/server/passkey.js +114 -30
  209. package/dist/server/passkey.js.map +1 -1
  210. package/dist/server/redirects.js +9 -3
  211. package/dist/server/redirects.js.map +1 -1
  212. package/dist/server/refresh.js +10 -7
  213. package/dist/server/refresh.js.map +1 -1
  214. package/dist/server/runtime.d.ts +14 -14
  215. package/dist/server/runtime.d.ts.map +1 -1
  216. package/dist/server/runtime.js +61 -19
  217. package/dist/server/runtime.js.map +1 -1
  218. package/dist/server/signin.js +34 -10
  219. package/dist/server/signin.js.map +1 -1
  220. package/dist/server/ssr.d.ts.map +1 -1
  221. package/dist/server/ssr.js +175 -184
  222. package/dist/server/ssr.js.map +1 -1
  223. package/dist/server/totp.js +78 -18
  224. package/dist/server/totp.js.map +1 -1
  225. package/dist/server/types.d.ts +13 -21
  226. package/dist/server/types.d.ts.map +1 -1
  227. package/dist/server/types.js.map +1 -1
  228. package/dist/server/users.js +6 -3
  229. package/dist/server/users.js.map +1 -1
  230. package/dist/server/utils.js +10 -4
  231. package/dist/server/utils.js.map +1 -1
  232. package/package.json +2 -6
  233. package/src/authorization/index.ts +1 -1
  234. package/src/cli/index.ts +1 -1
  235. package/src/client/core/types.ts +14 -14
  236. package/src/client/factors/device.ts +10 -12
  237. package/src/client/factors/passkey.ts +23 -26
  238. package/src/client/index.ts +54 -64
  239. package/src/client/runtime/invite.ts +5 -7
  240. package/src/component/index.ts +1 -0
  241. package/src/component/public/enterprise/audit.ts +6 -1
  242. package/src/component/public/enterprise/core.ts +1 -0
  243. package/src/component/public/enterprise/domains.ts +5 -1
  244. package/src/component/public/enterprise/scim.ts +1 -0
  245. package/src/component/public/enterprise/secrets.ts +1 -0
  246. package/src/component/public/enterprise/webhooks.ts +1 -0
  247. package/src/component/public/factors/devices.ts +1 -0
  248. package/src/component/public/factors/passkeys.ts +1 -0
  249. package/src/component/public/factors/totp.ts +1 -0
  250. package/src/component/public/groups/core.ts +1 -1
  251. package/src/component/public/groups/invites.ts +7 -1
  252. package/src/component/public/groups/members.ts +1 -0
  253. package/src/component/public/identity/accounts.ts +1 -0
  254. package/src/component/public/identity/codes.ts +1 -0
  255. package/src/component/public/identity/sessions.ts +1 -0
  256. package/src/component/public/identity/tokens.ts +1 -0
  257. package/src/component/public/identity/users.ts +1 -0
  258. package/src/component/public/identity/verifiers.ts +1 -0
  259. package/src/component/public/security/keys.ts +1 -0
  260. package/src/component/public/security/limits.ts +1 -0
  261. package/src/providers/password.ts +89 -110
  262. package/src/server/auth.ts +177 -111
  263. package/src/server/core.ts +197 -233
  264. package/src/server/crypto.ts +31 -29
  265. package/src/server/device.ts +65 -32
  266. package/src/server/enterprise/domain.ts +158 -170
  267. package/src/server/enterprise/http.ts +46 -39
  268. package/src/server/http.ts +36 -30
  269. package/src/server/identity.ts +5 -5
  270. package/src/server/index.ts +2 -0
  271. package/src/server/limits.ts +53 -80
  272. package/src/server/mounts.ts +47 -74
  273. package/src/server/mutations/account.ts +22 -36
  274. package/src/server/mutations/code.ts +6 -6
  275. package/src/server/mutations/invalidate.ts +1 -1
  276. package/src/server/mutations/oauth.ts +14 -8
  277. package/src/server/mutations/refresh.ts +5 -4
  278. package/src/server/mutations/register.ts +87 -132
  279. package/src/server/mutations/retrieve.ts +44 -44
  280. package/src/server/mutations/signature.ts +13 -6
  281. package/src/server/mutations/signout.ts +1 -1
  282. package/src/server/mutations/store.ts +16 -31
  283. package/src/server/mutations/verifier.ts +1 -1
  284. package/src/server/mutations/verify.ts +3 -5
  285. package/src/server/oauth.ts +60 -69
  286. package/src/server/passkey.ts +567 -517
  287. package/src/server/redirects.ts +10 -6
  288. package/src/server/refresh.ts +14 -18
  289. package/src/server/runtime.ts +70 -55
  290. package/src/server/signin.ts +44 -37
  291. package/src/server/ssr.ts +390 -407
  292. package/src/server/totp.ts +85 -35
  293. package/src/server/types.ts +19 -22
  294. package/src/server/users.ts +7 -6
  295. package/src/server/utils.ts +10 -12
  296. package/dist/component/server/authError.js +0 -34
  297. package/dist/component/server/authError.js.map +0 -1
  298. package/dist/component/server/errors.d.ts +0 -1
  299. package/dist/component/server/errors.js +0 -137
  300. package/dist/component/server/errors.js.map +0 -1
  301. package/dist/server/authError.d.ts +0 -46
  302. package/dist/server/authError.d.ts.map +0 -1
  303. package/dist/server/authError.js +0 -34
  304. package/dist/server/authError.js.map +0 -1
  305. package/dist/server/errors.d.ts +0 -177
  306. package/dist/server/errors.d.ts.map +0 -1
  307. package/dist/server/errors.js +0 -212
  308. package/dist/server/errors.js.map +0 -1
  309. package/src/server/authError.ts +0 -44
  310. package/src/server/errors.ts +0 -290
@@ -1,11 +1,10 @@
1
- import { AuthError } from "../authError.js";
2
1
  import { LOG_LEVELS, logWithLevel, maybeRedact } from "../utils.js";
3
- import { authDb } from "../db.js";
4
2
  import { verify } from "../crypto.js";
3
+ import { authDb } from "../db.js";
5
4
  import { AUTH_STORE_REF } from "./store/refs.js";
6
5
  import { isSignInRateLimited, recordFailedSignIn, resetSignInRateLimit } from "../limits.js";
7
- import { v } from "convex/values";
8
6
  import { Fx } from "@robelest/fx";
7
+ import { v } from "convex/values";
9
8
 
10
9
  //#region src/server/mutations/retrieve.ts
11
10
  const retrieveAccountWithCredentialsArgs = v.object({
@@ -25,29 +24,26 @@ function retrieveAccountWithCredentialsImpl(ctx, args, getProviderOrThrow, confi
25
24
  secret: maybeRedact(account.secret ?? "")
26
25
  }
27
26
  });
28
- return Fx.from({
29
- ok: async () => {
30
- const existingAccount = await db.accounts.get(providerId, account.id);
31
- if (existingAccount === null) return "InvalidAccountId";
32
- if (account.secret !== void 0) {
33
- if (await Fx.run(isSignInRateLimited(ctx, existingAccount._id, config))) return "TooManyFailedAttempts";
34
- if (!await Fx.run(verify(getProviderOrThrow(providerId), account.secret, existingAccount.secret ?? ""))) {
35
- await Fx.run(recordFailedSignIn(ctx, existingAccount._id, config));
36
- return "InvalidSecret";
37
- }
38
- await Fx.run(resetSignInRateLimit(ctx, existingAccount._id, config));
27
+ return Fx.gen(function* () {
28
+ const existingAccount = yield* Fx.promise(() => db.accounts.get(providerId, account.id));
29
+ if (existingAccount === null) return "InvalidAccountId";
30
+ if (account.secret !== void 0) {
31
+ if (yield* isSignInRateLimited(ctx, existingAccount._id, config)) return "TooManyFailedAttempts";
32
+ if (!(yield* verify(getProviderOrThrow(providerId), account.secret, existingAccount.secret ?? ""))) {
33
+ yield* recordFailedSignIn(ctx, existingAccount._id, config);
34
+ return "InvalidSecret";
39
35
  }
40
- const user = await db.users.getById(existingAccount.userId);
41
- if (user === null) {
42
- logWithLevel(LOG_LEVELS.ERROR, `Account ${existingAccount._id} is linked to missing user ${existingAccount.userId}`);
43
- return "InvalidAccountId";
44
- }
45
- return {
46
- account: existingAccount,
47
- user
48
- };
49
- },
50
- err: () => new AuthError("INTERNAL_ERROR", "Failed to look up account")
36
+ yield* resetSignInRateLimit(ctx, existingAccount._id, config);
37
+ }
38
+ const user = yield* Fx.promise(() => db.users.getById(existingAccount.userId));
39
+ if (user === null) {
40
+ logWithLevel(LOG_LEVELS.ERROR, `Account ${existingAccount._id} is linked to missing user ${existingAccount.userId}`);
41
+ return "InvalidAccountId";
42
+ }
43
+ return {
44
+ account: existingAccount,
45
+ user
46
+ };
51
47
  }).pipe(Fx.fold({
52
48
  ok: (v$1) => v$1,
53
49
  err: () => "InvalidAccountId"
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve.js","names":["Provider.verify","v"],"sources":["../../../../src/server/mutations/retrieve.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { Infer, v } from \"convex/values\";\n\nimport { authDb } from \"../db\";\nimport { AuthError } from \"../authError\";\nimport * as Provider from \"../crypto\";\nimport {\n isSignInRateLimited,\n recordFailedSignIn,\n resetSignInRateLimit,\n} from \"../limits\";\nimport { Doc, MutationCtx } from \"../types\";\nimport { LOG_LEVELS, logWithLevel, maybeRedact } from \"../utils\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const retrieveAccountWithCredentialsArgs = v.object({\n provider: v.string(),\n account: v.object({ id: v.string(), secret: v.optional(v.string()) }),\n});\n\ntype ReturnType =\n | \"InvalidAccountId\"\n | \"TooManyFailedAttempts\"\n | \"InvalidSecret\"\n | { account: Doc<\"Account\">; user: Doc<\"User\"> };\n\nexport function retrieveAccountWithCredentialsImpl(\n ctx: MutationCtx,\n args: Infer<typeof retrieveAccountWithCredentialsArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Fx<ReturnType> {\n const { provider: providerId, account } = args;\n const db = authDb(ctx, config);\n\n logWithLevel(LOG_LEVELS.DEBUG, \"retrieveAccountWithCredentialsImpl args:\", {\n provider: providerId,\n account: { id: account.id, secret: maybeRedact(account.secret ?? \"\") },\n });\n\n return Fx.from({\n ok: async () => {\n const existingAccount = (await db.accounts.get(\n providerId,\n account.id,\n )) as Doc<\"Account\"> | null;\n if (existingAccount === null) {\n return \"InvalidAccountId\" as const;\n }\n\n if (account.secret !== undefined) {\n const limited = await Fx.run(\n isSignInRateLimited(ctx, existingAccount._id, config),\n );\n if (limited) {\n return \"TooManyFailedAttempts\" as const;\n }\n\n const valid = await Fx.run(\n Provider.verify(\n getProviderOrThrow(providerId),\n account.secret,\n existingAccount.secret ?? \"\",\n ),\n );\n if (!valid) {\n await Fx.run(recordFailedSignIn(ctx, existingAccount._id, config));\n return \"InvalidSecret\" as const;\n }\n\n await Fx.run(resetSignInRateLimit(ctx, existingAccount._id, config));\n }\n\n const user = (await db.users.getById(\n existingAccount.userId,\n )) as Doc<\"User\"> | null;\n if (user === null) {\n logWithLevel(\n LOG_LEVELS.ERROR,\n `Account ${existingAccount._id} is linked to missing user ${existingAccount.userId}`,\n );\n return \"InvalidAccountId\" as const;\n }\n\n return { account: existingAccount, user } as const;\n },\n err: () => new AuthError(\"INTERNAL_ERROR\", \"Failed to look up account\"),\n }).pipe(\n Fx.fold({\n ok: (v) => v as ReturnType,\n err: () => \"InvalidAccountId\" as ReturnType,\n }),\n );\n}\n\nexport const callRetrieveAccountWithCredentials = async <\n DataModel extends GenericDataModel,\n>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof retrieveAccountWithCredentialsArgs>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"retrieveAccountWithCredentials\",\n ...args,\n },\n });\n};\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,qCAAqC,EAAE,OAAO;CACzD,UAAU,EAAE,QAAQ;CACpB,SAAS,EAAE,OAAO;EAAE,IAAI,EAAE,QAAQ;EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAAE,CAAC;CACtE,CAAC;AAQF,SAAgB,mCACd,KACA,MACA,oBACA,QACgB;CAChB,MAAM,EAAE,UAAU,YAAY,YAAY;CAC1C,MAAM,KAAK,OAAO,KAAK,OAAO;AAE9B,cAAa,WAAW,OAAO,4CAA4C;EACzE,UAAU;EACV,SAAS;GAAE,IAAI,QAAQ;GAAI,QAAQ,YAAY,QAAQ,UAAU,GAAG;GAAE;EACvE,CAAC;AAEF,QAAO,GAAG,KAAK;EACb,IAAI,YAAY;GACd,MAAM,kBAAmB,MAAM,GAAG,SAAS,IACzC,YACA,QAAQ,GACT;AACD,OAAI,oBAAoB,KACtB,QAAO;AAGT,OAAI,QAAQ,WAAW,QAAW;AAIhC,QAHgB,MAAM,GAAG,IACvB,oBAAoB,KAAK,gBAAgB,KAAK,OAAO,CACtD,CAEC,QAAO;AAUT,QAAI,CAPU,MAAM,GAAG,IACrBA,OACE,mBAAmB,WAAW,EAC9B,QAAQ,QACR,gBAAgB,UAAU,GAC3B,CACF,EACW;AACV,WAAM,GAAG,IAAI,mBAAmB,KAAK,gBAAgB,KAAK,OAAO,CAAC;AAClE,YAAO;;AAGT,UAAM,GAAG,IAAI,qBAAqB,KAAK,gBAAgB,KAAK,OAAO,CAAC;;GAGtE,MAAM,OAAQ,MAAM,GAAG,MAAM,QAC3B,gBAAgB,OACjB;AACD,OAAI,SAAS,MAAM;AACjB,iBACE,WAAW,OACX,WAAW,gBAAgB,IAAI,6BAA6B,gBAAgB,SAC7E;AACD,WAAO;;AAGT,UAAO;IAAE,SAAS;IAAiB;IAAM;;EAE3C,WAAW,IAAI,UAAU,kBAAkB,4BAA4B;EACxE,CAAC,CAAC,KACD,GAAG,KAAK;EACN,KAAK,QAAMC;EACX,WAAW;EACZ,CAAC,CACH;;AAGH,MAAa,qCAAqC,OAGhD,KACA,SACwB;AACxB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM;EACJ,MAAM;EACN,GAAG;EACJ,EACF,CAAC"}
1
+ {"version":3,"file":"retrieve.js","names":["Provider.verify","v"],"sources":["../../../../src/server/mutations/retrieve.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { authDb } from \"../db\";\nimport {\n isSignInRateLimited,\n recordFailedSignIn,\n resetSignInRateLimit,\n} from \"../limits\";\nimport { Doc, MutationCtx } from \"../types\";\nimport { LOG_LEVELS, logWithLevel, maybeRedact } from \"../utils\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const retrieveAccountWithCredentialsArgs = v.object({\n provider: v.string(),\n account: v.object({ id: v.string(), secret: v.optional(v.string()) }),\n});\n\ntype ReturnType =\n | \"InvalidAccountId\"\n | \"TooManyFailedAttempts\"\n | \"InvalidSecret\"\n | { account: Doc<\"Account\">; user: Doc<\"User\"> };\n\nexport function retrieveAccountWithCredentialsImpl(\n ctx: MutationCtx,\n args: Infer<typeof retrieveAccountWithCredentialsArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Fx<ReturnType> {\n const { provider: providerId, account } = args;\n const db = authDb(ctx, config);\n\n logWithLevel(LOG_LEVELS.DEBUG, \"retrieveAccountWithCredentialsImpl args:\", {\n provider: providerId,\n account: { id: account.id, secret: maybeRedact(account.secret ?? \"\") },\n });\n\n return Fx.gen(function* () {\n const existingAccount = yield* Fx.promise(\n () =>\n db.accounts.get(\n providerId,\n account.id,\n ) as Promise<Doc<\"Account\"> | null>,\n );\n if (existingAccount === null) {\n return \"InvalidAccountId\" as const;\n }\n\n if (account.secret !== undefined) {\n const limited = yield* isSignInRateLimited(\n ctx,\n existingAccount._id,\n config,\n );\n if (limited) {\n return \"TooManyFailedAttempts\" as const;\n }\n\n const valid = yield* Provider.verify(\n getProviderOrThrow(providerId),\n account.secret,\n existingAccount.secret ?? \"\",\n );\n if (!valid) {\n yield* recordFailedSignIn(ctx, existingAccount._id, config);\n return \"InvalidSecret\" as const;\n }\n\n yield* resetSignInRateLimit(ctx, existingAccount._id, config);\n }\n\n const user = yield* Fx.promise(\n () =>\n db.users.getById(existingAccount.userId) as Promise<Doc<\"User\"> | null>,\n );\n if (user === null) {\n logWithLevel(\n LOG_LEVELS.ERROR,\n `Account ${existingAccount._id} is linked to missing user ${existingAccount.userId}`,\n );\n return \"InvalidAccountId\" as const;\n }\n\n return { account: existingAccount, user } as ReturnType;\n }).pipe(\n Fx.fold({\n ok: (v) => v as ReturnType,\n err: () => \"InvalidAccountId\" as ReturnType,\n }),\n );\n}\n\nexport const callRetrieveAccountWithCredentials = async <\n DataModel extends GenericDataModel,\n>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof retrieveAccountWithCredentialsArgs>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"retrieveAccountWithCredentials\",\n ...args,\n },\n });\n};\n"],"mappings":";;;;;;;;;AAeA,MAAa,qCAAqC,EAAE,OAAO;CACzD,UAAU,EAAE,QAAQ;CACpB,SAAS,EAAE,OAAO;EAAE,IAAI,EAAE,QAAQ;EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAAE,CAAC;CACtE,CAAC;AAQF,SAAgB,mCACd,KACA,MACA,oBACA,QACgB;CAChB,MAAM,EAAE,UAAU,YAAY,YAAY;CAC1C,MAAM,KAAK,OAAO,KAAK,OAAO;AAE9B,cAAa,WAAW,OAAO,4CAA4C;EACzE,UAAU;EACV,SAAS;GAAE,IAAI,QAAQ;GAAI,QAAQ,YAAY,QAAQ,UAAU,GAAG;GAAE;EACvE,CAAC;AAEF,QAAO,GAAG,IAAI,aAAa;EACzB,MAAM,kBAAkB,OAAO,GAAG,cAE9B,GAAG,SAAS,IACV,YACA,QAAQ,GACT,CACJ;AACD,MAAI,oBAAoB,KACtB,QAAO;AAGT,MAAI,QAAQ,WAAW,QAAW;AAMhC,OALgB,OAAO,oBACrB,KACA,gBAAgB,KAChB,OACD,CAEC,QAAO;AAQT,OAAI,EALU,OAAOA,OACnB,mBAAmB,WAAW,EAC9B,QAAQ,QACR,gBAAgB,UAAU,GAC3B,GACW;AACV,WAAO,mBAAmB,KAAK,gBAAgB,KAAK,OAAO;AAC3D,WAAO;;AAGT,UAAO,qBAAqB,KAAK,gBAAgB,KAAK,OAAO;;EAG/D,MAAM,OAAO,OAAO,GAAG,cAEnB,GAAG,MAAM,QAAQ,gBAAgB,OAAO,CAC3C;AACD,MAAI,SAAS,MAAM;AACjB,gBACE,WAAW,OACX,WAAW,gBAAgB,IAAI,6BAA6B,gBAAgB,SAC7E;AACD,UAAO;;AAGT,SAAO;GAAE,SAAS;GAAiB;GAAM;GACzC,CAAC,KACD,GAAG,KAAK;EACN,KAAK,QAAMC;EACX,WAAW;EACZ,CAAC,CACH;;AAGH,MAAa,qCAAqC,OAGhD,KACA,SACwB;AACxB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM;EACJ,MAAM;EACN,GAAG;EACJ,EACF,CAAC"}
@@ -1,8 +1,8 @@
1
- import { AuthError } from "../authError.js";
2
1
  import { authDb } from "../db.js";
3
2
  import { AUTH_STORE_REF } from "./store/refs.js";
4
- import { v } from "convex/values";
3
+ import { Cv } from "@robelest/fx/convex";
5
4
  import { Fx } from "@robelest/fx";
5
+ import { v } from "convex/values";
6
6
 
7
7
  //#region src/server/mutations/signature.ts
8
8
  const verifierSignatureArgs = v.object({
@@ -15,8 +15,14 @@ function verifierSignatureImpl(ctx, args, config) {
15
15
  const db = authDb(ctx, config);
16
16
  const verifierDoc = yield* Fx.from({
17
17
  ok: () => db.verifiers.getById(verifier),
18
- err: () => new AuthError("INVALID_VERIFIER")
19
- }).pipe(Fx.chain((doc) => doc === null ? Fx.fail(new AuthError("INVALID_VERIFIER")) : Fx.succeed(doc)));
18
+ err: () => Cv.error({
19
+ code: "INVALID_VERIFIER",
20
+ message: "Invalid or expired verifier."
21
+ })
22
+ }).pipe(Fx.chain((doc) => doc === null ? Cv.fail({
23
+ code: "INVALID_VERIFIER",
24
+ message: "Invalid or expired verifier."
25
+ }) : Fx.succeed(doc)));
20
26
  yield* Fx.promise(() => db.verifiers.patch(verifierDoc._id, { signature }));
21
27
  });
22
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"signature.js","names":[],"sources":["../../../../src/server/mutations/signature.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { GenericId, Infer, v } from \"convex/values\";\n\nimport { authDb } from \"../db\";\nimport { AuthError } from \"../authError\";\nimport * as Provider from \"../crypto\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const verifierSignatureArgs = v.object({\n verifier: v.string(),\n signature: v.string(),\n});\n\ntype ReturnType = void;\n\nexport function verifierSignatureImpl(\n ctx: MutationCtx,\n args: Infer<typeof verifierSignatureArgs>,\n config: Provider.Config,\n): Fx<ReturnType, AuthError> {\n return Fx.gen(function* () {\n const { verifier, signature } = args;\n const db = authDb(ctx, config);\n const verifierDoc = yield* Fx.from({\n ok: () => db.verifiers.getById(verifier as GenericId<\"AuthVerifier\">),\n err: () => new AuthError(\"INVALID_VERIFIER\"),\n }).pipe(\n Fx.chain((doc) =>\n doc === null\n ? Fx.fail(new AuthError(\"INVALID_VERIFIER\"))\n : Fx.succeed(doc),\n ),\n );\n yield* Fx.promise(() => db.verifiers.patch(verifierDoc._id, { signature }));\n });\n}\n\nexport const callVerifierSignature = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof verifierSignatureArgs>,\n): Promise<void> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifierSignature\",\n ...args,\n },\n });\n};\n"],"mappings":";;;;;;;AAUA,MAAa,wBAAwB,EAAE,OAAO;CAC5C,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAIF,SAAgB,sBACd,KACA,MACA,QAC2B;AAC3B,QAAO,GAAG,IAAI,aAAa;EACzB,MAAM,EAAE,UAAU,cAAc;EAChC,MAAM,KAAK,OAAO,KAAK,OAAO;EAC9B,MAAM,cAAc,OAAO,GAAG,KAAK;GACjC,UAAU,GAAG,UAAU,QAAQ,SAAsC;GACrE,WAAW,IAAI,UAAU,mBAAmB;GAC7C,CAAC,CAAC,KACD,GAAG,OAAO,QACR,QAAQ,OACJ,GAAG,KAAK,IAAI,UAAU,mBAAmB,CAAC,GAC1C,GAAG,QAAQ,IAAI,CACpB,CACF;AACD,SAAO,GAAG,cAAc,GAAG,UAAU,MAAM,YAAY,KAAK,EAAE,WAAW,CAAC,CAAC;GAC3E;;AAGJ,MAAa,wBAAwB,OACnC,KACA,SACkB;AAClB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM;EACJ,MAAM;EACN,GAAG;EACJ,EACF,CAAC"}
1
+ {"version":3,"file":"signature.js","names":[],"sources":["../../../../src/server/mutations/signature.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { ConvexError, GenericId, Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { authDb } from \"../db\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const verifierSignatureArgs = v.object({\n verifier: v.string(),\n signature: v.string(),\n});\n\ntype ReturnType = void;\n\nexport function verifierSignatureImpl(\n ctx: MutationCtx,\n args: Infer<typeof verifierSignatureArgs>,\n config: Provider.Config,\n): Fx<ReturnType, ConvexError<any>> {\n return Fx.gen(function* () {\n const { verifier, signature } = args;\n const db = authDb(ctx, config);\n const verifierDoc = yield* Fx.from({\n ok: () => db.verifiers.getById(verifier as GenericId<\"AuthVerifier\">),\n err: () =>\n Cv.error({\n code: \"INVALID_VERIFIER\",\n message: \"Invalid or expired verifier.\",\n }),\n }).pipe(\n Fx.chain((doc) =>\n doc === null\n ? Cv.fail({\n code: \"INVALID_VERIFIER\",\n message: \"Invalid or expired verifier.\",\n })\n : Fx.succeed(doc),\n ),\n );\n yield* Fx.promise(() => db.verifiers.patch(verifierDoc._id, { signature }));\n });\n}\n\nexport const callVerifierSignature = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof verifierSignatureArgs>,\n): Promise<void> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifierSignature\",\n ...args,\n },\n });\n};\n"],"mappings":";;;;;;;AAUA,MAAa,wBAAwB,EAAE,OAAO;CAC5C,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAIF,SAAgB,sBACd,KACA,MACA,QACkC;AAClC,QAAO,GAAG,IAAI,aAAa;EACzB,MAAM,EAAE,UAAU,cAAc;EAChC,MAAM,KAAK,OAAO,KAAK,OAAO;EAC9B,MAAM,cAAc,OAAO,GAAG,KAAK;GACjC,UAAU,GAAG,UAAU,QAAQ,SAAsC;GACrE,WACE,GAAG,MAAM;IACP,MAAM;IACN,SAAS;IACV,CAAC;GACL,CAAC,CAAC,KACD,GAAG,OAAO,QACR,QAAQ,OACJ,GAAG,KAAK;GACN,MAAM;GACN,SAAS;GACV,CAAC,GACF,GAAG,QAAQ,IAAI,CACpB,CACF;AACD,SAAO,GAAG,cAAc,GAAG,UAAU,MAAM,YAAY,KAAK,EAAE,WAAW,CAAC,CAAC;GAC3E;;AAGJ,MAAa,wBAAwB,OACnC,KACA,SACkB;AAClB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM;EACJ,MAAM;EACN,GAAG;EACJ,EACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"signout.js","names":[],"sources":["../../../../src/server/mutations/signout.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"../db\";\nimport * as Provider from \"../crypto\";\nimport { deleteSession, getAuthSessionId } from \"../sessions\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\ntype ReturnType = {\n userId: GenericId<\"User\">;\n sessionId: GenericId<\"Session\">;\n} | null;\n\nexport function signOutImpl(\n ctx: MutationCtx,\n config: Provider.Config,\n): Fx<ReturnType, never> {\n return Fx.gen(function* () {\n const db = authDb(ctx, config);\n const sessionId = yield* Fx.promise(() => getAuthSessionId(ctx));\n if (sessionId === null) {\n return null;\n }\n const session = yield* Fx.promise(() => db.sessions.getById(sessionId));\n if (session === null) {\n return null;\n }\n yield* Fx.promise(() => deleteSession(ctx, session, config));\n return { userId: session.userId, sessionId: session._id };\n });\n}\n\nexport const callSignOut = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n): Promise<void> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"signOut\",\n },\n });\n};\n"],"mappings":";;;;;;AAeA,SAAgB,YACd,KACA,QACuB;AACvB,QAAO,GAAG,IAAI,aAAa;EACzB,MAAM,KAAK,OAAO,KAAK,OAAO;EAC9B,MAAM,YAAY,OAAO,GAAG,cAAc,iBAAiB,IAAI,CAAC;AAChE,MAAI,cAAc,KAChB,QAAO;EAET,MAAM,UAAU,OAAO,GAAG,cAAc,GAAG,SAAS,QAAQ,UAAU,CAAC;AACvE,MAAI,YAAY,KACd,QAAO;AAET,SAAO,GAAG,cAAc,cAAc,KAAK,SAAS,OAAO,CAAC;AAC5D,SAAO;GAAE,QAAQ,QAAQ;GAAQ,WAAW,QAAQ;GAAK;GACzD;;AAGJ,MAAa,cAAc,OACzB,QACkB;AAClB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM,EACJ,MAAM,WACP,EACF,CAAC"}
1
+ {"version":3,"file":"signout.js","names":[],"sources":["../../../../src/server/mutations/signout.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { GenericId } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { authDb } from \"../db\";\nimport { deleteSession, getAuthSessionId } from \"../sessions\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\ntype ReturnType = {\n userId: GenericId<\"User\">;\n sessionId: GenericId<\"Session\">;\n} | null;\n\nexport function signOutImpl(\n ctx: MutationCtx,\n config: Provider.Config,\n): Fx<ReturnType, never> {\n return Fx.gen(function* () {\n const db = authDb(ctx, config);\n const sessionId = yield* Fx.promise(() => getAuthSessionId(ctx));\n if (sessionId === null) {\n return null;\n }\n const session = yield* Fx.promise(() => db.sessions.getById(sessionId));\n if (session === null) {\n return null;\n }\n yield* Fx.promise(() => deleteSession(ctx, session, config));\n return { userId: session.userId, sessionId: session._id };\n });\n}\n\nexport const callSignOut = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n): Promise<void> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"signOut\",\n },\n });\n};\n"],"mappings":";;;;;;AAeA,SAAgB,YACd,KACA,QACuB;AACvB,QAAO,GAAG,IAAI,aAAa;EACzB,MAAM,KAAK,OAAO,KAAK,OAAO;EAC9B,MAAM,YAAY,OAAO,GAAG,cAAc,iBAAiB,IAAI,CAAC;AAChE,MAAI,cAAc,KAChB,QAAO;EAET,MAAM,UAAU,OAAO,GAAG,cAAc,GAAG,SAAS,QAAQ,UAAU,CAAC;AACvE,MAAI,YAAY,KACd,QAAO;AAET,SAAO,GAAG,cAAc,cAAc,KAAK,SAAS,OAAO,CAAC;AAC5D,SAAO;GAAE,QAAQ,QAAQ;GAAQ,WAAW,QAAQ;GAAK;GACzD;;AAGJ,MAAa,cAAc,OACzB,QACkB;AAClB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM,EACJ,MAAM,WACP,EACF,CAAC"}
@@ -11,8 +11,8 @@ import { signInArgs, signInImpl } from "./signin.js";
11
11
  import { signOutImpl } from "./signout.js";
12
12
  import { verifierImpl } from "./verifier.js";
13
13
  import { verifyCodeAndSignInArgs, verifyCodeAndSignInImpl } from "./verify.js";
14
- import { v } from "convex/values";
15
14
  import { Fx } from "@robelest/fx";
15
+ import { v } from "convex/values";
16
16
 
17
17
  //#region src/server/mutations/store.ts
18
18
  const storeArgs = v.object({ args: v.union(v.object({
@@ -50,32 +50,17 @@ const storeImpl = async (ctx, fnArgs, getProviderOrThrow, config) => {
50
50
  const args = fnArgs.args;
51
51
  logWithLevel(LOG_LEVELS.INFO, `\`auth:store\` type: ${args.type}`);
52
52
  return Fx.run(Fx.match(args, args.type, {
53
- signIn: (a) => Fx.from({
54
- ok: () => signInImpl(ctx, a, config),
55
- err: (e) => e
56
- }),
53
+ signIn: (a) => Fx.promise(() => signInImpl(ctx, a, config)),
57
54
  signOut: () => signOutImpl(ctx, config),
58
- refreshSession: (a) => Fx.from({
59
- ok: () => refreshSessionImpl(ctx, a, getProviderOrThrow, config),
60
- err: (e) => e
61
- }),
62
- verifyCodeAndSignIn: (a) => Fx.from({
63
- ok: () => verifyCodeAndSignInImpl(ctx, a, getProviderOrThrow, config),
64
- err: (e) => e
65
- }),
55
+ refreshSession: (a) => Fx.promise(() => refreshSessionImpl(ctx, a, getProviderOrThrow, config)),
56
+ verifyCodeAndSignIn: (a) => Fx.promise(() => verifyCodeAndSignInImpl(ctx, a, getProviderOrThrow, config)),
66
57
  verifier: () => verifierImpl(ctx, config),
67
- verifierSignature: (a) => verifierSignatureImpl(ctx, a, config).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),
68
- userOAuth: (a) => userOAuthImpl(ctx, a, getProviderOrThrow, config).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),
69
- createVerificationCode: (a) => Fx.from({
70
- ok: () => createVerificationCodeImpl(ctx, a, getProviderOrThrow, config),
71
- err: (e) => e
72
- }),
73
- createAccountFromCredentials: (a) => Fx.from({
74
- ok: () => createAccountFromCredentialsImpl(ctx, a, getProviderOrThrow, config),
75
- err: (e) => e
76
- }),
58
+ verifierSignature: (a) => verifierSignatureImpl(ctx, a, config).pipe(Fx.recover((e) => Fx.fatal(e))),
59
+ userOAuth: (a) => userOAuthImpl(ctx, a, getProviderOrThrow, config).pipe(Fx.recover((e) => Fx.fatal(e))),
60
+ createVerificationCode: (a) => Fx.promise(() => createVerificationCodeImpl(ctx, a, getProviderOrThrow, config)),
61
+ createAccountFromCredentials: (a) => Fx.promise(() => createAccountFromCredentialsImpl(ctx, a, getProviderOrThrow, config)),
77
62
  retrieveAccountWithCredentials: (a) => retrieveAccountWithCredentialsImpl(ctx, a, getProviderOrThrow, config),
78
- modifyAccount: (a) => modifyAccountImpl(ctx, a, getProviderOrThrow, config).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),
63
+ modifyAccount: (a) => modifyAccountImpl(ctx, a, getProviderOrThrow, config).pipe(Fx.recover((e) => Fx.fatal(e))),
79
64
  invalidateSessions: (a) => invalidateSessionsImpl(ctx, a, config)
80
65
  }));
81
66
  };
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":[],"sources":["../../../../src/server/mutations/store.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { MutationCtx } from \"../types\";\nimport { LOG_LEVELS, logWithLevel } from \"../utils\";\nimport { modifyAccountArgs, modifyAccountImpl } from \"./account\";\nimport { createVerificationCodeArgs, createVerificationCodeImpl } from \"./code\";\nimport { invalidateSessionsArgs, invalidateSessionsImpl } from \"./invalidate\";\nimport { userOAuthArgs, userOAuthImpl } from \"./oauth\";\nimport { refreshSessionArgs, refreshSessionImpl } from \"./refresh\";\nimport {\n createAccountFromCredentialsArgs,\n createAccountFromCredentialsImpl,\n} from \"./register\";\nimport {\n retrieveAccountWithCredentialsArgs,\n retrieveAccountWithCredentialsImpl,\n} from \"./retrieve\";\nimport { verifierSignatureArgs, verifierSignatureImpl } from \"./signature\";\nimport { signInArgs, signInImpl } from \"./signin\";\nimport { signOutImpl } from \"./signout\";\nimport { verifierImpl } from \"./verifier\";\nimport { verifyCodeAndSignInArgs, verifyCodeAndSignInImpl } from \"./verify\";\n\nexport const storeArgs = v.object({\n args: v.union(\n v.object({\n type: v.literal(\"signIn\"),\n ...signInArgs.fields,\n }),\n v.object({\n type: v.literal(\"signOut\"),\n }),\n v.object({\n type: v.literal(\"refreshSession\"),\n ...refreshSessionArgs.fields,\n }),\n v.object({\n type: v.literal(\"verifyCodeAndSignIn\"),\n ...verifyCodeAndSignInArgs.fields,\n }),\n v.object({\n type: v.literal(\"verifier\"),\n }),\n v.object({\n type: v.literal(\"verifierSignature\"),\n ...verifierSignatureArgs.fields,\n }),\n v.object({\n type: v.literal(\"userOAuth\"),\n ...userOAuthArgs.fields,\n }),\n v.object({\n type: v.literal(\"createVerificationCode\"),\n ...createVerificationCodeArgs.fields,\n }),\n v.object({\n type: v.literal(\"createAccountFromCredentials\"),\n ...createAccountFromCredentialsArgs.fields,\n }),\n v.object({\n type: v.literal(\"retrieveAccountWithCredentials\"),\n ...retrieveAccountWithCredentialsArgs.fields,\n }),\n v.object({\n type: v.literal(\"modifyAccount\"),\n ...modifyAccountArgs.fields,\n }),\n v.object({\n type: v.literal(\"invalidateSessions\"),\n ...invalidateSessionsArgs.fields,\n }),\n ),\n});\n\nexport const storeImpl = async (\n ctx: MutationCtx,\n fnArgs: Infer<typeof storeArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n) => {\n const args = fnArgs.args;\n logWithLevel(LOG_LEVELS.INFO, `\\`auth:store\\` type: ${args.type}`);\n return Fx.run(\n Fx.match(args, args.type, {\n signIn: (a) =>\n Fx.from({\n ok: () => signInImpl(ctx, a, config),\n err: (e) => e as never,\n }),\n signOut: () => signOutImpl(ctx, config),\n refreshSession: (a) =>\n Fx.from({\n ok: () => refreshSessionImpl(ctx, a, getProviderOrThrow, config),\n err: (e) => e as never,\n }),\n verifyCodeAndSignIn: (a) =>\n Fx.from({\n ok: () => verifyCodeAndSignInImpl(ctx, a, getProviderOrThrow, config),\n err: (e) => e as never,\n }),\n verifier: () => verifierImpl(ctx, config),\n verifierSignature: (a) =>\n verifierSignatureImpl(ctx, a, config).pipe(\n Fx.recover((e) => Fx.fatal(e.toConvexError())),\n ),\n userOAuth: (a) =>\n userOAuthImpl(ctx, a, getProviderOrThrow, config).pipe(\n Fx.recover((e) => Fx.fatal(e.toConvexError())),\n ),\n createVerificationCode: (a) =>\n Fx.from({\n ok: () =>\n createVerificationCodeImpl(ctx, a, getProviderOrThrow, config),\n err: (e) => e as never,\n }),\n createAccountFromCredentials: (a) =>\n Fx.from({\n ok: () =>\n createAccountFromCredentialsImpl(\n ctx,\n a,\n getProviderOrThrow,\n config,\n ),\n err: (e) => e as never,\n }),\n retrieveAccountWithCredentials: (a) =>\n retrieveAccountWithCredentialsImpl(ctx, a, getProviderOrThrow, config),\n modifyAccount: (a) =>\n modifyAccountImpl(ctx, a, getProviderOrThrow, config).pipe(\n Fx.recover((e) => Fx.fatal(e.toConvexError())),\n ),\n invalidateSessions: (a) => invalidateSessionsImpl(ctx, a, config),\n }),\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,MAAa,YAAY,EAAE,OAAO,EAChC,MAAM,EAAE,MACN,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,SAAS;CACzB,GAAG,WAAW;CACf,CAAC,EACF,EAAE,OAAO,EACP,MAAM,EAAE,QAAQ,UAAU,EAC3B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,iBAAiB;CACjC,GAAG,mBAAmB;CACvB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,sBAAsB;CACtC,GAAG,wBAAwB;CAC5B,CAAC,EACF,EAAE,OAAO,EACP,MAAM,EAAE,QAAQ,WAAW,EAC5B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,oBAAoB;CACpC,GAAG,sBAAsB;CAC1B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,YAAY;CAC5B,GAAG,cAAc;CAClB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,yBAAyB;CACzC,GAAG,2BAA2B;CAC/B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,+BAA+B;CAC/C,GAAG,iCAAiC;CACrC,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,iCAAiC;CACjD,GAAG,mCAAmC;CACvC,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,gBAAgB;CAChC,GAAG,kBAAkB;CACtB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,qBAAqB;CACrC,GAAG,uBAAuB;CAC3B,CAAC,CACH,EACF,CAAC;AAEF,MAAa,YAAY,OACvB,KACA,QACA,oBACA,WACG;CACH,MAAM,OAAO,OAAO;AACpB,cAAa,WAAW,MAAM,wBAAwB,KAAK,OAAO;AAClE,QAAO,GAAG,IACR,GAAG,MAAM,MAAM,KAAK,MAAM;EACxB,SAAS,MACP,GAAG,KAAK;GACN,UAAU,WAAW,KAAK,GAAG,OAAO;GACpC,MAAM,MAAM;GACb,CAAC;EACJ,eAAe,YAAY,KAAK,OAAO;EACvC,iBAAiB,MACf,GAAG,KAAK;GACN,UAAU,mBAAmB,KAAK,GAAG,oBAAoB,OAAO;GAChE,MAAM,MAAM;GACb,CAAC;EACJ,sBAAsB,MACpB,GAAG,KAAK;GACN,UAAU,wBAAwB,KAAK,GAAG,oBAAoB,OAAO;GACrE,MAAM,MAAM;GACb,CAAC;EACJ,gBAAgB,aAAa,KAAK,OAAO;EACzC,oBAAoB,MAClB,sBAAsB,KAAK,GAAG,OAAO,CAAC,KACpC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,eAAe,CAAC,CAAC,CAC/C;EACH,YAAY,MACV,cAAc,KAAK,GAAG,oBAAoB,OAAO,CAAC,KAChD,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,eAAe,CAAC,CAAC,CAC/C;EACH,yBAAyB,MACvB,GAAG,KAAK;GACN,UACE,2BAA2B,KAAK,GAAG,oBAAoB,OAAO;GAChE,MAAM,MAAM;GACb,CAAC;EACJ,+BAA+B,MAC7B,GAAG,KAAK;GACN,UACE,iCACE,KACA,GACA,oBACA,OACD;GACH,MAAM,MAAM;GACb,CAAC;EACJ,iCAAiC,MAC/B,mCAAmC,KAAK,GAAG,oBAAoB,OAAO;EACxE,gBAAgB,MACd,kBAAkB,KAAK,GAAG,oBAAoB,OAAO,CAAC,KACpD,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,eAAe,CAAC,CAAC,CAC/C;EACH,qBAAqB,MAAM,uBAAuB,KAAK,GAAG,OAAO;EAClE,CAAC,CACH"}
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../../../src/server/mutations/store.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { MutationCtx } from \"../types\";\nimport { LOG_LEVELS, logWithLevel } from \"../utils\";\nimport { modifyAccountArgs, modifyAccountImpl } from \"./account\";\nimport { createVerificationCodeArgs, createVerificationCodeImpl } from \"./code\";\nimport { invalidateSessionsArgs, invalidateSessionsImpl } from \"./invalidate\";\nimport { userOAuthArgs, userOAuthImpl } from \"./oauth\";\nimport { refreshSessionArgs, refreshSessionImpl } from \"./refresh\";\nimport {\n createAccountFromCredentialsArgs,\n createAccountFromCredentialsImpl,\n} from \"./register\";\nimport {\n retrieveAccountWithCredentialsArgs,\n retrieveAccountWithCredentialsImpl,\n} from \"./retrieve\";\nimport { verifierSignatureArgs, verifierSignatureImpl } from \"./signature\";\nimport { signInArgs, signInImpl } from \"./signin\";\nimport { signOutImpl } from \"./signout\";\nimport { verifierImpl } from \"./verifier\";\nimport { verifyCodeAndSignInArgs, verifyCodeAndSignInImpl } from \"./verify\";\n\nexport const storeArgs = v.object({\n args: v.union(\n v.object({\n type: v.literal(\"signIn\"),\n ...signInArgs.fields,\n }),\n v.object({\n type: v.literal(\"signOut\"),\n }),\n v.object({\n type: v.literal(\"refreshSession\"),\n ...refreshSessionArgs.fields,\n }),\n v.object({\n type: v.literal(\"verifyCodeAndSignIn\"),\n ...verifyCodeAndSignInArgs.fields,\n }),\n v.object({\n type: v.literal(\"verifier\"),\n }),\n v.object({\n type: v.literal(\"verifierSignature\"),\n ...verifierSignatureArgs.fields,\n }),\n v.object({\n type: v.literal(\"userOAuth\"),\n ...userOAuthArgs.fields,\n }),\n v.object({\n type: v.literal(\"createVerificationCode\"),\n ...createVerificationCodeArgs.fields,\n }),\n v.object({\n type: v.literal(\"createAccountFromCredentials\"),\n ...createAccountFromCredentialsArgs.fields,\n }),\n v.object({\n type: v.literal(\"retrieveAccountWithCredentials\"),\n ...retrieveAccountWithCredentialsArgs.fields,\n }),\n v.object({\n type: v.literal(\"modifyAccount\"),\n ...modifyAccountArgs.fields,\n }),\n v.object({\n type: v.literal(\"invalidateSessions\"),\n ...invalidateSessionsArgs.fields,\n }),\n ),\n});\n\nexport const storeImpl = async (\n ctx: MutationCtx,\n fnArgs: Infer<typeof storeArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n) => {\n const args = fnArgs.args;\n logWithLevel(LOG_LEVELS.INFO, `\\`auth:store\\` type: ${args.type}`);\n return Fx.run(\n Fx.match(args, args.type, {\n signIn: (a) => Fx.promise(() => signInImpl(ctx, a, config)),\n signOut: () => signOutImpl(ctx, config),\n refreshSession: (a) =>\n Fx.promise(() =>\n refreshSessionImpl(ctx, a, getProviderOrThrow, config),\n ),\n verifyCodeAndSignIn: (a) =>\n Fx.promise(() =>\n verifyCodeAndSignInImpl(ctx, a, getProviderOrThrow, config),\n ),\n verifier: () => verifierImpl(ctx, config),\n verifierSignature: (a) =>\n verifierSignatureImpl(ctx, a, config).pipe(\n Fx.recover((e) => Fx.fatal(e)),\n ),\n userOAuth: (a) =>\n userOAuthImpl(ctx, a, getProviderOrThrow, config).pipe(\n Fx.recover((e) => Fx.fatal(e)),\n ),\n createVerificationCode: (a) =>\n Fx.promise(() =>\n createVerificationCodeImpl(ctx, a, getProviderOrThrow, config),\n ),\n createAccountFromCredentials: (a) =>\n Fx.promise(() =>\n createAccountFromCredentialsImpl(ctx, a, getProviderOrThrow, config),\n ),\n retrieveAccountWithCredentials: (a) =>\n retrieveAccountWithCredentialsImpl(ctx, a, getProviderOrThrow, config),\n modifyAccount: (a) =>\n modifyAccountImpl(ctx, a, getProviderOrThrow, config).pipe(\n Fx.recover((e) => Fx.fatal(e)),\n ),\n invalidateSessions: (a) => invalidateSessionsImpl(ctx, a, config),\n }),\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,MAAa,YAAY,EAAE,OAAO,EAChC,MAAM,EAAE,MACN,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,SAAS;CACzB,GAAG,WAAW;CACf,CAAC,EACF,EAAE,OAAO,EACP,MAAM,EAAE,QAAQ,UAAU,EAC3B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,iBAAiB;CACjC,GAAG,mBAAmB;CACvB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,sBAAsB;CACtC,GAAG,wBAAwB;CAC5B,CAAC,EACF,EAAE,OAAO,EACP,MAAM,EAAE,QAAQ,WAAW,EAC5B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,oBAAoB;CACpC,GAAG,sBAAsB;CAC1B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,YAAY;CAC5B,GAAG,cAAc;CAClB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,yBAAyB;CACzC,GAAG,2BAA2B;CAC/B,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,+BAA+B;CAC/C,GAAG,iCAAiC;CACrC,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,iCAAiC;CACjD,GAAG,mCAAmC;CACvC,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,gBAAgB;CAChC,GAAG,kBAAkB;CACtB,CAAC,EACF,EAAE,OAAO;CACP,MAAM,EAAE,QAAQ,qBAAqB;CACrC,GAAG,uBAAuB;CAC3B,CAAC,CACH,EACF,CAAC;AAEF,MAAa,YAAY,OACvB,KACA,QACA,oBACA,WACG;CACH,MAAM,OAAO,OAAO;AACpB,cAAa,WAAW,MAAM,wBAAwB,KAAK,OAAO;AAClE,QAAO,GAAG,IACR,GAAG,MAAM,MAAM,KAAK,MAAM;EACxB,SAAS,MAAM,GAAG,cAAc,WAAW,KAAK,GAAG,OAAO,CAAC;EAC3D,eAAe,YAAY,KAAK,OAAO;EACvC,iBAAiB,MACf,GAAG,cACD,mBAAmB,KAAK,GAAG,oBAAoB,OAAO,CACvD;EACH,sBAAsB,MACpB,GAAG,cACD,wBAAwB,KAAK,GAAG,oBAAoB,OAAO,CAC5D;EACH,gBAAgB,aAAa,KAAK,OAAO;EACzC,oBAAoB,MAClB,sBAAsB,KAAK,GAAG,OAAO,CAAC,KACpC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,CAC/B;EACH,YAAY,MACV,cAAc,KAAK,GAAG,oBAAoB,OAAO,CAAC,KAChD,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,CAC/B;EACH,yBAAyB,MACvB,GAAG,cACD,2BAA2B,KAAK,GAAG,oBAAoB,OAAO,CAC/D;EACH,+BAA+B,MAC7B,GAAG,cACD,iCAAiC,KAAK,GAAG,oBAAoB,OAAO,CACrE;EACH,iCAAiC,MAC/B,mCAAmC,KAAK,GAAG,oBAAoB,OAAO;EACxE,gBAAgB,MACd,kBAAkB,KAAK,GAAG,oBAAoB,OAAO,CAAC,KACpD,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,CAC/B;EACH,qBAAqB,MAAM,uBAAuB,KAAK,GAAG,OAAO;EAClE,CAAC,CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"verifier.js","names":[],"sources":["../../../../src/server/mutations/verifier.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"../db\";\nimport * as Provider from \"../crypto\";\nimport { getAuthSessionId } from \"../sessions\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\ntype ReturnType = GenericId<\"AuthVerifier\">;\n\nexport function verifierImpl(\n ctx: MutationCtx,\n config: Provider.Config,\n): Fx<ReturnType, never> {\n return Fx.gen(function* () {\n return (yield* Fx.promise(async () =>\n authDb(ctx, config).verifiers.create(\n (await getAuthSessionId(ctx)) ?? undefined,\n ),\n )) as ReturnType;\n });\n}\n\nexport const callVerifier = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifier\",\n },\n });\n};\n"],"mappings":";;;;;;AAYA,SAAgB,aACd,KACA,QACuB;AACvB,QAAO,GAAG,IAAI,aAAa;AACzB,SAAQ,OAAO,GAAG,QAAQ,YACxB,OAAO,KAAK,OAAO,CAAC,UAAU,OAC3B,MAAM,iBAAiB,IAAI,IAAK,OAClC,CACF;GACD;;AAGJ,MAAa,eAAe,OAC1B,QACwB;AACxB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM,EACJ,MAAM,YACP,EACF,CAAC"}
1
+ {"version":3,"file":"verifier.js","names":[],"sources":["../../../../src/server/mutations/verifier.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { GenericId } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { authDb } from \"../db\";\nimport { getAuthSessionId } from \"../sessions\";\nimport { MutationCtx } from \"../types\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\ntype ReturnType = GenericId<\"AuthVerifier\">;\n\nexport function verifierImpl(\n ctx: MutationCtx,\n config: Provider.Config,\n): Fx<ReturnType, never> {\n return Fx.gen(function* () {\n return (yield* Fx.promise(async () =>\n authDb(ctx, config).verifiers.create(\n (await getAuthSessionId(ctx)) ?? undefined,\n ),\n )) as ReturnType;\n });\n}\n\nexport const callVerifier = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifier\",\n },\n });\n};\n"],"mappings":";;;;;;AAYA,SAAgB,aACd,KACA,QACuB;AACvB,QAAO,GAAG,IAAI,aAAa;AACzB,SAAQ,OAAO,GAAG,QAAQ,YACxB,OAAO,KAAK,OAAO,CAAC,UAAU,OAC3B,MAAM,iBAAiB,IAAI,IAAK,OAClC,CACF;GACD;;AAGJ,MAAa,eAAe,OAC1B,QACwB;AACxB,QAAO,IAAI,YAAY,gBAAgB,EACrC,MAAM,EACJ,MAAM,YACP,EACF,CAAC"}
@@ -6,8 +6,8 @@ import { upsertUserAndAccount } from "../users.js";
6
6
  import { isEnterpriseProviderId } from "../enterprise/shared.js";
7
7
  import { createSyntheticOAuthMaterializedConfig } from "../enterprise/oidc.js";
8
8
  import { isSignInRateLimited, recordFailedSignIn, resetSignInRateLimit } from "../limits.js";
9
- import { v } from "convex/values";
10
9
  import { Fx } from "@robelest/fx";
10
+ import { v } from "convex/values";
11
11
 
12
12
  //#region src/server/mutations/verify.ts
13
13
  const verifyCodeAndSignInArgs = v.object({
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","names":[],"sources":["../../../../src/server/mutations/verify.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { Infer, v } from \"convex/values\";\n\nimport { authDb } from \"../db\";\nimport * as Provider from \"../crypto\";\nimport {\n isSignInRateLimited,\n recordFailedSignIn,\n resetSignInRateLimit,\n} from \"../limits\";\nimport {\n createNewAndDeleteExistingSession,\n getAuthSessionId,\n maybeGenerateTokensForSession,\n} from \"../sessions\";\nimport {\n createSyntheticOAuthMaterializedConfig,\n} from \"../enterprise/oidc\";\nimport { isEnterpriseProviderId } from \"../enterprise/shared\";\nimport { MutationCtx, SessionInfo } from \"../types\";\nimport { upsertUserAndAccount } from \"../users\";\nimport { LOG_LEVELS, logWithLevel, sha256 } from \"../utils\";\nimport { requireEnv } from \"../utils\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const verifyCodeAndSignInArgs = v.object({\n params: v.any(),\n provider: v.optional(v.string()),\n verifier: v.optional(v.string()),\n generateTokens: v.boolean(),\n allowExtraProviders: v.boolean(),\n});\n\ntype ReturnType = null | SessionInfo;\n\n// ============================================================================\n// Small validators for the verification pipeline\n// ============================================================================\n\n/** A soft verification failure — logged and collapsed to null at the boundary. */\nclass VerifyFailure {\n readonly _tag = \"VerifyFailure\" as const;\n constructor(readonly reason: string) {}\n}\n\n// ============================================================================\n// Main exported function\n// ============================================================================\n\nexport async function verifyCodeAndSignInImpl(\n ctx: MutationCtx,\n args: Infer<typeof verifyCodeAndSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<ReturnType> {\n logWithLevel(LOG_LEVELS.DEBUG, \"verifyCodeAndSignInImpl args:\", {\n params: { email: args.params.email, phone: args.params.phone },\n provider: args.provider,\n verifier: args.verifier,\n generateTokens: args.generateTokens,\n allowExtraProviders: args.allowExtraProviders,\n });\n\n const { generateTokens, provider, allowExtraProviders } = args;\n if (generateTokens) {\n requireEnv(\"JWT_PRIVATE_KEY\");\n requireEnv(\"JWKS\");\n requireEnv(\"CONVEX_SITE_URL\");\n }\n const identifier: string | undefined = args.params.email ?? args.params.phone;\n\n try {\n if (identifier !== undefined) {\n const limited = await Fx.run(\n isSignInRateLimited(ctx, identifier, config),\n );\n if (limited) {\n throw new VerifyFailure(\n \"Too many failed attempts to verify code for this email\",\n );\n }\n }\n\n const db = authDb(ctx, config);\n const { params, verifier } = args;\n const hash = await sha256(params.code);\n const code = await db.verificationCodes.getByCode(hash);\n if (code === null) {\n throw new VerifyFailure(\"Invalid verification code\");\n }\n\n await db.verificationCodes.delete(code._id);\n\n if (code.verifier !== verifier) {\n throw new VerifyFailure(\"Invalid verifier\");\n }\n if (code.expirationTime < Date.now()) {\n throw new VerifyFailure(\"Expired verification code\");\n }\n if (provider !== undefined && code.provider !== provider) {\n throw new VerifyFailure(\n `Invalid provider \"${provider}\" for given \\`code\\``,\n );\n }\n\n const account = await db.accounts.getById(code.accountId);\n if (account === null) {\n throw new VerifyFailure(\n \"Account associated with this email has been deleted\",\n );\n }\n\n const codeProvider = isEnterpriseProviderId(code.provider)\n ? createSyntheticOAuthMaterializedConfig(code.provider)\n : getProviderOrThrow(code.provider, allowExtraProviders);\n\n if (\n codeProvider !== null &&\n (codeProvider.type === \"email\" || codeProvider.type === \"phone\") &&\n codeProvider.authorize !== undefined\n ) {\n await codeProvider.authorize(args.params, account);\n }\n\n const methodProvider = isEnterpriseProviderId(account.provider)\n ? createSyntheticOAuthMaterializedConfig(account.provider)\n : getProviderOrThrow(account.provider);\n\n const userId =\n methodProvider.type === \"oauth\"\n ? account.userId\n : (\n await upsertUserAndAccount(\n ctx,\n await getAuthSessionId(ctx),\n { existingAccount: account },\n {\n type: \"verification\",\n provider: methodProvider,\n profile: {\n ...(code.emailVerified !== undefined\n ? { email: code.emailVerified, emailVerified: true }\n : {}),\n ...(code.phoneVerified !== undefined\n ? { phone: code.phoneVerified, phoneVerified: true }\n : {}),\n },\n },\n config,\n )\n ).userId;\n\n if (identifier !== undefined) {\n await Fx.run(resetSignInRateLimit(ctx, identifier, config));\n }\n\n const sessionId = await createNewAndDeleteExistingSession(\n ctx,\n config,\n userId,\n );\n return await maybeGenerateTokensForSession(\n ctx,\n config,\n userId,\n sessionId,\n generateTokens,\n );\n } catch (error) {\n if (error instanceof VerifyFailure) {\n logWithLevel(LOG_LEVELS.ERROR, error.reason);\n if (identifier !== undefined) {\n await Fx.run(recordFailedSignIn(ctx, identifier, config));\n }\n return null;\n }\n logWithLevel(\n LOG_LEVELS.ERROR,\n `verifyCodeAndSignInImpl failed: ${String(error)}`,\n );\n return null;\n }\n}\n\n// ============================================================================\n// Action-level caller (unchanged — just forwards to mutation)\n// ============================================================================\n\nexport const callVerifyCodeAndSignIn = async <\n DataModel extends GenericDataModel,\n>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof verifyCodeAndSignInArgs>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifyCodeAndSignIn\",\n ...args,\n },\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"verify.js","names":[],"sources":["../../../../src/server/mutations/verify.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport type { GenericActionCtx, GenericDataModel } from \"convex/server\";\nimport { Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../crypto\";\nimport { authDb } from \"../db\";\nimport { createSyntheticOAuthMaterializedConfig } from \"../enterprise/oidc\";\nimport { isEnterpriseProviderId } from \"../enterprise/shared\";\nimport {\n isSignInRateLimited,\n recordFailedSignIn,\n resetSignInRateLimit,\n} from \"../limits\";\nimport {\n createNewAndDeleteExistingSession,\n getAuthSessionId,\n maybeGenerateTokensForSession,\n} from \"../sessions\";\nimport { MutationCtx, SessionInfo } from \"../types\";\nimport { upsertUserAndAccount } from \"../users\";\nimport { LOG_LEVELS, logWithLevel, sha256 } from \"../utils\";\nimport { requireEnv } from \"../utils\";\nimport { AUTH_STORE_REF } from \"./store/refs\";\n\nexport const verifyCodeAndSignInArgs = v.object({\n params: v.any(),\n provider: v.optional(v.string()),\n verifier: v.optional(v.string()),\n generateTokens: v.boolean(),\n allowExtraProviders: v.boolean(),\n});\n\ntype ReturnType = null | SessionInfo;\n\n// ============================================================================\n// Small validators for the verification pipeline\n// ============================================================================\n\n/** A soft verification failure — logged and collapsed to null at the boundary. */\nclass VerifyFailure {\n readonly _tag = \"VerifyFailure\" as const;\n constructor(readonly reason: string) {}\n}\n\n// ============================================================================\n// Main exported function\n// ============================================================================\n\nexport async function verifyCodeAndSignInImpl(\n ctx: MutationCtx,\n args: Infer<typeof verifyCodeAndSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<ReturnType> {\n logWithLevel(LOG_LEVELS.DEBUG, \"verifyCodeAndSignInImpl args:\", {\n params: { email: args.params.email, phone: args.params.phone },\n provider: args.provider,\n verifier: args.verifier,\n generateTokens: args.generateTokens,\n allowExtraProviders: args.allowExtraProviders,\n });\n\n const { generateTokens, provider, allowExtraProviders } = args;\n if (generateTokens) {\n requireEnv(\"JWT_PRIVATE_KEY\");\n requireEnv(\"JWKS\");\n requireEnv(\"CONVEX_SITE_URL\");\n }\n const identifier: string | undefined = args.params.email ?? args.params.phone;\n\n try {\n if (identifier !== undefined) {\n const limited = await Fx.run(\n isSignInRateLimited(ctx, identifier, config),\n );\n if (limited) {\n throw new VerifyFailure(\n \"Too many failed attempts to verify code for this email\",\n );\n }\n }\n\n const db = authDb(ctx, config);\n const { params, verifier } = args;\n const hash = await sha256(params.code);\n const code = await db.verificationCodes.getByCode(hash);\n if (code === null) {\n throw new VerifyFailure(\"Invalid verification code\");\n }\n\n await db.verificationCodes.delete(code._id);\n\n if (code.verifier !== verifier) {\n throw new VerifyFailure(\"Invalid verifier\");\n }\n if (code.expirationTime < Date.now()) {\n throw new VerifyFailure(\"Expired verification code\");\n }\n if (provider !== undefined && code.provider !== provider) {\n throw new VerifyFailure(\n `Invalid provider \"${provider}\" for given \\`code\\``,\n );\n }\n\n const account = await db.accounts.getById(code.accountId);\n if (account === null) {\n throw new VerifyFailure(\n \"Account associated with this email has been deleted\",\n );\n }\n\n const codeProvider = isEnterpriseProviderId(code.provider)\n ? createSyntheticOAuthMaterializedConfig(code.provider)\n : getProviderOrThrow(code.provider, allowExtraProviders);\n\n if (\n codeProvider !== null &&\n (codeProvider.type === \"email\" || codeProvider.type === \"phone\") &&\n codeProvider.authorize !== undefined\n ) {\n await codeProvider.authorize(args.params, account);\n }\n\n const methodProvider = isEnterpriseProviderId(account.provider)\n ? createSyntheticOAuthMaterializedConfig(account.provider)\n : getProviderOrThrow(account.provider);\n\n const userId =\n methodProvider.type === \"oauth\"\n ? account.userId\n : (\n await upsertUserAndAccount(\n ctx,\n await getAuthSessionId(ctx),\n { existingAccount: account },\n {\n type: \"verification\",\n provider: methodProvider,\n profile: {\n ...(code.emailVerified !== undefined\n ? { email: code.emailVerified, emailVerified: true }\n : {}),\n ...(code.phoneVerified !== undefined\n ? { phone: code.phoneVerified, phoneVerified: true }\n : {}),\n },\n },\n config,\n )\n ).userId;\n\n if (identifier !== undefined) {\n await Fx.run(resetSignInRateLimit(ctx, identifier, config));\n }\n\n const sessionId = await createNewAndDeleteExistingSession(\n ctx,\n config,\n userId,\n );\n return await maybeGenerateTokensForSession(\n ctx,\n config,\n userId,\n sessionId,\n generateTokens,\n );\n } catch (error) {\n if (error instanceof VerifyFailure) {\n logWithLevel(LOG_LEVELS.ERROR, error.reason);\n if (identifier !== undefined) {\n await Fx.run(recordFailedSignIn(ctx, identifier, config));\n }\n return null;\n }\n logWithLevel(\n LOG_LEVELS.ERROR,\n `verifyCodeAndSignInImpl failed: ${String(error)}`,\n );\n return null;\n }\n}\n\n// ============================================================================\n// Action-level caller (unchanged — just forwards to mutation)\n// ============================================================================\n\nexport const callVerifyCodeAndSignIn = async <\n DataModel extends GenericDataModel,\n>(\n ctx: GenericActionCtx<DataModel>,\n args: Infer<typeof verifyCodeAndSignInArgs>,\n): Promise<ReturnType> => {\n return ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"verifyCodeAndSignIn\",\n ...args,\n },\n });\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { AuthError } from "./authError.js";
2
1
  import { isLocalHost, logWithLevel } from "./utils.js";
3
2
  import { SHARED_COOKIE_OPTIONS } from "./cookies.js";
3
+ import { Cv } from "@robelest/fx/convex";
4
4
  import { Fx } from "@robelest/fx";
5
5
  import * as arctic from "arctic";
6
6
 
@@ -10,7 +10,7 @@ import * as arctic from "arctic";
10
10
  *
11
11
  * Uses Arctic for OAuth provider integration.
12
12
  *
13
- * All functions return `Fx<A, AuthError>` composed via `Fx.gen` pipelines.
13
+ * All functions return `Fx<A, ConvexError<any>>` composed via `Fx.gen` pipelines.
14
14
  *
15
15
  * @internal
16
16
  * @module
@@ -59,15 +59,24 @@ function isPKCEProvider(provider) {
59
59
  }
60
60
  /**
61
61
  * Exchange the authorization code for tokens via Arctic.
62
- * Maps Arctic-specific errors to typed `AuthError` failures.
62
+ * Maps Arctic-specific errors to typed `ConvexError<any>` failures.
63
63
  */
64
64
  function exchangeCode(arcticProvider, code, codeVerifier) {
65
65
  return Fx.from({
66
66
  ok: () => isPKCEProvider(arcticProvider) ? arcticProvider.validateAuthorizationCode(code, codeVerifier) : arcticProvider.validateAuthorizationCode(code),
67
67
  err: (e) => {
68
- if (e instanceof arctic.OAuth2RequestError) return new AuthError("OAUTH_PROVIDER_ERROR", `Token exchange failed: ${e.code}`);
69
- if (e instanceof arctic.ArcticFetchError) return new AuthError("OAUTH_PROVIDER_ERROR", `Network error during token exchange: ${e.message}`);
70
- return new AuthError("OAUTH_PROVIDER_ERROR", `Unexpected error during token exchange: ${e instanceof Error ? e.message : String(e)}`);
68
+ if (e instanceof arctic.OAuth2RequestError) return Cv.error({
69
+ code: "OAUTH_PROVIDER_ERROR",
70
+ message: `Token exchange failed: ${e.code}`
71
+ });
72
+ if (e instanceof arctic.ArcticFetchError) return Cv.error({
73
+ code: "OAUTH_PROVIDER_ERROR",
74
+ message: `Network error during token exchange: ${e.message}`
75
+ });
76
+ return Cv.error({
77
+ code: "OAUTH_PROVIDER_ERROR",
78
+ message: `Unexpected error during token exchange: ${e instanceof Error ? e.message : String(e)}`
79
+ });
71
80
  }
72
81
  }).pipe(Fx.chain((tokens) => {
73
82
  return Fx.succeed(tokens);
@@ -83,7 +92,10 @@ function extractProfile(providerId, oauthConfig, tokens) {
83
92
  return Fx.match(profileSource, profileSource.source, {
84
93
  callback: (_profileSource) => Fx.from({
85
94
  ok: () => oauthConfig.profile(tokens),
86
- err: (e) => new AuthError("OAUTH_INVALID_PROFILE", `Profile callback threw: ${e instanceof Error ? e.message : String(e)}`)
95
+ err: (e) => Cv.error({
96
+ code: "OAUTH_INVALID_PROFILE",
97
+ message: `Profile callback threw: ${e instanceof Error ? e.message : String(e)}`
98
+ })
87
99
  }),
88
100
  idToken: (_profileSource) => {
89
101
  const claims = arctic.decodeIdToken(tokens.idToken());
@@ -94,14 +106,20 @@ function extractProfile(providerId, oauthConfig, tokens) {
94
106
  image: claims.picture ?? void 0
95
107
  });
96
108
  },
97
- missing: (_profileSource) => Fx.fail(new AuthError("OAUTH_INVALID_PROFILE", `Provider "${providerId}" does not return an ID token. Add a \`profile\` callback in the OAuth() config to extract user info from the access token.`))
109
+ missing: (_profileSource) => Cv.fail({
110
+ code: "OAUTH_INVALID_PROFILE",
111
+ message: `Provider "${providerId}" does not return an ID token. Add a \`profile\` callback in the OAuth() config to extract user info from the access token.`
112
+ })
98
113
  });
99
114
  }
100
115
  /**
101
116
  * Validate that the profile has a non-empty string `id`.
102
117
  */
103
118
  function validateProfileId(providerId, profile) {
104
- return typeof profile.id === "string" && profile.id ? Fx.succeed(profile) : Fx.fail(new AuthError("OAUTH_INVALID_PROFILE", `The profile callback for "${providerId}" must return an object with a string \`id\` field.`));
119
+ return typeof profile.id === "string" && profile.id ? Fx.succeed(profile) : Cv.fail({
120
+ code: "OAUTH_INVALID_PROFILE",
121
+ message: `The profile callback for "${providerId}" must return an object with a string \`id\` field.`
122
+ });
105
123
  }
106
124
  /**
107
125
  * Create an OAuth authorization URL using an Arctic provider.
@@ -145,7 +163,7 @@ async function createOAuthAuthorizationURL(providerId, arcticProvider, oauthConf
145
163
  * Handle the OAuth callback: validate state, exchange code for tokens,
146
164
  * extract profile.
147
165
  *
148
- * Returns `Fx<CallbackResult, AuthError>` composed via `Fx.gen`.
166
+ * Returns `Fx<CallbackResult, ConvexError<any>>` composed via `Fx.gen`.
149
167
  */
150
168
  /** @internal */
151
169
  function handleOAuthCallback(providerId, arcticProvider, oauthConfig, params, cookies) {
@@ -153,7 +171,10 @@ function handleOAuthCallback(providerId, arcticProvider, oauthConfig, params, co
153
171
  const resCookies = [];
154
172
  const storedState = cookies[oauthCookieName("state", providerId)];
155
173
  const returnedState = params.state;
156
- yield* Fx.guard(!storedState || !returnedState || storedState !== returnedState, Fx.fail(new AuthError("OAUTH_INVALID_STATE")));
174
+ yield* Fx.guard(!storedState || !returnedState || storedState !== returnedState, Cv.fail({
175
+ code: "OAUTH_INVALID_STATE",
176
+ message: "Invalid OAuth state. Please try signing in again."
177
+ }));
157
178
  resCookies.push(clearCookie("state", providerId));
158
179
  if (params.error) {
159
180
  const cause = {
@@ -162,25 +183,41 @@ function handleOAuthCallback(providerId, arcticProvider, oauthConfig, params, co
162
183
  error_description: params.error_description
163
184
  };
164
185
  logWithLevel("DEBUG", "OAuthCallbackError", cause);
165
- yield* Fx.fail(new AuthError("OAUTH_PROVIDER_ERROR", "OAuth provider returned an error", { cause: JSON.stringify(cause) }));
186
+ yield* Cv.fail({
187
+ code: "OAUTH_PROVIDER_ERROR",
188
+ message: "OAuth provider returned an error",
189
+ cause: JSON.stringify(cause)
190
+ });
166
191
  }
167
- const code = yield* params.code != null ? Fx.succeed(params.code) : Fx.fail(new AuthError("OAUTH_PROVIDER_ERROR", "Missing authorization code in callback"));
192
+ const code = yield* params.code != null ? Fx.succeed(params.code) : Cv.fail({
193
+ code: "OAUTH_PROVIDER_ERROR",
194
+ message: "Missing authorization code in callback"
195
+ });
168
196
  let codeVerifier;
169
197
  if (isPKCEProvider(arcticProvider)) {
170
198
  const pkceCookieName = oauthCookieName("pkce", providerId);
171
- codeVerifier = yield* cookies[pkceCookieName] != null ? Fx.succeed(cookies[pkceCookieName]) : Fx.fail(new AuthError("OAUTH_MISSING_VERIFIER", "Missing PKCE verifier cookie for OAuth callback"));
199
+ codeVerifier = yield* cookies[pkceCookieName] != null ? Fx.succeed(cookies[pkceCookieName]) : Cv.fail({
200
+ code: "OAUTH_MISSING_VERIFIER",
201
+ message: "Missing PKCE verifier cookie for OAuth callback"
202
+ });
172
203
  resCookies.push(clearCookie("pkce", providerId));
173
204
  }
174
205
  let nonce;
175
206
  if (oauthConfig.nonce === true) {
176
207
  const nonceCookieName = oauthCookieName("nonce", providerId);
177
- nonce = yield* cookies[nonceCookieName] != null ? Fx.succeed(cookies[nonceCookieName]) : Fx.fail(new AuthError("OAUTH_PROVIDER_ERROR", "Missing nonce cookie for OAuth callback"));
208
+ nonce = yield* cookies[nonceCookieName] != null ? Fx.succeed(cookies[nonceCookieName]) : Cv.fail({
209
+ code: "OAUTH_PROVIDER_ERROR",
210
+ message: "Missing nonce cookie for OAuth callback"
211
+ });
178
212
  resCookies.push(clearCookie("nonce", providerId));
179
213
  }
180
214
  const tokens = yield* exchangeCode(arcticProvider, code, codeVerifier);
181
215
  if (oauthConfig.validateTokens !== void 0) yield* Fx.from({
182
216
  ok: () => oauthConfig.validateTokens(tokens, { nonce }),
183
- err: (e) => new AuthError("OAUTH_PROVIDER_ERROR", `Token validation failed: ${e instanceof Error ? e.message : String(e)}`)
217
+ err: (e) => Cv.error({
218
+ code: "OAUTH_PROVIDER_ERROR",
219
+ message: `Token validation failed: ${e instanceof Error ? e.message : String(e)}`
220
+ })
184
221
  });
185
222
  const profile = yield* validateProfileId(providerId, yield* extractProfile(providerId, oauthConfig, tokens));
186
223
  logWithLevel("DEBUG", "OAuth callback profile extracted", {
@@ -1 +1 @@
1
- {"version":3,"file":"oauth.js","names":[],"sources":["../../../src/server/oauth.ts"],"sourcesContent":["/**\n * Arctic-based OAuth flow implementation.\n *\n * Uses Arctic for OAuth provider integration.\n *\n * All functions return `Fx<A, AuthError>` composed via `Fx.gen` pipelines.\n *\n * @internal\n * @module\n */\n\nimport { Fx } from \"@robelest/fx\";\nimport * as arctic from \"arctic\";\n\nimport { SHARED_COOKIE_OPTIONS } from \"./cookies\";\nimport { AuthError } from \"./authError\";\nimport type { OAuthProfile } from \"./types\";\nimport { logWithLevel } from \"./utils\";\nimport { isLocalHost } from \"./utils\";\n\ntype OAuthProviderConfigLike = {\n scopes?: string[];\n profile?: (tokens: arctic.OAuth2Tokens) => Promise<OAuthProfile>;\n nonce?: boolean;\n validateTokens?: (\n tokens: arctic.OAuth2Tokens,\n ctx: { nonce?: string },\n ) => Promise<void>;\n};\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** A cookie to be set on the HTTP response. */\n/** @internal */\nexport interface OAuthCookie {\n name: string;\n value: string;\n options: Record<string, unknown>;\n}\n\n/** Result of creating an authorization URL. */\n/** @internal */\nexport interface AuthorizationResult {\n redirect: string;\n cookies: OAuthCookie[];\n signature: string;\n}\n\n/** Result of handling an OAuth callback. */\n/** @internal */\nexport interface CallbackResult {\n profile: OAuthProfile;\n providerAccountId: string;\n cookies: OAuthCookie[];\n signature: string;\n}\n\n// ============================================================================\n// Cookie helpers\n// ============================================================================\n\nconst COOKIE_TTL = 60 * 15; // 15 minutes\n\nfunction oauthCookieName(type: \"state\" | \"pkce\" | \"nonce\", providerId: string) {\n const prefix = !isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\";\n return prefix + providerId + \"OAuth\" + type;\n}\n\nfunction createCookie(\n type: \"state\" | \"pkce\" | \"nonce\",\n providerId: string,\n value: string,\n): OAuthCookie {\n const expires = new Date();\n expires.setTime(expires.getTime() + COOKIE_TTL * 1000);\n return {\n name: oauthCookieName(type, providerId),\n value,\n options: { ...SHARED_COOKIE_OPTIONS, expires },\n };\n}\n\nfunction clearCookie(\n type: \"state\" | \"pkce\" | \"nonce\",\n providerId: string,\n): OAuthCookie {\n return {\n name: oauthCookieName(type, providerId),\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n}\n\n// ============================================================================\n// Signature (ConvexAuth-specific verifier mechanism)\n// ============================================================================\n\n/**\n * Creates a signature string from the OAuth state parameters.\n * This is stored in the verifier table and validated during callback.\n */\n/** @internal */\nexport function getAuthorizationSignature({\n codeVerifier,\n state,\n}: {\n codeVerifier?: string;\n state?: string;\n}) {\n return [codeVerifier, state].filter((param) => param !== undefined).join(\" \");\n}\n\n// ============================================================================\n// PKCE Detection\n// ============================================================================\n\n/**\n * Detect whether an Arctic provider uses PKCE by checking the arity\n * of `createAuthorizationURL`. PKCE providers take 3 args\n * (state, codeVerifier, scopes), non-PKCE take 2 (state, scopes).\n */\nfunction isPKCEProvider(provider: any): boolean {\n return (\n typeof provider.createAuthorizationURL === \"function\" &&\n provider.createAuthorizationURL.length >= 3\n );\n}\n\n// ============================================================================\n// Token exchange — wraps Arctic's validateAuthorizationCode\n// ============================================================================\n\n/**\n * Exchange the authorization code for tokens via Arctic.\n * Maps Arctic-specific errors to typed `AuthError` failures.\n */\nfunction exchangeCode(\n arcticProvider: any,\n code: string,\n codeVerifier: string | undefined,\n): Fx<arctic.OAuth2Tokens, AuthError> {\n return Fx.from({\n ok: () =>\n isPKCEProvider(arcticProvider)\n ? arcticProvider.validateAuthorizationCode(code, codeVerifier)\n : arcticProvider.validateAuthorizationCode(code),\n err: (e) => {\n if (e instanceof arctic.OAuth2RequestError) {\n return new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n `Token exchange failed: ${e.code}`,\n );\n }\n if (e instanceof arctic.ArcticFetchError) {\n return new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n `Network error during token exchange: ${e.message}`,\n );\n }\n // Unknown error — treat as unrecoverable defect; we surface it as\n // an AuthError here so the pipeline type stays Fx<_, AuthError>.\n // The original `throw e` re-throw is replicated via Fx.fatal below.\n return new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n `Unexpected error during token exchange: ${e instanceof Error ? e.message : String(e)}`,\n );\n },\n }).pipe(\n Fx.chain((tokens) => {\n // If the original error was neither OAuth2RequestError nor\n // ArcticFetchError the old code re-threw it raw. We replicate that\n // by checking whether we created an \"Unexpected\" marker message\n // — but since `Fx.from` already mapped it, we just pass through.\n return Fx.succeed(tokens);\n }),\n );\n}\n\n/**\n * Extract the user profile from tokens using the config callback,\n * OIDC auto-decode, or fail if neither is available.\n */\nfunction extractProfile(\n providerId: string,\n oauthConfig: OAuthProviderConfigLike,\n tokens: arctic.OAuth2Tokens,\n): Fx<OAuthProfile, AuthError> {\n const hasIdToken =\n \"id_token\" in tokens.data &&\n typeof (tokens.data as any).id_token === \"string\";\n const profileSource = oauthConfig.profile\n ? { source: \"callback\" as const }\n : hasIdToken\n ? { source: \"idToken\" as const }\n : { source: \"missing\" as const };\n\n return Fx.match(profileSource, profileSource.source, {\n callback: (_profileSource) =>\n Fx.from({\n ok: () => oauthConfig.profile!(tokens),\n err: (e) =>\n new AuthError(\n \"OAUTH_INVALID_PROFILE\",\n `Profile callback threw: ${e instanceof Error ? e.message : String(e)}`,\n ),\n }),\n idToken: (_profileSource) => {\n const claims = arctic.decodeIdToken(tokens.idToken()) as Record<\n string,\n unknown\n >;\n return Fx.succeed({\n id: (claims.sub as string) ?? crypto.randomUUID(),\n name: (claims.name as string) ?? undefined,\n email: (claims.email as string) ?? undefined,\n image: (claims.picture as string) ?? undefined,\n });\n },\n missing: (_profileSource) =>\n Fx.fail(\n new AuthError(\n \"OAUTH_INVALID_PROFILE\",\n `Provider \"${providerId}\" does not return an ID token. ` +\n `Add a \\`profile\\` callback in the OAuth() config to extract user info from the access token.`,\n ),\n ),\n });\n}\n\n/**\n * Validate that the profile has a non-empty string `id`.\n */\nfunction validateProfileId(\n providerId: string,\n profile: OAuthProfile,\n): Fx<OAuthProfile, AuthError> {\n return typeof profile.id === \"string\" && profile.id\n ? Fx.succeed(profile)\n : Fx.fail(\n new AuthError(\n \"OAUTH_INVALID_PROFILE\",\n `The profile callback for \"${providerId}\" must return an object with a string \\`id\\` field.`,\n ),\n );\n}\n\n// ============================================================================\n// Authorization URL creation\n// ============================================================================\n\n/**\n * Create an OAuth authorization URL using an Arctic provider.\n *\n * Handles PKCE detection, state generation, and cookie creation.\n */\n/** @internal */\nexport async function createOAuthAuthorizationURL(\n providerId: string,\n arcticProvider: any,\n oauthConfig: OAuthProviderConfigLike,\n): Promise<AuthorizationResult> {\n const state = arctic.generateState();\n const cookies: OAuthCookie[] = [];\n let codeVerifier: string | undefined;\n\n const scopes = oauthConfig.scopes ?? [];\n\n let url: URL;\n\n if (isPKCEProvider(arcticProvider)) {\n codeVerifier = arctic.generateCodeVerifier();\n url = arcticProvider.createAuthorizationURL(state, codeVerifier, scopes);\n cookies.push(createCookie(\"pkce\", providerId, codeVerifier));\n } else {\n url = arcticProvider.createAuthorizationURL(state, scopes);\n }\n\n cookies.push(createCookie(\"state\", providerId, state));\n\n if (oauthConfig.nonce === true) {\n const nonce = arctic.generateState();\n url.searchParams.set(\"nonce\", nonce);\n cookies.push(createCookie(\"nonce\", providerId, nonce));\n }\n\n logWithLevel(\"DEBUG\", \"OAuth authorization URL created\", {\n url: url.toString(),\n providerId,\n hasPKCE: !!codeVerifier,\n });\n\n const signature = getAuthorizationSignature({ codeVerifier, state });\n\n return {\n redirect: url.toString(),\n cookies,\n signature,\n };\n}\n\n// ============================================================================\n// OAuth callback handling\n// ============================================================================\n\n/**\n * Handle the OAuth callback: validate state, exchange code for tokens,\n * extract profile.\n *\n * Returns `Fx<CallbackResult, AuthError>` composed via `Fx.gen`.\n */\n/** @internal */\nexport function handleOAuthCallback(\n providerId: string,\n arcticProvider: any,\n oauthConfig: OAuthProviderConfigLike,\n params: Record<string, string>,\n cookies: Record<string, string | undefined>,\n): Fx<CallbackResult, AuthError> {\n return Fx.gen(function* () {\n const resCookies: OAuthCookie[] = [];\n\n // 1. Validate state\n const stateCookieName = oauthCookieName(\"state\", providerId);\n const storedState = cookies[stateCookieName];\n const returnedState = params.state;\n\n yield* Fx.guard(\n !storedState || !returnedState || storedState !== returnedState,\n Fx.fail(new AuthError(\"OAUTH_INVALID_STATE\")),\n );\n resCookies.push(clearCookie(\"state\", providerId));\n\n // Check for error from provider\n if (params.error) {\n const cause = {\n providerId,\n error: params.error,\n error_description: params.error_description,\n };\n logWithLevel(\"DEBUG\", \"OAuthCallbackError\", cause);\n yield* Fx.fail(\n new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n \"OAuth provider returned an error\",\n {\n cause: JSON.stringify(cause),\n },\n ),\n );\n }\n\n // 2. Get code\n const code = yield* params.code != null\n ? Fx.succeed(params.code)\n : Fx.fail(\n new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n \"Missing authorization code in callback\",\n ),\n );\n\n // 3. Read PKCE verifier from cookie if applicable\n let codeVerifier: string | undefined;\n if (isPKCEProvider(arcticProvider)) {\n const pkceCookieName = oauthCookieName(\"pkce\", providerId);\n codeVerifier = yield* cookies[pkceCookieName] != null\n ? Fx.succeed(cookies[pkceCookieName]!)\n : Fx.fail(\n new AuthError(\n \"OAUTH_MISSING_VERIFIER\",\n \"Missing PKCE verifier cookie for OAuth callback\",\n ),\n );\n resCookies.push(clearCookie(\"pkce\", providerId));\n }\n\n let nonce: string | undefined;\n if (oauthConfig.nonce === true) {\n const nonceCookieName = oauthCookieName(\"nonce\", providerId);\n nonce = yield* cookies[nonceCookieName] != null\n ? Fx.succeed(cookies[nonceCookieName]!)\n : Fx.fail(\n new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n \"Missing nonce cookie for OAuth callback\",\n ),\n );\n resCookies.push(clearCookie(\"nonce\", providerId));\n }\n\n // 4. Exchange code for tokens\n const tokens = yield* exchangeCode(arcticProvider, code, codeVerifier);\n\n if (oauthConfig.validateTokens !== undefined) {\n yield* Fx.from({\n ok: () => oauthConfig.validateTokens!(tokens, { nonce }),\n err: (e) =>\n new AuthError(\n \"OAUTH_PROVIDER_ERROR\",\n `Token validation failed: ${e instanceof Error ? e.message : String(e)}`,\n ),\n });\n }\n\n // 5. Extract profile\n const rawProfile = yield* extractProfile(providerId, oauthConfig, tokens);\n const profile = yield* validateProfileId(providerId, rawProfile);\n\n logWithLevel(\"DEBUG\", \"OAuth callback profile extracted\", {\n providerId,\n profileId: profile.id,\n });\n\n // 6. Compute signature for verifier validation\n const state = storedState!;\n const signature = getAuthorizationSignature({ codeVerifier, state });\n\n return {\n profile,\n providerAccountId: profile.id,\n cookies: resCookies,\n signature,\n };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"oauth.js","names":[],"sources":["../../../src/server/oauth.ts"],"sourcesContent":["/**\n * Arctic-based OAuth flow implementation.\n *\n * Uses Arctic for OAuth provider integration.\n *\n * All functions return `Fx<A, ConvexError<any>>` composed via `Fx.gen` pipelines.\n *\n * @internal\n * @module\n */\n\nimport { Fx } from \"@robelest/fx\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport * as arctic from \"arctic\";\nimport type { ConvexError } from \"convex/values\";\n\nimport { SHARED_COOKIE_OPTIONS } from \"./cookies\";\nimport type { OAuthProfile } from \"./types\";\nimport { logWithLevel } from \"./utils\";\nimport { isLocalHost } from \"./utils\";\n\ntype OAuthProviderConfigLike = {\n scopes?: string[];\n profile?: (tokens: arctic.OAuth2Tokens) => Promise<OAuthProfile>;\n nonce?: boolean;\n validateTokens?: (\n tokens: arctic.OAuth2Tokens,\n ctx: { nonce?: string },\n ) => Promise<void>;\n};\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** A cookie to be set on the HTTP response. */\n/** @internal */\nexport interface OAuthCookie {\n name: string;\n value: string;\n options: Record<string, unknown>;\n}\n\n/** Result of creating an authorization URL. */\n/** @internal */\nexport interface AuthorizationResult {\n redirect: string;\n cookies: OAuthCookie[];\n signature: string;\n}\n\n/** Result of handling an OAuth callback. */\n/** @internal */\nexport interface CallbackResult {\n profile: OAuthProfile;\n providerAccountId: string;\n cookies: OAuthCookie[];\n signature: string;\n}\n\n// ============================================================================\n// Cookie helpers\n// ============================================================================\n\nconst COOKIE_TTL = 60 * 15; // 15 minutes\n\nfunction oauthCookieName(type: \"state\" | \"pkce\" | \"nonce\", providerId: string) {\n const prefix = !isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\";\n return prefix + providerId + \"OAuth\" + type;\n}\n\nfunction createCookie(\n type: \"state\" | \"pkce\" | \"nonce\",\n providerId: string,\n value: string,\n): OAuthCookie {\n const expires = new Date();\n expires.setTime(expires.getTime() + COOKIE_TTL * 1000);\n return {\n name: oauthCookieName(type, providerId),\n value,\n options: { ...SHARED_COOKIE_OPTIONS, expires },\n };\n}\n\nfunction clearCookie(\n type: \"state\" | \"pkce\" | \"nonce\",\n providerId: string,\n): OAuthCookie {\n return {\n name: oauthCookieName(type, providerId),\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n}\n\n// ============================================================================\n// Signature (ConvexAuth-specific verifier mechanism)\n// ============================================================================\n\n/**\n * Creates a signature string from the OAuth state parameters.\n * This is stored in the verifier table and validated during callback.\n */\n/** @internal */\nexport function getAuthorizationSignature({\n codeVerifier,\n state,\n}: {\n codeVerifier?: string;\n state?: string;\n}) {\n return [codeVerifier, state].filter((param) => param !== undefined).join(\" \");\n}\n\n// ============================================================================\n// PKCE Detection\n// ============================================================================\n\n/**\n * Detect whether an Arctic provider uses PKCE by checking the arity\n * of `createAuthorizationURL`. PKCE providers take 3 args\n * (state, codeVerifier, scopes), non-PKCE take 2 (state, scopes).\n */\nfunction isPKCEProvider(provider: any): boolean {\n return (\n typeof provider.createAuthorizationURL === \"function\" &&\n provider.createAuthorizationURL.length >= 3\n );\n}\n\n// ============================================================================\n// Token exchange — wraps Arctic's validateAuthorizationCode\n// ============================================================================\n\n/**\n * Exchange the authorization code for tokens via Arctic.\n * Maps Arctic-specific errors to typed `ConvexError<any>` failures.\n */\nfunction exchangeCode(\n arcticProvider: any,\n code: string,\n codeVerifier: string | undefined,\n): Fx<arctic.OAuth2Tokens, ConvexError<any>> {\n return Fx.from({\n ok: () =>\n isPKCEProvider(arcticProvider)\n ? arcticProvider.validateAuthorizationCode(code, codeVerifier)\n : arcticProvider.validateAuthorizationCode(code),\n err: (e) => {\n if (e instanceof arctic.OAuth2RequestError) {\n return Cv.error({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: `Token exchange failed: ${e.code}`,\n });\n }\n if (e instanceof arctic.ArcticFetchError) {\n return Cv.error({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: `Network error during token exchange: ${e.message}`,\n });\n }\n // Unknown error — treat as unrecoverable defect; we surface it as\n // an ConvexError<any> here so the pipeline type stays Fx<_, ConvexError<any>>.\n // The original `throw e` re-throw is replicated via Fx.fatal below.\n return Cv.error({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: `Unexpected error during token exchange: ${e instanceof Error ? e.message : String(e)}`,\n });\n },\n }).pipe(\n Fx.chain((tokens) => {\n // If the original error was neither OAuth2RequestError nor\n // ArcticFetchError the old code re-threw it raw. We replicate that\n // by checking whether we created an \"Unexpected\" marker message\n // — but since `Fx.from` already mapped it, we just pass through.\n return Fx.succeed(tokens);\n }),\n );\n}\n\n/**\n * Extract the user profile from tokens using the config callback,\n * OIDC auto-decode, or fail if neither is available.\n */\nfunction extractProfile(\n providerId: string,\n oauthConfig: OAuthProviderConfigLike,\n tokens: arctic.OAuth2Tokens,\n): Fx<OAuthProfile, ConvexError<any>> {\n const hasIdToken =\n \"id_token\" in tokens.data &&\n typeof (tokens.data as any).id_token === \"string\";\n const profileSource = oauthConfig.profile\n ? { source: \"callback\" as const }\n : hasIdToken\n ? { source: \"idToken\" as const }\n : { source: \"missing\" as const };\n\n return Fx.match(profileSource, profileSource.source, {\n callback: (_profileSource) =>\n Fx.from({\n ok: () => oauthConfig.profile!(tokens),\n err: (e) =>\n Cv.error({\n code: \"OAUTH_INVALID_PROFILE\",\n message: `Profile callback threw: ${e instanceof Error ? e.message : String(e)}`,\n }),\n }),\n idToken: (_profileSource) => {\n const claims = arctic.decodeIdToken(tokens.idToken()) as Record<\n string,\n unknown\n >;\n return Fx.succeed({\n id: (claims.sub as string) ?? crypto.randomUUID(),\n name: (claims.name as string) ?? undefined,\n email: (claims.email as string) ?? undefined,\n image: (claims.picture as string) ?? undefined,\n });\n },\n missing: (_profileSource) =>\n Cv.fail({\n code: \"OAUTH_INVALID_PROFILE\",\n message:\n `Provider \"${providerId}\" does not return an ID token. ` +\n `Add a \\`profile\\` callback in the OAuth() config to extract user info from the access token.`,\n }),\n });\n}\n\n/**\n * Validate that the profile has a non-empty string `id`.\n */\nfunction validateProfileId(\n providerId: string,\n profile: OAuthProfile,\n): Fx<OAuthProfile, ConvexError<any>> {\n return typeof profile.id === \"string\" && profile.id\n ? Fx.succeed(profile)\n : Cv.fail({\n code: \"OAUTH_INVALID_PROFILE\",\n message: `The profile callback for \"${providerId}\" must return an object with a string \\`id\\` field.`,\n });\n}\n\n// ============================================================================\n// Authorization URL creation\n// ============================================================================\n\n/**\n * Create an OAuth authorization URL using an Arctic provider.\n *\n * Handles PKCE detection, state generation, and cookie creation.\n */\n/** @internal */\nexport async function createOAuthAuthorizationURL(\n providerId: string,\n arcticProvider: any,\n oauthConfig: OAuthProviderConfigLike,\n): Promise<AuthorizationResult> {\n const state = arctic.generateState();\n const cookies: OAuthCookie[] = [];\n let codeVerifier: string | undefined;\n\n const scopes = oauthConfig.scopes ?? [];\n\n let url: URL;\n\n if (isPKCEProvider(arcticProvider)) {\n codeVerifier = arctic.generateCodeVerifier();\n url = arcticProvider.createAuthorizationURL(state, codeVerifier, scopes);\n cookies.push(createCookie(\"pkce\", providerId, codeVerifier));\n } else {\n url = arcticProvider.createAuthorizationURL(state, scopes);\n }\n\n cookies.push(createCookie(\"state\", providerId, state));\n\n if (oauthConfig.nonce === true) {\n const nonce = arctic.generateState();\n url.searchParams.set(\"nonce\", nonce);\n cookies.push(createCookie(\"nonce\", providerId, nonce));\n }\n\n logWithLevel(\"DEBUG\", \"OAuth authorization URL created\", {\n url: url.toString(),\n providerId,\n hasPKCE: !!codeVerifier,\n });\n\n const signature = getAuthorizationSignature({ codeVerifier, state });\n\n return {\n redirect: url.toString(),\n cookies,\n signature,\n };\n}\n\n// ============================================================================\n// OAuth callback handling\n// ============================================================================\n\n/**\n * Handle the OAuth callback: validate state, exchange code for tokens,\n * extract profile.\n *\n * Returns `Fx<CallbackResult, ConvexError<any>>` composed via `Fx.gen`.\n */\n/** @internal */\nexport function handleOAuthCallback(\n providerId: string,\n arcticProvider: any,\n oauthConfig: OAuthProviderConfigLike,\n params: Record<string, string>,\n cookies: Record<string, string | undefined>,\n): Fx<CallbackResult, ConvexError<any>> {\n return Fx.gen(function* () {\n const resCookies: OAuthCookie[] = [];\n\n // 1. Validate state\n const stateCookieName = oauthCookieName(\"state\", providerId);\n const storedState = cookies[stateCookieName];\n const returnedState = params.state;\n\n yield* Fx.guard(\n !storedState || !returnedState || storedState !== returnedState,\n Cv.fail({\n code: \"OAUTH_INVALID_STATE\",\n message: \"Invalid OAuth state. Please try signing in again.\",\n }),\n );\n resCookies.push(clearCookie(\"state\", providerId));\n\n // Check for error from provider\n if (params.error) {\n const cause = {\n providerId,\n error: params.error,\n error_description: params.error_description,\n };\n logWithLevel(\"DEBUG\", \"OAuthCallbackError\", cause);\n yield* Cv.fail({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: \"OAuth provider returned an error\",\n cause: JSON.stringify(cause),\n });\n }\n\n // 2. Get code\n const code = yield* params.code != null\n ? Fx.succeed(params.code)\n : Cv.fail({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: \"Missing authorization code in callback\",\n });\n\n // 3. Read PKCE verifier from cookie if applicable\n let codeVerifier: string | undefined;\n if (isPKCEProvider(arcticProvider)) {\n const pkceCookieName = oauthCookieName(\"pkce\", providerId);\n codeVerifier = yield* cookies[pkceCookieName] != null\n ? Fx.succeed(cookies[pkceCookieName]!)\n : Cv.fail({\n code: \"OAUTH_MISSING_VERIFIER\",\n message: \"Missing PKCE verifier cookie for OAuth callback\",\n });\n resCookies.push(clearCookie(\"pkce\", providerId));\n }\n\n let nonce: string | undefined;\n if (oauthConfig.nonce === true) {\n const nonceCookieName = oauthCookieName(\"nonce\", providerId);\n nonce = yield* cookies[nonceCookieName] != null\n ? Fx.succeed(cookies[nonceCookieName]!)\n : Cv.fail({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: \"Missing nonce cookie for OAuth callback\",\n });\n resCookies.push(clearCookie(\"nonce\", providerId));\n }\n\n // 4. Exchange code for tokens\n const tokens = yield* exchangeCode(arcticProvider, code, codeVerifier);\n\n if (oauthConfig.validateTokens !== undefined) {\n yield* Fx.from({\n ok: () => oauthConfig.validateTokens!(tokens, { nonce }),\n err: (e) =>\n Cv.error({\n code: \"OAUTH_PROVIDER_ERROR\",\n message: `Token validation failed: ${e instanceof Error ? e.message : String(e)}`,\n }),\n });\n }\n\n // 5. Extract profile\n const rawProfile = yield* extractProfile(providerId, oauthConfig, tokens);\n const profile = yield* validateProfileId(providerId, rawProfile);\n\n logWithLevel(\"DEBUG\", \"OAuth callback profile extracted\", {\n providerId,\n profileId: profile.id,\n });\n\n // 6. Compute signature for verifier validation\n const state = storedState!;\n const signature = getAuthorizationSignature({ codeVerifier, state });\n\n return {\n profile,\n providerAccountId: profile.id,\n cookies: resCookies,\n signature,\n };\n });\n}\n"],"mappings":"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"}