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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/README.md +10 -11
  2. package/dist/authorization/index.d.ts +1 -1
  3. package/dist/authorization/index.js +1 -1
  4. package/dist/authorization/index.js.map +1 -1
  5. package/dist/client/index.d.ts +1 -2
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +36 -39
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/component/client/index.d.ts +1 -2
  10. package/dist/component/index.js +2 -2
  11. package/dist/component/model.d.ts +9 -9
  12. package/dist/component/model.d.ts.map +1 -1
  13. package/dist/component/public/enterprise/audit.d.ts.map +1 -1
  14. package/dist/component/public/enterprise/audit.js.map +1 -1
  15. package/dist/component/public/enterprise/core.d.ts.map +1 -1
  16. package/dist/component/public/enterprise/core.js.map +1 -1
  17. package/dist/component/public/enterprise/domains.d.ts.map +1 -1
  18. package/dist/component/public/enterprise/domains.js.map +1 -1
  19. package/dist/component/public/enterprise/scim.d.ts.map +1 -1
  20. package/dist/component/public/enterprise/scim.js.map +1 -1
  21. package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
  22. package/dist/component/public/enterprise/secrets.js.map +1 -1
  23. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
  24. package/dist/component/public/enterprise/webhooks.js.map +1 -1
  25. package/dist/component/public/factors/devices.d.ts.map +1 -1
  26. package/dist/component/public/factors/devices.js.map +1 -1
  27. package/dist/component/public/factors/passkeys.d.ts.map +1 -1
  28. package/dist/component/public/factors/passkeys.js.map +1 -1
  29. package/dist/component/public/factors/totp.d.ts.map +1 -1
  30. package/dist/component/public/factors/totp.js.map +1 -1
  31. package/dist/component/public/groups/core.js.map +1 -1
  32. package/dist/component/public/groups/invites.d.ts.map +1 -1
  33. package/dist/component/public/groups/invites.js.map +1 -1
  34. package/dist/component/public/groups/members.d.ts.map +1 -1
  35. package/dist/component/public/groups/members.js.map +1 -1
  36. package/dist/component/public/identity/accounts.d.ts.map +1 -1
  37. package/dist/component/public/identity/accounts.js.map +1 -1
  38. package/dist/component/public/identity/codes.d.ts.map +1 -1
  39. package/dist/component/public/identity/codes.js.map +1 -1
  40. package/dist/component/public/identity/sessions.d.ts.map +1 -1
  41. package/dist/component/public/identity/sessions.js.map +1 -1
  42. package/dist/component/public/identity/tokens.d.ts.map +1 -1
  43. package/dist/component/public/identity/tokens.js.map +1 -1
  44. package/dist/component/public/identity/users.d.ts.map +1 -1
  45. package/dist/component/public/identity/users.js.map +1 -1
  46. package/dist/component/public/identity/verifiers.d.ts.map +1 -1
  47. package/dist/component/public/identity/verifiers.js.map +1 -1
  48. package/dist/component/public/security/keys.d.ts.map +1 -1
  49. package/dist/component/public/security/keys.js.map +1 -1
  50. package/dist/component/public/security/limits.d.ts.map +1 -1
  51. package/dist/component/public/security/limits.js.map +1 -1
  52. package/dist/component/schema.d.ts +41 -41
  53. package/dist/component/server/auth.d.ts +127 -130
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +100 -64
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/context.js +53 -0
  58. package/dist/component/server/context.js.map +1 -0
  59. package/dist/component/server/core.js +113 -250
  60. package/dist/component/server/core.js.map +1 -1
  61. package/dist/component/server/crypto.js +25 -7
  62. package/dist/component/server/crypto.js.map +1 -1
  63. package/dist/component/server/device.js +59 -16
  64. package/dist/component/server/device.js.map +1 -1
  65. package/dist/component/server/enterprise/domain.js +148 -59
  66. package/dist/component/server/enterprise/domain.js.map +1 -1
  67. package/dist/component/server/enterprise/http.js +36 -15
  68. package/dist/component/server/enterprise/http.js.map +1 -1
  69. package/dist/component/server/enterprise/oidc.js +1 -1
  70. package/dist/component/server/http.d.ts +85 -0
  71. package/dist/component/server/http.d.ts.map +1 -0
  72. package/dist/component/server/http.js +85 -22
  73. package/dist/component/server/http.js.map +1 -1
  74. package/dist/component/server/identity.js +5 -2
  75. package/dist/component/server/identity.js.map +1 -1
  76. package/dist/component/server/limits.js +21 -30
  77. package/dist/component/server/limits.js.map +1 -1
  78. package/dist/component/server/mutations/account.js +12 -10
  79. package/dist/component/server/mutations/account.js.map +1 -1
  80. package/dist/component/server/mutations/code.js +5 -2
  81. package/dist/component/server/mutations/code.js.map +1 -1
  82. package/dist/component/server/mutations/invalidate.js +1 -1
  83. package/dist/component/server/mutations/invalidate.js.map +1 -1
  84. package/dist/component/server/mutations/oauth.js +10 -4
  85. package/dist/component/server/mutations/oauth.js.map +1 -1
  86. package/dist/component/server/mutations/refresh.js +2 -2
  87. package/dist/component/server/mutations/refresh.js.map +1 -1
  88. package/dist/component/server/mutations/register.js +46 -42
  89. package/dist/component/server/mutations/register.js.map +1 -1
  90. package/dist/component/server/mutations/retrieve.js +21 -25
  91. package/dist/component/server/mutations/retrieve.js.map +1 -1
  92. package/dist/component/server/mutations/signature.js +10 -4
  93. package/dist/component/server/mutations/signature.js.map +1 -1
  94. package/dist/component/server/mutations/signout.js.map +1 -1
  95. package/dist/component/server/mutations/store.js +9 -24
  96. package/dist/component/server/mutations/store.js.map +1 -1
  97. package/dist/component/server/mutations/verifier.js.map +1 -1
  98. package/dist/component/server/mutations/verify.js +1 -1
  99. package/dist/component/server/mutations/verify.js.map +1 -1
  100. package/dist/component/server/oauth.js +53 -16
  101. package/dist/component/server/oauth.js.map +1 -1
  102. package/dist/component/server/passkey.js +115 -31
  103. package/dist/component/server/passkey.js.map +1 -1
  104. package/dist/component/server/redirects.js +9 -3
  105. package/dist/component/server/redirects.js.map +1 -1
  106. package/dist/component/server/refresh.js +10 -7
  107. package/dist/component/server/refresh.js.map +1 -1
  108. package/dist/component/server/runtime.d.ts +5 -5
  109. package/dist/component/server/runtime.js +156 -113
  110. package/dist/component/server/runtime.js.map +1 -1
  111. package/dist/component/server/signin.js +34 -10
  112. package/dist/component/server/signin.js.map +1 -1
  113. package/dist/component/server/totp.js +79 -19
  114. package/dist/component/server/totp.js.map +1 -1
  115. package/dist/component/server/types.d.ts +12 -20
  116. package/dist/component/server/types.d.ts.map +1 -1
  117. package/dist/component/server/types.js.map +1 -1
  118. package/dist/component/server/users.js +6 -3
  119. package/dist/component/server/users.js.map +1 -1
  120. package/dist/component/server/utils.js +10 -4
  121. package/dist/component/server/utils.js.map +1 -1
  122. package/dist/core/types.d.ts +14 -22
  123. package/dist/core/types.d.ts.map +1 -1
  124. package/dist/factors/device.js +8 -9
  125. package/dist/factors/device.js.map +1 -1
  126. package/dist/factors/passkey.js +18 -21
  127. package/dist/factors/passkey.js.map +1 -1
  128. package/dist/providers/password.js +66 -81
  129. package/dist/providers/password.js.map +1 -1
  130. package/dist/runtime/invite.js +2 -8
  131. package/dist/runtime/invite.js.map +1 -1
  132. package/dist/server/auth.d.ts +127 -130
  133. package/dist/server/auth.d.ts.map +1 -1
  134. package/dist/server/auth.js +100 -64
  135. package/dist/server/auth.js.map +1 -1
  136. package/dist/server/context.d.ts +1 -0
  137. package/dist/server/context.js +53 -0
  138. package/dist/server/context.js.map +1 -0
  139. package/dist/server/core.d.ts +74 -195
  140. package/dist/server/core.d.ts.map +1 -1
  141. package/dist/server/core.js +113 -250
  142. package/dist/server/core.js.map +1 -1
  143. package/dist/server/crypto.d.ts.map +1 -1
  144. package/dist/server/crypto.js +25 -7
  145. package/dist/server/crypto.js.map +1 -1
  146. package/dist/server/device.js +59 -16
  147. package/dist/server/device.js.map +1 -1
  148. package/dist/server/enterprise/domain.d.ts +0 -8
  149. package/dist/server/enterprise/domain.d.ts.map +1 -1
  150. package/dist/server/enterprise/domain.js +148 -59
  151. package/dist/server/enterprise/domain.js.map +1 -1
  152. package/dist/server/enterprise/http.d.ts.map +1 -1
  153. package/dist/server/enterprise/http.js +35 -14
  154. package/dist/server/enterprise/http.js.map +1 -1
  155. package/dist/server/http.d.ts +81 -3
  156. package/dist/server/http.d.ts.map +1 -1
  157. package/dist/server/http.js +84 -21
  158. package/dist/server/http.js.map +1 -1
  159. package/dist/server/identity.js +5 -2
  160. package/dist/server/identity.js.map +1 -1
  161. package/dist/server/index.d.ts +3 -2
  162. package/dist/server/index.js +2 -2
  163. package/dist/server/limits.js +21 -30
  164. package/dist/server/limits.js.map +1 -1
  165. package/dist/server/mounts.d.ts +25 -63
  166. package/dist/server/mounts.d.ts.map +1 -1
  167. package/dist/server/mounts.js +46 -107
  168. package/dist/server/mounts.js.map +1 -1
  169. package/dist/server/mutations/account.d.ts +8 -9
  170. package/dist/server/mutations/account.d.ts.map +1 -1
  171. package/dist/server/mutations/account.js +11 -9
  172. package/dist/server/mutations/account.js.map +1 -1
  173. package/dist/server/mutations/code.d.ts +12 -12
  174. package/dist/server/mutations/code.d.ts.map +1 -1
  175. package/dist/server/mutations/code.js +5 -2
  176. package/dist/server/mutations/code.js.map +1 -1
  177. package/dist/server/mutations/invalidate.d.ts +4 -4
  178. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  179. package/dist/server/mutations/invalidate.js.map +1 -1
  180. package/dist/server/mutations/oauth.d.ts +14 -12
  181. package/dist/server/mutations/oauth.d.ts.map +1 -1
  182. package/dist/server/mutations/oauth.js +9 -3
  183. package/dist/server/mutations/oauth.js.map +1 -1
  184. package/dist/server/mutations/refresh.d.ts +3 -3
  185. package/dist/server/mutations/refresh.d.ts.map +1 -1
  186. package/dist/server/mutations/refresh.js +1 -1
  187. package/dist/server/mutations/refresh.js.map +1 -1
  188. package/dist/server/mutations/register.d.ts +11 -11
  189. package/dist/server/mutations/register.d.ts.map +1 -1
  190. package/dist/server/mutations/register.js +45 -41
  191. package/dist/server/mutations/register.js.map +1 -1
  192. package/dist/server/mutations/retrieve.d.ts +6 -6
  193. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  194. package/dist/server/mutations/retrieve.js +20 -24
  195. package/dist/server/mutations/retrieve.js.map +1 -1
  196. package/dist/server/mutations/signature.d.ts +6 -7
  197. package/dist/server/mutations/signature.d.ts.map +1 -1
  198. package/dist/server/mutations/signature.js +9 -3
  199. package/dist/server/mutations/signature.js.map +1 -1
  200. package/dist/server/mutations/signin.d.ts +5 -5
  201. package/dist/server/mutations/signout.js.map +1 -1
  202. package/dist/server/mutations/store.d.ts +83 -83
  203. package/dist/server/mutations/store.js +8 -23
  204. package/dist/server/mutations/store.js.map +1 -1
  205. package/dist/server/mutations/verifier.js.map +1 -1
  206. package/dist/server/mutations/verify.d.ts +7 -7
  207. package/dist/server/mutations/verify.d.ts.map +1 -1
  208. package/dist/server/mutations/verify.js.map +1 -1
  209. package/dist/server/oauth.js +53 -16
  210. package/dist/server/oauth.js.map +1 -1
  211. package/dist/server/passkey.d.ts +2 -2
  212. package/dist/server/passkey.d.ts.map +1 -1
  213. package/dist/server/passkey.js +114 -30
  214. package/dist/server/passkey.js.map +1 -1
  215. package/dist/server/redirects.js +9 -3
  216. package/dist/server/redirects.js.map +1 -1
  217. package/dist/server/refresh.js +10 -7
  218. package/dist/server/refresh.js.map +1 -1
  219. package/dist/server/runtime.d.ts +11 -11
  220. package/dist/server/runtime.js +155 -112
  221. package/dist/server/runtime.js.map +1 -1
  222. package/dist/server/signin.js +34 -10
  223. package/dist/server/signin.js.map +1 -1
  224. package/dist/server/ssr.d.ts.map +1 -1
  225. package/dist/server/ssr.js +175 -184
  226. package/dist/server/ssr.js.map +1 -1
  227. package/dist/server/totp.js +78 -18
  228. package/dist/server/totp.js.map +1 -1
  229. package/dist/server/types.d.ts +13 -21
  230. package/dist/server/types.d.ts.map +1 -1
  231. package/dist/server/types.js.map +1 -1
  232. package/dist/server/users.js +6 -3
  233. package/dist/server/users.js.map +1 -1
  234. package/dist/server/utils.js +10 -4
  235. package/dist/server/utils.js.map +1 -1
  236. package/package.json +1 -5
  237. package/src/authorization/index.ts +1 -1
  238. package/src/client/core/types.ts +14 -14
  239. package/src/client/factors/device.ts +10 -12
  240. package/src/client/factors/passkey.ts +23 -26
  241. package/src/client/index.ts +54 -64
  242. package/src/client/runtime/invite.ts +5 -7
  243. package/src/component/index.ts +9 -3
  244. package/src/component/public/enterprise/audit.ts +6 -1
  245. package/src/component/public/enterprise/core.ts +1 -0
  246. package/src/component/public/enterprise/domains.ts +5 -1
  247. package/src/component/public/enterprise/scim.ts +1 -0
  248. package/src/component/public/enterprise/secrets.ts +1 -0
  249. package/src/component/public/enterprise/webhooks.ts +1 -0
  250. package/src/component/public/factors/devices.ts +1 -0
  251. package/src/component/public/factors/passkeys.ts +1 -0
  252. package/src/component/public/factors/totp.ts +1 -0
  253. package/src/component/public/groups/core.ts +1 -1
  254. package/src/component/public/groups/invites.ts +7 -1
  255. package/src/component/public/groups/members.ts +1 -0
  256. package/src/component/public/identity/accounts.ts +1 -0
  257. package/src/component/public/identity/codes.ts +1 -0
  258. package/src/component/public/identity/sessions.ts +1 -0
  259. package/src/component/public/identity/tokens.ts +1 -0
  260. package/src/component/public/identity/users.ts +1 -0
  261. package/src/component/public/identity/verifiers.ts +1 -0
  262. package/src/component/public/security/keys.ts +1 -0
  263. package/src/component/public/security/limits.ts +1 -0
  264. package/src/providers/password.ts +89 -110
  265. package/src/server/auth.ts +240 -182
  266. package/src/server/context.ts +90 -0
  267. package/src/server/core.ts +195 -286
  268. package/src/server/crypto.ts +31 -29
  269. package/src/server/device.ts +65 -32
  270. package/src/server/enterprise/domain.ts +158 -170
  271. package/src/server/enterprise/http.ts +46 -39
  272. package/src/server/http.ts +289 -30
  273. package/src/server/identity.ts +5 -5
  274. package/src/server/index.ts +9 -3
  275. package/src/server/limits.ts +53 -80
  276. package/src/server/mounts.ts +56 -80
  277. package/src/server/mutations/account.ts +22 -36
  278. package/src/server/mutations/code.ts +6 -6
  279. package/src/server/mutations/invalidate.ts +1 -1
  280. package/src/server/mutations/oauth.ts +14 -8
  281. package/src/server/mutations/refresh.ts +5 -4
  282. package/src/server/mutations/register.ts +87 -132
  283. package/src/server/mutations/retrieve.ts +44 -44
  284. package/src/server/mutations/signature.ts +13 -6
  285. package/src/server/mutations/signout.ts +1 -1
  286. package/src/server/mutations/store.ts +16 -31
  287. package/src/server/mutations/verifier.ts +1 -1
  288. package/src/server/mutations/verify.ts +3 -5
  289. package/src/server/oauth.ts +60 -69
  290. package/src/server/passkey.ts +567 -517
  291. package/src/server/redirects.ts +10 -6
  292. package/src/server/refresh.ts +14 -18
  293. package/src/server/runtime.ts +340 -302
  294. package/src/server/signin.ts +44 -37
  295. package/src/server/ssr.ts +390 -407
  296. package/src/server/totp.ts +85 -35
  297. package/src/server/types.ts +19 -22
  298. package/src/server/users.ts +7 -6
  299. package/src/server/utils.ts +10 -12
  300. package/dist/component/server/authError.js +0 -34
  301. package/dist/component/server/authError.js.map +0 -1
  302. package/dist/component/server/errors.d.ts +0 -1
  303. package/dist/component/server/errors.js +0 -137
  304. package/dist/component/server/errors.js.map +0 -1
  305. package/dist/server/authError.d.ts +0 -46
  306. package/dist/server/authError.d.ts.map +0 -1
  307. package/dist/server/authError.js +0 -34
  308. package/dist/server/authError.js.map +0 -1
  309. package/dist/server/errors.d.ts +0 -177
  310. package/dist/server/errors.d.ts.map +0 -1
  311. package/dist/server/errors.js +0 -212
  312. package/dist/server/errors.js.map +0 -1
  313. package/src/server/authError.ts +0 -44
  314. package/src/server/errors.ts +0 -290
