@estifanos-sh/convex-auth 0.0.2 → 0.0.4

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 (438) hide show
  1. package/README.md +29 -6
  2. package/dist/bin.js +2 -34
  3. package/dist/browser/index.d.ts +2 -2
  4. package/dist/browser/index.d.ts.map +1 -1
  5. package/dist/browser/index.js +15 -9
  6. package/dist/browser/index.js.map +1 -1
  7. package/dist/client/core/types.d.ts +68 -97
  8. package/dist/client/core/types.d.ts.map +1 -1
  9. package/dist/client/core/types.js.map +1 -1
  10. package/dist/client/errors.d.ts +15 -0
  11. package/dist/client/errors.d.ts.map +1 -0
  12. package/dist/client/errors.js +8 -1
  13. package/dist/client/errors.js.map +1 -1
  14. package/dist/client/factors/device.js +4 -4
  15. package/dist/client/factors/device.js.map +1 -1
  16. package/dist/client/factors/totp.js +20 -12
  17. package/dist/client/factors/totp.js.map +1 -1
  18. package/dist/client/factors/webauthn.js +6 -2
  19. package/dist/client/factors/webauthn.js.map +1 -1
  20. package/dist/client/index.d.ts +6 -3
  21. package/dist/client/index.d.ts.map +1 -1
  22. package/dist/client/index.js +13 -10
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/component/_generated/api.d.ts +13 -3
  25. package/dist/component/_generated/api.d.ts.map +1 -1
  26. package/dist/component/_generated/api.js.map +1 -1
  27. package/dist/component/_generated/component.d.ts +365 -74
  28. package/dist/component/_generated/component.d.ts.map +1 -1
  29. package/dist/component/_generated/dataModel.d.ts +2 -2
  30. package/dist/component/_generated/dataModel.d.ts.map +1 -1
  31. package/dist/component/_generated/server.d.ts +13 -2
  32. package/dist/component/_generated/server.d.ts.map +1 -1
  33. package/dist/component/_generated/server.js +2 -1
  34. package/dist/component/_generated/server.js.map +1 -1
  35. package/dist/component/account.d.ts +52 -38
  36. package/dist/component/account.d.ts.map +1 -1
  37. package/dist/component/account.js +42 -23
  38. package/dist/component/account.js.map +1 -1
  39. package/dist/component/batch.d.ts +3 -0
  40. package/dist/component/batch.d.ts.map +1 -0
  41. package/dist/component/batch.js +17 -0
  42. package/dist/component/batch.js.map +1 -0
  43. package/dist/component/connection/audit.d.ts +30 -27
  44. package/dist/component/connection/audit.d.ts.map +1 -1
  45. package/dist/component/connection/audit.js +16 -23
  46. package/dist/component/connection/audit.js.map +1 -1
  47. package/dist/component/connection/cache.d.ts +6 -6
  48. package/dist/component/connection/cache.js +0 -1
  49. package/dist/component/connection/cache.js.map +1 -1
  50. package/dist/component/connection/domain/verification.d.ts +19 -19
  51. package/dist/component/connection/domain/verification.js +1 -2
  52. package/dist/component/connection/domain/verification.js.map +1 -1
  53. package/dist/component/connection/domain.d.ts +20 -20
  54. package/dist/component/connection/domain.js +1 -2
  55. package/dist/component/connection/domain.js.map +1 -1
  56. package/dist/component/connection/saml/assertion.d.ts +4 -4
  57. package/dist/component/connection/saml/assertion.js +0 -1
  58. package/dist/component/connection/saml/assertion.js.map +1 -1
  59. package/dist/component/connection/saml/request.d.ts +8 -9
  60. package/dist/component/connection/saml/request.d.ts.map +1 -1
  61. package/dist/component/connection/saml/request.js +0 -2
  62. package/dist/component/connection/saml/request.js.map +1 -1
  63. package/dist/component/connection/scim/config.d.ts +13 -13
  64. package/dist/component/connection/scim/config.d.ts.map +1 -1
  65. package/dist/component/connection/scim/config.js +2 -2
  66. package/dist/component/connection/scim/config.js.map +1 -1
  67. package/dist/component/connection/scim/identity.d.ts +122 -61
  68. package/dist/component/connection/scim/identity.d.ts.map +1 -1
  69. package/dist/component/connection/scim/identity.js +408 -60
  70. package/dist/component/connection/scim/identity.js.map +1 -1
  71. package/dist/component/connection/secret.d.ts +15 -15
  72. package/dist/component/connection/secret.d.ts.map +1 -1
  73. package/dist/component/connection/secret.js +2 -2
  74. package/dist/component/connection/secret.js.map +1 -1
  75. package/dist/component/connection/webhook/delivery.d.ts +69 -53
  76. package/dist/component/connection/webhook/delivery.d.ts.map +1 -1
  77. package/dist/component/connection/webhook/delivery.js +129 -172
  78. package/dist/component/connection/webhook/delivery.js.map +1 -1
  79. package/dist/component/connection/webhook/endpoint.d.ts +21 -21
  80. package/dist/component/connection/webhook/endpoint.d.ts.map +1 -1
  81. package/dist/component/connection/webhook/endpoint.js +2 -2
  82. package/dist/component/connection/webhook/endpoint.js.map +1 -1
  83. package/dist/component/connection/webhook/events.d.ts +26 -0
  84. package/dist/component/connection/webhook/events.d.ts.map +1 -0
  85. package/dist/component/connection/webhook/events.js +39 -0
  86. package/dist/component/connection/webhook/events.js.map +1 -0
  87. package/dist/component/connection.d.ts +39 -39
  88. package/dist/component/connection.d.ts.map +1 -1
  89. package/dist/component/connection.js +9 -9
  90. package/dist/component/connection.js.map +1 -1
  91. package/dist/component/context.d.ts +115 -0
  92. package/dist/component/context.d.ts.map +1 -0
  93. package/dist/component/context.js +49 -0
  94. package/dist/component/context.js.map +1 -0
  95. package/dist/component/convex.config.d.ts +2 -2
  96. package/dist/component/crons.d.ts +2 -2
  97. package/dist/component/documents.d.ts +1244 -0
  98. package/dist/component/documents.d.ts.map +1 -0
  99. package/dist/component/documents.js +44 -0
  100. package/dist/component/documents.js.map +1 -0
  101. package/dist/component/event.d.ts +142 -141
  102. package/dist/component/event.d.ts.map +1 -1
  103. package/dist/component/event.js +90 -119
  104. package/dist/component/event.js.map +1 -1
  105. package/dist/component/eventstream.d.ts +30 -0
  106. package/dist/component/eventstream.d.ts.map +1 -0
  107. package/dist/component/eventstream.js +60 -0
  108. package/dist/component/eventstream.js.map +1 -0
  109. package/dist/component/factor/device.d.ts +25 -24
  110. package/dist/component/factor/device.d.ts.map +1 -1
  111. package/dist/component/factor/device.js +7 -4
  112. package/dist/component/factor/device.js.map +1 -1
  113. package/dist/component/factor/passkey.d.ts +173 -85
  114. package/dist/component/factor/passkey.d.ts.map +1 -1
  115. package/dist/component/factor/passkey.js +138 -27
  116. package/dist/component/factor/passkey.js.map +1 -1
  117. package/dist/component/factor/totp.d.ts +56 -50
  118. package/dist/component/factor/totp.d.ts.map +1 -1
  119. package/dist/component/factor/totp.js +6 -5
  120. package/dist/component/factor/totp.js.map +1 -1
  121. package/dist/component/group/active.d.ts +326 -22
  122. package/dist/component/group/active.d.ts.map +1 -1
  123. package/dist/component/group/active.js +22 -13
  124. package/dist/component/group/active.js.map +1 -1
  125. package/dist/component/group/invite.d.ts +30 -30
  126. package/dist/component/group/invite.d.ts.map +1 -1
  127. package/dist/component/group/invite.js +2 -2
  128. package/dist/component/group/invite.js.map +1 -1
  129. package/dist/component/group/member.d.ts +36 -36
  130. package/dist/component/group/member.d.ts.map +1 -1
  131. package/dist/component/group/member.js +5 -3
  132. package/dist/component/group/member.js.map +1 -1
  133. package/dist/component/group.d.ts +49 -64
  134. package/dist/component/group.d.ts.map +1 -1
  135. package/dist/component/group.js +5 -3
  136. package/dist/component/group.js.map +1 -1
  137. package/dist/component/http.d.ts +2 -2
  138. package/dist/component/index.d.ts +0 -10
  139. package/dist/component/index.js +1 -3
  140. package/dist/component/limits.d.ts +5 -4
  141. package/dist/component/limits.d.ts.map +1 -1
  142. package/dist/component/limits.js +7 -4
  143. package/dist/component/limits.js.map +1 -1
  144. package/dist/component/maintenance.d.ts +6 -3
  145. package/dist/component/maintenance.d.ts.map +1 -1
  146. package/dist/component/maintenance.js +45 -6
  147. package/dist/component/maintenance.js.map +1 -1
  148. package/dist/component/model.d.ts +847 -1723
  149. package/dist/component/model.d.ts.map +1 -1
  150. package/dist/component/model.js +28 -317
  151. package/dist/component/model.js.map +1 -1
  152. package/dist/component/modules.d.ts +11 -5
  153. package/dist/component/modules.d.ts.map +1 -1
  154. package/dist/component/modules.js +4 -1
  155. package/dist/component/modules.js.map +1 -1
  156. package/dist/component/oauth/client.d.ts +20 -20
  157. package/dist/component/oauth/client.d.ts.map +1 -1
  158. package/dist/component/oauth/client.js +2 -2
  159. package/dist/component/oauth/client.js.map +1 -1
  160. package/dist/component/oauth/code.d.ts +14 -14
  161. package/dist/component/oauth/code.js +1 -2
  162. package/dist/component/oauth/code.js.map +1 -1
  163. package/dist/component/oauth/refresh.d.ts +20 -20
  164. package/dist/component/oauth/refresh.js +1 -2
  165. package/dist/component/oauth/refresh.js.map +1 -1
  166. package/dist/component/schema.d.ts +1148 -607
  167. package/dist/component/schema.d.ts.map +1 -1
  168. package/dist/component/schema.js +43 -15
  169. package/dist/component/schema.js.map +1 -1
  170. package/dist/component/session.d.ts +68 -32
  171. package/dist/component/session.d.ts.map +1 -1
  172. package/dist/component/session.js +118 -34
  173. package/dist/component/session.js.map +1 -1
  174. package/dist/component/token/continuation.d.ts +47 -18
  175. package/dist/component/token/continuation.d.ts.map +1 -1
  176. package/dist/component/token/continuation.js +76 -18
  177. package/dist/component/token/continuation.js.map +1 -1
  178. package/dist/component/token/enrollment.d.ts +42 -0
  179. package/dist/component/token/enrollment.d.ts.map +1 -0
  180. package/dist/component/token/enrollment.js +57 -0
  181. package/dist/component/token/enrollment.js.map +1 -0
  182. package/dist/component/token/pkce.d.ts +31 -24
  183. package/dist/component/token/pkce.d.ts.map +1 -1
  184. package/dist/component/token/pkce.js +15 -20
  185. package/dist/component/token/pkce.js.map +1 -1
  186. package/dist/component/token/refresh.d.ts +42 -35
  187. package/dist/component/token/refresh.d.ts.map +1 -1
  188. package/dist/component/token/refresh.js +16 -8
  189. package/dist/component/token/refresh.js.map +1 -1
  190. package/dist/component/token/verification.d.ts +34 -30
  191. package/dist/component/token/verification.d.ts.map +1 -1
  192. package/dist/component/token/verification.js +7 -6
  193. package/dist/component/token/verification.js.map +1 -1
  194. package/dist/component/user/email.d.ts +21 -21
  195. package/dist/component/user/email.d.ts.map +1 -1
  196. package/dist/component/user/email.js +2 -2
  197. package/dist/component/user/email.js.map +1 -1
  198. package/dist/component/user/key.d.ts +36 -36
  199. package/dist/component/user/key.d.ts.map +1 -1
  200. package/dist/component/user/key.js +2 -2
  201. package/dist/component/user/key.js.map +1 -1
  202. package/dist/component/user.d.ts +52 -48
  203. package/dist/component/user.d.ts.map +1 -1
  204. package/dist/component/user.js +157 -36
  205. package/dist/component/user.js.map +1 -1
  206. package/dist/expo/index.d.ts +4 -4
  207. package/dist/expo/index.d.ts.map +1 -1
  208. package/dist/expo/index.js +20 -13
  209. package/dist/expo/index.js.map +1 -1
  210. package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/ReadableLogRecord.d.ts +2 -2
  211. package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/SdkLogRecord.d.ts +1 -1
  212. package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/MeterProvider.d.ts +2 -2
  213. package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricData.d.ts +1 -1
  214. package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/Span.d.ts +1 -1
  215. package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/ReadableSpan.d.ts +1 -1
  216. package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/types.d.ts +1 -1
  217. package/dist/otel.d.ts +1 -1
  218. package/dist/providers/anonymous.d.ts +11 -5
  219. package/dist/providers/anonymous.d.ts.map +1 -1
  220. package/dist/providers/anonymous.js +4 -1
  221. package/dist/providers/anonymous.js.map +1 -1
  222. package/dist/providers/apple.d.ts +1 -1
  223. package/dist/providers/apple.js +19 -4
  224. package/dist/providers/apple.js.map +1 -1
  225. package/dist/providers/credentials.d.ts +22 -14
  226. package/dist/providers/credentials.d.ts.map +1 -1
  227. package/dist/providers/credentials.js +2 -0
  228. package/dist/providers/credentials.js.map +1 -1
  229. package/dist/providers/custom.d.ts +3 -3
  230. package/dist/providers/custom.d.ts.map +1 -1
  231. package/dist/providers/custom.js.map +1 -1
  232. package/dist/providers/email.d.ts +3 -3
  233. package/dist/providers/email.d.ts.map +1 -1
  234. package/dist/providers/email.js.map +1 -1
  235. package/dist/providers/github.d.ts +1 -1
  236. package/dist/providers/github.d.ts.map +1 -1
  237. package/dist/providers/github.js +9 -4
  238. package/dist/providers/github.js.map +1 -1
  239. package/dist/providers/google.d.ts +1 -1
  240. package/dist/providers/google.d.ts.map +1 -1
  241. package/dist/providers/google.js +9 -4
  242. package/dist/providers/google.js.map +1 -1
  243. package/dist/providers/microsoft.d.ts +1 -1
  244. package/dist/providers/microsoft.d.ts.map +1 -1
  245. package/dist/providers/microsoft.js +12 -4
  246. package/dist/providers/microsoft.js.map +1 -1
  247. package/dist/providers/password.d.ts +117 -15
  248. package/dist/providers/password.d.ts.map +1 -1
  249. package/dist/providers/password.js +71 -26
  250. package/dist/providers/password.js.map +1 -1
  251. package/dist/providers/phone.d.ts +4 -3
  252. package/dist/providers/phone.d.ts.map +1 -1
  253. package/dist/providers/phone.js.map +1 -1
  254. package/dist/providers/webauthn.d.ts.map +1 -1
  255. package/dist/providers/webauthn.js +6 -0
  256. package/dist/providers/webauthn.js.map +1 -1
  257. package/dist/react/index.d.ts +40 -41
  258. package/dist/react/index.d.ts.map +1 -1
  259. package/dist/react/index.js +27 -52
  260. package/dist/react/index.js.map +1 -1
  261. package/dist/server/auth.d.ts +224 -126
  262. package/dist/server/auth.d.ts.map +1 -1
  263. package/dist/server/auth.js +44 -25
  264. package/dist/server/auth.js.map +1 -1
  265. package/dist/server/component/factor/db.js +13 -6
  266. package/dist/server/component/factor/db.js.map +1 -1
  267. package/dist/server/config.d.ts +2 -1
  268. package/dist/server/connection/domain.d.ts +4 -65
  269. package/dist/server/connection/domain.d.ts.map +1 -1
  270. package/dist/server/connection/domain.js +29 -5
  271. package/dist/server/connection/domain.js.map +1 -1
  272. package/dist/server/connection/group/service.js +7 -5
  273. package/dist/server/connection/group/service.js.map +1 -1
  274. package/dist/server/connection/http.js +136 -291
  275. package/dist/server/connection/http.js.map +1 -1
  276. package/dist/server/connection/oidc.js +8 -17
  277. package/dist/server/connection/oidc.js.map +1 -1
  278. package/dist/server/connection/policy/domain.js +4 -1
  279. package/dist/server/connection/policy/domain.js.map +1 -1
  280. package/dist/server/connection/policy.js +14 -13
  281. package/dist/server/connection/policy.js.map +1 -1
  282. package/dist/server/connection/provision.js +5 -19
  283. package/dist/server/connection/provision.js.map +1 -1
  284. package/dist/server/connection/saml/encoding.js +2 -6
  285. package/dist/server/connection/saml/encoding.js.map +1 -1
  286. package/dist/server/connection/saml/signature.js +2 -2
  287. package/dist/server/connection/saml/signature.js.map +1 -1
  288. package/dist/server/connection/saml/xmldsig.js +1 -1
  289. package/dist/server/connection/saml/xmldsig.js.map +1 -1
  290. package/dist/server/connection/webhook.js +4 -36
  291. package/dist/server/connection/webhook.js.map +1 -1
  292. package/dist/server/context.d.ts +2 -2
  293. package/dist/server/context.d.ts.map +1 -1
  294. package/dist/server/context.js +35 -24
  295. package/dist/server/context.js.map +1 -1
  296. package/dist/server/contract.d.ts.map +1 -1
  297. package/dist/server/contract.js +6 -14
  298. package/dist/server/contract.js.map +1 -1
  299. package/dist/server/core.js +46 -1
  300. package/dist/server/core.js.map +1 -1
  301. package/dist/server/cors.d.ts +2 -2
  302. package/dist/server/cors.d.ts.map +1 -1
  303. package/dist/server/crypto.js.map +1 -1
  304. package/dist/server/db.js +6 -2
  305. package/dist/server/db.js.map +1 -1
  306. package/dist/server/device.js +1 -1
  307. package/dist/server/domains/account.d.ts +9 -1
  308. package/dist/server/domains/account.d.ts.map +1 -1
  309. package/dist/server/domains/account.js +21 -9
  310. package/dist/server/domains/account.js.map +1 -1
  311. package/dist/server/domains/context.js +34 -0
  312. package/dist/server/domains/context.js.map +1 -0
  313. package/dist/server/domains/factor.d.ts +3 -2
  314. package/dist/server/domains/factor.d.ts.map +1 -1
  315. package/dist/server/domains/factor.js.map +1 -1
  316. package/dist/server/domains/group.js.map +1 -1
  317. package/dist/server/domains/invite.js.map +1 -1
  318. package/dist/server/domains/key.js.map +1 -1
  319. package/dist/server/domains/member.js.map +1 -1
  320. package/dist/server/domains/session.js.map +1 -1
  321. package/dist/server/domains/user.js.map +1 -1
  322. package/dist/server/env.js +2 -22
  323. package/dist/server/env.js.map +1 -1
  324. package/dist/server/events.d.ts +3 -1
  325. package/dist/server/events.d.ts.map +1 -1
  326. package/dist/server/events.js +6 -6
  327. package/dist/server/events.js.map +1 -1
  328. package/dist/server/facade.d.ts +1 -33
  329. package/dist/server/facade.d.ts.map +1 -1
  330. package/dist/server/facade.js +1 -2
  331. package/dist/server/facade.js.map +1 -1
  332. package/dist/server/http.d.ts +1 -0
  333. package/dist/server/http.d.ts.map +1 -1
  334. package/dist/server/http.js +4 -4
  335. package/dist/server/http.js.map +1 -1
  336. package/dist/server/identity/claims.js +1 -1
  337. package/dist/server/identity/convex.d.ts +2 -0
  338. package/dist/server/identity/convex.d.ts.map +1 -1
  339. package/dist/server/index.d.ts +6 -9
  340. package/dist/server/index.js +2 -4
  341. package/dist/server/limits.js +23 -2
  342. package/dist/server/limits.js.map +1 -1
  343. package/dist/server/mutations/calls.js +3 -2
  344. package/dist/server/mutations/credentials/signin.js +7 -3
  345. package/dist/server/mutations/credentials/signin.js.map +1 -1
  346. package/dist/server/mutations/enrollment.js +87 -0
  347. package/dist/server/mutations/enrollment.js.map +1 -0
  348. package/dist/server/mutations/invalidate.js +5 -10
  349. package/dist/server/mutations/invalidate.js.map +1 -1
  350. package/dist/server/mutations/oauth.js +3 -3
  351. package/dist/server/mutations/oauth.js.map +1 -1
  352. package/dist/server/mutations/refresh.js +1 -0
  353. package/dist/server/mutations/refresh.js.map +1 -1
  354. package/dist/server/mutations/register.js +52 -21
  355. package/dist/server/mutations/register.js.map +1 -1
  356. package/dist/server/mutations/retrieve.js +4 -1
  357. package/dist/server/mutations/retrieve.js.map +1 -1
  358. package/dist/server/mutations/signin.js +2 -2
  359. package/dist/server/mutations/signin.js.map +1 -1
  360. package/dist/server/mutations/store.js +6 -1
  361. package/dist/server/mutations/store.js.map +1 -1
  362. package/dist/server/mutations/verify.js +6 -4
  363. package/dist/server/mutations/verify.js.map +1 -1
  364. package/dist/server/oauth/client.d.ts +1 -1
  365. package/dist/server/oauth/client.js.map +1 -1
  366. package/dist/server/oauth/code.d.ts +1 -1
  367. package/dist/server/oauth/code.js.map +1 -1
  368. package/dist/server/oauth/factory.js +89 -13
  369. package/dist/server/oauth/factory.js.map +1 -1
  370. package/dist/server/oauth/handlers.js +2 -2
  371. package/dist/server/oauth/runtime.js +24 -9
  372. package/dist/server/oauth/runtime.js.map +1 -1
  373. package/dist/server/preload.d.ts.map +1 -1
  374. package/dist/server/preload.js +14 -11
  375. package/dist/server/preload.js.map +1 -1
  376. package/dist/server/runtime.d.ts +236 -148
  377. package/dist/server/runtime.d.ts.map +1 -1
  378. package/dist/server/runtime.js +92 -44
  379. package/dist/server/runtime.js.map +1 -1
  380. package/dist/server/session/lifecycle.d.ts +2 -1
  381. package/dist/server/session/lifecycle.d.ts.map +1 -1
  382. package/dist/server/session/lifecycle.js +29 -10
  383. package/dist/server/session/lifecycle.js.map +1 -1
  384. package/dist/server/signin/flow.js +2 -2
  385. package/dist/server/signin/flow.js.map +1 -1
  386. package/dist/server/telemetry.js +1 -1
  387. package/dist/server/tokens.js +6 -3
  388. package/dist/server/tokens.js.map +1 -1
  389. package/dist/server/totp.js +4 -3
  390. package/dist/server/totp.js.map +1 -1
  391. package/dist/server/types.d.ts +131 -45
  392. package/dist/server/types.d.ts.map +1 -1
  393. package/dist/server/user/account.js +0 -1
  394. package/dist/server/user/account.js.map +1 -1
  395. package/dist/server/validators.d.ts +930 -944
  396. package/dist/server/validators.d.ts.map +1 -1
  397. package/dist/server/validators.js +66 -39
  398. package/dist/server/validators.js.map +1 -1
  399. package/dist/server/webauthn.js +60 -29
  400. package/dist/server/webauthn.js.map +1 -1
  401. package/dist/shared/brand.d.ts +3 -7
  402. package/dist/shared/brand.d.ts.map +1 -1
  403. package/dist/shared/params.d.ts +78 -0
  404. package/dist/shared/params.d.ts.map +1 -0
  405. package/dist/shared/results.d.ts +13 -1
  406. package/dist/shared/results.d.ts.map +1 -1
  407. package/dist/svelte/context.svelte.d.ts +23 -12
  408. package/dist/svelte/context.svelte.d.ts.map +1 -1
  409. package/dist/svelte/context.svelte.js +20 -13
  410. package/dist/svelte/index.d.ts +1 -4
  411. package/dist/svelte/index.d.ts.map +1 -1
  412. package/dist/svelte/index.js +1 -4
  413. package/dist/test.d.ts +1204 -611
  414. package/dist/test.d.ts.map +1 -1
  415. package/dist/test.js +50 -1
  416. package/dist/test.js.map +1 -1
  417. package/package.json +5 -14
  418. package/dist/_generated/component.d.ts +0 -3074
  419. package/dist/_generated/component.d.ts.map +0 -1
  420. package/dist/component/functions.d.ts +0 -9
  421. package/dist/component/functions.d.ts.map +0 -1
  422. package/dist/component/functions.js +0 -3
  423. package/dist/core/index.d.ts +0 -533
  424. package/dist/core/index.d.ts.map +0 -1
  425. package/dist/core/index.js +0 -86
  426. package/dist/core/index.js.map +0 -1
  427. package/dist/model.js.map +0 -1
  428. package/dist/server/env.d.ts +0 -28
  429. package/dist/server/env.d.ts.map +0 -1
  430. package/dist/svelte/AuthLoading.svelte +0 -13
  431. package/dist/svelte/AuthLoading.svelte.d.ts +0 -8
  432. package/dist/svelte/AuthLoading.svelte.d.ts.map +0 -1
  433. package/dist/svelte/SignedIn.svelte +0 -14
  434. package/dist/svelte/SignedIn.svelte.d.ts +0 -8
  435. package/dist/svelte/SignedIn.svelte.d.ts.map +0 -1
  436. package/dist/svelte/SignedOut.svelte +0 -13
  437. package/dist/svelte/SignedOut.svelte.d.ts +0 -8
  438. package/dist/svelte/SignedOut.svelte.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode } from "../shared/codes.js";
