@robelest/convex-auth 0.0.3-preview → 0.0.3-preview.3

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 (304) hide show
  1. package/dist/bin.cjs +15 -15
  2. package/dist/client/index.d.ts +40 -12
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +73 -12
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +2 -2
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/component.d.ts +1 -1
  9. package/dist/component/_generated/component.d.ts.map +1 -1
  10. package/dist/component/{portalBridge.d.ts → bridge.d.ts} +2 -2
  11. package/dist/component/bridge.d.ts.map +1 -0
  12. package/dist/component/{portalBridge.js → bridge.js} +2 -2
  13. package/dist/component/bridge.js.map +1 -0
  14. package/dist/component/index.d.ts +11 -4
  15. package/dist/component/index.d.ts.map +1 -1
  16. package/dist/component/index.js +8 -2
  17. package/dist/component/index.js.map +1 -1
  18. package/dist/component/public.d.ts +24 -17
  19. package/dist/component/public.d.ts.map +1 -1
  20. package/dist/component/public.js +23 -4
  21. package/dist/component/public.js.map +1 -1
  22. package/dist/component/schema.d.ts +11 -7
  23. package/dist/component/schema.d.ts.map +1 -1
  24. package/dist/component/schema.js +4 -1
  25. package/dist/component/schema.js.map +1 -1
  26. package/dist/providers/anonymous.d.ts +3 -0
  27. package/dist/providers/anonymous.d.ts.map +1 -1
  28. package/dist/providers/anonymous.js +3 -0
  29. package/dist/providers/anonymous.js.map +1 -1
  30. package/dist/providers/credentials.d.ts +3 -0
  31. package/dist/providers/credentials.d.ts.map +1 -1
  32. package/dist/providers/credentials.js +3 -0
  33. package/dist/providers/credentials.js.map +1 -1
  34. package/dist/providers/email.d.ts +3 -0
  35. package/dist/providers/email.d.ts.map +1 -1
  36. package/dist/providers/email.js +3 -0
  37. package/dist/providers/email.js.map +1 -1
  38. package/dist/providers/passkey.d.ts +7 -1
  39. package/dist/providers/passkey.d.ts.map +1 -1
  40. package/dist/providers/passkey.js +7 -1
  41. package/dist/providers/passkey.js.map +1 -1
  42. package/dist/providers/password.d.ts +3 -0
  43. package/dist/providers/password.d.ts.map +1 -1
  44. package/dist/providers/password.js +3 -0
  45. package/dist/providers/password.js.map +1 -1
  46. package/dist/providers/phone.d.ts +3 -0
  47. package/dist/providers/phone.d.ts.map +1 -1
  48. package/dist/providers/phone.js +3 -0
  49. package/dist/providers/phone.js.map +1 -1
  50. package/dist/providers/totp.d.ts +8 -0
  51. package/dist/providers/totp.d.ts.map +1 -1
  52. package/dist/providers/totp.js +8 -0
  53. package/dist/providers/totp.js.map +1 -1
  54. package/dist/server/{convex-auth.d.ts → auth.d.ts} +226 -36
  55. package/dist/server/auth.d.ts.map +1 -0
  56. package/dist/server/{convex-auth.js → auth.js} +287 -111
  57. package/dist/server/auth.js.map +1 -0
  58. package/dist/server/errors.d.ts +148 -0
  59. package/dist/server/errors.d.ts.map +1 -0
  60. package/dist/server/errors.js +179 -0
  61. package/dist/server/errors.js.map +1 -0
  62. package/dist/server/implementation/index.d.ts +170 -48
  63. package/dist/server/implementation/index.d.ts.map +1 -1
  64. package/dist/server/implementation/index.js +383 -167
  65. package/dist/server/implementation/index.js.map +1 -1
  66. package/dist/server/implementation/{apiKey.d.ts → keys.d.ts} +1 -1
  67. package/dist/server/implementation/keys.d.ts.map +1 -0
  68. package/dist/server/implementation/{apiKey.js → keys.js} +4 -5
  69. package/dist/server/implementation/keys.js.map +1 -0
  70. package/dist/server/implementation/mutations/{modifyAccount.d.ts → account.d.ts} +3 -3
  71. package/dist/server/implementation/mutations/account.d.ts.map +1 -0
  72. package/dist/server/implementation/mutations/{modifyAccount.js → account.js} +4 -3
  73. package/dist/server/implementation/mutations/account.js.map +1 -0
  74. package/dist/server/implementation/mutations/{createVerificationCode.d.ts → code.d.ts} +1 -1
  75. package/dist/server/implementation/mutations/code.d.ts.map +1 -0
  76. package/dist/server/implementation/mutations/{createVerificationCode.js → code.js} +2 -2
  77. package/dist/server/implementation/mutations/code.js.map +1 -0
  78. package/dist/server/implementation/mutations/index.d.ts +33 -33
  79. package/dist/server/implementation/mutations/index.d.ts.map +1 -1
  80. package/dist/server/implementation/mutations/index.js +22 -22
  81. package/dist/server/implementation/mutations/index.js.map +1 -1
  82. package/dist/server/implementation/mutations/{invalidateSessions.d.ts → invalidate.d.ts} +1 -1
  83. package/dist/server/implementation/mutations/invalidate.d.ts.map +1 -0
  84. package/dist/server/implementation/mutations/{invalidateSessions.js → invalidate.js} +2 -2
  85. package/dist/server/implementation/mutations/invalidate.js.map +1 -0
  86. package/dist/server/implementation/mutations/{userOAuth.d.ts → oauth.d.ts} +3 -3
  87. package/dist/server/implementation/mutations/oauth.d.ts.map +1 -0
  88. package/dist/server/implementation/mutations/{userOAuth.js → oauth.js} +4 -3
  89. package/dist/server/implementation/mutations/oauth.js.map +1 -0
  90. package/dist/server/implementation/mutations/{refreshSession.d.ts → refresh.d.ts} +1 -1
  91. package/dist/server/implementation/mutations/refresh.d.ts.map +1 -0
  92. package/dist/server/implementation/mutations/{refreshSession.js → refresh.js} +3 -3
  93. package/dist/server/implementation/mutations/refresh.js.map +1 -0
  94. package/dist/server/implementation/mutations/{createAccountFromCredentials.d.ts → register.d.ts} +4 -4
  95. package/dist/server/implementation/mutations/register.d.ts.map +1 -0
  96. package/dist/server/implementation/mutations/{createAccountFromCredentials.js → register.js} +4 -3
  97. package/dist/server/implementation/mutations/register.js.map +1 -0
  98. package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.d.ts → retrieve.d.ts} +3 -3
  99. package/dist/server/implementation/mutations/retrieve.d.ts.map +1 -0
  100. package/dist/server/implementation/mutations/{retrieveAccountWithCredentials.js → retrieve.js} +3 -3
  101. package/dist/server/implementation/mutations/retrieve.js.map +1 -0
  102. package/dist/server/implementation/mutations/{verifierSignature.d.ts → signature.d.ts} +1 -1
  103. package/dist/server/implementation/mutations/signature.d.ts.map +1 -0
  104. package/dist/server/implementation/mutations/{verifierSignature.js → signature.js} +4 -3
  105. package/dist/server/implementation/mutations/signature.js.map +1 -0
  106. package/dist/server/implementation/mutations/{signIn.d.ts → signin.d.ts} +1 -1
  107. package/dist/server/implementation/mutations/{signIn.d.ts.map → signin.d.ts.map} +1 -1
  108. package/dist/server/implementation/mutations/{signIn.js → signin.js} +2 -2
  109. package/dist/server/implementation/mutations/{signIn.js.map → signin.js.map} +1 -1
  110. package/dist/server/implementation/mutations/{signOut.d.ts → signout.d.ts} +1 -1
  111. package/dist/server/implementation/mutations/{signOut.d.ts.map → signout.d.ts.map} +1 -1
  112. package/dist/server/implementation/mutations/{signOut.js → signout.js} +2 -2
  113. package/dist/server/implementation/mutations/{signOut.js.map → signout.js.map} +1 -1
  114. package/dist/server/implementation/mutations/{storeRef.d.ts → store.d.ts} +1 -1
  115. package/dist/server/implementation/mutations/store.d.ts.map +1 -0
  116. package/dist/server/implementation/mutations/{storeRef.js → store.js} +1 -1
  117. package/dist/server/implementation/mutations/store.js.map +1 -0
  118. package/dist/server/implementation/mutations/verifier.js +1 -1
  119. package/dist/server/implementation/mutations/verifier.js.map +1 -1
  120. package/dist/server/implementation/mutations/{verifyCodeAndSignIn.d.ts → verify.d.ts} +1 -1
  121. package/dist/server/implementation/mutations/verify.d.ts.map +1 -0
  122. package/dist/server/implementation/mutations/{verifyCodeAndSignIn.js → verify.js} +3 -3
  123. package/dist/server/implementation/mutations/verify.js.map +1 -0
  124. package/dist/server/implementation/passkey.d.ts.map +1 -1
  125. package/dist/server/implementation/passkey.js +47 -55
  126. package/dist/server/implementation/passkey.js.map +1 -1
  127. package/dist/server/implementation/provider.d.ts.map +1 -1
  128. package/dist/server/implementation/provider.js +5 -4
  129. package/dist/server/implementation/provider.js.map +1 -1
  130. package/dist/server/implementation/{rateLimit.d.ts → ratelimit.d.ts} +1 -1
  131. package/dist/server/implementation/{rateLimit.d.ts.map → ratelimit.d.ts.map} +1 -1
  132. package/dist/server/implementation/{rateLimit.js → ratelimit.js} +1 -1
  133. package/dist/server/implementation/{rateLimit.js.map → ratelimit.js.map} +1 -1
  134. package/dist/server/implementation/redirects.d.ts.map +1 -1
  135. package/dist/server/implementation/redirects.js +2 -1
  136. package/dist/server/implementation/redirects.js.map +1 -1
  137. package/dist/server/implementation/{refreshTokens.d.ts → refresh.d.ts} +1 -1
  138. package/dist/server/implementation/refresh.d.ts.map +1 -0
  139. package/dist/server/implementation/{refreshTokens.js → refresh.js} +3 -2
  140. package/dist/server/implementation/refresh.js.map +1 -0
  141. package/dist/server/implementation/sessions.js +1 -1
  142. package/dist/server/implementation/sessions.js.map +1 -1
  143. package/dist/server/implementation/{signIn.d.ts → signin.d.ts} +1 -1
  144. package/dist/server/implementation/{signIn.d.ts.map → signin.d.ts.map} +1 -1
  145. package/dist/server/implementation/{signIn.js → signin.js} +12 -8
  146. package/dist/server/implementation/signin.js.map +1 -0
  147. package/dist/server/implementation/totp.d.ts.map +1 -1
  148. package/dist/server/implementation/totp.js +29 -29
  149. package/dist/server/implementation/totp.js.map +1 -1
  150. package/dist/server/implementation/types.d.ts +131 -1
  151. package/dist/server/implementation/types.d.ts.map +1 -1
  152. package/dist/server/implementation/types.js +65 -1
  153. package/dist/server/implementation/types.js.map +1 -1
  154. package/dist/server/implementation/users.d.ts.map +1 -1
  155. package/dist/server/implementation/users.js +3 -2
  156. package/dist/server/implementation/users.js.map +1 -1
  157. package/dist/server/index.d.ts +131 -1
  158. package/dist/server/index.d.ts.map +1 -1
  159. package/dist/server/index.js +117 -1
  160. package/dist/server/index.js.map +1 -1
  161. package/dist/server/oauth/{authorizationUrl.d.ts → authorization.d.ts} +1 -1
  162. package/dist/server/oauth/authorization.d.ts.map +1 -0
  163. package/dist/server/oauth/{authorizationUrl.js → authorization.js} +4 -3
  164. package/dist/server/oauth/authorization.js.map +1 -0
  165. package/dist/server/oauth/callback.d.ts.map +1 -1
  166. package/dist/server/oauth/callback.js +7 -6
  167. package/dist/server/oauth/callback.js.map +1 -1
  168. package/dist/server/oauth/checks.d.ts.map +1 -1
  169. package/dist/server/oauth/checks.js +2 -1
  170. package/dist/server/oauth/checks.js.map +1 -1
  171. package/dist/server/oauth/{convexAuth.d.ts → helpers.d.ts} +1 -1
  172. package/dist/server/oauth/helpers.d.ts.map +1 -0
  173. package/dist/server/oauth/{convexAuth.js → helpers.js} +6 -5
  174. package/dist/server/oauth/helpers.js.map +1 -0
  175. package/dist/server/oauth/lib/utils/{customFetch.d.ts → fetch.d.ts} +1 -1
  176. package/dist/server/oauth/lib/utils/fetch.d.ts.map +1 -0
  177. package/dist/server/oauth/lib/utils/{customFetch.js → fetch.js} +1 -1
  178. package/dist/server/oauth/lib/utils/fetch.js.map +1 -0
  179. package/dist/server/{provider_utils.d.ts → providers.d.ts} +1 -1
  180. package/dist/server/providers.d.ts.map +1 -0
  181. package/dist/server/{provider_utils.js → providers.js} +1 -1
  182. package/dist/server/providers.js.map +1 -0
  183. package/dist/server/{email-templates.d.ts → templates.d.ts} +8 -1
  184. package/dist/server/templates.d.ts.map +1 -0
  185. package/dist/server/{portal-email.js → templates.js} +74 -3
  186. package/dist/server/templates.js.map +1 -0
  187. package/dist/server/types.d.ts +88 -5
  188. package/dist/server/types.d.ts.map +1 -1
  189. package/dist/server/utils.d.ts.map +1 -1
  190. package/dist/server/utils.js +2 -1
  191. package/dist/server/utils.js.map +1 -1
  192. package/dist/server/version.d.ts +1 -1
  193. package/dist/server/version.d.ts.map +1 -1
  194. package/dist/server/version.js +1 -1
  195. package/dist/server/version.js.map +1 -1
  196. package/package.json +5 -1
  197. package/src/cli/index.ts +5 -5
  198. package/src/cli/{portal-link.ts → link.ts} +1 -1
  199. package/src/cli/utils.ts +1 -1
  200. package/src/client/index.ts +102 -17
  201. package/src/component/_generated/api.ts +2 -2
  202. package/src/component/_generated/component.ts +1 -1
  203. package/src/component/{portalBridge.ts → bridge.ts} +2 -2
  204. package/src/component/index.ts +10 -2
  205. package/src/component/public.ts +25 -4
  206. package/src/component/schema.ts +4 -1
  207. package/src/providers/anonymous.ts +3 -0
  208. package/src/providers/credentials.ts +3 -0
  209. package/src/providers/email.ts +3 -0
  210. package/src/providers/passkey.ts +8 -1
  211. package/src/providers/password.ts +3 -0
  212. package/src/providers/phone.ts +3 -0
  213. package/src/providers/totp.ts +9 -0
  214. package/src/server/auth.ts +969 -0
  215. package/src/server/errors.ts +275 -0
  216. package/src/server/implementation/index.ts +370 -88
  217. package/src/server/implementation/{apiKey.ts → keys.ts} +7 -6
  218. package/src/server/implementation/mutations/{modifyAccount.ts → account.ts} +3 -4
  219. package/src/server/implementation/mutations/{createVerificationCode.ts → code.ts} +1 -1
  220. package/src/server/implementation/mutations/index.ts +22 -22
  221. package/src/server/implementation/mutations/{invalidateSessions.ts → invalidate.ts} +1 -1
  222. package/src/server/implementation/mutations/{userOAuth.ts → oauth.ts} +3 -2
  223. package/src/server/implementation/mutations/{refreshSession.ts → refresh.ts} +2 -2
  224. package/src/server/implementation/mutations/{createAccountFromCredentials.ts → register.ts} +3 -2
  225. package/src/server/implementation/mutations/{retrieveAccountWithCredentials.ts → retrieve.ts} +2 -2
  226. package/src/server/implementation/mutations/{verifierSignature.ts → signature.ts} +3 -2
  227. package/src/server/implementation/mutations/{signIn.ts → signin.ts} +1 -1
  228. package/src/server/implementation/mutations/{signOut.ts → signout.ts} +1 -1
  229. package/src/server/implementation/mutations/verifier.ts +1 -1
  230. package/src/server/implementation/mutations/{verifyCodeAndSignIn.ts → verify.ts} +2 -2
  231. package/src/server/implementation/passkey.ts +86 -116
  232. package/src/server/implementation/provider.ts +5 -8
  233. package/src/server/implementation/redirects.ts +2 -3
  234. package/src/server/implementation/{refreshTokens.ts → refresh.ts} +2 -1
  235. package/src/server/implementation/sessions.ts +1 -1
  236. package/src/server/implementation/{signIn.ts → signin.ts} +13 -11
  237. package/src/server/implementation/totp.ts +60 -84
  238. package/src/server/implementation/types.ts +316 -1
  239. package/src/server/implementation/users.ts +4 -7
  240. package/src/server/index.ts +142 -3
  241. package/src/server/oauth/{authorizationUrl.ts → authorization.ts} +3 -2
  242. package/src/server/oauth/callback.ts +7 -6
  243. package/src/server/oauth/checks.ts +3 -1
  244. package/src/server/oauth/{convexAuth.ts → helpers.ts} +8 -5
  245. package/src/server/{portal-email.ts → templates.ts} +78 -2
  246. package/src/server/types.ts +133 -4
  247. package/src/server/utils.ts +3 -1
  248. package/src/server/version.ts +1 -1
  249. package/dist/component/portalBridge.d.ts.map +0 -1
  250. package/dist/component/portalBridge.js.map +0 -1
  251. package/dist/server/convex-auth.d.ts.map +0 -1
  252. package/dist/server/convex-auth.js.map +0 -1
  253. package/dist/server/convex_types.d.ts +0 -17
  254. package/dist/server/convex_types.d.ts.map +0 -1
  255. package/dist/server/convex_types.js +0 -2
  256. package/dist/server/convex_types.js.map +0 -1
  257. package/dist/server/email-templates.d.ts.map +0 -1
  258. package/dist/server/email-templates.js +0 -74
  259. package/dist/server/email-templates.js.map +0 -1
  260. package/dist/server/implementation/apiKey.d.ts.map +0 -1
  261. package/dist/server/implementation/apiKey.js.map +0 -1
  262. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +0 -1
  263. package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +0 -1
  264. package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +0 -1
  265. package/dist/server/implementation/mutations/createVerificationCode.js.map +0 -1
  266. package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +0 -1
  267. package/dist/server/implementation/mutations/invalidateSessions.js.map +0 -1
  268. package/dist/server/implementation/mutations/modifyAccount.d.ts.map +0 -1
  269. package/dist/server/implementation/mutations/modifyAccount.js.map +0 -1
  270. package/dist/server/implementation/mutations/refreshSession.d.ts.map +0 -1
  271. package/dist/server/implementation/mutations/refreshSession.js.map +0 -1
  272. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +0 -1
  273. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +0 -1
  274. package/dist/server/implementation/mutations/storeRef.d.ts.map +0 -1
  275. package/dist/server/implementation/mutations/storeRef.js.map +0 -1
  276. package/dist/server/implementation/mutations/userOAuth.d.ts.map +0 -1
  277. package/dist/server/implementation/mutations/userOAuth.js.map +0 -1
  278. package/dist/server/implementation/mutations/verifierSignature.d.ts.map +0 -1
  279. package/dist/server/implementation/mutations/verifierSignature.js.map +0 -1
  280. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +0 -1
  281. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +0 -1
  282. package/dist/server/implementation/refreshTokens.d.ts.map +0 -1
  283. package/dist/server/implementation/refreshTokens.js.map +0 -1
  284. package/dist/server/implementation/signIn.js.map +0 -1
  285. package/dist/server/oauth/authorizationUrl.d.ts.map +0 -1
  286. package/dist/server/oauth/authorizationUrl.js.map +0 -1
  287. package/dist/server/oauth/convexAuth.d.ts.map +0 -1
  288. package/dist/server/oauth/convexAuth.js.map +0 -1
  289. package/dist/server/oauth/lib/utils/customFetch.d.ts.map +0 -1
  290. package/dist/server/oauth/lib/utils/customFetch.js.map +0 -1
  291. package/dist/server/portal-email.d.ts +0 -19
  292. package/dist/server/portal-email.d.ts.map +0 -1
  293. package/dist/server/portal-email.js.map +0 -1
  294. package/dist/server/provider_utils.d.ts.map +0 -1
  295. package/dist/server/provider_utils.js.map +0 -1
  296. package/src/server/convex-auth.ts +0 -602
  297. package/src/server/convex_types.ts +0 -55
  298. package/src/server/email-templates.ts +0 -77
  299. /package/src/cli/{generateKeys.ts → keys.ts} +0 -0
  300. /package/src/cli/{portal-upload.ts → upload.ts} +0 -0
  301. /package/src/server/implementation/mutations/{storeRef.ts → store.ts} +0 -0
  302. /package/src/server/implementation/{rateLimit.ts → ratelimit.ts} +0 -0
  303. /package/src/server/oauth/lib/utils/{customFetch.ts → fetch.ts} +0 -0
  304. /package/src/server/{provider_utils.ts → providers.ts} +0 -0