@@ -1,11 +1,10 @@
1
+ import { Fx } from "@robelest/fx";
2
+ import { Cv } from "@robelest/fx/convex";
1
3
  import type { GenericActionCtx, HttpRouter } from "convex/server";
4
+ import { ConvexError } from "convex/values";
2
5
  import { serialize as serializeCookie } from "cookie";
3
6
 
4
7
  import { redirectToParamCookie, useRedirectToParam } from "../cookies";
5
- import { isAuthError } from "../errors";
6
- import { Fx } from "@robelest/fx";
7
-
8
- import { AuthError } from "../authError";
9
8
  import { addSSORoutes, convertErrorsToResponse, getCookies } from "../http";
10
9
  import type { SSORuntimeRoute } from "../http";
11
10
  import { createOAuthAuthorizationURL, handleOAuthCallback } from "../oauth";
@@ -207,12 +206,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
207
206
  runtimeRoute.protocol !== "saml" ||
208
207
  runtimeRoute.rest.length !== 1 ||
209
208
  runtimeRoute.rest[0] !== "acs",
210
- Fx.fail(
211
- new AuthError(
212
- "INVALID_PARAMETERS",
213
- "Invalid enterprise runtime path.",
214
- ).toConvexError(),
215
- ),
209
+ Cv.fail({
210
+ code: "INVALID_PARAMETERS",
211
+ message: "Invalid enterprise runtime path.",
212
+ }),
216
213
  );