2
- import { createUnauthenticatedAuthContext, getAuthContextForUser, getSessionUserId } from "./context.js";
2
+ import { createUnauthenticatedAuthContext, getAuthContext, getAuthContextForUser } from "./context.js";
3
3
  import { logError } from "./log.js";
4
4
  import { verifyOAuthToken } from "./tokens.js";
5
5
  import { buildCorsHeaders, corsPreflightHandler, corsPreflightResponse, registerCorsPreflight, withCors } from "./cors.js";
@@ -98,9 +98,9 @@ async function getHttpOAuthContext(auth, ctx, request, resource, issuer) {
98
98
  async function resolveHttpAuthContext(auth, ctx, request, resource, issuer) {
99
99
  const oauthContext = await getHttpOAuthContext(auth, ctx, request, resource, issuer);
100
100
  if (oauthContext !== null) return oauthContext;
101
- const sessionUserId = await getSessionUserId(ctx);
102
- if (sessionUserId !== null) return {
103
- ...await getAuthContextForUser(auth, ctx, sessionUserId),
101
+ const sessionContext = await getAuthContext(auth, ctx);
102
+ if (sessionContext !== null) return {
103
+ ...sessionContext,
104
104
  source: "session",
105
105
  key: null,
106
106
  oauth: null
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","names":["parseCookies"],"sources":["../../src/server/http.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n HttpRouter,\n UserIdentity,\n httpActionGeneric,\n} from \"convex/server\";\nimport { ConvexError } from \"convex/values\";\nimport { parse as parseCookies } from \"cookie\";\n\nimport { ErrorCode } from \"../shared/codes\";\nimport {\n buildCorsHeaders,\n corsPreflightHandler,\n corsPreflightResponse,\n registerCorsPreflight,\n withCors,\n} from \"./cors\";\nimport type { AuthContext, OptionalAuthContext, UserDoc } from \"./auth\";\nimport type { ComponentCtx, ComponentReadCtx as HttpQueryCtx } from \"./component/context\";\nimport {\n createUnauthenticatedAuthContext,\n getAuthContextForUser,\n getSessionUserId,\n} from \"./context\";\nimport { logError } from \"./log\";\nimport { verifyOAuthToken } from \"./tokens\";\nimport type { CorsConfig, HttpKeyContext } from \"./types\";\nimport { extractBearerToken } from \"./utils/bearer\";\nimport type { WellKnownEndpoint, WellKnownResponse } from \"./wellknown\";\n\ntype HttpIdentityCtx = {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n };\n};\ntype HttpContextCtx = HttpIdentityCtx & HttpQueryCtx;\n\ntype HttpContextAuthLike = {\n user: {\n get: (ctx: HttpQueryCtx, args: { id: string }) => Promise<UserDoc | null>;\n };\n active: {\n get: (\n ctx: HttpQueryCtx,\n args: { userId: string },\n ) => Promise<{\n groupId: string;\n roleIds: string[];\n grants: string[];\n } | null>;\n };\n key: {\n verify: (\n ctx: ComponentCtx,\n args: { secret: string },\n ) => Promise<{\n userId: string;\n keyId: string;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n }>;\n };\n};\n\ntype HttpContextRuntimeOptions = {\n issuer?: () => string;\n};\n\n/**\n * Auth context returned by `auth.request.context(ctx, request)`.\n *\n * This resolves raw HTTP authentication in two steps:\n * 1. session auth from `ctx.auth.getUserIdentity()`\n * 2. API key auth from `Authorization: Bearer sk_*`\n *\n * The `source` field tells you which authentication path succeeded.\n * When `source === \"key\"`, the verified API key metadata is available on\n * `key`.\n *\n * @example\n * ```ts\n * const authContext = await auth.request.context(ctx, request);\n * if (authContext.source === \"key\") {\n * console.log(authContext.key.keyId);\n * }\n * ```\n */\nexport type HttpAuthContext =\n | (AuthContext & {\n /** The request authenticated through a browser or session token. */\n source: \"session\";\n /** No API key was used for this request. */\n key: null;\n oauth: null;\n })\n | (AuthContext & {\n /** The request authenticated through an API key. */\n source: \"key\";\n /** Verified API key metadata for the request. */\n key: HttpKeyContext[\"key\"];\n oauth: null;\n })\n | (AuthContext & {\n source: \"oauth\";\n key: null;\n oauth: { clientId: string; scopes: string[] };\n });\n\n/**\n * Nullable HTTP auth context returned by\n * `auth.request.context.optional(ctx, request)`.\n *\n * This preserves a stable auth-shaped object for raw `httpAction` handlers\n * that allow anonymous callers.\n */\nexport type OptionalHttpAuthContext =\n | (OptionalAuthContext & {\n /** No authentication source was resolved. */\n source: null;\n /** No API key metadata is available. */\n key: null;\n oauth: null;\n })\n | HttpAuthContext;\n\n/**\n * Configuration for {@link defineAuth().request.context}.\n *\n * This mirrors {@link AuthContextConfig} for raw HTTP handlers and adds support\n * for enriching mixed session/API-key auth results.\n *\n * @typeParam TResolve - Extra fields returned from `resolve()` and merged into\n * the resolved HTTP auth context.\n *\n * @example\n * ```ts\n * const authContext = await auth.request.context(ctx, request, {\n * resolve: async (_ctx, user, authState) => ({\n * email: user.email,\n * isMachineRequest: authState.source === \"key\",\n * }),\n * });\n * ```\n */\nexport type HttpAuthContextConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n> = {\n /**\n * Attach additional derived fields to the resolved HTTP auth context.\n *\n * This callback runs only when authentication succeeds.\n */\n resolve?: (ctx: TCtx, user: UserDoc, auth: HttpAuthContext) => Promise<TResolve> | TResolve;\n /**\n * Override or wrap HTTP auth resolution.\n *\n * Return `undefined` to use the built-in session-or-key resolver, `null` for\n * an explicit unauthenticated state, or a fully resolved\n * {@link HttpAuthContext}.\n */\n authResolve?: (\n ctx: TCtx,\n fallback: () => Promise<HttpAuthContext | null>,\n ) => Promise<HttpAuthContext | null | undefined> | HttpAuthContext | null | undefined;\n /**\n * RFC 8707 resource assertion (opt-in). When set, an OAuth `at+jwt` bearer is\n * only accepted on this route if its `resource` claim equals this value — so a\n * token minted for a different protected resource is not honored here. Omit it\n * (the default) to accept any valid OAuth identity; capability is still bounded\n * by scope grant-intersection regardless. The `/mcp` route sets this to its\n * canonical resource.\n */\n resource?: string;\n};\n\nfunction createNotSignedInError() {\n return new ConvexError({\n code: ErrorCode.NOT_SIGNED_IN,\n message: \"Authentication required.\",\n });\n}\n\n/** A `{ code, message }` error body for the non-OAuth JSON HTTP surface. */\nexport type JsonErrorBody = { code: string; message: string };\n\nfunction isJsonErrorBody(value: unknown): value is JsonErrorBody {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"code\" in value) || !(\"message\" in value)) return false;\n return typeof value.code === \"string\" && typeof value.message === \"string\";\n}\n\n/**\n * Extract a `{ code, message }` body from a thrown value for the non-OAuth JSON\n * HTTP surface. A structured `ConvexError` carrying `code`/`message` is surfaced\n * verbatim; anything else collapses to an opaque `INTERNAL_ERROR`.\n */\nexport function convexErrorToBody(error: unknown): JsonErrorBody {\n if (error instanceof ConvexError && isJsonErrorBody(error.data)) return error.data;\n return { code: ErrorCode.INTERNAL_ERROR, message: \"An unexpected error occurred.\" };\n}\n\n/**\n * Build a JSON error `Response` for the non-OAuth HTTP surface, in the single\n * `{ code, message }` shape. OAuth wire endpoints use the RFC 6749\n * `{ error, error_description }` shape instead (see `oauth/*`).\n */\nexport function jsonError(\n status: number,\n body: JsonErrorBody,\n extraHeaders?: Record<string, string>,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\", ...extraHeaders },\n });\n}\n\nasync function getHttpKeyContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n): Promise<HttpAuthContext | null> {\n const token = extractBearerToken(request);\n if (token === null || !token.startsWith(\"sk_\")) {\n return null;\n }\n\n try {\n /**\n * The `Bearer sk_*` branch only runs inside an HTTP action, where the\n * resolved `ctx` additionally carries the `runMutation` that `key.verify`\n * needs. The HTTP-context surface is otherwise query-shaped, so this one\n * narrow assertion supplies that single missing member at the boundary.\n */\n const keyVerifyCtx: ComponentCtx = ctx as HttpContextCtx & Pick<ComponentCtx, \"runMutation\">;\n const verified = await auth.key.verify(keyVerifyCtx, {\n secret: token,\n });\n const authContext = await getAuthContextForUser(auth, ctx, verified.userId);\n return {\n ...authContext,\n source: \"key\",\n key: {\n userId: verified.userId,\n keyId: verified.keyId,\n scopes: verified.scopes,\n },\n oauth: null,\n };\n } catch (err) {\n console.error(\"[auth] HTTP key verification failed\", { err });\n return null;\n }\n}\n\nasync function getHttpOAuthContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n resource: string | undefined,\n issuer: string | undefined,\n): Promise<HttpAuthContext | null> {\n const token = extractBearerToken(request);\n if (token === null || token.startsWith(\"sk_\")) return null;\n const tokenOptions: NonNullable<Parameters<typeof verifyOAuthToken>[1]> = {};\n if (issuer !== undefined) tokenOptions.issuer = issuer;\n if (resource !== undefined) tokenOptions.resource = resource;\n const oauthPayload = await verifyOAuthToken(token, tokenOptions);\n if (!oauthPayload) return null;\n try {\n const authContext = await getAuthContextForUser(\n auth,\n ctx,\n oauthPayload.userId,\n oauthPayload.scopes,\n );\n return {\n ...authContext,\n source: \"oauth\",\n key: null,\n oauth: { clientId: oauthPayload.clientId, scopes: oauthPayload.scopes },\n };\n } catch {\n return null;\n }\n}\n\nasync function resolveHttpAuthContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n resource: string | undefined,\n issuer: string | undefined,\n): Promise<HttpAuthContext | null> {\n const oauthContext = await getHttpOAuthContext(auth, ctx, request, resource, issuer);\n if (oauthContext !== null) return oauthContext;\n\n const sessionUserId = await getSessionUserId(ctx);\n if (sessionUserId !== null) {\n const authContext = await getAuthContextForUser(auth, ctx, sessionUserId);\n return {\n ...authContext,\n source: \"session\",\n key: null,\n oauth: null,\n };\n }\n\n return await getHttpKeyContext(auth, ctx, request);\n}\n\n/**\n * Resolver for `auth.request.context(ctx, request, config?)`.\n *\n * Authentication sources are tried in a fixed order: OAuth access tokens\n * first (an `at+jwt` bearer is also a valid Convex identity, so it must be\n * classified as `oauth` before the session branch would claim it), then the\n * session identity, then `Bearer sk_*` API keys. Session therefore takes\n * precedence over an API key when both are present.\n */\nexport interface HttpContextResolver {\n <\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n >(\n ctx: TCtx,\n request: Request,\n config?: HttpAuthContextConfig<TResolve, TCtx>,\n ): Promise<HttpAuthContext & TResolve>;\n}\n\n/**\n * Nullable variant for `auth.request.context.optional(ctx, request, config?)`.\n */\nexport interface OptionalHttpContextResolver {\n <\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n >(\n ctx: TCtx,\n request: Request,\n config?: HttpAuthContextConfig<TResolve, TCtx>,\n ): Promise<OptionalHttpAuthContext & TResolve>;\n}\n\nasync function resolveHttpContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n config: HttpAuthContextConfig<Record<string, unknown>> | undefined,\n optional: boolean,\n options: HttpContextRuntimeOptions,\n) {\n const fallback = () =>\n resolveHttpAuthContext(auth, ctx, request, config?.resource, options.issuer?.());\n const authOverride = config?.authResolve ? await config.authResolve(ctx, fallback) : undefined;\n const resolved = authOverride === undefined ? await fallback() : authOverride;\n\n if (resolved === null) {\n if (!optional) {\n throw createNotSignedInError();\n }\n return {\n ...createUnauthenticatedAuthContext(),\n source: null,\n key: null,\n oauth: null,\n };\n }\n\n const extra = config?.resolve ? await config.resolve(ctx, resolved.user, resolved) : {};\n\n return {\n ...resolved,\n ...extra,\n };\n}\n\n/**\n * @internal\n * Create the implementation behind `auth.request.context(...)` and\n * `auth.request.context.optional(...)`.\n *\n * The two assertions below bind a concrete (monomorphic) closure to the\n * generic call-signature interfaces {@link HttpContextResolver} /\n * {@link OptionalHttpContextResolver}; TypeScript cannot infer those generics\n * from a non-generic body, so the typed assertion is the irreducible bridge.\n */\nexport function createHttpContext(\n auth: HttpContextAuthLike,\n options: HttpContextRuntimeOptions = {},\n): HttpContextResolver & { optional: OptionalHttpContextResolver } {\n const required = ((\n ctx: HttpContextCtx,\n request: Request,\n config?: HttpAuthContextConfig<Record<string, unknown>>,\n ) => resolveHttpContext(auth, ctx, request, config, false, options)) as HttpContextResolver & {\n optional: OptionalHttpContextResolver;\n };\n\n required.optional = ((\n ctx: HttpContextCtx,\n request: Request,\n config?: HttpAuthContextConfig<Record<string, unknown>>,\n ) =>\n resolveHttpContext(auth, ctx, request, config, true, options)) as OptionalHttpContextResolver;\n\n return required;\n}\n\n/**\n * Build the Bearer-key HTTP action wrapper behind `auth.request.action(...)`.\n *\n * Returns a function that wraps a handler with `Authorization: Bearer sk_*`\n * verification, optional scope checks, CORS headers, and JSON error envelopes.\n *\n * @param auth - Provides `key.verify` for API key verification.\n * @param defaultOrigins - Allowed CORS origins (or a getter) used when a route\n * supplies no `cors` config.\n */\nexport function createHttpAction(\n auth: {\n key: {\n verify: (\n ctx: GenericActionCtx<GenericDataModel>,\n args: { secret: string },\n ) => Promise<{\n userId: string;\n keyId: string;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n }>;\n };\n },\n defaultOrigins: string[] | (() => string[]),\n) {\n return (\n handler: (\n ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,\n request: Request,\n ) => Promise<Response | Record<string, unknown>>,\n options?: {\n scope?: { resource: string; action: string };\n cors?: CorsConfig;\n },\n ) => {\n return httpActionGeneric(async (genericCtx, request) => {\n const corsHeaders = buildCorsHeaders(request, options?.cors, defaultOrigins);\n\n try {\n const rawKey = extractBearerToken(request);\n if (rawKey === null) {\n return jsonError(\n 401,\n {\n code: ErrorCode.MISSING_BEARER_TOKEN,\n message: \"Missing or malformed Authorization: Bearer header.\",\n },\n corsHeaders,\n );\n }\n\n let keyResult:\n | {\n ok: true;\n value: {\n userId: string;\n keyId: string;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n };\n }\n | { ok: false; error: unknown };\n try {\n const value = await auth.key.verify(genericCtx, { secret: rawKey });\n keyResult = { ok: true, value };\n } catch (error) {\n keyResult = { ok: false, error };\n }\n\n if (!keyResult.ok) {\n if (\n keyResult.error instanceof ConvexError &&\n typeof keyResult.error.data === \"object\" &&\n keyResult.error.data !== null &&\n \"code\" in keyResult.error.data &&\n \"message\" in keyResult.error.data\n ) {\n return jsonError(403, convexErrorToBody(keyResult.error), corsHeaders);\n }\n throw keyResult.error;\n }\n\n if (\n options?.scope &&\n !keyResult.value.scopes.can(options.scope.resource, options.scope.action)\n ) {\n return jsonError(\n 403,\n {\n code: ErrorCode.SCOPE_CHECK_FAILED,\n message: \"This API key does not have the required permissions.\",\n },\n corsHeaders,\n );\n }\n\n const enrichedCtx = Object.assign(genericCtx, {\n key: {\n userId: keyResult.value.userId,\n keyId: keyResult.value.keyId,\n scopes: keyResult.value.scopes,\n },\n });\n const result = await handler(enrichedCtx, request);\n\n return result instanceof Response\n ? (() => {\n const headers = new Headers(result.headers);\n for (const [k, val] of Object.entries(corsHeaders)) {\n if (!headers.has(k)) headers.set(k, val);\n }\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers,\n });\n })()\n : new Response(JSON.stringify(result), {\n status: 200,\n headers: {\n ...corsHeaders,\n \"Content-Type\": \"application/json\",\n },\n });\n } catch (error) {\n logError(error);\n return jsonError(\n 500,\n { code: ErrorCode.INTERNAL_ERROR, message: \"An unexpected error occurred.\" },\n corsHeaders,\n );\n }\n });\n };\n}\n\n/**\n * Build the route registrar behind `auth.request.route(...)`.\n *\n * Returns a function that registers a Bearer-authenticated route plus its\n * matching `OPTIONS` CORS preflight in one call.\n *\n * @param wrapAction - The wrapper produced by {@link createHttpAction}.\n * @param defaultOrigins - Allowed CORS origins (or a getter) for the preflight.\n */\nexport function createHttpRoute(\n wrapAction: ReturnType<typeof createHttpAction>,\n defaultOrigins: string[] | (() => string[]),\n) {\n return (\n http: { route: (config: unknown) => void },\n routeConfig: {\n path: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n handler: (\n ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,\n request: Request,\n ) => Promise<Response | Record<string, unknown>>;\n scope?: { resource: string; action: string };\n cors?: CorsConfig;\n },\n ) => {\n http.route({\n path: routeConfig.path,\n method: \"OPTIONS\",\n handler: httpActionGeneric(async (_ctx, request) =>\n corsPreflightResponse(request, routeConfig.cors, defaultOrigins),\n ),\n });\n\n http.route({\n path: routeConfig.path,\n method: routeConfig.method,\n handler: wrapAction(routeConfig.handler, {\n scope: routeConfig.scope,\n cors: routeConfig.cors,\n }),\n });\n };\n}\n\n/**\n * Wrap an HTTP action so thrown `ConvexError`s become JSON error responses.\n *\n * Structured `{ code, message }` errors return `errorStatusCode`; other\n * `ConvexError`s return that status with a plain status text; any other error\n * is logged and returns a 500.\n *\n * @param errorStatusCode - Status used for `ConvexError`s.\n * @param action - The wrapped HTTP action.\n */\nexport function convertErrorsToResponse(\n errorStatusCode: number,\n action: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>,\n) {\n return async (ctx: GenericActionCtx<GenericDataModel>, request: Request) => {\n try {\n return await action(ctx, request);\n } catch (error) {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n \"code\" in error.data &&\n \"message\" in error.data\n ) {\n return jsonError(errorStatusCode, convexErrorToBody(error));\n }\n if (error instanceof ConvexError) {\n return new Response(null, {\n status: errorStatusCode,\n statusText: typeof error.data === \"string\" ? error.data : \"Error\",\n });\n }\n logError(error);\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\",\n });\n }\n };\n}\n\n/** Parse the request's `Cookie` header into a name-to-value record. */\nexport function getCookies(request: Request): Record<string, string | undefined> {\n return parseCookies(request.headers.get(\"Cookie\") ?? \"\");\n}\n\n/** Parsed group connection runtime route: connection id, protocol, and remaining path segments. */\nexport type ConnectionRuntimeRoute = {\n pathname?: string;\n connectionId: string;\n protocol: \"oidc\" | \"saml\" | \"scim\";\n rest: string[];\n};\n\nfunction parseConnectionRuntimeRoute(\n pathname: string,\n routeBase: string,\n): ConnectionRuntimeRoute | null {\n const runtimePrefix = `${routeBase}/`;\n const runtimeParts = pathname.startsWith(runtimePrefix)\n ? pathname.slice(runtimePrefix.length).split(\"/\").filter(Boolean)\n : [];\n const [runtimeConnectionId, protocol, ...rest] = runtimeParts;\n if (\n runtimeConnectionId === undefined ||\n (protocol !== \"oidc\" && protocol !== \"saml\" && protocol !== \"scim\") ||\n rest.length === 0\n ) {\n return null;\n }\n return {\n pathname,\n connectionId: runtimeConnectionId,\n protocol,\n rest,\n };\n}\n\n/**\n * Register the OpenID discovery and JWKS endpoints on an HTTP router.\n *\n * Adds `<routeBase>/.well-known/openid-configuration` and\n * `<routeBase>/.well-known/jwks.json`. OAuth-specific discovery fields are\n * included only when `deps.oauth` is provided.\n *\n * Discovery is served at both the issuer prefix and the root, including the\n * RFC 8414 `oauth-authorization-server` path conventions, because clients\n * treat either the issuer (`/auth`) or the origin as the base. These exact\n * routes win over the static-hosting catch-all, so zero-config\n * `mcp login <url>` resolves to JSON rather than the SPA HTML fallback.\n */\nexport function addOpenIdRoutes(\n http: HttpRouter,\n deps: {\n getIssuer: () => string;\n getJwks: () => string;\n routeBase?: string;\n oauth?: {\n scopes?: string[];\n };\n },\n) {\n const cacheControl = \"public, max-age=15, stale-while-revalidate=15, stale-if-error=86400\";\n const routeBase = deps.routeBase ?? \"\";\n\n const buildMetadata = () => {\n const issuer = deps.getIssuer();\n const body: Record<string, unknown> = {\n issuer,\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n if (deps.oauth) {\n body.authorization_endpoint = `${issuer}/oauth2/authorize`;\n body.token_endpoint = `${issuer}/oauth2/token`;\n body.registration_endpoint = `${issuer}/oauth2/register`;\n body.response_types_supported = [\"code\"];\n body.grant_types_supported = [\"authorization_code\", \"refresh_token\", \"client_credentials\"];\n body.code_challenge_methods_supported = [\"S256\"];\n body.token_endpoint_auth_methods_supported = [\n \"client_secret_post\",\n \"client_secret_basic\",\n \"none\",\n ];\n body.scopes_supported = deps.oauth.scopes ?? [\"openid\", \"profile\", \"email\", \"offline_access\"];\n }\n return body;\n };\n\n const metadataHandler = httpActionGeneric(\n withCors(async () => {\n return new Response(JSON.stringify(buildMetadata()), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": cacheControl },\n });\n }),\n );\n\n const jwksHandler = httpActionGeneric(\n withCors(async () => {\n return new Response(deps.getJwks(), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": cacheControl },\n });\n }),\n );\n\n const oidcPaths = new Set<string>([\n `${routeBase}/.well-known/openid-configuration`,\n \"/.well-known/openid-configuration\",\n ]);\n const asPaths = deps.oauth\n ? new Set<string>([\n `/.well-known/oauth-authorization-server${routeBase}`,\n `${routeBase}/.well-known/oauth-authorization-server`,\n \"/.well-known/oauth-authorization-server\",\n ])\n : new Set<string>();\n const jwksPaths = new Set<string>([\n `${routeBase}/.well-known/jwks.json`,\n \"/.well-known/jwks.json\",\n ]);\n\n for (const path of [...oidcPaths, ...asPaths]) {\n http.route({ path, method: \"GET\", handler: metadataHandler });\n }\n for (const path of jwksPaths) {\n http.route({ path, method: \"GET\", handler: jwksHandler });\n }\n registerCorsPreflight(http, [...oidcPaths, ...asPaths, ...jwksPaths]);\n}\n\n/** Register root `/.well-known/*` app discovery routes on an HTTP router. */\nexport function addWellKnownRoutes(\n http: HttpRouter,\n deps: {\n getResponse: (endpoint: WellKnownEndpoint) => WellKnownResponse | null;\n },\n) {\n const routes: Array<{ endpoint: WellKnownEndpoint; path: string }> = [\n {\n endpoint: \"apple-app-site-association\",\n path: \"/.well-known/apple-app-site-association\",\n },\n { endpoint: \"assetlinks.json\", path: \"/.well-known/assetlinks.json\" },\n { endpoint: \"webauthn\", path: \"/.well-known/webauthn\" },\n { endpoint: \"change-password\", path: \"/.well-known/change-password\" },\n { endpoint: \"security.txt\", path: \"/.well-known/security.txt\" },\n ];\n\n for (const route of routes) {\n http.route({\n path: route.path,\n method: \"GET\",\n handler: httpActionGeneric(async () => {\n const result = deps.getResponse(route.endpoint);\n if (result === null) {\n return new Response(null, { status: 404 });\n }\n return new Response(result.body, {\n status: result.status,\n headers: result.headers,\n });\n }),\n });\n }\n}\n\n/**\n * Register the OAuth sign-in and callback routes on an HTTP router.\n *\n * Adds `GET <routeBase>/signin/*` and `GET`/`POST <routeBase>/callback/*`.\n */\nexport function addAuthRoutes(\n http: HttpRouter,\n deps: {\n handleSignIn: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n handleCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n routeBase?: string;\n },\n) {\n const routeBase = deps.routeBase ?? \"/api/auth\";\n const routePrefix = routeBase === \"\" ? \"\" : routeBase;\n http.route({\n pathPrefix: `${routePrefix}/signin/`,\n method: \"GET\",\n handler: httpActionGeneric(deps.handleSignIn),\n });\n\n const callbackHandler = httpActionGeneric(deps.handleCallback);\n\n http.route({\n pathPrefix: `${routePrefix}/callback/`,\n method: \"GET\",\n handler: callbackHandler,\n });\n\n http.route({\n pathPrefix: `${routePrefix}/callback/`,\n method: \"POST\",\n handler: callbackHandler,\n });\n}\n\n/**\n * Register the OAuth provider authorize, token, registration, and RFC 7592\n * client-management endpoints on an HTTP router.\n *\n * Adds `GET <base>/oauth2/authorize`, `POST <base>/oauth2/token`,\n * `POST <base>/oauth2/register` (DCR), and `GET`/`PUT`/`DELETE`\n * `<base>/oauth2/register/<client_id>` (management). The exact `register` path\n * serves DCR; the `register/` prefix serves per-client management.\n */\nexport function addOAuthProviderRoutes(\n http: HttpRouter,\n deps: {\n handleAuthorize: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleToken: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n handleRegister: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleManage: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n routeBase?: string;\n },\n) {\n const base = deps.routeBase ?? \"\";\n const authorizePath = `${base}/oauth2/authorize`;\n const tokenPath = `${base}/oauth2/token`;\n const registerPath = `${base}/oauth2/register`;\n const managePrefix = `${registerPath}/`;\n\n http.route({\n path: authorizePath,\n method: \"GET\",\n handler: httpActionGeneric(withCors(deps.handleAuthorize)),\n });\n http.route({\n path: tokenPath,\n method: \"POST\",\n handler: httpActionGeneric(withCors(deps.handleToken)),\n });\n http.route({\n path: registerPath,\n method: \"POST\",\n handler: httpActionGeneric(withCors(deps.handleRegister)),\n });\n for (const method of [\"GET\", \"PUT\", \"DELETE\"] as const) {\n http.route({\n pathPrefix: managePrefix,\n method,\n handler: httpActionGeneric(withCors(deps.handleManage)),\n });\n }\n http.route({ pathPrefix: managePrefix, method: \"OPTIONS\", handler: corsPreflightHandler });\n registerCorsPreflight(http, [authorizePath, tokenPath, registerPath]);\n}\n\n/**\n * Register the group connection runtime routes (SAML, OIDC, SCIM) on a router.\n *\n * Mounts method-specific handlers under `<routeBase>/<connectionId>/<protocol>/...`\n * (plus an optional shared OIDC callback path), dispatching by\n * `${protocol}:${endpoint}` and returning SCIM errors for unmatched\n * `PATCH`/`DELETE` requests.\n */\nexport function addConnectionRoutes(\n http: HttpRouter,\n deps: {\n routeBase: string;\n sharedOidcCallbackPath?: string;\n convertErrorsToResponse: typeof convertErrorsToResponse;\n handleSamlMetadata: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleSamlSignIn: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcSignIn: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcSharedCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleSamlAcs: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleSamlSlo: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleScimRequest: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n scimError: (status: number, scimType: string, detail: string) => Response;\n },\n) {\n const routePrefix = `${deps.routeBase}/`;\n const sharedOidcCallbackPath = deps.sharedOidcCallbackPath\n ? (() => {\n if (/^https?:\\/\\//.test(deps.sharedOidcCallbackPath)) {\n return new URL(deps.sharedOidcCallbackPath).pathname;\n }\n return deps.sharedOidcCallbackPath.startsWith(\"/\")\n ? deps.sharedOidcCallbackPath\n : `/${deps.sharedOidcCallbackPath}`;\n })()\n : undefined;\n\n if (sharedOidcCallbackPath) {\n http.route({\n path: sharedOidcCallbackPath,\n method: \"GET\",\n handler: httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) =>\n deps.handleOidcSharedCallback(ctx, request),\n ),\n ),\n });\n\n http.route({\n path: sharedOidcCallbackPath,\n method: \"POST\",\n handler: httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) =>\n deps.handleOidcSharedCallback(ctx, request),\n ),\n ),\n });\n }\n\n type ConnRouteHandler = (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n const scimRoute: ConnRouteHandler = (ctx, request) => deps.handleScimRequest(ctx, request);\n\n const matchConnectionRoute = (\n route: ConnectionRuntimeRoute | null,\n handlers: Record<string, ConnRouteHandler>,\n ): ConnRouteHandler | null => {\n const endpoint = route?.rest[0];\n if (!route || endpoint === undefined) return null;\n const key =\n route.protocol === \"scim\"\n ? endpoint === \"v2\"\n ? \"scim:v2\"\n : null\n : route.rest.length === 1\n ? `${route.protocol}:${endpoint}`\n : null;\n return key ? (handlers[key] ?? null) : null;\n };\n\n const connectionRouteHandler = (handlers: Record<string, ConnRouteHandler>) =>\n httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) => {\n const route = parseConnectionRuntimeRoute(new URL(request.url).pathname, deps.routeBase);\n const handler = matchConnectionRoute(route, handlers);\n if (!handler || !route) {\n throw new ConvexError({\n code: ErrorCode.INVALID_PARAMETERS,\n message: \"Invalid connection runtime path.\",\n });\n }\n return await handler(ctx, request, route);\n }),\n );\n\n http.route({\n pathPrefix: routePrefix,\n method: \"GET\",\n handler: connectionRouteHandler({\n \"saml:metadata\": deps.handleSamlMetadata,\n \"saml:signin\": deps.handleSamlSignIn,\n \"saml:acs\": deps.handleSamlAcs,\n \"saml:slo\": deps.handleSamlSlo,\n \"oidc:signin\": deps.handleOidcSignIn,\n \"oidc:callback\": deps.handleOidcCallback,\n \"scim:v2\": scimRoute,\n }),\n });\n\n http.route({\n pathPrefix: routePrefix,\n method: \"POST\",\n handler: connectionRouteHandler({\n \"saml:acs\": deps.handleSamlAcs,\n \"saml:slo\": deps.handleSamlSlo,\n \"oidc:callback\": deps.handleOidcCallback,\n \"scim:v2\": scimRoute,\n }),\n });\n\n http.route({\n pathPrefix: routePrefix,\n method: \"PUT\",\n handler: connectionRouteHandler({\n \"scim:v2\": scimRoute,\n }),\n });\n\n for (const method of [\"PATCH\", \"DELETE\"] as const) {\n http.route({\n pathPrefix: routePrefix,\n method,\n handler: httpActionGeneric(async (ctx, request) => {\n const route = parseConnectionRuntimeRoute(new URL(request.url).pathname, deps.routeBase);\n if (!route || route.protocol !== \"scim\" || route.rest[0] !== \"v2\") {\n return deps.scimError(404, \"notFound\", \"SCIM resource not found.\");\n }\n return await deps.handleScimRequest(ctx, request);\n }),\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http.js","names":["parseCookies"],"sources":["../../src/server/http.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n HttpRouter,\n UserIdentity,\n httpActionGeneric,\n} from \"convex/server\";\nimport { ConvexError, type GenericId } from \"convex/values\";\nimport { parse as parseCookies } from \"cookie\";\n\nimport { ErrorCode } from \"../shared/codes\";\nimport {\n buildCorsHeaders,\n corsPreflightHandler,\n corsPreflightResponse,\n registerCorsPreflight,\n withCors,\n} from \"./cors\";\nimport type { AuthContext, OptionalAuthContext } from \"./auth\";\nimport type { UserDoc } from \"./context\";\nimport type { ComponentCtx, ComponentReadCtx as HttpQueryCtx } from \"./component/context\";\nimport {\n createUnauthenticatedAuthContext,\n getAuthContext,\n getAuthContextForUser,\n type AuthLike,\n} from \"./context\";\nimport { logError } from \"./log\";\nimport { verifyOAuthToken } from \"./tokens\";\nimport type { CorsConfig, HttpKeyContext } from \"./types\";\nimport { extractBearerToken } from \"./utils/bearer\";\nimport type { WellKnownEndpoint, WellKnownResponse } from \"./wellknown\";\n\ntype HttpIdentityCtx = {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n };\n};\ntype HttpContextCtx = HttpIdentityCtx & HttpQueryCtx;\n\ntype HttpContextAuthLike = AuthLike & {\n key: {\n verify: (\n ctx: ComponentCtx,\n args: { secret: string },\n ) => Promise<{\n userId: GenericId<\"User\">;\n keyId: GenericId<\"ApiKey\">;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n }>;\n };\n};\n\ntype HttpContextRuntimeOptions = {\n issuer?: () => string;\n};\n\n/**\n * Auth context returned by `auth.request.context(ctx, request)`.\n *\n * This resolves raw HTTP authentication in two steps:\n * 1. session auth from `ctx.auth.getUserIdentity()`\n * 2. API key auth from `Authorization: Bearer sk_*`\n *\n * The `source` field tells you which authentication path succeeded.\n * When `source === \"key\"`, the verified API key metadata is available on\n * `key`.\n *\n * @example\n * ```ts\n * const authContext = await auth.request.context(ctx, request);\n * if (authContext.source === \"key\") {\n * console.log(authContext.key.keyId);\n * }\n * ```\n */\nexport type HttpAuthContext =\n | (AuthContext & {\n /** The request authenticated through a browser or session token. */\n source: \"session\";\n /** No API key was used for this request. */\n key: null;\n oauth: null;\n })\n | (AuthContext & {\n /** The request authenticated through an API key. */\n source: \"key\";\n /** Verified API key metadata for the request. */\n key: HttpKeyContext[\"key\"];\n oauth: null;\n })\n | (AuthContext & {\n source: \"oauth\";\n key: null;\n oauth: { clientId: string; scopes: string[] };\n });\n\n/**\n * Nullable HTTP auth context returned by\n * `auth.request.context.optional(ctx, request)`.\n *\n * This preserves a stable auth-shaped object for raw `httpAction` handlers\n * that allow anonymous callers.\n */\nexport type OptionalHttpAuthContext =\n | (OptionalAuthContext & {\n /** No authentication source was resolved. */\n source: null;\n /** No API key metadata is available. */\n key: null;\n oauth: null;\n })\n | HttpAuthContext;\n\n/**\n * Configuration for {@link defineAuth().request.context}.\n *\n * This mirrors {@link AuthContextConfig} for raw HTTP handlers and adds support\n * for enriching mixed session/API-key auth results.\n *\n * @typeParam TResolve - Extra fields returned from `resolve()` and merged into\n * the resolved HTTP auth context.\n *\n * @example\n * ```ts\n * const authContext = await auth.request.context(ctx, request, {\n * resolve: async (_ctx, user, authState) => ({\n * email: user.email,\n * isMachineRequest: authState.source === \"key\",\n * }),\n * });\n * ```\n */\nexport type HttpAuthContextConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n> = {\n /**\n * Attach additional derived fields to the resolved HTTP auth context.\n *\n * This callback runs only when authentication succeeds.\n */\n resolve?: (ctx: TCtx, user: UserDoc, auth: HttpAuthContext) => Promise<TResolve> | TResolve;\n /**\n * Override or wrap HTTP auth resolution.\n *\n * Return `undefined` to use the built-in session-or-key resolver, `null` for\n * an explicit unauthenticated state, or a fully resolved\n * {@link HttpAuthContext}.\n */\n authResolve?: (\n ctx: TCtx,\n fallback: () => Promise<HttpAuthContext | null>,\n ) => Promise<HttpAuthContext | null | undefined> | HttpAuthContext | null | undefined;\n /**\n * RFC 8707 resource assertion (opt-in). When set, an OAuth `at+jwt` bearer is\n * only accepted on this route if its `resource` claim equals this value — so a\n * token minted for a different protected resource is not honored here. Omit it\n * (the default) to accept any valid OAuth identity; capability is still bounded\n * by scope grant-intersection regardless. The `/mcp` route sets this to its\n * canonical resource.\n */\n resource?: string;\n};\n\nfunction createNotSignedInError() {\n return new ConvexError({\n code: ErrorCode.NOT_SIGNED_IN,\n message: \"Authentication required.\",\n });\n}\n\n/** A `{ code, message }` error body for the non-OAuth JSON HTTP surface. */\nexport type JsonErrorBody = { code: string; message: string };\n\nfunction isJsonErrorBody(value: unknown): value is JsonErrorBody {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"code\" in value) || !(\"message\" in value)) return false;\n return typeof value.code === \"string\" && typeof value.message === \"string\";\n}\n\n/**\n * Extract a `{ code, message }` body from a thrown value for the non-OAuth JSON\n * HTTP surface. A structured `ConvexError` carrying `code`/`message` is surfaced\n * verbatim; anything else collapses to an opaque `INTERNAL_ERROR`.\n */\nexport function convexErrorToBody(error: unknown): JsonErrorBody {\n if (error instanceof ConvexError && isJsonErrorBody(error.data)) return error.data;\n return { code: ErrorCode.INTERNAL_ERROR, message: \"An unexpected error occurred.\" };\n}\n\n/**\n * Build a JSON error `Response` for the non-OAuth HTTP surface, in the single\n * `{ code, message }` shape. OAuth wire endpoints use the RFC 6749\n * `{ error, error_description }` shape instead (see `oauth/*`).\n */\nexport function jsonError(\n status: number,\n body: JsonErrorBody,\n extraHeaders?: Record<string, string>,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\", ...extraHeaders },\n });\n}\n\nasync function getHttpKeyContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n): Promise<HttpAuthContext | null> {\n const token = extractBearerToken(request);\n if (token === null || !token.startsWith(\"sk_\")) {\n return null;\n }\n\n try {\n /**\n * The `Bearer sk_*` branch only runs inside an HTTP action, where the\n * resolved `ctx` additionally carries the `runMutation` that `key.verify`\n * needs. The HTTP-context surface is otherwise query-shaped, so this one\n * narrow assertion supplies that single missing member at the boundary.\n */\n const keyVerifyCtx: ComponentCtx = ctx as HttpContextCtx & Pick<ComponentCtx, \"runMutation\">;\n const verified = await auth.key.verify(keyVerifyCtx, {\n secret: token,\n });\n const authContext = await getAuthContextForUser(auth, ctx, verified.userId);\n return {\n ...authContext,\n source: \"key\",\n key: {\n userId: verified.userId,\n keyId: verified.keyId,\n scopes: verified.scopes,\n },\n oauth: null,\n };\n } catch (err) {\n console.error(\"[auth] HTTP key verification failed\", { err });\n return null;\n }\n}\n\nasync function getHttpOAuthContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n resource: string | undefined,\n issuer: string | undefined,\n): Promise<HttpAuthContext | null> {\n const token = extractBearerToken(request);\n if (token === null || token.startsWith(\"sk_\")) return null;\n const tokenOptions: NonNullable<Parameters<typeof verifyOAuthToken>[1]> = {};\n if (issuer !== undefined) tokenOptions.issuer = issuer;\n if (resource !== undefined) tokenOptions.resource = resource;\n const oauthPayload = await verifyOAuthToken(token, tokenOptions);\n if (!oauthPayload) return null;\n try {\n const authContext = await getAuthContextForUser(\n auth,\n ctx,\n oauthPayload.userId,\n oauthPayload.scopes,\n );\n return {\n ...authContext,\n source: \"oauth\",\n key: null,\n oauth: { clientId: oauthPayload.clientId, scopes: oauthPayload.scopes },\n };\n } catch {\n return null;\n }\n}\n\nasync function resolveHttpAuthContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n resource: string | undefined,\n issuer: string | undefined,\n): Promise<HttpAuthContext | null> {\n const oauthContext = await getHttpOAuthContext(auth, ctx, request, resource, issuer);\n if (oauthContext !== null) return oauthContext;\n\n const sessionContext = await getAuthContext(auth, ctx);\n if (sessionContext !== null) {\n return {\n ...sessionContext,\n source: \"session\",\n key: null,\n oauth: null,\n };\n }\n\n return await getHttpKeyContext(auth, ctx, request);\n}\n\n/**\n * Resolver for `auth.request.context(ctx, request, config?)`.\n *\n * Authentication sources are tried in a fixed order: OAuth access tokens\n * first (an `at+jwt` bearer is also a valid Convex identity, so it must be\n * classified as `oauth` before the session branch would claim it), then the\n * session identity, then `Bearer sk_*` API keys. Session therefore takes\n * precedence over an API key when both are present.\n */\nexport interface HttpContextResolver {\n <\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n >(\n ctx: TCtx,\n request: Request,\n config?: HttpAuthContextConfig<TResolve, TCtx>,\n ): Promise<HttpAuthContext & TResolve>;\n}\n\n/**\n * Nullable variant for `auth.request.context.optional(ctx, request, config?)`.\n */\nexport interface OptionalHttpContextResolver {\n <\n TResolve extends Record<string, unknown> = Record<string, never>,\n TCtx extends HttpContextCtx = HttpContextCtx,\n >(\n ctx: TCtx,\n request: Request,\n config?: HttpAuthContextConfig<TResolve, TCtx>,\n ): Promise<OptionalHttpAuthContext & TResolve>;\n}\n\nasync function resolveHttpContext(\n auth: HttpContextAuthLike,\n ctx: HttpContextCtx,\n request: Request,\n config: HttpAuthContextConfig<Record<string, unknown>> | undefined,\n optional: boolean,\n options: HttpContextRuntimeOptions,\n) {\n const fallback = () =>\n resolveHttpAuthContext(auth, ctx, request, config?.resource, options.issuer?.());\n const authOverride = config?.authResolve ? await config.authResolve(ctx, fallback) : undefined;\n const resolved = authOverride === undefined ? await fallback() : authOverride;\n\n if (resolved === null) {\n if (!optional) {\n throw createNotSignedInError();\n }\n return {\n ...createUnauthenticatedAuthContext(),\n source: null,\n key: null,\n oauth: null,\n };\n }\n\n const extra = config?.resolve ? await config.resolve(ctx, resolved.user, resolved) : {};\n\n return {\n ...resolved,\n ...extra,\n };\n}\n\n/**\n * @internal\n * Create the implementation behind `auth.request.context(...)` and\n * `auth.request.context.optional(...)`.\n *\n * The two assertions below bind a concrete (monomorphic) closure to the\n * generic call-signature interfaces {@link HttpContextResolver} /\n * {@link OptionalHttpContextResolver}; TypeScript cannot infer those generics\n * from a non-generic body, so the typed assertion is the irreducible bridge.\n */\nexport function createHttpContext(\n auth: HttpContextAuthLike,\n options: HttpContextRuntimeOptions = {},\n): HttpContextResolver & { optional: OptionalHttpContextResolver } {\n const required = ((\n ctx: HttpContextCtx,\n request: Request,\n config?: HttpAuthContextConfig<Record<string, unknown>>,\n ) => resolveHttpContext(auth, ctx, request, config, false, options)) as HttpContextResolver & {\n optional: OptionalHttpContextResolver;\n };\n\n required.optional = ((\n ctx: HttpContextCtx,\n request: Request,\n config?: HttpAuthContextConfig<Record<string, unknown>>,\n ) =>\n resolveHttpContext(auth, ctx, request, config, true, options)) as OptionalHttpContextResolver;\n\n return required;\n}\n\n/**\n * Build the Bearer-key HTTP action wrapper behind `auth.request.action(...)`.\n *\n * Returns a function that wraps a handler with `Authorization: Bearer sk_*`\n * verification, optional scope checks, CORS headers, and JSON error envelopes.\n *\n * @param auth - Provides `key.verify` for API key verification.\n * @param defaultOrigins - Allowed CORS origins (or a getter) used when a route\n * supplies no `cors` config.\n */\nexport function createHttpAction(\n auth: {\n key: {\n verify: (\n ctx: GenericActionCtx<GenericDataModel>,\n args: { secret: string },\n ) => Promise<{\n userId: GenericId<\"User\">;\n keyId: GenericId<\"ApiKey\">;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n }>;\n };\n },\n defaultOrigins: string[] | (() => string[]),\n) {\n return (\n handler: (\n ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,\n request: Request,\n ) => Promise<Response | Record<string, unknown>>,\n options?: {\n scope?: { resource: string; action: string };\n cors?: CorsConfig;\n },\n ) => {\n return httpActionGeneric(async (genericCtx, request) => {\n const corsHeaders = buildCorsHeaders(request, options?.cors, defaultOrigins);\n\n try {\n const rawKey = extractBearerToken(request);\n if (rawKey === null) {\n return jsonError(\n 401,\n {\n code: ErrorCode.MISSING_BEARER_TOKEN,\n message: \"Missing or malformed Authorization: Bearer header.\",\n },\n corsHeaders,\n );\n }\n\n let keyResult:\n | {\n ok: true;\n value: {\n userId: GenericId<\"User\">;\n keyId: GenericId<\"ApiKey\">;\n scopes: HttpKeyContext[\"key\"][\"scopes\"];\n };\n }\n | { ok: false; error: unknown };\n try {\n const value = await auth.key.verify(genericCtx, { secret: rawKey });\n keyResult = { ok: true, value };\n } catch (error) {\n keyResult = { ok: false, error };\n }\n\n if (!keyResult.ok) {\n if (\n keyResult.error instanceof ConvexError &&\n typeof keyResult.error.data === \"object\" &&\n keyResult.error.data !== null &&\n \"code\" in keyResult.error.data &&\n \"message\" in keyResult.error.data\n ) {\n return jsonError(403, convexErrorToBody(keyResult.error), corsHeaders);\n }\n throw keyResult.error;\n }\n\n if (\n options?.scope &&\n !keyResult.value.scopes.can(options.scope.resource, options.scope.action)\n ) {\n return jsonError(\n 403,\n {\n code: ErrorCode.SCOPE_CHECK_FAILED,\n message: \"This API key does not have the required permissions.\",\n },\n corsHeaders,\n );\n }\n\n const enrichedCtx = Object.assign(genericCtx, {\n key: {\n userId: keyResult.value.userId,\n keyId: keyResult.value.keyId,\n scopes: keyResult.value.scopes,\n },\n });\n const result = await handler(enrichedCtx, request);\n\n return result instanceof Response\n ? (() => {\n const headers = new Headers(result.headers);\n for (const [k, val] of Object.entries(corsHeaders)) {\n if (!headers.has(k)) headers.set(k, val);\n }\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers,\n });\n })()\n : new Response(JSON.stringify(result), {\n status: 200,\n headers: {\n ...corsHeaders,\n \"Content-Type\": \"application/json\",\n },\n });\n } catch (error) {\n logError(error);\n return jsonError(\n 500,\n { code: ErrorCode.INTERNAL_ERROR, message: \"An unexpected error occurred.\" },\n corsHeaders,\n );\n }\n });\n };\n}\n\n/**\n * Build the route registrar behind `auth.request.route(...)`.\n *\n * Returns a function that registers a Bearer-authenticated route plus its\n * matching `OPTIONS` CORS preflight in one call.\n *\n * @param wrapAction - The wrapper produced by {@link createHttpAction}.\n * @param defaultOrigins - Allowed CORS origins (or a getter) for the preflight.\n */\nexport function createHttpRoute(\n wrapAction: ReturnType<typeof createHttpAction>,\n defaultOrigins: string[] | (() => string[]),\n) {\n return (\n http: { route: (config: unknown) => void },\n routeConfig: {\n path: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n handler: (\n ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,\n request: Request,\n ) => Promise<Response | Record<string, unknown>>;\n scope?: { resource: string; action: string };\n cors?: CorsConfig;\n },\n ) => {\n http.route({\n path: routeConfig.path,\n method: \"OPTIONS\",\n handler: httpActionGeneric(async (_ctx, request) =>\n corsPreflightResponse(request, routeConfig.cors, defaultOrigins),\n ),\n });\n\n http.route({\n path: routeConfig.path,\n method: routeConfig.method,\n handler: wrapAction(routeConfig.handler, {\n scope: routeConfig.scope,\n cors: routeConfig.cors,\n }),\n });\n };\n}\n\n/**\n * Wrap an HTTP action so thrown `ConvexError`s become JSON error responses.\n *\n * Structured `{ code, message }` errors return `errorStatusCode`; other\n * `ConvexError`s return that status with a plain status text; any other error\n * is logged and returns a 500.\n *\n * @param errorStatusCode - Status used for `ConvexError`s.\n * @param action - The wrapped HTTP action.\n */\nexport function convertErrorsToResponse(\n errorStatusCode: number,\n action: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>,\n) {\n return async (ctx: GenericActionCtx<GenericDataModel>, request: Request) => {\n try {\n return await action(ctx, request);\n } catch (error) {\n if (\n error instanceof ConvexError &&\n typeof error.data === \"object\" &&\n error.data !== null &&\n \"code\" in error.data &&\n \"message\" in error.data\n ) {\n return jsonError(errorStatusCode, convexErrorToBody(error));\n }\n if (error instanceof ConvexError) {\n return new Response(null, {\n status: errorStatusCode,\n statusText: typeof error.data === \"string\" ? error.data : \"Error\",\n });\n }\n logError(error);\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\",\n });\n }\n };\n}\n\n/** Parse the request's `Cookie` header into a name-to-value record. */\nexport function getCookies(request: Request): Record<string, string | undefined> {\n return parseCookies(request.headers.get(\"Cookie\") ?? \"\");\n}\n\n/** Parsed group connection runtime route: connection id, protocol, and remaining path segments. */\nexport type ConnectionRuntimeRoute = {\n pathname?: string;\n connectionId: string;\n protocol: \"oidc\" | \"saml\" | \"scim\";\n rest: string[];\n};\n\nfunction parseConnectionRuntimeRoute(\n pathname: string,\n routeBase: string,\n): ConnectionRuntimeRoute | null {\n const runtimePrefix = `${routeBase}/`;\n const runtimeParts = pathname.startsWith(runtimePrefix)\n ? pathname.slice(runtimePrefix.length).split(\"/\").filter(Boolean)\n : [];\n const [runtimeConnectionId, protocol, ...rest] = runtimeParts;\n if (\n runtimeConnectionId === undefined ||\n (protocol !== \"oidc\" && protocol !== \"saml\" && protocol !== \"scim\") ||\n rest.length === 0\n ) {\n return null;\n }\n return {\n pathname,\n connectionId: runtimeConnectionId,\n protocol,\n rest,\n };\n}\n\n/**\n * Register the OpenID discovery and JWKS endpoints on an HTTP router.\n *\n * Adds `<routeBase>/.well-known/openid-configuration` and\n * `<routeBase>/.well-known/jwks.json`. OAuth-specific discovery fields are\n * included only when `deps.oauth` is provided.\n *\n * Discovery is served at both the issuer prefix and the root, including the\n * RFC 8414 `oauth-authorization-server` path conventions, because clients\n * treat either the issuer (`/auth`) or the origin as the base. These exact\n * routes win over the static-hosting catch-all, so zero-config\n * `mcp login <url>` resolves to JSON rather than the SPA HTML fallback.\n */\nexport function addOpenIdRoutes(\n http: HttpRouter,\n deps: {\n getIssuer: () => string;\n getJwks: () => string;\n routeBase?: string;\n oauth?: {\n scopes?: string[];\n };\n },\n) {\n const cacheControl = \"public, max-age=15, stale-while-revalidate=15, stale-if-error=86400\";\n const routeBase = deps.routeBase ?? \"\";\n\n const buildMetadata = () => {\n const issuer = deps.getIssuer();\n const body: Record<string, unknown> = {\n issuer,\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n if (deps.oauth) {\n body.authorization_endpoint = `${issuer}/oauth2/authorize`;\n body.token_endpoint = `${issuer}/oauth2/token`;\n body.registration_endpoint = `${issuer}/oauth2/register`;\n body.response_types_supported = [\"code\"];\n body.grant_types_supported = [\"authorization_code\", \"refresh_token\", \"client_credentials\"];\n body.code_challenge_methods_supported = [\"S256\"];\n body.token_endpoint_auth_methods_supported = [\n \"client_secret_post\",\n \"client_secret_basic\",\n \"none\",\n ];\n body.scopes_supported = deps.oauth.scopes ?? [\"openid\", \"profile\", \"email\", \"offline_access\"];\n }\n return body;\n };\n\n const metadataHandler = httpActionGeneric(\n withCors(async () => {\n return new Response(JSON.stringify(buildMetadata()), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": cacheControl },\n });\n }),\n );\n\n const jwksHandler = httpActionGeneric(\n withCors(async () => {\n return new Response(deps.getJwks(), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": cacheControl },\n });\n }),\n );\n\n const oidcPaths = new Set<string>([\n `${routeBase}/.well-known/openid-configuration`,\n \"/.well-known/openid-configuration\",\n ]);\n const asPaths = deps.oauth\n ? new Set<string>([\n `/.well-known/oauth-authorization-server${routeBase}`,\n `${routeBase}/.well-known/oauth-authorization-server`,\n \"/.well-known/oauth-authorization-server\",\n ])\n : new Set<string>();\n const jwksPaths = new Set<string>([\n `${routeBase}/.well-known/jwks.json`,\n \"/.well-known/jwks.json\",\n ]);\n\n for (const path of [...oidcPaths, ...asPaths]) {\n http.route({ path, method: \"GET\", handler: metadataHandler });\n }\n for (const path of jwksPaths) {\n http.route({ path, method: \"GET\", handler: jwksHandler });\n }\n registerCorsPreflight(http, [...oidcPaths, ...asPaths, ...jwksPaths]);\n}\n\n/** Register root `/.well-known/*` app discovery routes on an HTTP router. */\nexport function addWellKnownRoutes(\n http: HttpRouter,\n deps: {\n getResponse: (endpoint: WellKnownEndpoint) => WellKnownResponse | null;\n },\n) {\n const routes: Array<{ endpoint: WellKnownEndpoint; path: string }> = [\n {\n endpoint: \"apple-app-site-association\",\n path: \"/.well-known/apple-app-site-association\",\n },\n { endpoint: \"assetlinks.json\", path: \"/.well-known/assetlinks.json\" },\n { endpoint: \"webauthn\", path: \"/.well-known/webauthn\" },\n { endpoint: \"change-password\", path: \"/.well-known/change-password\" },\n { endpoint: \"security.txt\", path: \"/.well-known/security.txt\" },\n ];\n\n for (const route of routes) {\n http.route({\n path: route.path,\n method: \"GET\",\n handler: httpActionGeneric(async () => {\n const result = deps.getResponse(route.endpoint);\n if (result === null) {\n return new Response(null, { status: 404 });\n }\n return new Response(result.body, {\n status: result.status,\n headers: result.headers,\n });\n }),\n });\n }\n}\n\n/**\n * Register the OAuth sign-in and callback routes on an HTTP router.\n *\n * Adds `GET <routeBase>/signin/*` and `GET`/`POST <routeBase>/callback/*`.\n */\nexport function addAuthRoutes(\n http: HttpRouter,\n deps: {\n handleSignIn: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n handleCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n routeBase?: string;\n },\n) {\n const routeBase = deps.routeBase ?? \"/api/auth\";\n const routePrefix = routeBase === \"\" ? \"\" : routeBase;\n http.route({\n pathPrefix: `${routePrefix}/signin/`,\n method: \"GET\",\n handler: httpActionGeneric(deps.handleSignIn),\n });\n\n const callbackHandler = httpActionGeneric(deps.handleCallback);\n\n http.route({\n pathPrefix: `${routePrefix}/callback/`,\n method: \"GET\",\n handler: callbackHandler,\n });\n\n http.route({\n pathPrefix: `${routePrefix}/callback/`,\n method: \"POST\",\n handler: callbackHandler,\n });\n}\n\n/**\n * Register the OAuth provider authorize, token, registration, and RFC 7592\n * client-management endpoints on an HTTP router.\n *\n * Adds `GET <base>/oauth2/authorize`, `POST <base>/oauth2/token`,\n * `POST <base>/oauth2/register` (DCR), and `GET`/`PUT`/`DELETE`\n * `<base>/oauth2/register/<client_id>` (management). The exact `register` path\n * serves DCR; the `register/` prefix serves per-client management.\n */\nexport function addOAuthProviderRoutes(\n http: HttpRouter,\n deps: {\n handleAuthorize: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleToken: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n handleRegister: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleManage: (ctx: GenericActionCtx<GenericDataModel>, request: Request) => Promise<Response>;\n routeBase?: string;\n },\n) {\n const base = deps.routeBase ?? \"\";\n const authorizePath = `${base}/oauth2/authorize`;\n const tokenPath = `${base}/oauth2/token`;\n const registerPath = `${base}/oauth2/register`;\n const managePrefix = `${registerPath}/`;\n\n http.route({\n path: authorizePath,\n method: \"GET\",\n handler: httpActionGeneric(withCors(deps.handleAuthorize)),\n });\n http.route({\n path: tokenPath,\n method: \"POST\",\n handler: httpActionGeneric(withCors(deps.handleToken)),\n });\n http.route({\n path: registerPath,\n method: \"POST\",\n handler: httpActionGeneric(withCors(deps.handleRegister)),\n });\n for (const method of [\"GET\", \"PUT\", \"DELETE\"] as const) {\n http.route({\n pathPrefix: managePrefix,\n method,\n handler: httpActionGeneric(withCors(deps.handleManage)),\n });\n }\n http.route({ pathPrefix: managePrefix, method: \"OPTIONS\", handler: corsPreflightHandler });\n registerCorsPreflight(http, [authorizePath, tokenPath, registerPath]);\n}\n\n/**\n * Register the group connection runtime routes (SAML, OIDC, SCIM) on a router.\n *\n * Mounts method-specific handlers under `<routeBase>/<connectionId>/<protocol>/...`\n * (plus an optional shared OIDC callback path), dispatching by\n * `${protocol}:${endpoint}` and returning SCIM errors for unmatched\n * `PATCH`/`DELETE` requests.\n */\nexport function addConnectionRoutes(\n http: HttpRouter,\n deps: {\n routeBase: string;\n sharedOidcCallbackPath?: string;\n convertErrorsToResponse: typeof convertErrorsToResponse;\n handleSamlMetadata: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleSamlSignIn: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcSignIn: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleOidcSharedCallback: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n handleSamlAcs: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleSamlSlo: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n handleScimRequest: (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n ) => Promise<Response>;\n scimError: (status: number, scimType: string, detail: string) => Response;\n },\n) {\n const routePrefix = `${deps.routeBase}/`;\n const sharedOidcCallbackPath = deps.sharedOidcCallbackPath\n ? (() => {\n if (/^https?:\\/\\//.test(deps.sharedOidcCallbackPath)) {\n return new URL(deps.sharedOidcCallbackPath).pathname;\n }\n return deps.sharedOidcCallbackPath.startsWith(\"/\")\n ? deps.sharedOidcCallbackPath\n : `/${deps.sharedOidcCallbackPath}`;\n })()\n : undefined;\n\n if (sharedOidcCallbackPath) {\n http.route({\n path: sharedOidcCallbackPath,\n method: \"GET\",\n handler: httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) =>\n deps.handleOidcSharedCallback(ctx, request),\n ),\n ),\n });\n\n http.route({\n path: sharedOidcCallbackPath,\n method: \"POST\",\n handler: httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) =>\n deps.handleOidcSharedCallback(ctx, request),\n ),\n ),\n });\n }\n\n type ConnRouteHandler = (\n ctx: GenericActionCtx<GenericDataModel>,\n request: Request,\n route: ConnectionRuntimeRoute,\n ) => Promise<Response>;\n const scimRoute: ConnRouteHandler = (ctx, request) => deps.handleScimRequest(ctx, request);\n\n const matchConnectionRoute = (\n route: ConnectionRuntimeRoute | null,\n handlers: Record<string, ConnRouteHandler>,\n ): ConnRouteHandler | null => {\n const endpoint = route?.rest[0];\n if (!route || endpoint === undefined) return null;\n const key =\n route.protocol === \"scim\"\n ? endpoint === \"v2\"\n ? \"scim:v2\"\n : null\n : route.rest.length === 1\n ? `${route.protocol}:${endpoint}`\n : null;\n return key ? (handlers[key] ?? null) : null;\n };\n\n const connectionRouteHandler = (handlers: Record<string, ConnRouteHandler>) =>\n httpActionGeneric(\n deps.convertErrorsToResponse(400, async (ctx, request) => {\n const route = parseConnectionRuntimeRoute(new URL(request.url).pathname, deps.routeBase);\n const handler = matchConnectionRoute(route, handlers);\n if (!handler || !route) {\n throw new ConvexError({\n code: ErrorCode.INVALID_PARAMETERS,\n message: \"Invalid connection runtime path.\",\n });\n }\n return await handler(ctx, request, route);\n }),\n );\n\n http.route({\n pathPrefix: routePrefix,\n method: \"GET\",\n handler: connectionRouteHandler({\n \"saml:metadata\": deps.handleSamlMetadata,\n \"saml:signin\": deps.handleSamlSignIn,\n \"saml:acs\": deps.handleSamlAcs,\n \"saml:slo\": deps.handleSamlSlo,\n \"oidc:signin\": deps.handleOidcSignIn,\n \"oidc:callback\": deps.handleOidcCallback,\n \"scim:v2\": scimRoute,\n }),\n });\n\n http.route({\n pathPrefix: routePrefix,\n method: \"POST\",\n handler: connectionRouteHandler({\n \"saml:acs\": deps.handleSamlAcs,\n \"saml:slo\": deps.handleSamlSlo,\n \"oidc:callback\": deps.handleOidcCallback,\n \"scim:v2\": scimRoute,\n }),\n });\n\n http.route({\n pathPrefix: routePrefix,\n method: \"PUT\",\n handler: connectionRouteHandler({\n \"scim:v2\": scimRoute,\n }),\n });\n\n for (const method of [\"PATCH\", \"DELETE\"] as const) {\n http.route({\n pathPrefix: routePrefix,\n method,\n handler: httpActionGeneric(async (ctx, request) => {\n const route = parseConnectionRuntimeRoute(new URL(request.url).pathname, deps.routeBase);\n if (!route || route.protocol !== \"scim\" || route.rest[0] !== \"v2\") {\n return deps.scimError(404, \"notFound\", \"SCIM resource not found.\");\n }\n return await deps.handleScimRequest(ctx, request);\n }),\n });\n }\n}\n"],"mappings":"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"}
@@ -54,5 +54,5 @@ async function getAuthenticatedSessionIdOrNull(ctx) {
54
54
  }
55
55
 
56
56
  //#endregion
57
- export { getAuthenticatedSessionIdOrNull, getAuthenticatedUserIdOrNull, getUserIdentityOrNull, oauthScopesFromIdentity, userIdFromIdentity };
57
+ export { getAuthenticatedSessionIdOrNull, getAuthenticatedUserIdOrNull, getUserIdentityOrNull, oauthScopesFromIdentity, sessionIdFromIdentity, userIdFromIdentity };
58
58
  //# sourceMappingURL=claims.js.map
@@ -10,6 +10,8 @@ declare module "convex/server" {
10
10
  * native identity access can read `identity.sid` directly.
11
11
  */
12
12
  readonly sid?: GenericId<"Session">;
13
+ /** Session epoch captured in the convex-auth access token. */
14
+ readonly session_epoch?: number;
13
15
  }
14
16
  } //# sourceMappingURL=convex.d.ts.map
15
17
  //# sourceMappingURL=convex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convex.d.ts","names":[],"sources":["../../../src/server/identity/convex.ts"],"mappings":";;;;YAGY,YAAA;IAHmC;;;;;;IAAA,SAUlC,GAAA,GAAM,SAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"convex.d.ts","names":[],"sources":["../../../src/server/identity/convex.ts"],"mappings":";;;;YAGY,YAAA;IAHmC;;;;;;IAAA,SAUlC,GAAA,GAAM,SAAA;IAEN;IAAA,SAAA,aAAA;EAAA;AAAA"}
@@ -1,17 +1,14 @@
1
- import { vGroupDoc, vGroupInviteDoc, vGroupMemberDoc, vPaginated, vUserDoc, vUserEmailDoc } from "../component/model.js";
2
- import { AuthComponentApi } from "./component/api.js";
3
1
  import { AuthEventKind } from "../shared/event/kinds.js";
4
2
  import { AuthEvent, AuthEventHandlerMap, AuthEventTarget, AuthEventWhere, authEvents } from "./events.js";
5
- import { AuthDataModel, Doc, GenericDoc, Grant, PermissionsConfig, RoleId } from "./types.js";
6
- import { AuthContext, AuthContextConfig, OptionalAuthContext, UserDoc } from "./context.js";
7
- import { AuthConfig, InferAuth } from "./facade.js";
3
+ import { Grant, PermissionsConfig, RoleId } from "./types.js";
8
4
  import "./identity/convex.js";
9
5
  import { McpToolDef } from "./mcp.js";
6
+ import { AuthContext, AuthContextConfig, OptionalAuthContext } from "./context.js";
7
+ import { AuthConfig } from "./facade.js";
10
8
  import { WellKnownEndpoint, WellKnownOptions, WellKnownResponse, wellKnown } from "./wellknown.js";
11
9
  import { HttpAuthContext, HttpAuthContextConfig, OptionalHttpAuthContext } from "./http.js";
12
- import { AuthExtendValidators, AuthValidators, Group, Membership, Viewer, createAuthValidators } from "./validators.js";
13
- import { AuthApi, AuthApiBase, ConvexAuthResult, InferClientApi, defineAuth } from "./auth.js";
14
- import { AuthEnv, authEnv } from "./env.js";
10
+ import { AuthExtendValidators, vAuthId } from "./validators.js";
11
+ import { defineAuth } from "./auth.js";
15
12
  import { corsHeaders, corsPreflightHandler, registerCorsPreflight, withCors, withCorsResponse } from "./cors.js";
16
13
  import { AuthCookie, AuthCookieConfig, AuthCookies, PreloadResult, ServerOptions, authCookieNames, parseAuthCookies, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies } from "./preload.js";
17
- export { type AuthApi, type AuthApiBase, type AuthComponentApi, type AuthConfig, type AuthContext, type AuthContextConfig, type AuthCookie, type AuthCookieConfig, type AuthCookies, type AuthDataModel, type AuthEnv, type AuthEvent, type AuthEventHandlerMap, type AuthEventKind, type AuthEventTarget, type AuthEventWhere, type AuthExtendValidators, type AuthValidators, type ConvexAuthResult, type Doc, type GenericDoc, type Grant, type Group, type HttpAuthContext, type HttpAuthContextConfig, type InferAuth, type InferClientApi, type McpToolDef, type Membership, type OptionalAuthContext, type OptionalHttpAuthContext, type PermissionsConfig, type PreloadResult, type RoleId, type ServerOptions, type UserDoc, type Viewer, type WellKnownEndpoint, type WellKnownOptions, type WellKnownResponse, authCookieNames, authEnv, authEvents, corsHeaders, corsPreflightHandler, createAuthValidators, defineAuth, parseAuthCookies, registerCorsPreflight, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies, vGroupDoc, vGroupInviteDoc, vGroupMemberDoc, vPaginated, vUserDoc, vUserEmailDoc, wellKnown, withCors, withCorsResponse };
14
+ export { type AuthConfig, type AuthContext, type AuthContextConfig, type AuthCookie, type AuthCookieConfig, type AuthCookies, type AuthEvent, type AuthEventHandlerMap, type AuthEventKind, type AuthEventTarget, type AuthEventWhere, type AuthExtendValidators, type Grant, type HttpAuthContext, type HttpAuthContextConfig, type McpToolDef, type OptionalAuthContext, type OptionalHttpAuthContext, type PermissionsConfig, type PreloadResult, type RoleId, type ServerOptions, type WellKnownEndpoint, type WellKnownOptions, type WellKnownResponse, authCookieNames, authEvents, corsHeaders, corsPreflightHandler, defineAuth, parseAuthCookies, registerCorsPreflight, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies, vAuthId, wellKnown, withCors, withCorsResponse };
@@ -1,11 +1,9 @@
1
1
  import "./identity/convex.js";
2
- import { authEnv } from "./env.js";
3
2
  import { authEvents } from "./events.js";
4
- import { vGroupDoc, vGroupInviteDoc, vGroupMemberDoc, vPaginated, vUserDoc, vUserEmailDoc } from "../component/model.js";
5
3
  import { corsHeaders, corsPreflightHandler, registerCorsPreflight, withCors, withCorsResponse } from "./cors.js";
6
4
  import { wellKnown } from "./wellknown.js";
7
- import { createAuthValidators } from "./validators.js";
5
+ import { vAuthId } from "./validators.js";
8
6
  import { defineAuth } from "./auth.js";
9
7
  import { authCookieNames, parseAuthCookies, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies } from "./preload.js";
10
8
 
11
- export { authCookieNames, authEnv, authEvents, corsHeaders, corsPreflightHandler, createAuthValidators, defineAuth, parseAuthCookies, registerCorsPreflight, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies, vGroupDoc, vGroupInviteDoc, vGroupMemberDoc, vPaginated, vUserDoc, vUserEmailDoc, wellKnown, withCors, withCorsResponse };
9
+ export { authCookieNames, authEvents, corsHeaders, corsPreflightHandler, defineAuth, parseAuthCookies, registerCorsPreflight, serializeAuthCookies, server, shouldProxyAuthAction, structuredAuthCookies, vAuthId, wellKnown, withCors, withCorsResponse };
@@ -1,3 +1,8 @@
1
+ import { requireAuthKey } from "./env.js";
2
+ import { SHA256 } from "@oslojs/crypto/sha2";
3
+ import { encodeHexLowerCase } from "@oslojs/encoding";
4
+ import { hmac } from "@oslojs/crypto/hmac";
5
+
1
6
  //#region src/server/limits.ts
2
7
  const DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR = 10;
3
8
  /** Resolve the configured guessable-secret attempt capacity. @internal */
@@ -5,6 +10,21 @@ function maxSignInAttempts(config) {
5
10
  return config.signIn?.maxFailedAttemptsPerHour ?? DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR;
6
11
  }
7
12
  /**
13
+ * Produce the opaque token-bucket key for a credentials sign-in identity.
14
+ *
15
+ * The component persists this value, so it must never contain the submitted
16
+ * account identifier. It is deliberately derived before account lookup: known
17
+ * and unknown accounts then consume the same bucket. The normalized identifier
18
+ * treats user-facing credential IDs consistently without changing provider
19
+ * account lookup semantics.
20
+ *
21
+ * @internal
22
+ */
23
+ function credentialsSignInLimitIdentifier(provider, accountId) {
24
+ const canonical = JSON.stringify([provider, accountId.trim().toLowerCase()]);
25
+ return encodeHexLowerCase(hmac(SHA256, new TextEncoder().encode(requireAuthKey("webauthnMaskingKey")), new TextEncoder().encode(`credentials-sign-in:${canonical}`)));
26
+ }
27
+ /**
8
28
  * Check whether the given identifier is currently rate-limited.
9
29
  *
10
30
  * @internal
@@ -12,7 +32,8 @@ function maxSignInAttempts(config) {
12
32
  async function isSignInRateLimited(ctx, identifier, config) {
13
33
  const { ok } = await ctx.runQuery(config.component.limits.signInCheck, {
14
34
  identifier,
15
- maxAttemptsPerHour: maxSignInAttempts(config)
35
+ maxAttemptsPerHour: maxSignInAttempts(config),
36
+ now: Date.now()
16
37
  });
17
38
  return !ok;
18
39
  }
@@ -37,5 +58,5 @@ async function resetSignInRateLimit(ctx, identifier, config) {
37
58
  }
38
59
 
39
60
  //#endregion
40
- export { isSignInRateLimited, maxSignInAttempts, recordFailedSignIn, resetSignInRateLimit };
61
+ export { credentialsSignInLimitIdentifier, isSignInRateLimited, maxSignInAttempts, recordFailedSignIn, resetSignInRateLimit };
41
62
  //# sourceMappingURL=limits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","names":[],"sources":["../../src/server/limits.ts"],"sourcesContent":["import type { FunctionReference, FunctionReturnType, OptionalRestArgs } from \"convex/server\";\n\nimport type { ConvexAuthConfig } from \"./types\";\n\nconst DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR = 10;\n\n/**\n * Minimal context the rate-limit helpers depend on. The component API is\n * threaded in via the explicit `config` argument, so only `runQuery` /\n * `runMutation` are required. Typed with the narrowest call shape the helpers\n * actually use so both mutation handlers (credentials sign-in) and action\n * handlers (the TOTP ceremony) satisfy it, despite their differing\n * `runQuery`/`runMutation` option overloads.\n *\n * @internal\n */\nexport type SignInLimitCtx = {\n runQuery: <Query extends FunctionReference<\"query\", \"public\" | \"internal\">>(\n query: Query,\n ...args: OptionalRestArgs<Query>\n ) => Promise<FunctionReturnType<Query>>;\n runMutation: <Mutation extends FunctionReference<\"mutation\", \"public\" | \"internal\">>(\n mutation: Mutation,\n ...args: OptionalRestArgs<Mutation>\n ) => Promise<FunctionReturnType<Mutation>>;\n};\n\n/**\n * Minimal config shape the rate-limit helpers depend on. Both\n * {@link ConvexAuthConfig} and `ConvexAuthMaterializedConfig` satisfy it, so\n * the helpers work from mutation and action handlers alike.\n *\n * @internal\n */\nexport type SignInLimitConfig = Pick<ConvexAuthConfig, \"component\" | \"signIn\">;\n\n/** Resolve the configured guessable-secret attempt capacity. @internal */\nexport function maxSignInAttempts(config: SignInLimitConfig) {\n return config.signIn?.maxFailedAttemptsPerHour ?? DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR;\n}\n\n/**\n * Check whether the given identifier is currently rate-limited.\n *\n * @internal\n */\nexport async function isSignInRateLimited(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<boolean> {\n const { ok } = await ctx.runQuery(config.component.limits.signInCheck, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n });\n return !ok;\n}\n\n/**\n * Record a failed sign-in attempt for the given identifier.\n *\n * @internal\n */\nexport async function recordFailedSignIn(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<void> {\n await ctx.runMutation(config.component.limits.signInRecord, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n });\n}\n\n/**\n * Reserve one sign-in attempt up front, consuming a token as part of the same\n * atomic component mutation. Returns `true` when the caller is **blocked** (the\n * bucket was already empty, so no token was taken) and `false` when a token was\n * successfully consumed and the caller may proceed.\n *\n * Unlike {@link isSignInRateLimited} — a non-consuming peek — this closes the\n * check-then-record TOCTOU that lets N concurrent guesses in an **action**\n * (TOTP / passkey verify) all pass a `check()` before any `limit()` commits.\n * Reserving before the verification makes each attempt consume a token\n * atomically, so the per-hour cap bounds *attempts*, not merely logged failures.\n *\n * A single normal attempt consumes exactly one token; callers refund it on a\n * successful verification via {@link resetSignInRateLimit}. On a failed\n * verification the caller must NOT also call {@link recordFailedSignIn} — the\n * reservation already counted the attempt.\n *\n * @internal\n */\nexport async function reserveSignInAttempt(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<boolean> {\n const { ok } = await ctx.runMutation(config.component.limits.signInRecord, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n });\n return !ok;\n}\n\n/**\n * Reset the rate limit for the given identifier.\n *\n * @internal\n */\nexport async function resetSignInRateLimit(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<void> {\n await ctx.runMutation(config.component.limits.signInReset, { identifier });\n}\n"],"mappings":";AAIA,MAAM,wCAAwC;;AAiC9C,SAAgB,kBAAkB,QAA2B;AAC3D,QAAO,OAAO,QAAQ,4BAA4B;;;;;;;AAQpD,eAAsB,oBACpB,KACA,YACA,QACkB;CAClB,MAAM,EAAE,OAAO,MAAM,IAAI,SAAS,OAAO,UAAU,OAAO,aAAa;EACrE;EACA,oBAAoB,kBAAkB,OAAO;EAC9C,CAAC;AACF,QAAO,CAAC;;;;;;;AAQV,eAAsB,mBACpB,KACA,YACA,QACe;AACf,OAAM,IAAI,YAAY,OAAO,UAAU,OAAO,cAAc;EAC1D;EACA,oBAAoB,kBAAkB,OAAO;EAC9C,CAAC;;;;;;;AAuCJ,eAAsB,qBACpB,KACA,YACA,QACe;AACf,OAAM,IAAI,YAAY,OAAO,UAAU,OAAO,aAAa,EAAE,YAAY,CAAC"}