@@ -9,20 +9,28 @@ import {
9
9
  internalMutationGeneric,
10
10
  } from "convex/server";
11
11
  import { ConvexError, GenericId, v } from "convex/values";
12
+ import { throwAuthError, isAuthError } from "../errors.js";
12
13
  import { parse as parseCookies, serialize as serializeCookie } from "cookie";
13
14
  import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
14
- import { FunctionReferenceFromExport } from "../convex_types.js";
15
+ import { FunctionReferenceFromExport } from "../types.js";
15
16
  import {
16
17
  configDefaults,
17
18
  listAvailableProviders,
18
19
  materializeProvider,
19
- } from "../provider_utils.js";
20
+ } from "../providers.js";
20
21
  import {
21
22
  AuthProviderConfig,
22
23
  ConvexAuthConfig,
24
+ CorsConfig,
25
+ HttpKeyContext,
23
26
  } from "../types.js";
24
27
  import { requireEnv } from "../utils.js";
25
- import { ActionCtx, MutationCtx, Tokens } from "./types.js";
28
+ import {
29
+ ActionCtx,
30
+ MutationCtx,
31
+ Tokens,
32
+ KeyDoc,
33
+ } from "./types.js";
26
34
  export { Doc, Tokens } from "./types.js";
27
35
  import {
28
36
  LOG_LEVELS,
@@ -42,7 +50,7 @@ import {
42
50
  storeArgs,
43
51
  storeImpl,
44
52
  } from "./mutations/index.js";
45
- import { signInImpl } from "./signIn.js";
53
+ import { signInImpl } from "./signin.js";
46
54
  import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
47
55
  import {
48
56
  generateApiKey,
@@ -50,12 +58,12 @@ import {
50
58
  buildScopeChecker,
51
59
  validateScopes,
52
60
  checkKeyRateLimit,
53
- } from "./apiKey.js";
54
- import { getAuthorizationUrl } from "../oauth/authorizationUrl.js";
61
+ } from "./keys.js";
62
+ import { getAuthorizationUrl } from "../oauth/authorization.js";
55
63
  import {
56
64
  defaultCookiesOptions,
57
65
  oAuthConfigToInternalProvider,
58
- } from "../oauth/convexAuth.js";
66
+ } from "../oauth/helpers.js";
59
67
  import { handleOAuth } from "../oauth/callback.js";