217
214
 
218
215
  const enterpriseId = runtimeRoute.enterpriseId;
@@ -230,10 +227,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
230
227
  config: loaded.config,
231
228
  }),
232
229
  err: (e) =>
233
- new AuthError(
234
- "OAUTH_PROVIDER_ERROR",
235
- `SAML response parse failed: ${e instanceof Error ? e.message : String(e)}`,
236
- ).toConvexError(),
230
+ Cv.error({
231
+ code: "OAUTH_PROVIDER_ERROR",
232
+ message: `SAML response parse failed: ${e instanceof Error ? e.message : String(e)}`,
233
+ }),
237
234
  });
238
235
 
239
236
  yield* Fx.from({
@@ -247,10 +244,11 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
247
244
  return Promise.resolve();
248
245
  },
249
246
  err: () =>
250
- new AuthError(
251
- "OAUTH_INVALID_STATE",
252
- "SAML RelayState did not match the pending login request.",
253
- ).toConvexError(),
247
+ Cv.error({
248
+ code: "OAUTH_INVALID_STATE",
249
+ message:
250
+ "SAML RelayState did not match the pending login request.",
251
+ }),
254
252
  });
255
253
 
256
254
  const { samlAttributes, samlSessionIndex, ...userProfile } =
@@ -331,10 +329,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
331
329
  runtimeRoute.rest.length !== 1 ||