1
+ {"version":3,"file":"limits.js","names":[],"sources":["../../src/server/limits.ts"],"sourcesContent":["import { hmac } from \"@oslojs/crypto/hmac\";\nimport { SHA256 } from \"@oslojs/crypto/sha2\";\nimport { encodeHexLowerCase } from \"@oslojs/encoding\";\nimport type { FunctionReference, FunctionReturnType, OptionalRestArgs } from \"convex/server\";\n\nimport { requireAuthKey } from \"./env\";\nimport type { ConvexAuthConfig } from \"./types\";\n\nconst DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR = 10;\n\n/**\n * Minimal context the rate-limit helpers depend on. The component API is\n * threaded in via the explicit `config` argument, so only `runQuery` /\n * `runMutation` are required. Typed with the narrowest call shape the helpers\n * actually use so both mutation handlers (credentials sign-in) and action\n * handlers (the TOTP ceremony) satisfy it, despite their differing\n * `runQuery`/`runMutation` option overloads.\n *\n * @internal\n */\nexport type SignInLimitCtx = {\n runQuery: <Query extends FunctionReference<\"query\", \"public\" | \"internal\">>(\n query: Query,\n ...args: OptionalRestArgs<Query>\n ) => Promise<FunctionReturnType<Query>>;\n runMutation: <Mutation extends FunctionReference<\"mutation\", \"public\" | \"internal\">>(\n mutation: Mutation,\n ...args: OptionalRestArgs<Mutation>\n ) => Promise<FunctionReturnType<Mutation>>;\n};\n\n/**\n * Minimal config shape the rate-limit helpers depend on. Both\n * {@link ConvexAuthConfig} and `ConvexAuthMaterializedConfig` satisfy it, so\n * the helpers work from mutation and action handlers alike.\n *\n * @internal\n */\nexport type SignInLimitConfig = Pick<ConvexAuthConfig, \"component\" | \"signIn\">;\n\n/** Resolve the configured guessable-secret attempt capacity. @internal */\nexport function maxSignInAttempts(config: SignInLimitConfig) {\n return config.signIn?.maxFailedAttemptsPerHour ?? DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR;\n}\n\n/**\n * Produce the opaque token-bucket key for a credentials sign-in identity.\n *\n * The component persists this value, so it must never contain the submitted\n * account identifier. It is deliberately derived before account lookup: known\n * and unknown accounts then consume the same bucket. The normalized identifier\n * treats user-facing credential IDs consistently without changing provider\n * account lookup semantics.\n *\n * @internal\n */\nexport function credentialsSignInLimitIdentifier(provider: string, accountId: string) {\n const canonical = JSON.stringify([provider, accountId.trim().toLowerCase()]);\n return encodeHexLowerCase(\n hmac(\n SHA256,\n new TextEncoder().encode(requireAuthKey(\"webauthnMaskingKey\")),\n new TextEncoder().encode(`credentials-sign-in:${canonical}`),\n ),\n );\n}\n\n/**\n * Check whether the given identifier is currently rate-limited.\n *\n * @internal\n */\nexport async function isSignInRateLimited(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<boolean> {\n const { ok } = await ctx.runQuery(config.component.limits.signInCheck, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n now: Date.now(),\n });\n return !ok;\n}\n\n/**\n * Record a failed sign-in attempt for the given identifier.\n *\n * @internal\n */\nexport async function recordFailedSignIn(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<void> {\n await ctx.runMutation(config.component.limits.signInRecord, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n });\n}\n\n/**\n * Reserve one sign-in attempt up front, consuming a token as part of the same\n * atomic component mutation. Returns `true` when the caller is **blocked** (the\n * bucket was already empty, so no token was taken) and `false` when a token was\n * successfully consumed and the caller may proceed.\n *\n * Unlike {@link isSignInRateLimited} — a non-consuming peek — this closes the\n * check-then-record TOCTOU that lets N concurrent guesses in an **action**\n * (TOTP / passkey verify) all pass a `check()` before any `limit()` commits.\n * Reserving before the verification makes each attempt consume a token\n * atomically, so the per-hour cap bounds *attempts*, not merely logged failures.\n *\n * A single normal attempt consumes exactly one token; callers refund it on a\n * successful verification via {@link resetSignInRateLimit}. On a failed\n * verification the caller must NOT also call {@link recordFailedSignIn} — the\n * reservation already counted the attempt.\n *\n * @internal\n */\nexport async function reserveSignInAttempt(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<boolean> {\n const { ok } = await ctx.runMutation(config.component.limits.signInRecord, {\n identifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n });\n return !ok;\n}\n\n/**\n * Reset the rate limit for the given identifier.\n *\n * @internal\n */\nexport async function resetSignInRateLimit(\n ctx: SignInLimitCtx,\n identifier: string,\n config: SignInLimitConfig,\n): Promise<void> {\n await ctx.runMutation(config.component.limits.signInReset, { identifier });\n}\n"],"mappings":";;;;;;AAQA,MAAM,wCAAwC;;AAiC9C,SAAgB,kBAAkB,QAA2B;AAC3D,QAAO,OAAO,QAAQ,4BAA4B;;;;;;;;;;;;;AAcpD,SAAgB,iCAAiC,UAAkB,WAAmB;CACpF,MAAM,YAAY,KAAK,UAAU,CAAC,UAAU,UAAU,MAAM,CAAC,aAAa,CAAC,CAAC;AAC5E,QAAO,mBACL,KACE,QACA,IAAI,aAAa,CAAC,OAAO,eAAe,qBAAqB,CAAC,EAC9D,IAAI,aAAa,CAAC,OAAO,uBAAuB,YAAY,CAC7D,CACF;;;;;;;AAQH,eAAsB,oBACpB,KACA,YACA,QACkB;CAClB,MAAM,EAAE,OAAO,MAAM,IAAI,SAAS,OAAO,UAAU,OAAO,aAAa;EACrE;EACA,oBAAoB,kBAAkB,OAAO;EAC7C,KAAK,KAAK,KAAK;EAChB,CAAC;AACF,QAAO,CAAC;;;;;;;AAQV,eAAsB,mBACpB,KACA,YACA,QACe;AACf,OAAM,IAAI,YAAY,OAAO,UAAU,OAAO,cAAc;EAC1D;EACA,oBAAoB,kBAAkB,OAAO;EAC9C,CAAC;;;;;;;AAuCJ,eAAsB,qBACpB,KACA,YACA,QACe;AACf,OAAM,IAAI,YAAY,OAAO,UAAU,OAAO,aAAa,EAAE,YAAY,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { callModifyAccount } from "./account.js";
2
2
  import { callCreateVerificationCode } from "./code.js";
3
3
  import { callCredentialsSignIn } from "./credentials/signin.js";
4
+ import { callCreateAccountFromCredentials } from "./register.js";
5
+ import { callCompleteCredentialEnrollment } from "./enrollment.js";
4
6
  import { callInvalidateSessions } from "./invalidate.js";
5
7
  import { callUserOAuth } from "./oauth.js";
6
8
  import { callRefreshSession } from "./refresh.js";
7
- import { callCreateAccountFromCredentials } from "./register.js";
8
9
  import { callRetrieveAccountWithCredentials } from "./retrieve.js";
9
10
  import { callVerifierSignature } from "./signature.js";
10
11
  import { callSignIn } from "./signin.js";
@@ -13,4 +14,4 @@ import { callVerifier } from "./verifier.js";
13
14
  import { callVerifyCodeAndSignIn } from "./verify.js";
14
15
  import { storeImpl, vStoreArgs } from "./store.js";
15
16
 
16
- export { callCreateAccountFromCredentials, callCreateVerificationCode, callCredentialsSignIn, callInvalidateSessions, callModifyAccount, callRefreshSession, callRetrieveAccountWithCredentials, callSignIn, callSignOut, callUserOAuth, callVerifier, callVerifierSignature, callVerifyCodeAndSignIn, storeImpl, vStoreArgs };
17
+ export { callCompleteCredentialEnrollment, callCreateAccountFromCredentials, callCreateVerificationCode, callCredentialsSignIn, callInvalidateSessions, callModifyAccount, callRefreshSession, callRetrieveAccountWithCredentials, callSignIn, callSignOut, callUserOAuth, callVerifier, callVerifierSignature, callVerifyCodeAndSignIn, storeImpl, vStoreArgs };
@@ -3,10 +3,10 @@ import { queueAuthEvent } from "../../events.js";
3
3
  import { log, maybeRedact } from "../../log.js";
4
4
  import { encodeRefreshToken, refreshTokenExpirationTime } from "../../token/refresh.js";
5
5
  import { setActiveSpanAttributes, withSpan } from "../../utils/span.js";
6
- import { buildSessionIdentity, getAuthSessionId, sessionExpirationTime } from "../../session/lifecycle.js";
6
+ import { buildSessionIdentity, getAuthSessionReplacement, sessionExpirationTime } from "../../session/lifecycle.js";
7
7
  import { verify } from "../../crypto.js";
8
8
  import { AUTH_STORE_REF } from "../store/refs.js";
9
- import { maxSignInAttempts } from "../../limits.js";
9
+ import { credentialsSignInLimitIdentifier, maxSignInAttempts } from "../../limits.js";
10
10
  import { buildKnownSignInIdentityAttributes } from "../../telemetry.js";
11
11
  import { v } from "convex/values";
12
12
 
@@ -62,6 +62,7 @@ async function credentialsSignInImpl(ctx, args, getProviderOrThrow, config) {
62
62
  }
63
63
  async function credentialsSignInInner(ctx, args, getProviderOrThrow, config) {
64
64
  const { provider: providerId, account, generateTokens, requireVerifiedEmail, enforceTotp } = args;
65
+ const limitIdentifier = credentialsSignInLimitIdentifier(providerId, account.id);
65
66
  log(LOG_LEVELS.DEBUG, "credentialsSignInImpl args:", {
66
67
  provider: providerId,
67
68
  account: {
@@ -75,6 +76,7 @@ async function credentialsSignInInner(ctx, args, getProviderOrThrow, config) {
75
76
  const begun = await ctx.runMutation(config.component.account.beginCredentialsSignIn, {
76
77
  provider: providerId,
77
78
  providerAccountId: account.id,
79
+ limitIdentifier,
78
80
  maxAttemptsPerHour: maxSignInAttempts(config),
79
81
  reserveAttempt: true,
80
82
  includeTotp: enforceTotp
@@ -89,9 +91,10 @@ async function credentialsSignInInner(ctx, args, getProviderOrThrow, config) {
89
91
  if (!await withSpan("convex-auth.credentials.verify", { providerId }, () => verify(getProviderOrThrow(providerId), account.secret, existingAccount.secret ?? ""))) return { kind: "invalidSecret" };
90
92
  const complete = async (issueSession) => await ctx.runMutation(config.component.account.completeCredentialsSignIn, {
91
93
  accountId: existingAccount._id,
94
+ limitIdentifier,
92
95
  issueSession,
93
96
  generateTokens,
94
- replaceSessionId: issueSession ? await getAuthSessionId(ctx) ?? void 0 : void 0,
97
+ replaceSession: issueSession ? await getAuthSessionReplacement(ctx) : void 0,
95
98
  sessionExpirationTime: sessionExpirationTime(config),
96
99
  refreshTokenExpirationTime: refreshTokenExpirationTime(config)
97
100
  });
@@ -183,6 +186,7 @@ async function credentialsSignInInner(ctx, args, getProviderOrThrow, config) {
183
186
  issuance: {
184
187
  userId,
185
188
  sessionId,
189
+ sessionExpirationTime: completed.sessionExpirationTime,
186
190
  identity: buildSessionIdentity(userId, sessionId, completed.user),
187
191
  refreshToken: generateTokens && refreshTokenId !== void 0 ? encodeRefreshToken(refreshTokenId, sessionId) : null
188
192
  },
@@ -1 +1 @@
1
- {"version":3,"file":"signin.js","names":["Provider.verify"],"sources":["../../../../src/server/mutations/credentials/signin.ts"],"sourcesContent":["/**\n * Credentials verification backed by two aggregate component transactions:\n * one to reserve/load sign-in state and one to refund-or-issue. This avoids a\n * separate component call for every account, user, factor, limiter, and session\n * operation.\n *\n * @internal\n */\n\nimport type { GenericDataModel } from \"convex/server\";\nimport { GenericId, Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../../crypto\";\nimport type { Hashed } from \"../../../shared/brand\";\nimport { queueAuthEvent } from \"../../events\";\nimport { maxSignInAttempts } from \"../../limits\";\nimport { LOG_LEVELS, log, maybeRedact } from \"../../log\";\nimport {\n buildSessionIdentity,\n getAuthSessionId,\n sessionExpirationTime,\n} from \"../../session/lifecycle\";\nimport type { SessionIssuance } from \"../../session/lifecycle\";\nimport { encodeRefreshToken, refreshTokenExpirationTime } from \"../../token/refresh\";\nimport { buildKnownSignInIdentityAttributes } from \"../../telemetry\";\nimport {\n type CrossComponentUserDoc,\n GenericActionCtxWithAuthConfig,\n MutationCtx,\n} from \"../../types\";\nimport { setActiveSpanAttributes, withSpan } from \"../../utils/span\";\nimport { AUTH_STORE_REF } from \"../store/refs\";\n\n/**\n * A well-formed but intentionally unmatchable password hash. Verifying any\n * secret against it runs the provider's full (scrypt) work and always fails.\n * Used on the missing-account branch so sign-in spends comparable time whether\n * or not the account exists — closing the user-enumeration timing oracle.\n *\n * The prefix and params mirror the default password provider's scrypt format so\n * that provider performs a real scrypt hash; the salt and digest are all zero,\n * a digest scrypt can never actually produce, so verification never succeeds.\n */\nconst DUMMY_PASSWORD_HASH =\n `scrypt:N=16384,r=16,p=1,dkLen=64$${\"0\".repeat(64)}$${\"0\".repeat(128)}` as Hashed<\"Password\">;\n\n/**\n * Run a throwaway secret verification purely to equalize timing on branches\n * that reject before the real verify would run. Never authenticates: it\n * verifies against {@link DUMMY_PASSWORD_HASH} (which cannot match) and swallows\n * any error, so a misconfigured or non-credentials provider still yields the\n * unified rejection rather than a distinguishable error or timing.\n */\nasync function burnVerifyForTiming(\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n providerId: string,\n secret: string,\n): Promise<void> {\n try {\n await Provider.verify(getProviderOrThrow(providerId), secret, DUMMY_PASSWORD_HASH);\n } catch {\n // Intentionally ignored — this path exists only to equalize timing.\n }\n}\n\n/** Argument validator for the combined credentials-verify + session-issue mutation. */\nexport const vCredentialsSignInArgs = v.object({\n provider: v.string(),\n account: v.object({ id: v.string(), secret: v.string() }),\n generateTokens: v.boolean(),\n requireVerifiedEmail: v.boolean(),\n enforceTotp: v.boolean(),\n});\n\ntype CredentialsSignInResult =\n | { kind: \"invalidAccount\" }\n | { kind: \"tooManyAttempts\" }\n | { kind: \"invalidSecret\" }\n | {\n kind: \"emailVerificationRequired\";\n account: { _id: string; emailVerified?: string };\n user: {\n _id: string;\n email?: string;\n };\n }\n | {\n kind: \"signedIn\";\n issuance: SessionIssuance;\n account: { _id: string; emailVerified?: string };\n user: {\n _id: string;\n email?: string;\n hasTotp?: boolean;\n };\n }\n | {\n kind: \"totpRequired\";\n account: { _id: string; emailVerified?: string };\n user: { _id: string; email?: string };\n };\n\n/**\n * Verify credentials and issue a session through one app mutation.\n *\n * Enforces sign-in rate limiting, optional verified-email and TOTP gates, and\n * returns a discriminated result (`invalidAccount`, `tooManyAttempts`,\n * `invalidSecret`, `emailVerificationRequired`, `totpRequired`, or `signedIn`).\n *\n * @internal\n */\nexport async function credentialsSignInImpl(\n ctx: MutationCtx,\n args: Infer<typeof vCredentialsSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<CredentialsSignInResult> {\n return withSpan(\n \"convex-auth.mutations.credentialsSignIn\",\n { provider: args.provider, generateTokens: args.generateTokens },\n () => credentialsSignInInner(ctx, args, getProviderOrThrow, config),\n );\n}\n\nasync function credentialsSignInInner(\n ctx: MutationCtx,\n args: Infer<typeof vCredentialsSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<CredentialsSignInResult> {\n const { provider: providerId, account, generateTokens, requireVerifiedEmail, enforceTotp } = args;\n log(LOG_LEVELS.DEBUG, \"credentialsSignInImpl args:\", {\n provider: providerId,\n account: { id: account.id, secret: maybeRedact(account.secret) },\n generateTokens,\n requireVerifiedEmail,\n enforceTotp,\n });\n\n const begun = (await ctx.runMutation(config.component.account.beginCredentialsSignIn, {\n provider: providerId,\n providerAccountId: account.id,\n maxAttemptsPerHour: maxSignInAttempts(config),\n reserveAttempt: true,\n includeTotp: enforceTotp,\n })) as\n | { status: \"invalid\" }\n | { status: \"limited\" }\n | {\n status: \"ready\";\n account: {\n _id: string;\n userId: string;\n secret?: string;\n emailVerified?: string;\n };\n user: CrossComponentUserDoc;\n hasTotp: boolean;\n };\n if (begun.status === \"invalid\") {\n // A real account pays the full secret-verify (scrypt) cost below, so burn\n // the same cost here before returning the unified rejection. Without this a\n // missing account returns early and its faster response leaks (by timing)\n // that the account does not exist. Result intentionally ignored.\n await burnVerifyForTiming(getProviderOrThrow, providerId, account.secret);\n return { kind: \"invalidAccount\" };\n }\n if (begun.status === \"limited\") {\n return { kind: \"tooManyAttempts\" };\n }\n const existingAccount = begun.account;\n const user = begun.user;\n\n const verified = await withSpan(\"convex-auth.credentials.verify\", { providerId }, () =>\n Provider.verify(\n getProviderOrThrow(providerId),\n account.secret,\n (existingAccount.secret ?? \"\") as Hashed<\"Password\">,\n ),\n );\n if (!verified) {\n // beginCredentialsSignIn reserved this failed attempt transactionally.\n return { kind: \"invalidSecret\" };\n }\n\n const complete = async (issueSession: boolean) =>\n (await ctx.runMutation(config.component.account.completeCredentialsSignIn, {\n accountId: existingAccount._id,\n issueSession,\n generateTokens,\n replaceSessionId: issueSession ? ((await getAuthSessionId(ctx)) ?? undefined) : undefined,\n sessionExpirationTime: sessionExpirationTime(config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(config),\n })) as\n | { status: \"rejected\" | \"reset\" }\n | {\n status: \"accepted\";\n user: typeof user;\n sessionId: string;\n refreshTokenId?: string;\n replacedSessionId?: string;\n };\n\n if (requireVerifiedEmail && !existingAccount.emailVerified) {\n await complete(false);\n return {\n kind: \"emailVerificationRequired\",\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: {\n _id: user._id,\n email: user.email,\n },\n };\n }\n\n if (enforceTotp && begun.hasTotp) {\n await complete(false);\n return {\n kind: \"totpRequired\",\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: { _id: user._id, email: user.email },\n };\n }\n\n const completed = await complete(true);\n if (completed.status !== \"accepted\") return { kind: \"invalidAccount\" };\n\n const userId = completed.user._id as GenericId<\"User\">;\n const sessionId = completed.sessionId as GenericId<\"Session\">;\n setActiveSpanAttributes({\n \"auth.signin.result\": \"success\",\n ...buildKnownSignInIdentityAttributes(config, { userId, sessionId }, completed.user.email),\n });\n if (completed.replacedSessionId !== undefined) {\n const replacedSessionId = completed.replacedSessionId as GenericId<\"Session\">;\n await queueAuthEvent(ctx, config, {\n kind: \"session.invalidated\",\n actor: { type: \"system\" },\n subject: { type: \"session\", id: replacedSessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: replacedSessionId },\n ],\n outcome: \"success\",\n data: { userId, reason: \"replaced\" },\n });\n }\n await queueAuthEvent(ctx, config, {\n kind: \"session.signed_in\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"session\", id: sessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: sessionId },\n ],\n outcome: \"success\",\n data: { provider: providerId },\n });\n const refreshTokenId = completed.refreshTokenId as GenericId<\"RefreshToken\"> | undefined;\n const issuance: SessionIssuance = {\n userId,\n sessionId,\n identity: buildSessionIdentity(userId, sessionId, completed.user),\n refreshToken:\n generateTokens && refreshTokenId !== undefined\n ? encodeRefreshToken(refreshTokenId, sessionId)\n : null,\n };\n\n return {\n kind: \"signedIn\",\n issuance,\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: {\n _id: user._id,\n email: user.email,\n hasTotp: begun.hasTotp,\n },\n };\n}\n\n/** @internal */\nexport const callCredentialsSignIn = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtxWithAuthConfig<DataModel>,\n args: Infer<typeof vCredentialsSignInArgs>,\n): Promise<CredentialsSignInResult> => {\n return (await ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"credentialsSignIn\",\n ...args,\n },\n })) as CredentialsSignInResult;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"signin.js","names":["Provider.verify"],"sources":["../../../../src/server/mutations/credentials/signin.ts"],"sourcesContent":["/**\n * Credentials verification backed by two aggregate component transactions:\n * one to reserve/load sign-in state and one to refund-or-issue. This avoids a\n * separate component call for every account, user, factor, limiter, and session\n * operation.\n *\n * @internal\n */\n\nimport type { GenericDataModel } from \"convex/server\";\nimport { GenericId, Infer, v } from \"convex/values\";\n\nimport * as Provider from \"../../crypto\";\nimport type { Hashed } from \"../../../shared/brand\";\nimport { queueAuthEvent } from \"../../events\";\nimport { credentialsSignInLimitIdentifier, maxSignInAttempts } from \"../../limits\";\nimport { LOG_LEVELS, log, maybeRedact } from \"../../log\";\nimport {\n buildSessionIdentity,\n getAuthSessionReplacement,\n sessionExpirationTime,\n} from \"../../session/lifecycle\";\nimport type { SessionIssuance } from \"../../session/lifecycle\";\nimport { encodeRefreshToken, refreshTokenExpirationTime } from \"../../token/refresh\";\nimport { buildKnownSignInIdentityAttributes } from \"../../telemetry\";\nimport {\n type CrossComponentUserDoc,\n GenericActionCtxWithAuthConfig,\n MutationCtx,\n} from \"../../types\";\nimport { setActiveSpanAttributes, withSpan } from \"../../utils/span\";\nimport { AUTH_STORE_REF } from \"../store/refs\";\n\n/**\n * A well-formed but intentionally unmatchable password hash. Verifying any\n * secret against it runs the provider's full (scrypt) work and always fails.\n * Used on the missing-account branch so sign-in spends comparable time whether\n * or not the account exists — closing the user-enumeration timing oracle.\n *\n * The prefix and params mirror the default password provider's scrypt format so\n * that provider performs a real scrypt hash; the salt and digest are all zero,\n * a digest scrypt can never actually produce, so verification never succeeds.\n */\nconst DUMMY_PASSWORD_HASH =\n `scrypt:N=16384,r=16,p=1,dkLen=64$${\"0\".repeat(64)}$${\"0\".repeat(128)}` as Hashed<\"Password\">;\n\n/**\n * Run a throwaway secret verification purely to equalize timing on branches\n * that reject before the real verify would run. Never authenticates: it\n * verifies against {@link DUMMY_PASSWORD_HASH} (which cannot match) and swallows\n * any error, so a misconfigured or non-credentials provider still yields the\n * unified rejection rather than a distinguishable error or timing.\n */\nasync function burnVerifyForTiming(\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n providerId: string,\n secret: string,\n): Promise<void> {\n try {\n await Provider.verify(getProviderOrThrow(providerId), secret, DUMMY_PASSWORD_HASH);\n } catch {\n // Intentionally ignored — this path exists only to equalize timing.\n }\n}\n\n/** Argument validator for the combined credentials-verify + session-issue mutation. */\nexport const vCredentialsSignInArgs = v.object({\n provider: v.string(),\n account: v.object({ id: v.string(), secret: v.string() }),\n generateTokens: v.boolean(),\n requireVerifiedEmail: v.boolean(),\n enforceTotp: v.boolean(),\n});\n\ntype CredentialsSignInResult =\n | { kind: \"invalidAccount\" }\n | { kind: \"tooManyAttempts\" }\n | { kind: \"invalidSecret\" }\n | {\n kind: \"emailVerificationRequired\";\n account: { _id: string; emailVerified?: string };\n user: {\n _id: string;\n email?: string;\n };\n }\n | {\n kind: \"signedIn\";\n issuance: SessionIssuance;\n account: { _id: string; emailVerified?: string };\n user: {\n _id: string;\n email?: string;\n hasTotp?: boolean;\n };\n }\n | {\n kind: \"totpRequired\";\n account: { _id: string; emailVerified?: string };\n user: { _id: string; email?: string };\n };\n\n/**\n * Verify credentials and issue a session through one app mutation.\n *\n * Enforces sign-in rate limiting, optional verified-email and TOTP gates, and\n * returns a discriminated result (`invalidAccount`, `tooManyAttempts`,\n * `invalidSecret`, `emailVerificationRequired`, `totpRequired`, or `signedIn`).\n *\n * @internal\n */\nexport async function credentialsSignInImpl(\n ctx: MutationCtx,\n args: Infer<typeof vCredentialsSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<CredentialsSignInResult> {\n return withSpan(\n \"convex-auth.mutations.credentialsSignIn\",\n { provider: args.provider, generateTokens: args.generateTokens },\n () => credentialsSignInInner(ctx, args, getProviderOrThrow, config),\n );\n}\n\nasync function credentialsSignInInner(\n ctx: MutationCtx,\n args: Infer<typeof vCredentialsSignInArgs>,\n getProviderOrThrow: Provider.GetProviderOrThrowFunc,\n config: Provider.Config,\n): Promise<CredentialsSignInResult> {\n const { provider: providerId, account, generateTokens, requireVerifiedEmail, enforceTotp } = args;\n const limitIdentifier = credentialsSignInLimitIdentifier(providerId, account.id);\n log(LOG_LEVELS.DEBUG, \"credentialsSignInImpl args:\", {\n provider: providerId,\n account: { id: account.id, secret: maybeRedact(account.secret) },\n generateTokens,\n requireVerifiedEmail,\n enforceTotp,\n });\n\n const begun = (await ctx.runMutation(config.component.account.beginCredentialsSignIn, {\n provider: providerId,\n providerAccountId: account.id,\n limitIdentifier,\n maxAttemptsPerHour: maxSignInAttempts(config),\n reserveAttempt: true,\n includeTotp: enforceTotp,\n })) as\n | { status: \"invalid\" }\n | { status: \"limited\" }\n | {\n status: \"ready\";\n account: {\n _id: string;\n userId: string;\n secret?: string;\n emailVerified?: string;\n };\n user: CrossComponentUserDoc;\n hasTotp: boolean;\n };\n if (begun.status === \"invalid\") {\n // A real account pays the full secret-verify (scrypt) cost below, so burn\n // the same cost here before returning the unified rejection. Without this a\n // missing account returns early and its faster response leaks (by timing)\n // that the account does not exist. Result intentionally ignored.\n await burnVerifyForTiming(getProviderOrThrow, providerId, account.secret);\n return { kind: \"invalidAccount\" };\n }\n if (begun.status === \"limited\") {\n return { kind: \"tooManyAttempts\" };\n }\n const existingAccount = begun.account;\n const user = begun.user;\n\n const verified = await withSpan(\"convex-auth.credentials.verify\", { providerId }, () =>\n Provider.verify(\n getProviderOrThrow(providerId),\n account.secret,\n (existingAccount.secret ?? \"\") as Hashed<\"Password\">,\n ),\n );\n if (!verified) {\n // beginCredentialsSignIn reserved this failed attempt transactionally.\n return { kind: \"invalidSecret\" };\n }\n\n const complete = async (issueSession: boolean) =>\n (await ctx.runMutation(config.component.account.completeCredentialsSignIn, {\n accountId: existingAccount._id,\n limitIdentifier,\n issueSession,\n generateTokens,\n replaceSession: issueSession ? await getAuthSessionReplacement(ctx) : undefined,\n sessionExpirationTime: sessionExpirationTime(config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(config),\n })) as\n | { status: \"rejected\" | \"reset\" }\n | {\n status: \"accepted\";\n user: typeof user;\n sessionId: string;\n sessionExpirationTime: number;\n refreshTokenId?: string;\n replacedSessionId?: string;\n };\n\n if (requireVerifiedEmail && !existingAccount.emailVerified) {\n await complete(false);\n return {\n kind: \"emailVerificationRequired\",\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: {\n _id: user._id,\n email: user.email,\n },\n };\n }\n\n if (enforceTotp && begun.hasTotp) {\n await complete(false);\n return {\n kind: \"totpRequired\",\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: { _id: user._id, email: user.email },\n };\n }\n\n const completed = await complete(true);\n if (completed.status !== \"accepted\") return { kind: \"invalidAccount\" };\n\n const userId = completed.user._id as GenericId<\"User\">;\n const sessionId = completed.sessionId as GenericId<\"Session\">;\n setActiveSpanAttributes({\n \"auth.signin.result\": \"success\",\n ...buildKnownSignInIdentityAttributes(config, { userId, sessionId }, completed.user.email),\n });\n if (completed.replacedSessionId !== undefined) {\n const replacedSessionId = completed.replacedSessionId as GenericId<\"Session\">;\n await queueAuthEvent(ctx, config, {\n kind: \"session.invalidated\",\n actor: { type: \"system\" },\n subject: { type: \"session\", id: replacedSessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: replacedSessionId },\n ],\n outcome: \"success\",\n data: { userId, reason: \"replaced\" },\n });\n }\n await queueAuthEvent(ctx, config, {\n kind: \"session.signed_in\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"session\", id: sessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: sessionId },\n ],\n outcome: \"success\",\n data: { provider: providerId },\n });\n const refreshTokenId = completed.refreshTokenId as GenericId<\"RefreshToken\"> | undefined;\n const issuance: SessionIssuance = {\n userId,\n sessionId,\n sessionExpirationTime: completed.sessionExpirationTime,\n identity: buildSessionIdentity(userId, sessionId, completed.user),\n refreshToken:\n generateTokens && refreshTokenId !== undefined\n ? encodeRefreshToken(refreshTokenId, sessionId)\n : null,\n };\n\n return {\n kind: \"signedIn\",\n issuance,\n account: {\n _id: existingAccount._id,\n emailVerified: existingAccount.emailVerified,\n },\n user: {\n _id: user._id,\n email: user.email,\n hasTotp: begun.hasTotp,\n },\n };\n}\n\n/** @internal */\nexport const callCredentialsSignIn = async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtxWithAuthConfig<DataModel>,\n args: Infer<typeof vCredentialsSignInArgs>,\n): Promise<CredentialsSignInResult> => {\n return (await ctx.runMutation(AUTH_STORE_REF, {\n args: {\n type: \"credentialsSignIn\",\n ...args,\n },\n })) as CredentialsSignInResult;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAM,sBACJ,oCAAoC,IAAI,OAAO,GAAG,CAAC,GAAG,IAAI,OAAO,IAAI;;;;;;;;AASvE,eAAe,oBACb,oBACA,YACA,QACe;AACf,KAAI;AACF,QAAMA,OAAgB,mBAAmB,WAAW,EAAE,QAAQ,oBAAoB;SAC5E;;;AAMV,MAAa,yBAAyB,EAAE,OAAO;CAC7C,UAAU,EAAE,QAAQ;CACpB,SAAS,EAAE,OAAO;EAAE,IAAI,EAAE,QAAQ;EAAE,QAAQ,EAAE,QAAQ;EAAE,CAAC;CACzD,gBAAgB,EAAE,SAAS;CAC3B,sBAAsB,EAAE,SAAS;CACjC,aAAa,EAAE,SAAS;CACzB,CAAC;;;;;;;;;;AAuCF,eAAsB,sBACpB,KACA,MACA,oBACA,QACkC;AAClC,QAAO,SACL,2CACA;EAAE,UAAU,KAAK;EAAU,gBAAgB,KAAK;EAAgB,QAC1D,uBAAuB,KAAK,MAAM,oBAAoB,OAAO,CACpE;;AAGH,eAAe,uBACb,KACA,MACA,oBACA,QACkC;CAClC,MAAM,EAAE,UAAU,YAAY,SAAS,gBAAgB,sBAAsB,gBAAgB;CAC7F,MAAM,kBAAkB,iCAAiC,YAAY,QAAQ,GAAG;AAChF,KAAI,WAAW,OAAO,+BAA+B;EACnD,UAAU;EACV,SAAS;GAAE,IAAI,QAAQ;GAAI,QAAQ,YAAY,QAAQ,OAAO;GAAE;EAChE;EACA;EACA;EACD,CAAC;CAEF,MAAM,QAAS,MAAM,IAAI,YAAY,OAAO,UAAU,QAAQ,wBAAwB;EACpF,UAAU;EACV,mBAAmB,QAAQ;EAC3B;EACA,oBAAoB,kBAAkB,OAAO;EAC7C,gBAAgB;EAChB,aAAa;EACd,CAAC;AAcF,KAAI,MAAM,WAAW,WAAW;AAK9B,QAAM,oBAAoB,oBAAoB,YAAY,QAAQ,OAAO;AACzE,SAAO,EAAE,MAAM,kBAAkB;;AAEnC,KAAI,MAAM,WAAW,UACnB,QAAO,EAAE,MAAM,mBAAmB;CAEpC,MAAM,kBAAkB,MAAM;CAC9B,MAAM,OAAO,MAAM;AASnB,KAAI,CAPa,MAAM,SAAS,kCAAkC,EAAE,YAAY,QAC9EA,OACE,mBAAmB,WAAW,EAC9B,QAAQ,QACP,gBAAgB,UAAU,GAC5B,CACF,CAGC,QAAO,EAAE,MAAM,iBAAiB;CAGlC,MAAM,WAAW,OAAO,iBACrB,MAAM,IAAI,YAAY,OAAO,UAAU,QAAQ,2BAA2B;EACzE,WAAW,gBAAgB;EAC3B;EACA;EACA;EACA,gBAAgB,eAAe,MAAM,0BAA0B,IAAI,GAAG;EACtE,uBAAuB,sBAAsB,OAAO;EACpD,4BAA4B,2BAA2B,OAAO;EAC/D,CAAC;AAWJ,KAAI,wBAAwB,CAAC,gBAAgB,eAAe;AAC1D,QAAM,SAAS,MAAM;AACrB,SAAO;GACL,MAAM;GACN,SAAS;IACP,KAAK,gBAAgB;IACrB,eAAe,gBAAgB;IAChC;GACD,MAAM;IACJ,KAAK,KAAK;IACV,OAAO,KAAK;IACb;GACF;;AAGH,KAAI,eAAe,MAAM,SAAS;AAChC,QAAM,SAAS,MAAM;AACrB,SAAO;GACL,MAAM;GACN,SAAS;IACP,KAAK,gBAAgB;IACrB,eAAe,gBAAgB;IAChC;GACD,MAAM;IAAE,KAAK,KAAK;IAAK,OAAO,KAAK;IAAO;GAC3C;;CAGH,MAAM,YAAY,MAAM,SAAS,KAAK;AACtC,KAAI,UAAU,WAAW,WAAY,QAAO,EAAE,MAAM,kBAAkB;CAEtE,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,YAAY,UAAU;AAC5B,yBAAwB;EACtB,sBAAsB;EACtB,GAAG,mCAAmC,QAAQ;GAAE;GAAQ;GAAW,EAAE,UAAU,KAAK,MAAM;EAC3F,CAAC;AACF,KAAI,UAAU,sBAAsB,QAAW;EAC7C,MAAM,oBAAoB,UAAU;AACpC,QAAM,eAAe,KAAK,QAAQ;GAChC,MAAM;GACN,OAAO,EAAE,MAAM,UAAU;GACzB,SAAS;IAAE,MAAM;IAAW,IAAI;IAAmB;GACnD,SAAS,CACP;IAAE,MAAM;IAAQ,IAAI;IAAQ,EAC5B;IAAE,MAAM;IAAW,IAAI;IAAmB,CAC3C;GACD,SAAS;GACT,MAAM;IAAE;IAAQ,QAAQ;IAAY;GACrC,CAAC;;AAEJ,OAAM,eAAe,KAAK,QAAQ;EAChC,MAAM;EACN,OAAO;GAAE,MAAM;GAAQ,IAAI;GAAQ;EACnC,SAAS;GAAE,MAAM;GAAW,IAAI;GAAW;EAC3C,SAAS,CACP;GAAE,MAAM;GAAQ,IAAI;GAAQ,EAC5B;GAAE,MAAM;GAAW,IAAI;GAAW,CACnC;EACD,SAAS;EACT,MAAM,EAAE,UAAU,YAAY;EAC/B,CAAC;CACF,MAAM,iBAAiB,UAAU;AAYjC,QAAO;EACL,MAAM;EACN,UAbgC;GAChC;GACA;GACA,uBAAuB,UAAU;GACjC,UAAU,qBAAqB,QAAQ,WAAW,UAAU,KAAK;GACjE,cACE,kBAAkB,mBAAmB,SACjC,mBAAmB,gBAAgB,UAAU,GAC7C;GACP;EAKC,SAAS;GACP,KAAK,gBAAgB;GACrB,eAAe,gBAAgB;GAChC;EACD,MAAM;GACJ,KAAK,KAAK;GACV,OAAO,KAAK;GACZ,SAAS,MAAM;GAChB;EACF;;;AAIH,MAAa,wBAAwB,OACnC,KACA,SACqC;AACrC,QAAQ,MAAM,IAAI,YAAY,gBAAgB,EAC5C,MAAM;EACJ,MAAM;EACN,GAAG;EACJ,EACF,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { ErrorCode } from "../../shared/codes.js";
2
+ import { AUTH_STORE_REF } from "./store/refs.js";
3
+ import { createAccountFromHashedCredentialsImpl } from "./register.js";
4
+ import { ConvexError, v } from "convex/values";
5
+
6
+ //#region src/server/mutations/enrollment.ts
7
+ const vAttestationEvidence = v.object({
8
+ verifier: v.string(),
9
+ aaguid: v.string(),
10
+ format: v.string(),
11
+ metadataDescription: v.optional(v.string()),
12
+ verifiedAt: v.number(),
13
+ status: v.literal("trusted")
14
+ });
15
+ /** Arguments for the single enrollment-commit transaction. */
16
+ const vCompleteCredentialEnrollmentArgs = v.object({
17
+ continuationId: v.string(),
18
+ provider: v.string(),
19
+ credentialId: v.string(),
20
+ publicKey: v.bytes(),
21
+ algorithm: v.number(),
22
+ counter: v.number(),
23
+ transports: v.optional(v.array(v.string())),
24
+ deviceType: v.string(),
25
+ backedUp: v.boolean(),
26
+ name: v.optional(v.string()),
27
+ attestation: v.optional(vAttestationEvidence),
28
+ createdAt: v.number(),
29
+ now: v.number(),
30
+ sessionExpirationTime: v.number(),
31
+ refreshTokenExpirationTime: v.number()
32
+ });
33
+ /** Commit the staged identity, credential account, passkey, and session atomically. */
34
+ async function completeCredentialEnrollmentImpl(ctx, args, services) {
35
+ const enrollment = await ctx.runQuery(services.config.component.token.enrollment.get, {
36
+ continuationId: args.continuationId,
37
+ provider: args.provider,
38
+ now: args.now
39
+ });
40
+ if (enrollment === null) return { status: "rejected" };
41
+ const provisioned = await createAccountFromHashedCredentialsImpl(ctx, {
42
+ provider: enrollment.provider,
43
+ account: {
44
+ id: enrollment.providerAccountId,
45
+ ...enrollment.secret === void 0 ? {} : { secret: enrollment.secret }
46
+ },
47
+ profile: enrollment.profile,
48
+ shouldLinkViaEmail: enrollment.shouldLinkViaEmail,
49
+ shouldLinkViaPhone: enrollment.shouldLinkViaPhone
50
+ }, services.providerRegistry.getProviderOrThrow, services.config);
51
+ const completed = await ctx.runMutation(services.config.component.factor.passkey.completeEnrollment, {
52
+ userId: provisioned.user._id,
53
+ credentialId: args.credentialId,
54
+ publicKey: args.publicKey,
55
+ algorithm: args.algorithm,
56
+ counter: args.counter,
57
+ ...args.transports === void 0 ? {} : { transports: args.transports },
58
+ deviceType: args.deviceType,
59
+ backedUp: args.backedUp,
60
+ ...args.name === void 0 ? {} : { name: args.name },
61
+ ...args.attestation === void 0 ? {} : { attestation: args.attestation },
62
+ createdAt: args.createdAt,
63
+ continuationId: args.continuationId,
64
+ enrollmentId: enrollment._id,
65
+ credentialsAccountId: provisioned.account._id,
66
+ provider: args.provider,
67
+ now: args.now,
68
+ sessionExpirationTime: args.sessionExpirationTime,
69
+ refreshTokenExpirationTime: args.refreshTokenExpirationTime
70
+ });
71
+ if (completed.status === "rejected") throw new ConvexError({
72
+ code: ErrorCode.CONTINUATION_INVALID,
73
+ message: "Invalid or expired credentials enrollment continuation."
74
+ });
75
+ return completed;
76
+ }
77
+ /** Call the atomic enrollment-commit store mutation from an auth action. */
78
+ async function callCompleteCredentialEnrollment(ctx, args) {
79
+ return await ctx.runMutation(AUTH_STORE_REF, { args: {
80
+ type: "completeCredentialEnrollment",
81
+ ...args
82
+ } });
83
+ }
84
+
85
+ //#endregion
86
+ export { callCompleteCredentialEnrollment, completeCredentialEnrollmentImpl, vCompleteCredentialEnrollmentArgs };
87
+ //# sourceMappingURL=enrollment.js.map