60
68
 
61
69
  /**
@@ -95,7 +103,7 @@ export type SignOutAction = FunctionReferenceFromExport<
95
103
  * `convex/auth.ts` file.
96
104
  */
97
105
  export function Auth(config_: ConvexAuthConfig) {
98
- const config = configDefaults(config_ as any);
106
+ const config = configDefaults(config_);
99
107
  const hasOAuth = config.providers.some(
100
108
  (provider) => provider.type === "oauth" || provider.type === "oidc",
101
109
  );
@@ -113,11 +121,11 @@ export function Auth(config_: ConvexAuthConfig) {
113
121
  ) => {
114
122
  const provider = getProvider(id, allowExtraProviders);
115
123
  if (provider === undefined) {
116
- const message =
124
+ const detail =
117
125
  `Provider \`${id}\` is not configured, ` +
118
126
  `available providers are ${listAvailableProviders(config, allowExtraProviders)}.`;
119
- logWithLevel(LOG_LEVELS.ERROR, message);
120
- throw new Error(message);
127
+ logWithLevel(LOG_LEVELS.ERROR, detail);
128
+ throwAuthError("PROVIDER_NOT_CONFIGURED", detail, { provider: id });
121
129
  }
122
130
  return provider;
123
131
  };
@@ -146,6 +154,9 @@ export function Auth(config_: ConvexAuthConfig) {
146
154
  /**
147
155
  * Get the current user's ID from the auth context, or `null` if
148
156
  * not signed in.
157
+ *
158
+ * @param ctx - Any Convex context with an `auth` field (query, mutation, or action).
159
+ * @returns The user's `Id<"user">`, or `null` when unauthenticated.
149
160
  */
150
161
  current: async (ctx: { auth: Auth }) => {
151
162
  const identity = await ctx.auth.getUserIdentity();
@@ -158,24 +169,35 @@ export function Auth(config_: ConvexAuthConfig) {
158
169
  /**
159
170
  * Get the current user's ID, or throw if not signed in.
160
171
  * Use this when authentication is required.
172
+ *
173
+ * @param ctx - Any Convex context with an `auth` field.
174
+ * @returns The user's `Id<"user">`.
175
+ * @throws `ConvexError` with code `NOT_SIGNED_IN` when unauthenticated.
161
176
  */
162
177
  require: async (ctx: { auth: Auth }) => {
163
178
  const identity = await ctx.auth.getUserIdentity();
164
179
  if (identity === null) {
165
- throw new Error("Not signed in");
180
+ throwAuthError("NOT_SIGNED_IN");
166
181
  }
167
182
  const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
168
183
  return userId as GenericId<"user">;
169
184
  },
170
185
  /**
171
186
  * Retrieve a user document by their ID.
187
+ *
188
+ * @param ctx - Convex context with `runQuery`.
189
+ * @param userId - The user document ID.
190
+ * @returns The user document, or `null` if not found.
172
191
  */
173
192
  get: async (ctx: ComponentReadCtx, userId: string) => {
174
193
  return await ctx.runQuery(config.component.public.userGetById, { userId });
175
194
  },
176
195
  /**
177
196
  * Get the currently signed-in user's document, or `null` if not
178
- * signed in. Convenience method combining `current` + `get`.
197
+ * signed in. Convenience combining `current()` + `get()`.
198
+ *
199
+ * @param ctx - Convex context with `auth` and `runQuery`.
200
+ * @returns The user document, or `null` when unauthenticated.
179
201
  */
180
202
  viewer: async (ctx: ComponentAuthReadCtx) => {
181
203
  const userId = await auth.user.current(ctx);
@@ -184,6 +206,23 @@ export function Auth(config_: ConvexAuthConfig) {
184
206
  }
185
207
  return await ctx.runQuery(config.component.public.userGetById, { userId });
186
208
  },
209
+ /**
210
+ * Update a user document with partial data.
211
+ *
212
+ * @param ctx - Convex context with `runMutation`.
213
+ * @param userId - The user document ID.
214
+ * @param data - Partial data to merge into the user document.
215
+ */
216
+ patch: async (
217
+ ctx: ComponentCtx,
218
+ userId: string,
219
+ data: Record<string, unknown>,
220
+ ) => {
221
+ await ctx.runMutation(config.component.public.userPatch, {
222
+ userId,
223
+ data,
224
+ });
225
+ },
187
226
  /**
188
227
  * Query a user's group memberships.
189
228
  */
@@ -215,6 +254,9 @@ export function Auth(config_: ConvexAuthConfig) {
215
254
  /**
216
255
  * Get the current session ID from the auth context, or `null` if
217
256
  * not signed in.
257
+ *
258
+ * @param ctx - Any Convex context with an `auth` field.
259
+ * @returns The session's `Id<"session">`, or `null` when unauthenticated.
218
260
  */
219
261
  current: async (ctx: { auth: Auth }) => {
220
262
  const identity = await ctx.auth.getUserIdentity();
@@ -226,6 +268,10 @@ export function Auth(config_: ConvexAuthConfig) {
226
268
  },
227
269
  /**
228
270
  * Invalidate sessions for a user, optionally preserving specific sessions.
271
+ *
272
+ * @param ctx - Convex action context.
273
+ * @param args.userId - The user whose sessions to invalidate.
274
+ * @param args.except - Session IDs to preserve (e.g. the current session).
229
275
  */
230
276
  invalidate: async <DataModel extends GenericDataModel>(
231
277
  ctx: GenericActionCtx<DataModel>,
@@ -241,16 +287,25 @@ export function Auth(config_: ConvexAuthConfig) {
241
287
  account: {
242
288
  /**
243
289
  * Create an account and user for a credentials provider.
290
+ *
291
+ * @param ctx - Convex action context.
292
+ * @param args - Provider ID, account credentials, profile data, and link flags.
293
+ * @returns `{ account, user }` — the created account and user documents.
244
294
  */
245
295
  create: async <DataModel extends GenericDataModel>(
246
296
  ctx: GenericActionCtx<DataModel>,
247
297
  args: CreateAccountArgs,
248
298
  ) => {
249
299
  const actionCtx = ctx as unknown as ActionCtx;
250
- return await callCreateAccountFromCredentials(actionCtx, args as any);
300
+ return await callCreateAccountFromCredentials(actionCtx, args);
251
301
  },
252
302
  /**
253
303
  * Retrieve an account and user for a credentials provider.
304
+ *
305
+ * @param ctx - Convex action context.
306
+ * @param args - Provider ID and account credentials (id, optional secret).
307
+ * @returns `{ account, user }` — the matched account and user documents.
308
+ * @throws `ConvexError` with code `ACCOUNT_NOT_FOUND` when no match exists.
254
309
  */
255
310
  get: async <DataModel extends GenericDataModel>(
256
311
  ctx: GenericActionCtx<DataModel>,
@@ -259,12 +314,15 @@ export function Auth(config_: ConvexAuthConfig) {
259
314
  const actionCtx = ctx as unknown as ActionCtx;
260
315
  const result = await callRetreiveAccountWithCredentials(actionCtx, args);
261
316
  if (typeof result === "string") {
262
- throw new Error(result);
317
+ throwAuthError("ACCOUNT_NOT_FOUND", result);
263
318
  }
264
319
  return result;
265
320
  },
266
321
  /**
267
- * Update credentials for an existing account.
322
+ * Update credentials (secret) for an existing account.
323
+ *
324
+ * @param ctx - Convex action context.
325
+ * @param args - Provider ID and new account credentials (id + secret).
268
326
  */
269
327
  updateCredentials: async <DataModel extends GenericDataModel>(
270
328
  ctx: GenericActionCtx<DataModel>,
@@ -277,6 +335,11 @@ export function Auth(config_: ConvexAuthConfig) {
277
335
  provider: {
278
336
  /**
279
337
  * Sign in via another provider, typically from a credentials flow.
338
+ *
339
+ * @param ctx - Convex action context.
340
+ * @param provider - The provider config to sign in with.
341
+ * @param args - Optional account ID and params.
342
+ * @returns `{ userId, sessionId }` on success, or `null`.
280
343
  */
281
344
  signIn: async <DataModel extends GenericDataModel>(
282
345
  ctx: GenericActionCtx<DataModel>,
@@ -289,7 +352,8 @@ export function Auth(config_: ConvexAuthConfig) {
289
352
  const result = await signInImpl(
290
353
  enrichCtx(ctx),
291
354
  materializeProvider(provider),
292
- args as any,
355
+ // params type widened: Record<string, unknown> → Record<string, any>
356
+ args as { accountId?: GenericId<"account">; params?: Record<string, any> },
293
357
  {
294
358
  generateTokens: false,
295
359
  allowExtraProviders: true,
@@ -326,6 +390,7 @@ export function Auth(config_: ConvexAuthConfig) {
326
390
  data: {
327
391
  name: string;
328
392
  slug?: string;
393
+ type?: string;
329
394
  parentGroupId?: string;
330
395
  extend?: Record<string, unknown>;
331
396
  },
@@ -345,8 +410,12 @@ export function Auth(config_: ConvexAuthConfig) {
345
410
  * List groups. When `parentGroupId` is provided, returns children of
346
411
  * that group. When omitted, returns root-level groups (no parent).
347
412
  */
348
- list: async (ctx: ComponentReadCtx, opts?: { parentGroupId?: string }) => {
413
+ list: async (
414
+ ctx: ComponentReadCtx,
415
+ opts?: { type?: string; parentGroupId?: string },
416
+ ) => {
349
417
  return await ctx.runQuery(config.component.public.groupList, {
418
+ type: opts?.type,
350
419
  parentGroupId: opts?.parentGroupId,
351
420
  });
352
421
  },
@@ -514,15 +583,17 @@ export function Auth(config_: ConvexAuthConfig) {
514
583
  * the timestamp. If the invite has a group, the caller is responsible
515
584
  * for creating the member record via `auth.group.member.add` in the
516
585
  * same Convex mutation for transactional safety.
517
- *
518
- * @throws ConvexError with code `INVITE_NOT_FOUND` when the invite does
519
- * not exist.
520
- * @throws ConvexError with code `INVITE_NOT_PENDING` when the invite is
521
- * not in `pending` status.
522
586
  *
587
+ * @param ctx - Convex context with `runMutation`.
588
+ * @param inviteId - The invite document ID.
589
+ * @param acceptedByUserId - User accepting the invite (recorded for audit).
590
+ * @throws `ConvexError` with code `INVITE_NOT_FOUND` when the invite does not exist.
591
+ * @throws `ConvexError` with code `INVITE_NOT_PENDING` when the invite is not in `pending` status.
592
+ *
593
+ * @example
523
594
  * ```ts
524
- * export const acceptInvite = mutation({
525
- * args: { inviteId: v.string() },
595
+ * export const acceptInvite = mutation({
596
+ * args: { inviteId: v.string() },
526
597
  * handler: async (ctx, { inviteId }) => {
527
598
  * const userId = await auth.user.require(ctx);
528
599
  * const invite = await auth.invite.get(ctx, inviteId);
@@ -546,14 +617,14 @@ export function Auth(config_: ConvexAuthConfig) {
546
617
  ...(acceptedByUserId ? { acceptedByUserId } : {}),
547
618
  });
548
619
  },
549
- /**
550
- * Revoke a pending invitation.
551
- *
552
- * @throws ConvexError with code `INVITE_NOT_FOUND` when the invite does
553
- * not exist.
554
- * @throws ConvexError with code `INVITE_NOT_PENDING` when the invite is
555
- * not in `pending` status.
556
- */
620
+ /**
621
+ * Revoke a pending invitation.
622
+ *
623
+ * @param ctx - Convex context with `runMutation`.
624
+ * @param inviteId - The invite document ID.
625
+ * @throws `ConvexError` with code `INVITE_NOT_FOUND` when the invite does not exist.
626
+ * @throws `ConvexError` with code `INVITE_NOT_PENDING` when the invite is not in `pending` status.
627
+ */
557
628
  revoke: async (ctx: ComponentCtx, inviteId: string) => {
558
629
  await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
559
630
  },
@@ -685,10 +756,10 @@ export function Auth(config_: ConvexAuthConfig) {
685
756
 
686
757
  const { raw, hashedKey, displayPrefix } = await generateApiKey(prefix);
687
758
 
688
- const keyId = await ctx.runMutation(
759
+ const keyId = (await ctx.runMutation(
689
760
  config.component.public.keyInsert,
690
761
  {
691
- userId: opts.userId as any,
762
+ userId: opts.userId,
692
763
  prefix: displayPrefix,
693
764
  hashedKey,
694
765
  name: opts.name,
@@ -696,9 +767,9 @@ export function Auth(config_: ConvexAuthConfig) {
696
767
  rateLimit: opts.rateLimit ?? config.apiKeys?.defaultRateLimit,
697
768
  expiresAt: opts.expiresAt,
698
769
  },
699
- );
770
+ )) as string;
700
771
 
701
- return { keyId: keyId as string, raw };
772
+ return { keyId, raw };
702
773
  },
703
774
 
704
775
  /**
@@ -719,22 +790,22 @@ export function Auth(config_: ConvexAuthConfig) {
719
790
  }> => {
720
791
  const hashedKey = await hashApiKey(rawKey);
721
792
 
722
- const key = await ctx.runQuery(
793
+ const key = (await ctx.runQuery(
723
794
  config.component.public.keyGetByHashedKey,
724
795
  { hashedKey },
725
- );
796
+ )) as KeyDoc | null;
726
797
  if (!key) {
727
- throw new Error("Invalid API key");
798
+ throwAuthError("INVALID_API_KEY");
728
799
  }
729
800
  if (key.revoked) {
730
- throw new Error("API key has been revoked");
801
+ throwAuthError("API_KEY_REVOKED");
731
802
  }
732
803
  if (key.expiresAt && key.expiresAt < Date.now()) {
733
- throw new Error("API key has expired");
804
+ throwAuthError("API_KEY_EXPIRED");
734
805
  }
735
806
 
736
807
  // Check per-key rate limit
737
- const patchData: Record<string, any> = { lastUsedAt: Date.now() };
808
+ const patchData: Record<string, unknown> = { lastUsedAt: Date.now() };
738
809
 
739
810
  if (key.rateLimit) {
740
811
  const { limited, newState } = checkKeyRateLimit(
@@ -742,7 +813,7 @@ export function Auth(config_: ConvexAuthConfig) {
742
813
  key.rateLimitState ?? undefined,
743
814
  );
744
815
  if (limited) {
745
- throw new Error("API key rate limit exceeded");
816
+ throwAuthError("API_KEY_RATE_LIMITED");
746
817
  }
747
818
  patchData.rateLimitState = newState;
748
819
  }
@@ -754,8 +825,8 @@ export function Auth(config_: ConvexAuthConfig) {
754
825
  });
755
826
 
756
827
  return {
757
- userId: key.userId as string,
758
- keyId: key._id as string,
828
+ userId: key.userId,
829
+ keyId: key._id,
759
830
  scopes: buildScopeChecker(key.scopes),
760
831
  };
761
832
  },
@@ -764,22 +835,22 @@ export function Auth(config_: ConvexAuthConfig) {
764
835
  * List all API keys for a user.
765
836
  * Never includes the raw key — only the display prefix.
766
837
  */
767
- list: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
768
- return await ctx.runQuery(
838
+ list: async (ctx: ComponentReadCtx, opts: { userId: string }): Promise<KeyDoc[]> => {
839
+ return (await ctx.runQuery(
769
840
  config.component.public.keyListByUserId,
770
- { userId: opts.userId as any },
771
- );
841
+ { userId: opts.userId },
842
+ )) as KeyDoc[];
772
843
  },
773
844
 
774
845
  /**
775
846
  * Get a single API key by its document ID.
776
847
  * Returns `null` if not found.
777
848
  */
778
- get: async (ctx: ComponentReadCtx, keyId: string) => {
779
- return await ctx.runQuery(
849
+ get: async (ctx: ComponentReadCtx, keyId: string): Promise<KeyDoc | null> => {
850
+ return (await ctx.runQuery(
780
851
  config.component.public.keyGetById,
781
- { keyId: keyId as any },
782
- );
852
+ { keyId },
853
+ )) as KeyDoc | null;
783
854
  },
784
855
 
785
856
  /**
@@ -798,7 +869,7 @@ export function Auth(config_: ConvexAuthConfig) {
798
869
  validateScopes(data.scopes, config.apiKeys?.scopes);
799
870
  }
800
871
  await ctx.runMutation(config.component.public.keyPatch, {
801
- keyId: keyId as any,
872
+ keyId,
802
873
  data,
803
874
  });
804
875
  },
@@ -809,7 +880,7 @@ export function Auth(config_: ConvexAuthConfig) {
809
880
  */
810
881
  revoke: async (ctx: ComponentCtx, keyId: string) => {
811
882
  await ctx.runMutation(config.component.public.keyPatch, {
812
- keyId: keyId as any,
883
+ keyId,
813
884
  data: { revoked: true },
814
885
  });
815
886
  },
@@ -819,37 +890,41 @@ export function Auth(config_: ConvexAuthConfig) {
819
890
  */
820
891
  remove: async (ctx: ComponentCtx, keyId: string) => {
821
892
  await ctx.runMutation(config.component.public.keyDelete, {
822
- keyId: keyId as any,
893
+ keyId,
823
894
  });
824
895
  },
825
896
  },
826
897
  /**
827
- * Add HTTP actions for JWT verification and OAuth sign-in.
828
- *
829
- * ```ts
830
- * import { httpRouter } from "convex/server";
831
- * import { auth } from "./auth.js";
832
- *
833
- * const http = httpRouter();
834
- *
835
- * auth.addHttpRoutes(http);
836
- *
837
- * export default http;
838
- * ```
839
- *
840
- * The following routes are handled always:
841
- *
842
- * - `/.well-known/openid-configuration`
843
- * - `/.well-known/jwks.json`
844
- *
845
- * The following routes are handled if OAuth is configured:
846
- *
847
- * - `/api/auth/signin/*`
848
- * - `/api/auth/callback/*`
849
- *
850
- * @param http your HTTP router
898
+ * HTTP namespace route registration and Bearer-authenticated endpoints.
851
899
  */
852
- addHttpRoutes: (http: HttpRouter) => {
900
+ http: {
901
+ /**
902
+ * Register core HTTP routes for JWT verification and OAuth sign-in.
903
+ *
904
+ * ```ts
905
+ * import { httpRouter } from "convex/server";
906
+ * import { auth } from "./auth.js";
907
+ *
908
+ * const http = httpRouter();
909
+ *
910
+ * auth.http.add(http);
911
+ *
912
+ * export default http;
913
+ * ```
914
+ *
915
+ * The following routes are handled always:
916
+ *
917
+ * - `/.well-known/openid-configuration`
918
+ * - `/.well-known/jwks.json`
919
+ *
920
+ * The following routes are handled if OAuth is configured:
921
+ *
922
+ * - `/api/auth/signin/*`
923
+ * - `/api/auth/callback/*`
924
+ *
925
+ * @param http your HTTP router
926
+ */
927
+ add: (http: HttpRouter) => {
853
928
  http.route({
854
929
  path: "/.well-known/openid-configuration",
855
930
  method: "GET",
@@ -899,11 +974,11 @@ export function Auth(config_: ConvexAuthConfig) {
899
974
  const pathParts = url.pathname.split("/");
900
975
  const providerId = pathParts.at(-1)!;
901
976
  if (providerId === null) {
902
- throw new Error("Missing provider id");
977
+ throwAuthError("OAUTH_MISSING_PROVIDER");
903
978
  }
904
979
  const verifier = url.searchParams.get("code");
905
980
  if (verifier === null) {
906
- throw new Error("Missing sign-in verifier");
981
+ throwAuthError("OAUTH_MISSING_VERIFIER");
907
982
  }
908
983
  const provider = getProviderOrThrow(
909
984
  providerId,
@@ -992,8 +1067,10 @@ export function Auth(config_: ConvexAuthConfig) {
992
1067
  );
993
1068
 
994
1069
  if (typeof id !== "string") {
995
- throw new Error(
1070
+ throwAuthError(
1071
+ "OAUTH_INVALID_PROFILE",
996
1072
  `The profile method of the ${providerId} config must return a string ID`,
1073
+ { provider: providerId },
997
1074
  );
998
1075
  }
999
1076
 
@@ -1035,6 +1112,203 @@ export function Auth(config_: ConvexAuthConfig) {
1035
1112
  });
1036
1113
  }
1037
1114
  },
1115
+
1116
+ /**
1117
+ * Wrap an HTTP action handler with Bearer token authentication.
1118
+ *
1119
+ * Extracts the `Authorization: Bearer <key>` header, verifies the
1120
+ * API key via `auth.key.verify()`, and injects `ctx.key` with the
1121
+ * verified key info. Returns structured JSON error responses for
1122
+ * missing/invalid/revoked/expired/rate-limited keys.
1123
+ *
1124
+ * If the handler returns a plain object, it is auto-wrapped in a
1125
+ * `200 JSON` response. If it returns a `Response`, CORS headers
1126
+ * are merged and the response is passed through.
1127
+ *
1128
+ * ```ts
1129
+ * const handler = auth.http.action(async (ctx, request) => {
1130
+ * const data = await ctx.runQuery(api.data.get, { userId: ctx.key.userId });
1131
+ * return { data };
1132
+ * });
1133
+ * http.route({ path: "/api/data", method: "GET", handler });
1134
+ * ```
1135
+ *
1136
+ * @param handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
1137
+ * @param options.scope - Optional scope check; returns 403 if the key lacks permission.
1138
+ * @param options.cors - CORS config; defaults to permissive (`*`).
1139
+ */
1140
+ action: (
1141
+ handler: (
1142
+ ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,
1143
+ request: Request,
1144
+ ) => Promise<Response | Record<string, unknown>>,
1145
+ options?: {
1146
+ scope?: { resource: string; action: string };
1147
+ cors?: CorsConfig;
1148
+ },
1149
+ ) => {
1150
+ const corsConfig = options?.cors ?? {};
1151
+ const corsHeaders: Record<string, string> = {
1152
+ "Access-Control-Allow-Origin": corsConfig.origin ?? "*",
1153
+ "Access-Control-Allow-Methods":
1154
+ corsConfig.methods ?? "GET,POST,PUT,PATCH,DELETE,OPTIONS",
1155
+ "Access-Control-Allow-Headers":
1156
+ corsConfig.headers ?? "Content-Type,Authorization",
1157
+ };
1158
+
1159
+ const jsonError = (
1160
+ status: number,
1161
+ code: string,
1162
+ message: string,
1163
+ ) =>
1164
+ new Response(JSON.stringify({ error: message, code }), {
1165
+ status,
1166
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
1167
+ });
1168
+
1169
+ return httpActionGeneric(async (genericCtx, request) => {
1170
+ const ctx = genericCtx as unknown as GenericActionCtx<GenericDataModel>;
1171
+
1172
+ try {
1173
+ // 1. Extract Bearer token
1174
+ const authHeader = request.headers.get("Authorization");
1175
+ if (!authHeader?.startsWith("Bearer ")) {
1176
+ return jsonError(
1177
+ 401,
1178
+ "MISSING_BEARER_TOKEN",
1179
+ "Missing or malformed Authorization: Bearer header.",
1180
+ );
1181
+ }
1182
+ const rawKey = authHeader.slice(7);
1183
+
1184
+ // 2. Verify API key
1185
+ let keyResult: { userId: string; keyId: string; scopes: import("../types.js").ScopeChecker };
1186
+ try {
1187
+ keyResult = await auth.key.verify(ctx, rawKey);
1188
+ } catch (error: unknown) {
1189
+ if (isAuthError(error)) {
1190
+ const { code, message } = error.data as { code: string; message: string };
1191
+ return jsonError(403, code, message);
1192
+ }
1193
+ throw error;
1194
+ }
1195
+
1196
+ // 3. Optional scope check
1197
+ if (options?.scope) {
1198
+ if (!keyResult.scopes.can(options.scope.resource, options.scope.action)) {
1199
+ return jsonError(
1200
+ 403,
1201
+ "SCOPE_CHECK_FAILED",
1202
+ "This API key does not have the required permissions.",
1203
+ );
1204
+ }
1205
+ }
1206
+
1207
+ // 4. Enrich context with key info
1208
+ const enrichedCtx = Object.assign(ctx, {
1209
+ key: {
1210
+ userId: keyResult.userId,
1211
+ keyId: keyResult.keyId,
1212
+ scopes: keyResult.scopes,
1213
+ },
1214
+ });
1215
+
1216
+ // 5. Call handler
1217
+ const result = await handler(enrichedCtx, request);
1218
+
1219
+ // 6. Auto-wrap plain objects as JSON responses
1220
+ if (result instanceof Response) {
1221
+ // Merge CORS headers into existing response
1222
+ const headers = new Headers(result.headers);
1223
+ for (const [k, val] of Object.entries(corsHeaders)) {
1224
+ if (!headers.has(k)) headers.set(k, val);
1225
+ }
1226
+ return new Response(result.body, {
1227
+ status: result.status,
1228
+ statusText: result.statusText,
1229
+ headers,
1230
+ });
1231
+ }
1232
+
1233
+ return new Response(JSON.stringify(result), {
1234
+ status: 200,
1235
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
1236
+ });
1237
+ } catch (error: unknown) {
1238
+ logError(error);
1239
+ return jsonError(500, "INTERNAL_ERROR", "An unexpected error occurred.");
1240
+ }
1241
+ });
1242
+ },
1243
+
1244
+ /**
1245
+ * Register a Bearer-authenticated route **and** its OPTIONS preflight
1246
+ * in a single call.
1247
+ *
1248
+ * ```ts
1249
+ * auth.http.route(http, {
1250
+ * path: "/api/messages",
1251
+ * method: "POST",
1252
+ * handler: async (ctx, request) => {
1253
+ * const { body } = await request.json();
1254
+ * await ctx.runMutation(internal.messages.sendAsUser, {
1255
+ * userId: ctx.key.userId,
1256
+ * body,
1257
+ * });
1258
+ * return { success: true };
1259
+ * },
1260
+ * });
1261
+ * ```
1262
+ *
1263
+ * @param http - The Convex HTTP router.
1264
+ * @param routeConfig.path - The URL path to match.
1265
+ * @param routeConfig.method - HTTP method (GET, POST, PUT, PATCH, DELETE).
1266
+ * @param routeConfig.handler - Receives enriched `ctx` (with `ctx.key`) and the raw `Request`.
1267
+ * @param routeConfig.scope - Optional scope check; returns 403 if the key lacks permission.
1268
+ * @param routeConfig.cors - CORS config; defaults to permissive (`*`).
1269
+ */
1270
+ route: (
1271
+ http: HttpRouter,
1272
+ routeConfig: {
1273
+ path: string;
1274
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1275
+ handler: (
1276
+ ctx: GenericActionCtx<GenericDataModel> & HttpKeyContext,
1277
+ request: Request,
1278
+ ) => Promise<Response | Record<string, unknown>>;
1279
+ scope?: { resource: string; action: string };
1280
+ cors?: CorsConfig;
1281
+ },
1282
+ ) => {
1283
+ const corsConfig = routeConfig.cors ?? {};
1284
+ const corsHeaders: Record<string, string> = {
1285
+ "Access-Control-Allow-Origin": corsConfig.origin ?? "*",
1286
+ "Access-Control-Allow-Methods":
1287
+ corsConfig.methods ?? "GET,POST,PUT,PATCH,DELETE,OPTIONS",
1288
+ "Access-Control-Allow-Headers":
1289
+ corsConfig.headers ?? "Content-Type,Authorization",
1290
+ };
1291
+
1292
+ // Register OPTIONS preflight
1293
+ http.route({
1294
+ path: routeConfig.path,
1295
+ method: "OPTIONS",
1296
+ handler: httpActionGeneric(async () => {
1297
+ return new Response(null, { status: 204, headers: corsHeaders });
1298
+ }),
1299
+ });
1300
+
1301
+ // Register the main route with Bearer auth wrapping
1302
+ http.route({
1303
+ path: routeConfig.path,
1304
+ method: routeConfig.method,
1305
+ handler: auth.http.action(routeConfig.handler, {
1306
+ scope: routeConfig.scope,
1307
+ cors: routeConfig.cors,
1308
+ }),
1309
+ });
1310
+ },
1311
+ },
1038
1312
  };
1039
1313
  const enrichCtx = <DataModel extends GenericDataModel>(
1040
1314
  ctx: GenericActionCtx<DataModel>,
@@ -1106,7 +1380,7 @@ export function Auth(config_: ConvexAuthConfig) {
1106
1380
  return { totpSetup: { uri: result.uri, secret: result.secret, totpId: result.totpId }, verifier: result.verifier };
1107
1381
  default: {
1108
1382
  const _typecheck: never = result;
1109
- throw new Error(`Unexpected result from signIn, ${result as any}`);
1383
+ throwAuthError("INTERNAL_ERROR", `Unexpected result from signIn, ${String(result)}`);
1110
1384
  }
1111
1385
  }
1112
1386
  },
@@ -1143,10 +1417,18 @@ function convertErrorsToResponse(
1143
1417
  try {
1144
1418
  return await action(ctx, request);
1145
1419
  } catch (error) {
1146
- if (error instanceof ConvexError) {
1420
+ if (isAuthError(error)) {
1421
+ return new Response(
1422
+ JSON.stringify({ code: error.data.code, message: error.data.message }),
1423
+ {
1424
+ status: errorStatusCode,
1425
+ headers: { "Content-Type": "application/json" },
1426
+ },
1427
+ );
1428
+ } else if (error instanceof ConvexError) {
1147
1429
  return new Response(null, {
1148
1430
  status: errorStatusCode,
1149
- statusText: error.data,
1431
+ statusText: typeof error.data === "string" ? error.data : "Error",
1150
1432
  });
1151
1433
  } else {
1152
1434
  logError(error);