332
330
  runtimeRoute.rest[0] !== "slo"
333
331
  ) {
334
- throw new AuthError(
335
- "INVALID_PARAMETERS",
336
- "Invalid enterprise runtime path.",
337
- ).toConvexError();
332
+ throw Cv.error({
333
+ code: "INVALID_PARAMETERS",
334
+ message: "Invalid enterprise runtime path.",
335
+ });
338
336
  }
339
337
  const { loaded, enterprise } = await loadActiveEnterpriseSamlOrThrow(
340
338
  ctx,
@@ -371,10 +369,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
371
369
  if (parsedMessage.hasSamlResponse) {
372
370
  return new Response(null, { status: 204 });
373
371
  }
374
- throw new AuthError(
375
- "INVALID_PARAMETERS",
376
- "Missing SAML logout payload.",
377
- ).toConvexError();
372
+ throw Cv.error({
373
+ code: "INVALID_PARAMETERS",
374
+ message: "Missing SAML logout payload.",
375
+ });
378
376
  };
379
377
 
380
378
  const handleScimRequest = async (
@@ -645,7 +643,7 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
645
643
  userId,
646
644
  data: patchData,
647
645
  });
648
- const resolution = await auth.member.resolve(state.ctx, {
646
+ const resolution = await auth.member.inspect(state.ctx, {
649
647
  groupId: state.enterprise.groupId,
650
648
  userId,
651
649
  });
@@ -711,7 +709,7 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
711
709
  );
712
710
  if (missing) return missing;
713
711
  const userId = state.parsedPath.resourceId!;
714
- const resolution = await auth.member.resolve(state.ctx, {
712
+ const resolution = await auth.member.inspect(state.ctx, {
715
713
  groupId: state.enterprise.groupId,
716
714
  userId,
717
715
  });
@@ -961,15 +959,12 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
961
959
  );
962
960
  const userId = match?.[1];
963
961
  if (userId) {
964
- const resolution = await auth.member.resolve(
965
- state.ctx,
966
- { groupId, userId },
967
- );
962
+ const resolution = await auth.member.inspect(state.ctx, {
963
+ groupId,
964
+ userId,
965
+ });
968
966
  if (resolution.membership) {
969
- await auth.member.delete(
970
- state.ctx,
971
- resolution.membership._id,
972
- );
967
+ await auth.member.delete(state.ctx, resolution.membership._id);
973
968
  }
974
969
  }
975
970
  }
@@ -1105,7 +1100,13 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
1105
1100
  ) {
1106
1101
  return scimError(400, "invalidFilter", error.message);
1107
1102
  }
1108
- if (isAuthError(error)) {
1103
+ if (
1104
+ error instanceof ConvexError &&
1105
+ typeof error.data === "object" &&
1106
+ error.data !== null &&
1107
+ "code" in error.data &&
1108
+ "message" in error.data
1109
+ ) {
1109
1110
  const code = error.data.code as string;
1110
1111
  const status =
1111
1112
  code === "MISSING_BEARER_TOKEN" || code === "INVALID_API_KEY"
@@ -1141,7 +1142,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
1141
1142
  const url = new URL(request.url);
1142
1143
  const verifier = url.searchParams.get("code");
1143
1144
  if (!verifier) {
1144
- throw new AuthError("OAUTH_MISSING_VERIFIER").toConvexError();
1145
+ throw Cv.error({
1146
+ code: "OAUTH_MISSING_VERIFIER",
1147
+ message: "Missing sign-in verifier.",
1148
+ });
1145
1149
  }
1146
1150
  const { loaded, enterprise } = await loadActiveEnterpriseSamlOrThrow(
1147
1151
  ctx,
@@ -1204,7 +1208,10 @@ export function addEnterpriseHttpRuntime(deps: EnterpriseHttpRuntimeDeps) {
1204
1208
  const url = new URL(request.url);
1205
1209
  const verifier = url.searchParams.get("code");
1206
1210
  if (!verifier) {
1207
- throw new AuthError("OAUTH_MISSING_VERIFIER").toConvexError();
1211
+ throw Cv.error({
1212
+ code: "OAUTH_MISSING_VERIFIER",
1213
+ message: "Missing sign-in verifier.",
1214
+ });
1208
1215
  }
1209
1216
  const { enterprise, oidc } = await loadEnterpriseOidcOrThrow(
1210
1217
  ctx,
@@ -1,3 +1,5 @@
1
+ import { Fx } from "@robelest/fx";
2
+ import { Cv } from "@robelest/fx/convex";
1
3
  import {
2
4
  GenericActionCtx,
3
5
  GenericDataModel,
@@ -7,13 +9,262 @@ import {
7
9
  import { ConvexError } from "convex/values";
8
10
  import { parse as parseCookies } from "cookie";
9
11
 
10
- import { isAuthError } from "./errors";
11
- import { Fx } from "@robelest/fx";
12
-
13
- import { AuthError } from "./authError";
12
+ import type {
13
+ AuthContext,
14
+ OptionalAuthContext,
15
+ UserDoc,
16
+ } from "./auth";
17
+ import {
18
+ createUnauthenticatedAuthContext,
19
+ getAuthContextForUser,
20
+ getSessionUserId,
21
+ } from "./context";
14
22
  import type { CorsConfig, HttpKeyContext } from "./types";
15
23
  import { logError } from "./utils";
16
24
 
25
+ type HttpContextAuthLike = {
26
+ user: {
27
+ get: (ctx: any, userId: string) => Promise<UserDoc>;
28
+ getActiveGroup: (
29
+ ctx: any,
30
+ args: { userId: string },
31
+ ) => Promise<string | null>;
32
+ };
33
+ member: {
34
+ inspect: (
35
+ ctx: any,
36
+ args: { userId: string; groupId: string },
37
+ ) => Promise<{
38
+ membership: unknown;
39
+ roleIds: string[];
40
+ grants: string[];
41
+ }>;
42
+ };
43
+ key: {
44
+ verify: (ctx: GenericActionCtx<any>, rawKey: string) => Promise<{
45
+ userId: string;
46
+ keyId: string;
47
+ scopes: HttpKeyContext["key"]["scopes"];
48
+ }>;
49
+ };
50
+ };
51
+
52
+ /**
53
+ * Auth context returned by `auth.http.context(ctx, request)`.
54
+ *
55
+ * This resolves raw HTTP authentication in two steps:
56
+ * 1. session auth from `ctx.auth.getUserIdentity()`
57
+ * 2. API key auth from `Authorization: Bearer sk_*`
58
+ *
59
+ * The `source` field tells you which authentication path succeeded.
60
+ * When `source === "key"`, the verified API key metadata is available on
61
+ * `key`.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const authContext = await auth.http.context(ctx, request);
66
+ * if (authContext.source === "key") {
67
+ * console.log(authContext.key.keyId);
68
+ * }
69
+ * ```
70
+ */
71
+ export type HttpAuthContext =
72
+ | (AuthContext & {
73
+ /** The request authenticated through a browser or session token. */
74
+ source: "session";
75
+ /** No API key was used for this request. */
76
+ key: null;
77
+ })
78
+ | (AuthContext & {
79
+ /** The request authenticated through an API key. */
80
+ source: "key";
81
+ /** Verified API key metadata for the request. */
82
+ key: HttpKeyContext["key"];
83
+ });
84
+
85
+ /**
86
+ * Nullable HTTP auth context returned by
87
+ * `auth.http.context(ctx, request, { optional: true })`.
88
+ *
89
+ * This preserves a stable auth-shaped object for raw `httpAction` handlers
90
+ * that allow anonymous callers.
91
+ */
92
+ export type OptionalHttpAuthContext =
93
+ | (OptionalAuthContext & {
94
+ /** No authentication source was resolved. */
95
+ source: null;
96
+ /** No API key metadata is available. */
97
+ key: null;
98
+ })
99
+ | HttpAuthContext;
100
+
101
+ /**
102
+ * Configuration for {@link createAuth().http.context}.
103
+ *
104
+ * This mirrors {@link AuthContextConfig} for raw HTTP handlers and adds support
105
+ * for enriching mixed session/API-key auth results.
106
+ *
107
+ * @typeParam TResolve - Extra fields returned from `resolve()` and merged into
108
+ * the resolved HTTP auth context.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const authContext = await auth.http.context(ctx, request, {
113
+ * resolve: async (_ctx, user, authState) => ({
114
+ * email: user.email,
115
+ * isMachineRequest: authState.source === "key",
116
+ * }),
117
+ * });
118
+ * ```
119
+ */
120
+ export type HttpAuthContextConfig<
121
+ TResolve extends Record<string, unknown> = Record<string, never>,
122
+ > = {
123
+ /**
124
+ * Allow unauthenticated callers and return a null-shaped auth object instead
125
+ * of throwing `NOT_SIGNED_IN`.
126
+ */
127
+ optional?: boolean;
128
+ /**
129
+ * Attach additional derived fields to the resolved HTTP auth context.
130
+ *
131
+ * This callback runs only when authentication succeeds.
132
+ */
133
+ resolve?: (
134
+ ctx: GenericActionCtx<any>,
135
+ user: UserDoc,
136
+ auth: HttpAuthContext,
137
+ ) => Promise<TResolve> | TResolve;
138
+ /**
139
+ * Override or wrap HTTP auth resolution.
140
+ *
141
+ * Return `undefined` to use the built-in session-or-key resolver, `null` for
142
+ * an explicit unauthenticated state, or a fully resolved
143
+ * {@link HttpAuthContext}.
144
+ */
145
+ authResolve?: (
146
+ ctx: GenericActionCtx<any>,
147
+ fallback: () => Promise<HttpAuthContext | null>,
148
+ ) =>
149
+ | Promise<HttpAuthContext | null | undefined>
150
+ | HttpAuthContext
151
+ | null
152
+ | undefined;
153
+ };
154
+
155
+ function createNotSignedInError() {
156
+ return Cv.error({
157
+ code: "NOT_SIGNED_IN",
158
+ message: "Authentication required.",
159
+ });
160
+ }
161
+
162
+ async function getHttpKeyContext(
163
+ auth: HttpContextAuthLike,
164
+ ctx: GenericActionCtx<any>,
165
+ request: Request,
166
+ ): Promise<HttpAuthContext | null> {
167
+ const authHeader = request.headers.get("Authorization");
168
+ if (!authHeader?.startsWith("Bearer sk_")) {
169
+ return null;
170
+ }
171
+
172
+ try {
173
+ const verified = await auth.key.verify(ctx, authHeader.slice(7));
174
+ const authContext = await getAuthContextForUser(auth, ctx, verified.userId);
175
+ return {
176
+ ...authContext,
177
+ source: "key",
178
+ key: {
179
+ userId: verified.userId,
180
+ keyId: verified.keyId,
181
+ scopes: verified.scopes,
182
+ },
183
+ };
184
+ } catch {
185
+ return null;
186
+ }
187
+ }
188
+
189
+ async function resolveHttpAuthContext(
190
+ auth: HttpContextAuthLike,
191
+ ctx: GenericActionCtx<any>,
192
+ request: Request,
193
+ ): Promise<HttpAuthContext | null> {
194
+ const sessionUserId = await getSessionUserId(ctx);
195
+ if (sessionUserId !== null) {
196
+ const authContext = await getAuthContextForUser(auth, ctx, sessionUserId);
197
+ return {
198
+ ...authContext,
199
+ source: "session",
200
+ key: null,
201
+ };
202
+ }
203
+
204
+ return await getHttpKeyContext(auth, ctx, request);
205
+ }
206
+
207
+ /**
208
+ * @internal
209
+ * Create the implementation behind `auth.http.context(...)`.
210
+ */
211
+ export function createHttpContext(auth: HttpContextAuthLike): {
212
+ <TResolve extends Record<string, unknown> = Record<string, never>>(
213
+ ctx: GenericActionCtx<any>,
214
+ request: Request,
215
+ config: HttpAuthContextConfig<TResolve> & { optional: true },
216
+ ): Promise<OptionalHttpAuthContext & TResolve>;
217
+ <TResolve extends Record<string, unknown> = Record<string, never>>(
218
+ ctx: GenericActionCtx<any>,
219
+ request: Request,
220
+ config?: HttpAuthContextConfig<TResolve>,
221
+ ): Promise<HttpAuthContext & TResolve>;
222
+ } {
223
+ return (async (
224
+ ctx: GenericActionCtx<any>,
225
+ request: Request,
226
+ config?: HttpAuthContextConfig<any>,
227
+ ) => {
228
+ const fallback = () => resolveHttpAuthContext(auth, ctx, request);
229
+ const authOverride = config?.authResolve
230
+ ? await config.authResolve(ctx, fallback)
231
+ : undefined;
232
+ const resolved =
233
+ authOverride === undefined ? await fallback() : authOverride;
234
+
235
+ if (resolved === null) {
236
+ if (config?.optional !== true) {
237
+ throw createNotSignedInError();
238
+ }
239
+ return {
240
+ ...createUnauthenticatedAuthContext(),
241
+ source: null,
242
+ key: null,
243
+ };
244
+ }
245
+
246
+ const extra = config?.resolve
247
+ ? await config.resolve(ctx, resolved.user, resolved)
248
+ : {};
249
+
250
+ return {
251
+ ...resolved,
252
+ ...extra,
253
+ };
254
+ }) as {
255
+ <TResolve extends Record<string, unknown> = Record<string, never>>(
256
+ ctx: GenericActionCtx<any>,
257
+ request: Request,
258
+ config: HttpAuthContextConfig<TResolve> & { optional: true },
259
+ ): Promise<OptionalHttpAuthContext & TResolve>;
260
+ <TResolve extends Record<string, unknown> = Record<string, never>>(
261
+ ctx: GenericActionCtx<any>,
262
+ request: Request,
263
+ config?: HttpAuthContextConfig<TResolve>,
264
+ ): Promise<HttpAuthContext & TResolve>;
265
+ };
266
+ }
267
+
17
268
  export function createHttpAction(auth: {
18
269
  key: { verify: (ctx: GenericActionCtx<any>, rawKey: string) => Promise<any> };
19
270
  }) {
@@ -59,19 +310,21 @@ export function createHttpAction(auth: {
59
310
  const rawKey = authHeader.slice(7);
60
311
 
61
312
  const keyResult = await Fx.run(
62
- Fx.from({
63
- ok: () => auth.key.verify(genericCtx, rawKey),
64
- err: (error) => error,
65
- }).pipe(
66
- Fx.fold({
67
- ok: (result) => ({ ok: true, value: result }) as const,
68
- err: (error) => ({ ok: false, error }) as const,
69
- }),
313
+ Fx.attempt(
314
+ () => auth.key.verify(genericCtx, rawKey),
315
+ (result) => ({ ok: true, value: result }) as const,
316
+ (error) => ({ ok: false, error }) as const,
70
317
  ),
71
318
  );
72
319
 
73
320
  if (!keyResult.ok) {
74
- if (isAuthError(keyResult.error)) {
321
+ if (
322
+ keyResult.error instanceof ConvexError &&
323
+ typeof keyResult.error.data === "object" &&
324
+ keyResult.error.data !== null &&
325
+ "code" in keyResult.error.data &&
326
+ "message" in keyResult.error.data
327
+ ) {
75
328
  const { code, message } = keyResult.error.data as {
76
329
  code: string;
77
330
  message: string;
@@ -219,7 +472,13 @@ export function convertErrorsToResponse(
219
472
  err: (error) => error,
220
473
  }).pipe(
221
474
  Fx.recover((error) => {
222
- if (isAuthError(error)) {
475
+ if (
476
+ error instanceof ConvexError &&
477
+ typeof error.data === "object" &&
478
+ error.data !== null &&
479
+ "code" in error.data &&
480
+ "message" in error.data
481
+ ) {
223
482
  return Fx.succeed(
224
483
  new Response(
225
484
  JSON.stringify({
@@ -426,10 +685,10 @@ export function addSSORoutes(
426
685
  deps.routeBase,
427
686
  );
428
687
  if (!route) {
429
- throw new AuthError(
430
- "INVALID_PARAMETERS",
431
- "Invalid enterprise runtime path.",
432
- ).toConvexError();
688
+ throw Cv.error({
689
+ code: "INVALID_PARAMETERS",
690
+ message: "Invalid enterprise runtime path.",
691
+ });
433
692
  }
434
693
  if (route.protocol === "saml" && route.rest.length === 1) {
435
694
  if (route.rest[0] === "metadata") {
@@ -456,10 +715,10 @@ export function addSSORoutes(
456
715
  if (route.protocol === "scim" && route.rest[0] === "v2") {
457
716
  return await deps.handleScimRequest(ctx, request);
458
717
  }
459
- throw new AuthError(
460
- "INVALID_PARAMETERS",
461
- "Invalid enterprise runtime path.",
462
- ).toConvexError();
718
+ throw Cv.error({
719
+ code: "INVALID_PARAMETERS",
720
+ message: "Invalid enterprise runtime path.",
721
+ });
463
722
  }),
464
723
  ),
465
724
  });
@@ -484,10 +743,10 @@ export function addSSORoutes(
484
743
  if (route?.protocol === "scim" && route.rest[0] === "v2") {
485
744
  return await deps.handleScimRequest(ctx, request);
486
745
  }
487
- throw new AuthError(
488
- "INVALID_PARAMETERS",
489
- "Invalid enterprise runtime path.",
490
- ).toConvexError();
746
+ throw Cv.error({
747
+ code: "INVALID_PARAMETERS",
748
+ message: "Invalid enterprise runtime path.",
749
+ });
491
750
  }),
492
751
  ),
493
752
  });
@@ -504,10 +763,10 @@ export function addSSORoutes(
504
763
  if (route?.protocol === "scim" && route.rest[0] === "v2") {
505
764
  return await deps.handleScimRequest(ctx, request);
506
765
  }
507
- throw new AuthError(
508
- "INVALID_PARAMETERS",
509
- "Invalid enterprise runtime path.",
510
- ).toConvexError();
766
+ throw Cv.error({
767
+ code: "INVALID_PARAMETERS",
768
+ message: "Invalid enterprise runtime path.",
769
+ });
511
770
  }),
512
771
  ),
513
772
  });
@@ -1,4 +1,4 @@
1
- import { AuthError } from "./authError";
1
+ import { Cv } from "@robelest/fx/convex";
2
2
 
3
3
  /** @internal */
4
4
  export function userIdFromIdentitySubject(subject: string): string {
@@ -9,10 +9,10 @@ export function userIdFromIdentitySubject(subject: string): string {
9
9
  rest.length === 0 ||
10
10
  rest.some((segment) => segment.length === 0)
11
11
  ) {
12
- throw new AuthError(
13
- "INTERNAL_ERROR",
14
- "Authenticated identity subject is malformed.",
15
- );
12
+ throw Cv.error({
13
+ code: "INTERNAL_ERROR",
14
+ message: "Authenticated identity subject is malformed.",
15
+ });
16
16
  }
17
17
  return userId;
18
18
  }
@@ -1,15 +1,21 @@
1
- export { AuthCtx, createAuth } from "./auth";
1
+ export { createAuth } from "./auth";
2
2
  export type {
3
3
  AuthApi,
4
4
  AuthApiBase,
5
+ AuthContext,
6
+ AuthContextConfig,
5
7
  AuthConfig,
6
- AuthCtxConfig,
7
- AuthResolvedContext,
8
8
  ConvexAuthResult,
9
9
  InferAuth,
10
10
  InferClientApi,
11
+ OptionalAuthContext,
11
12
  UserDoc,
12
13
  } from "./auth";
14
+ export type {
15
+ HttpAuthContext,
16
+ HttpAuthContextConfig,
17
+ OptionalHttpAuthContext,
18
+ } from "./http";
13
19
  export type {
14
20
  EnterpriseAdminAuthorizationInput,
15
21
  EnterpriseAdminPermission,