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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/dist/authorization/index.d.ts +1 -1
  2. package/dist/authorization/index.js +1 -1
  3. package/dist/authorization/index.js.map +1 -1
  4. package/dist/client/index.d.ts +1 -2
  5. package/dist/client/index.d.ts.map +1 -1
  6. package/dist/client/index.js +36 -39
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/component/client/index.d.ts +1 -2
  9. package/dist/component/convex.config.d.ts +2 -2
  10. package/dist/component/convex.config.d.ts.map +1 -1
  11. package/dist/component/model.d.ts +5 -5
  12. package/dist/component/model.d.ts.map +1 -1
  13. package/dist/component/public/enterprise/audit.d.ts.map +1 -1
  14. package/dist/component/public/enterprise/audit.js.map +1 -1
  15. package/dist/component/public/enterprise/core.d.ts.map +1 -1
  16. package/dist/component/public/enterprise/core.js.map +1 -1
  17. package/dist/component/public/enterprise/domains.d.ts.map +1 -1
  18. package/dist/component/public/enterprise/domains.js.map +1 -1
  19. package/dist/component/public/enterprise/scim.d.ts.map +1 -1
  20. package/dist/component/public/enterprise/scim.js.map +1 -1
  21. package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
  22. package/dist/component/public/enterprise/secrets.js.map +1 -1
  23. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
  24. package/dist/component/public/enterprise/webhooks.js.map +1 -1
  25. package/dist/component/public/factors/devices.d.ts.map +1 -1
  26. package/dist/component/public/factors/devices.js.map +1 -1
  27. package/dist/component/public/factors/passkeys.d.ts.map +1 -1
  28. package/dist/component/public/factors/passkeys.js.map +1 -1
  29. package/dist/component/public/factors/totp.d.ts.map +1 -1
  30. package/dist/component/public/factors/totp.js.map +1 -1
  31. package/dist/component/public/groups/core.js.map +1 -1
  32. package/dist/component/public/groups/invites.d.ts.map +1 -1
  33. package/dist/component/public/groups/invites.js.map +1 -1
  34. package/dist/component/public/groups/members.d.ts.map +1 -1
  35. package/dist/component/public/groups/members.js.map +1 -1
  36. package/dist/component/public/identity/accounts.d.ts.map +1 -1
  37. package/dist/component/public/identity/accounts.js.map +1 -1
  38. package/dist/component/public/identity/codes.d.ts.map +1 -1
  39. package/dist/component/public/identity/codes.js.map +1 -1
  40. package/dist/component/public/identity/sessions.d.ts.map +1 -1
  41. package/dist/component/public/identity/sessions.js.map +1 -1
  42. package/dist/component/public/identity/tokens.d.ts.map +1 -1
  43. package/dist/component/public/identity/tokens.js.map +1 -1
  44. package/dist/component/public/identity/users.d.ts.map +1 -1
  45. package/dist/component/public/identity/users.js.map +1 -1
  46. package/dist/component/public/identity/verifiers.d.ts.map +1 -1
  47. package/dist/component/public/identity/verifiers.js.map +1 -1
  48. package/dist/component/public/security/keys.d.ts.map +1 -1
  49. package/dist/component/public/security/keys.js.map +1 -1
  50. package/dist/component/public/security/limits.d.ts.map +1 -1
  51. package/dist/component/public/security/limits.js.map +1 -1
  52. package/dist/component/schema.d.ts +39 -39
  53. package/dist/component/server/auth.d.ts +95 -52
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +63 -43
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/core.js +116 -235
  58. package/dist/component/server/core.js.map +1 -1
  59. package/dist/component/server/crypto.js +25 -7
  60. package/dist/component/server/crypto.js.map +1 -1
  61. package/dist/component/server/device.js +58 -15
  62. package/dist/component/server/device.js.map +1 -1
  63. package/dist/component/server/enterprise/domain.js +148 -59
  64. package/dist/component/server/enterprise/domain.js.map +1 -1
  65. package/dist/component/server/enterprise/http.js +36 -15
  66. package/dist/component/server/enterprise/http.js.map +1 -1
  67. package/dist/component/server/enterprise/oidc.js +1 -1
  68. package/dist/component/server/http.js +26 -21
  69. package/dist/component/server/http.js.map +1 -1
  70. package/dist/component/server/identity.js +5 -2
  71. package/dist/component/server/identity.js.map +1 -1
  72. package/dist/component/server/limits.js +21 -30
  73. package/dist/component/server/limits.js.map +1 -1
  74. package/dist/component/server/mutations/account.js +12 -10
  75. package/dist/component/server/mutations/account.js.map +1 -1
  76. package/dist/component/server/mutations/code.js +5 -2
  77. package/dist/component/server/mutations/code.js.map +1 -1
  78. package/dist/component/server/mutations/invalidate.js +1 -1
  79. package/dist/component/server/mutations/invalidate.js.map +1 -1
  80. package/dist/component/server/mutations/oauth.js +10 -4
  81. package/dist/component/server/mutations/oauth.js.map +1 -1
  82. package/dist/component/server/mutations/refresh.js +2 -2
  83. package/dist/component/server/mutations/refresh.js.map +1 -1
  84. package/dist/component/server/mutations/register.js +46 -42
  85. package/dist/component/server/mutations/register.js.map +1 -1
  86. package/dist/component/server/mutations/retrieve.js +21 -25
  87. package/dist/component/server/mutations/retrieve.js.map +1 -1
  88. package/dist/component/server/mutations/signature.js +10 -4
  89. package/dist/component/server/mutations/signature.js.map +1 -1
  90. package/dist/component/server/mutations/signout.js.map +1 -1
  91. package/dist/component/server/mutations/store.js +9 -24
  92. package/dist/component/server/mutations/store.js.map +1 -1
  93. package/dist/component/server/mutations/verifier.js.map +1 -1
  94. package/dist/component/server/mutations/verify.js +1 -1
  95. package/dist/component/server/mutations/verify.js.map +1 -1
  96. package/dist/component/server/oauth.js +53 -16
  97. package/dist/component/server/oauth.js.map +1 -1
  98. package/dist/component/server/passkey.js +115 -31
  99. package/dist/component/server/passkey.js.map +1 -1
  100. package/dist/component/server/redirects.js +9 -3
  101. package/dist/component/server/redirects.js.map +1 -1
  102. package/dist/component/server/refresh.js +10 -7
  103. package/dist/component/server/refresh.js.map +1 -1
  104. package/dist/component/server/runtime.d.ts +3 -3
  105. package/dist/component/server/runtime.d.ts.map +1 -1
  106. package/dist/component/server/runtime.js +62 -20
  107. package/dist/component/server/runtime.js.map +1 -1
  108. package/dist/component/server/signin.js +34 -10
  109. package/dist/component/server/signin.js.map +1 -1
  110. package/dist/component/server/totp.js +79 -19
  111. package/dist/component/server/totp.js.map +1 -1
  112. package/dist/component/server/types.d.ts +12 -20
  113. package/dist/component/server/types.d.ts.map +1 -1
  114. package/dist/component/server/types.js.map +1 -1
  115. package/dist/component/server/users.js +6 -3
  116. package/dist/component/server/users.js.map +1 -1
  117. package/dist/component/server/utils.js +10 -4
  118. package/dist/component/server/utils.js.map +1 -1
  119. package/dist/core/types.d.ts +14 -22
  120. package/dist/core/types.d.ts.map +1 -1
  121. package/dist/factors/device.js +8 -9
  122. package/dist/factors/device.js.map +1 -1
  123. package/dist/factors/passkey.js +18 -21
  124. package/dist/factors/passkey.js.map +1 -1
  125. package/dist/providers/password.js +66 -81
  126. package/dist/providers/password.js.map +1 -1
  127. package/dist/runtime/invite.js +2 -8
  128. package/dist/runtime/invite.js.map +1 -1
  129. package/dist/server/auth.d.ts +95 -52
  130. package/dist/server/auth.d.ts.map +1 -1
  131. package/dist/server/auth.js +63 -43
  132. package/dist/server/auth.js.map +1 -1
  133. package/dist/server/core.d.ts +71 -159
  134. package/dist/server/core.d.ts.map +1 -1
  135. package/dist/server/core.js +116 -235
  136. package/dist/server/core.js.map +1 -1
  137. package/dist/server/crypto.d.ts.map +1 -1
  138. package/dist/server/crypto.js +25 -7
  139. package/dist/server/crypto.js.map +1 -1
  140. package/dist/server/device.js +58 -15
  141. package/dist/server/device.js.map +1 -1
  142. package/dist/server/enterprise/domain.d.ts +0 -8
  143. package/dist/server/enterprise/domain.d.ts.map +1 -1
  144. package/dist/server/enterprise/domain.js +148 -59
  145. package/dist/server/enterprise/domain.js.map +1 -1
  146. package/dist/server/enterprise/http.d.ts.map +1 -1
  147. package/dist/server/enterprise/http.js +35 -14
  148. package/dist/server/enterprise/http.js.map +1 -1
  149. package/dist/server/http.d.ts +2 -2
  150. package/dist/server/http.d.ts.map +1 -1
  151. package/dist/server/http.js +25 -20
  152. package/dist/server/http.js.map +1 -1
  153. package/dist/server/identity.js +5 -2
  154. package/dist/server/identity.js.map +1 -1
  155. package/dist/server/index.d.ts +2 -2
  156. package/dist/server/limits.js +21 -30
  157. package/dist/server/limits.js.map +1 -1
  158. package/dist/server/mounts.d.ts +26 -64
  159. package/dist/server/mounts.d.ts.map +1 -1
  160. package/dist/server/mounts.js +45 -106
  161. package/dist/server/mounts.js.map +1 -1
  162. package/dist/server/mutations/account.d.ts +8 -9
  163. package/dist/server/mutations/account.d.ts.map +1 -1
  164. package/dist/server/mutations/account.js +11 -9
  165. package/dist/server/mutations/account.js.map +1 -1
  166. package/dist/server/mutations/code.d.ts +13 -13
  167. package/dist/server/mutations/code.d.ts.map +1 -1
  168. package/dist/server/mutations/code.js +5 -2
  169. package/dist/server/mutations/code.js.map +1 -1
  170. package/dist/server/mutations/invalidate.d.ts +4 -4
  171. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  172. package/dist/server/mutations/invalidate.js.map +1 -1
  173. package/dist/server/mutations/oauth.d.ts +12 -10
  174. package/dist/server/mutations/oauth.d.ts.map +1 -1
  175. package/dist/server/mutations/oauth.js +9 -3
  176. package/dist/server/mutations/oauth.js.map +1 -1
  177. package/dist/server/mutations/refresh.d.ts +3 -3
  178. package/dist/server/mutations/refresh.d.ts.map +1 -1
  179. package/dist/server/mutations/refresh.js +1 -1
  180. package/dist/server/mutations/refresh.js.map +1 -1
  181. package/dist/server/mutations/register.d.ts +11 -11
  182. package/dist/server/mutations/register.d.ts.map +1 -1
  183. package/dist/server/mutations/register.js +45 -41
  184. package/dist/server/mutations/register.js.map +1 -1
  185. package/dist/server/mutations/retrieve.d.ts +6 -6
  186. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  187. package/dist/server/mutations/retrieve.js +20 -24
  188. package/dist/server/mutations/retrieve.js.map +1 -1
  189. package/dist/server/mutations/signature.d.ts +6 -7
  190. package/dist/server/mutations/signature.d.ts.map +1 -1
  191. package/dist/server/mutations/signature.js +9 -3
  192. package/dist/server/mutations/signature.js.map +1 -1
  193. package/dist/server/mutations/signin.d.ts +5 -5
  194. package/dist/server/mutations/signin.d.ts.map +1 -1
  195. package/dist/server/mutations/signout.js.map +1 -1
  196. package/dist/server/mutations/store.d.ts +97 -97
  197. package/dist/server/mutations/store.d.ts.map +1 -1
  198. package/dist/server/mutations/store.js +8 -23
  199. package/dist/server/mutations/store.js.map +1 -1
  200. package/dist/server/mutations/verifier.js.map +1 -1
  201. package/dist/server/mutations/verify.d.ts +10 -10
  202. package/dist/server/mutations/verify.d.ts.map +1 -1
  203. package/dist/server/mutations/verify.js.map +1 -1
  204. package/dist/server/oauth.js +53 -16
  205. package/dist/server/oauth.js.map +1 -1
  206. package/dist/server/passkey.d.ts +2 -2
  207. package/dist/server/passkey.d.ts.map +1 -1
  208. package/dist/server/passkey.js +114 -30
  209. package/dist/server/passkey.js.map +1 -1
  210. package/dist/server/redirects.js +9 -3
  211. package/dist/server/redirects.js.map +1 -1
  212. package/dist/server/refresh.js +10 -7
  213. package/dist/server/refresh.js.map +1 -1
  214. package/dist/server/runtime.d.ts +14 -14
  215. package/dist/server/runtime.d.ts.map +1 -1
  216. package/dist/server/runtime.js +61 -19
  217. package/dist/server/runtime.js.map +1 -1
  218. package/dist/server/signin.js +34 -10
  219. package/dist/server/signin.js.map +1 -1
  220. package/dist/server/ssr.d.ts.map +1 -1
  221. package/dist/server/ssr.js +175 -184
  222. package/dist/server/ssr.js.map +1 -1
  223. package/dist/server/totp.js +78 -18
  224. package/dist/server/totp.js.map +1 -1
  225. package/dist/server/types.d.ts +13 -21
  226. package/dist/server/types.d.ts.map +1 -1
  227. package/dist/server/types.js.map +1 -1
  228. package/dist/server/users.js +6 -3
  229. package/dist/server/users.js.map +1 -1
  230. package/dist/server/utils.js +10 -4
  231. package/dist/server/utils.js.map +1 -1
  232. package/package.json +2 -6
  233. package/src/authorization/index.ts +1 -1
  234. package/src/cli/index.ts +1 -1
  235. package/src/client/core/types.ts +14 -14
  236. package/src/client/factors/device.ts +10 -12
  237. package/src/client/factors/passkey.ts +23 -26
  238. package/src/client/index.ts +54 -64
  239. package/src/client/runtime/invite.ts +5 -7
  240. package/src/component/index.ts +1 -0
  241. package/src/component/public/enterprise/audit.ts +6 -1
  242. package/src/component/public/enterprise/core.ts +1 -0
  243. package/src/component/public/enterprise/domains.ts +5 -1
  244. package/src/component/public/enterprise/scim.ts +1 -0
  245. package/src/component/public/enterprise/secrets.ts +1 -0
  246. package/src/component/public/enterprise/webhooks.ts +1 -0
  247. package/src/component/public/factors/devices.ts +1 -0
  248. package/src/component/public/factors/passkeys.ts +1 -0
  249. package/src/component/public/factors/totp.ts +1 -0
  250. package/src/component/public/groups/core.ts +1 -1
  251. package/src/component/public/groups/invites.ts +7 -1
  252. package/src/component/public/groups/members.ts +1 -0
  253. package/src/component/public/identity/accounts.ts +1 -0
  254. package/src/component/public/identity/codes.ts +1 -0
  255. package/src/component/public/identity/sessions.ts +1 -0
  256. package/src/component/public/identity/tokens.ts +1 -0
  257. package/src/component/public/identity/users.ts +1 -0
  258. package/src/component/public/identity/verifiers.ts +1 -0
  259. package/src/component/public/security/keys.ts +1 -0
  260. package/src/component/public/security/limits.ts +1 -0
  261. package/src/providers/password.ts +89 -110
  262. package/src/server/auth.ts +177 -111
  263. package/src/server/core.ts +197 -233
  264. package/src/server/crypto.ts +31 -29
  265. package/src/server/device.ts +65 -32
  266. package/src/server/enterprise/domain.ts +158 -170
  267. package/src/server/enterprise/http.ts +46 -39
  268. package/src/server/http.ts +36 -30
  269. package/src/server/identity.ts +5 -5
  270. package/src/server/index.ts +2 -0
  271. package/src/server/limits.ts +53 -80
  272. package/src/server/mounts.ts +47 -74
  273. package/src/server/mutations/account.ts +22 -36
  274. package/src/server/mutations/code.ts +6 -6
  275. package/src/server/mutations/invalidate.ts +1 -1
  276. package/src/server/mutations/oauth.ts +14 -8
  277. package/src/server/mutations/refresh.ts +5 -4
  278. package/src/server/mutations/register.ts +87 -132
  279. package/src/server/mutations/retrieve.ts +44 -44
  280. package/src/server/mutations/signature.ts +13 -6
  281. package/src/server/mutations/signout.ts +1 -1
  282. package/src/server/mutations/store.ts +16 -31
  283. package/src/server/mutations/verifier.ts +1 -1
  284. package/src/server/mutations/verify.ts +3 -5
  285. package/src/server/oauth.ts +60 -69
  286. package/src/server/passkey.ts +567 -517
  287. package/src/server/redirects.ts +10 -6
  288. package/src/server/refresh.ts +14 -18
  289. package/src/server/runtime.ts +70 -55
  290. package/src/server/signin.ts +44 -37
  291. package/src/server/ssr.ts +390 -407
  292. package/src/server/totp.ts +85 -35
  293. package/src/server/types.ts +19 -22
  294. package/src/server/users.ts +7 -6
  295. package/src/server/utils.ts +10 -12
  296. package/dist/component/server/authError.js +0 -34
  297. package/dist/component/server/authError.js.map +0 -1
  298. package/dist/component/server/errors.d.ts +0 -1
  299. package/dist/component/server/errors.js +0 -137
  300. package/dist/component/server/errors.js.map +0 -1
  301. package/dist/server/authError.d.ts +0 -46
  302. package/dist/server/authError.d.ts.map +0 -1
  303. package/dist/server/authError.js +0 -34
  304. package/dist/server/authError.js.map +0 -1
  305. package/dist/server/errors.d.ts +0 -177
  306. package/dist/server/errors.d.ts.map +0 -1
  307. package/dist/server/errors.js +0 -212
  308. package/dist/server/errors.js.map +0 -1
  309. package/src/server/authError.ts +0 -44
  310. package/src/server/errors.ts +0 -290
@@ -4,13 +4,12 @@
4
4
  * @module
5
5
  */
6
6
 
7
+ import { Cv } from "@robelest/fx/convex";
7
8
  import type { UserIdentity } from "convex/server";
8
9
  import type { GenericId } from "convex/values";
9
10
 
10
11
  import type { AuthApiRefs } from "../client/index";
11
12
  import { Auth as AuthFactory } from "./runtime";
12
- import { Fx } from "@robelest/fx";
13
- import { AuthError } from "./authError";
14
13
  import type { Doc } from "./types";
15
14
  import type {
16
15
  AuthAuthorizationConfig,
@@ -35,11 +34,14 @@ import type {
35
34
  */
36
35
  export type AuthConfig = Omit<ConvexAuthConfig, "component">;
37
36
 
37
+ /** Canonical user document type exposed by Convex Auth. */
38
+ export type UserDoc = Doc<"User">;
39
+
38
40
  type MemberApiWithAuthorization<
39
41
  TAuthorization extends AuthAuthorizationConfig | undefined,
40
42
  > = Omit<
41
43
  ReturnType<typeof AuthFactory>["auth"]["member"],
42
- "create" | "list" | "update" | "resolve"
44
+ "create" | "list" | "update" | "inspect" | "require"
43
45
  > & {
44
46
  create: (
45
47
  ctx: Parameters<
@@ -52,7 +54,7 @@ type MemberApiWithAuthorization<
52
54
  status?: string;
53
55
  extend?: Record<string, unknown>;
54
56
  },
55
- ) => Promise<{ ok: true; memberId: string }>;
57
+ ) => Promise<{ memberId: string }>;
56
58
  list: (
57
59
  ctx: Parameters<
58
60
  ReturnType<typeof AuthFactory>["auth"]["member"]["list"]
@@ -76,10 +78,21 @@ type MemberApiWithAuthorization<
76
78
  >[0],
77
79
  memberId: string,
78
80
  data: Record<string, unknown> & { roleIds?: AuthRoleId<TAuthorization>[] },
79
- ) => Promise<{ ok: true; memberId: string }>;
80
- resolve: (
81
+ ) => Promise<{ memberId: string }>;
82
+ inspect: (
81
83
  ctx: Parameters<
82
- ReturnType<typeof AuthFactory>["auth"]["member"]["resolve"]
84
+ ReturnType<typeof AuthFactory>["auth"]["member"]["inspect"]
85
+ >[0],
86
+ opts: {
87
+ userId: string;
88
+ groupId: string;
89
+ ancestry?: boolean;
90
+ maxDepth?: number;
91
+ },
92
+ ) => ReturnType<ReturnType<typeof AuthFactory>["auth"]["member"]["inspect"]>;
93
+ require: (
94
+ ctx: Parameters<
95
+ ReturnType<typeof AuthFactory>["auth"]["member"]["require"]
83
96
  >[0],
84
97
  opts: {
85
98
  userId: string;
@@ -89,10 +102,9 @@ type MemberApiWithAuthorization<
89
102
  grants?: AuthGrant<TAuthorization>[];
90
103
  maxDepth?: number;
91
104
  },
92
- ) => ReturnType<ReturnType<typeof AuthFactory>["auth"]["member"]["resolve"]>;
105
+ ) => ReturnType<ReturnType<typeof AuthFactory>["auth"]["member"]["require"]>;
93
106
  };
94
107
 
95
-
96
108
  /**
97
109
  * The base auth API surface returned by {@link createAuth}.
98
110
  *
@@ -124,30 +136,29 @@ export type AuthApiBase<
124
136
  key: ReturnType<typeof AuthFactory>["auth"]["key"];
125
137
  http: ReturnType<typeof AuthFactory>["auth"]["http"];
126
138
  /**
127
- * Resolve the current user's auth context. Framework-agnostic — use
139
+ * Resolve the current request's auth context. Framework-agnostic — use
128
140
  * this in fluent-convex middleware, custom wrappers, or anywhere you
129
- * need the resolved `{ userId, user, groupId, role, grants }` object.
141
+ * need the current `{ userId, user, groupId, role, grants }` object.
130
142
  *
131
- * Returns `null` when unauthenticated. Does not throw.
143
+ * Throws a structured `ConvexError` when unauthenticated.
132
144
  *
133
145
  * @param ctx - Convex query, mutation, or action context.
134
- * @returns The resolved auth context, or `null`.
146
+ * @returns The current auth context.
135
147
  *
136
148
  * @example fluent-convex middleware
137
149
  * ```ts
138
150
  * const withAuth = convex.createMiddleware(async (ctx, next) => {
139
- * return next({ ...ctx, auth: await auth.resolve(ctx) });
151
+ * return next({ ...ctx, auth: await auth.context(ctx) });
140
152
  * });
141
153
  * ```
142
154
  *
143
155
  * @example Direct usage in a handler
144
156
  * ```ts
145
- * const resolved = await auth.resolve(ctx);
146
- * if (!resolved) return { ok: false, code: "NOT_SIGNED_IN" };
147
- * const { userId, grants } = resolved;
157
+ * const authContext = await auth.context(ctx);
158
+ * const { userId, grants } = authContext;
148
159
  * ```
149
160
  */
150
- resolve: (ctx: any) => Promise<AuthResolvedContext | null>;
161
+ context: (ctx: any) => Promise<AuthContext>;
151
162
  /**
152
163
  * Context enrichment for convex-helpers `customQuery` / `customMutation` /
153
164
  * `customAction`.
@@ -156,9 +167,9 @@ export type AuthApiBase<
156
167
  * and grants, then attaches them to `ctx.auth`. Returns a `Customization`
157
168
  * object compatible with convex-helpers' custom function builders.
158
169
  *
159
- * `ctx.auth` is `{ userId, user, groupId, role, grants }` when
160
- * authenticated, `null` when unauthenticated. No throwing your
161
- * handler decides how to respond.
170
+ * `ctx.auth` is the current request auth context.
171
+ * By default this throws when unauthenticated so handlers can assume
172
+ * `ctx.auth.userId` and `ctx.auth.user` exist.
162
173
  *
163
174
  * @returns A convex-helpers `Customization` object.
164
175
  *
@@ -180,7 +191,6 @@ export type AuthApiBase<
180
191
  * export const list = authQuery({
181
192
  * args: { workspaceId: v.string() },
182
193
  * handler: async (ctx, args) => {
183
- * if (!ctx.auth) return [];
184
194
  * const { userId, groupId, grants } = ctx.auth;
185
195
  * // business logic
186
196
  * },
@@ -190,24 +200,40 @@ export type AuthApiBase<
190
200
  ctx: () => {
191
201
  args: Record<string, never>;
192
202
  input: (ctx: any) => Promise<{
193
- ctx: { auth: AuthResolvedContext | null };
203
+ ctx: { auth: AuthContext };
194
204
  args: Record<string, never>;
195
205
  }>;
196
206
  };
197
207
  };
198
208
 
199
209
  /**
200
- * Resolved auth context injected into `ctx.auth` by `auth.ctx()`.
210
+ * Current request auth context injected into `ctx.auth` by `auth.ctx()` and
211
+ * {@link AuthCtx}. This is the authenticated auth shape returned by
212
+ * {@link createAuth().context}. Optional context builders may still surface
213
+ * nullable fields when `optional: true` is used.
201
214
  *
202
- * - `null` when unauthenticated.
203
215
  * - `groupId` is `null` when the user has no active group set.
204
- * - `role` / `grants` are `null` / `[]` when no active group or no membership.
216
+ * - `role` is `null` when no active group or no membership is resolved.
217
+ * - `grants` is `[]` when no active group or no membership is resolved.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * import type { AuthContext } from "@robelest/convex-auth/server";
222
+ *
223
+ * const mockAuth: AuthContext = {
224
+ * userId: "user123" as Id<"User">,
225
+ * user: { _id: "user123", email: "test@example.com" },
226
+ * groupId: "group456",
227
+ * role: "admin",
228
+ * grants: ["read", "write"],
229
+ * };
230
+ * ```
205
231
  */
206
- export type AuthResolvedContext = {
232
+ export type AuthContext = {
207
233
  /** The authenticated user's document ID. */
208
- userId: string;
234
+ userId: GenericId<"User">;
209
235
  /** The authenticated user's full document. */
210
- user: any;
236
+ user: UserDoc;
211
237
  /** The user's active group ID, or `null` if none set. */
212
238
  groupId: string | null;
213
239
  /** The user's primary role in the active group, or `null`. */
@@ -216,6 +242,20 @@ export type AuthResolvedContext = {
216
242
  grants: string[];
217
243
  };
218
244
 
245
+ type AuthCtxBase = {
246
+ getUserIdentity: () => Promise<UserIdentity | null>;
247
+ };
248
+
249
+ type RequiredAuthCtxState = AuthCtxBase & AuthContext;
250
+
251
+ type OptionalAuthCtxState = AuthCtxBase & {
252
+ userId: GenericId<"User"> | null;
253
+ user: UserDoc | null;
254
+ groupId: string | null;
255
+ role: string | null;
256
+ grants: string[];
257
+ };
258
+
219
259
  type InternalSsoApi = ReturnType<typeof AuthFactory>["auth"]["sso"];
220
260
 
221
261
  type PublicSsoAdminApi = {
@@ -231,7 +271,6 @@ type PublicSsoAdminApi = {
231
271
  isPrimary?: boolean;
232
272
  }>,
233
273
  ) => Promise<{
234
- ok: true;
235
274
  enterpriseId: string;
236
275
  domains: Array<{
237
276
  domainId: string;
@@ -246,7 +285,6 @@ type PublicSsoAdminApi = {
246
285
  ctx: Parameters<InternalSsoApi["connection"]["create"]>[0],
247
286
  args: { enterpriseId: string; domain: string },
248
287
  ) => Promise<{
249
- ok: true;
250
288
  enterpriseId: string;
251
289
  domain: string;
252
290
  requestedAt: number;
@@ -261,7 +299,6 @@ type PublicSsoAdminApi = {
261
299
  ctx: Parameters<InternalSsoApi["connection"]["create"]>[0],
262
300
  args: { enterpriseId: string; domain: string },
263
301
  ) => Promise<{
264
- ok: boolean;
265
302
  enterpriseId: string;
266
303
  domain: string;
267
304
  verifiedAt?: number;
@@ -370,7 +407,7 @@ export type InferClientApi<T> =
370
407
  : AuthApiRefs;
371
408
 
372
409
  /** @internal */
373
- export type AuthLike = Pick<AuthApiBase, "user">;
410
+ export type AuthLike = Pick<AuthApiBase, "user" | "member">;
374
411
 
375
412
  // ============================================================================
376
413
  // Auth setup APIs
@@ -415,9 +452,12 @@ export type AuthLike = Pick<AuthApiBase, "user">;
415
452
  * 1. `user.id(ctx)` → userId or null (exit early)
416
453
  * 2. `user.get(ctx, userId)` → user doc (cached per-execution)
417
454
  * 3. `user.getActiveGroup(ctx, { userId })` → groupId or null
418
- * 4. If groupId → `member.resolve(ctx, { userId, groupId })` → role + grants
455
+ * 4. If groupId → `member.inspect(ctx, { userId, groupId })` → role + grants
419
456
  */
420
- async function resolveAuthContext(auth: any, ctx: any) {
457
+ async function getAuthContext(
458
+ auth: AuthLike,
459
+ ctx: any,
460
+ ): Promise<AuthContext | null> {
421
461
  const userId = await auth.user.id(ctx);
422
462
  if (!userId) return null;
423
463
  const user = await auth.user.get(ctx, userId);
@@ -425,7 +465,7 @@ async function resolveAuthContext(auth: any, ctx: any) {
425
465
  let role: string | null = null;
426
466
  let grants: string[] = [];
427
467
  if (groupId) {
428
- const resolved = await auth.member.resolve(ctx, { userId, groupId });
468
+ const resolved = await auth.member.inspect(ctx, { userId, groupId });
429
469
  if (resolved.membership) {
430
470
  role = resolved.roleIds[0] ?? null;
431
471
  grants = resolved.grants;
@@ -473,10 +513,10 @@ export function createAuth<
473
513
  ) => {
474
514
  const enterprise = await connectionApi.get(ctx, enterpriseId);
475
515
  if (enterprise === null) {
476
- throw new AuthError(
477
- "INVALID_PARAMETERS",
478
- "Enterprise not found.",
479
- ).toConvexError();
516
+ throw Cv.error({
517
+ code: "INVALID_PARAMETERS",
518
+ message: "Enterprise not found.",
519
+ });
480
520
  }
481
521
 
482
522
  const normalized = domains.map((entry: (typeof domains)[number]) => ({
@@ -486,16 +526,16 @@ export function createAuth<
486
526
  const deduped = new Map<string, (typeof normalized)[number]>();
487
527
  for (const entry of normalized) {
488
528
  if (entry.domain.length === 0) {
489
- throw new AuthError(
490
- "INVALID_PARAMETERS",
491
- "Domain must not be empty.",
492
- ).toConvexError();
529
+ throw Cv.error({
530
+ code: "INVALID_PARAMETERS",
531
+ message: "Domain must not be empty.",
532
+ });
493
533
  }
494
534
  if (deduped.has(entry.domain)) {
495
- throw new AuthError(
496
- "INVALID_PARAMETERS",
497
- `Duplicate domain: ${entry.domain}`,
498
- ).toConvexError();
535
+ throw Cv.error({
536
+ code: "INVALID_PARAMETERS",
537
+ message: `Duplicate domain: ${entry.domain}`,
538
+ });
499
539
  }
500
540
  deduped.set(entry.domain, entry);
501
541
  }
@@ -505,10 +545,10 @@ export function createAuth<
505
545
  (entry) => entry.isPrimary,
506
546
  ).length;
507
547
  if (primaryCount > 1) {
508
- throw new AuthError(
509
- "INVALID_PARAMETERS",
510
- "Only one primary domain may be set.",
511
- ).toConvexError();
548
+ throw Cv.error({
549
+ code: "INVALID_PARAMETERS",
550
+ message: "Only one primary domain may be set.",
551
+ });
512
552
  }
513
553
  if (nextDomains.length > 0 && primaryCount === 0) {
514
554
  nextDomains[0] = { ...nextDomains[0], isPrimary: true };
@@ -555,7 +595,6 @@ export function createAuth<
555
595
 
556
596
  const updatedDomains = await domainApi.list(ctx, enterpriseId);
557
597
  return {
558
- ok: true as const,
559
598
  enterpriseId,
560
599
  domains: updatedDomains.map(
561
600
  (domain: (typeof updatedDomains)[number]) => ({
@@ -629,12 +668,27 @@ export function createAuth<
629
668
  },
630
669
  http: authResult.auth.http,
631
670
 
632
- resolve: (ctx: any) => resolveAuthContext(authResult.auth, ctx),
671
+ context: async (ctx: any) => {
672
+ const authContext = await getAuthContext(authResult.auth, ctx);
673
+ if (authContext === null) {
674
+ throw Cv.error({
675
+ code: "NOT_SIGNED_IN",
676
+ message: "Authentication required.",
677
+ });
678
+ }
679
+ return authContext;
680
+ },
633
681
 
634
682
  ctx: () => ({
635
683
  args: {},
636
684
  input: async (ctx: any) => {
637
- const authCtx = await resolveAuthContext(authResult.auth, ctx);
685
+ const authCtx = await getAuthContext(authResult.auth, ctx);
686
+ if (authCtx === null) {
687
+ throw Cv.error({
688
+ code: "NOT_SIGNED_IN",
689
+ message: "Authentication required.",
690
+ });
691
+ }
638
692
  return { ctx: { auth: authCtx }, args: {} };
639
693
  },
640
694
  }),
@@ -645,9 +699,6 @@ export function createAuth<
645
699
  // AuthCtx — ctx enrichment for customQuery / customMutation
646
700
  // ============================================================================
647
701
 
648
- /** Canonical user document type exposed by Convex Auth. */
649
- export type UserDoc = Doc<"User">;
650
-
651
702
  /**
652
703
  * Configuration for {@link AuthCtx} context enrichment.
653
704
  *
@@ -660,17 +711,50 @@ export type AuthCtxConfig<
660
711
  /** Allow unauthenticated callers and return `userId: null` / `user: null`. */
661
712
  optional?: boolean;
662
713
  /**
663
- * Attach additional derived fields to the auth context after the user is resolved.
714
+ * Attach additional derived fields to the auth context after the base auth
715
+ * context is resolved.
664
716
  */
665
- resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;
717
+ resolve?: (
718
+ ctx: any,
719
+ user: UserDoc,
720
+ auth: AuthContext,
721
+ ) => Promise<TResolve> | TResolve;
722
+ /**
723
+ * Override or wrap the base auth resolution used by {@link AuthCtx}.
724
+ *
725
+ * Return `undefined` to fall back to the built-in resolver,
726
+ * `null` for an explicit unauthenticated state, or an
727
+ * {@link AuthContext} object to provide a pre-resolved auth state.
728
+ * This is useful for tests, proxy auth, impersonation flows, or any
729
+ * environment that needs to inject auth without depending on the standard
730
+ * Convex auth tables.
731
+ *
732
+ * @param ctx - The Convex function context.
733
+ * @param fallback - The built-in auth resolver used by {@link AuthCtx}.
734
+ * @returns Resolved auth state, `null`, or `undefined` to use the fallback.
735
+ *
736
+ * @example
737
+ * ```ts
738
+ * const authCtx = AuthCtx(auth, {
739
+ * authResolve: async (ctx, fallback) => {
740
+ * const injected = getInjectedAuth(ctx);
741
+ * return injected ?? (await fallback());
742
+ * },
743
+ * });
744
+ * ```
745
+ */
746
+ authResolve?: (
747
+ ctx: any,
748
+ fallback: () => Promise<AuthContext | null>,
749
+ ) => Promise<AuthContext | null | undefined> | AuthContext | null | undefined;
666
750
  };
667
751
 
668
752
  /**
669
753
  * Create a context enrichment for `customQuery` / `customMutation` — optional auth.
670
754
  *
671
755
  * When `optional: true` is set, unauthenticated requests are allowed.
672
- * The enriched `ctx.auth` will have `userId: null` and `user: null`
673
- * for unauthenticated callers.
756
+ * The enriched `ctx.auth` will have `userId: null`, `user: null`,
757
+ * `groupId: null`, `role: null`, and `grants: []` for unauthenticated callers.
674
758
  *
675
759
  * @param auth - The auth API object returned by {@link createAuth}.
676
760
  * @param config - Configuration with `optional: true` and an optional
@@ -701,11 +785,7 @@ export function AuthCtx<
701
785
  _extra?: any,
702
786
  ) => Promise<{
703
787
  ctx: {
704
- auth: {
705
- getUserIdentity: () => Promise<UserIdentity | null>;
706
- userId: GenericId<"User"> | null;
707
- user: UserDoc | null;
708
- } & TResolve;
788
+ auth: OptionalAuthCtxState & TResolve;
709
789
  };
710
790
  args: {};
711
791
  }>;
@@ -713,10 +793,8 @@ export function AuthCtx<
713
793
  /**
714
794
  * Create a context enrichment for `customQuery` / `customMutation` — required auth (default).
715
795
  *
716
- * When `optional` is omitted or `false`, the inferred type is the authenticated
717
- * auth shape. At runtime this helper still resolves instead of throwing, so if
718
- * no user is signed in the returned `ctx.auth.userId` and `ctx.auth.user` are
719
- * `null`.
796
+ * When `optional` is omitted or `false`, unauthenticated requests throw a
797
+ * structured `ConvexError` before your handler runs.
720
798
  *
721
799
  * @param auth - The auth API object returned by {@link createAuth}.
722
800
  * @param config - Optional configuration with a `resolve` callback
@@ -746,11 +824,7 @@ export function AuthCtx<
746
824
  _extra?: any,
747
825
  ) => Promise<{
748
826
  ctx: {
749
- auth: {
750
- getUserIdentity: () => Promise<UserIdentity | null>;
751
- userId: GenericId<"User">;
752
- user: UserDoc;
753
- } & TResolve;
827
+ auth: RequiredAuthCtxState & TResolve;
754
828
  };
755
829
  args: {};
756
830
  }>;
@@ -761,39 +835,31 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
761
835
  args: {},
762
836
  input: async (ctx: any, _args: any, _extra?: any) => {
763
837
  const nativeAuth = ctx.auth;
764
- const modeDispatch =
765
- config?.optional === true
766
- ? { mode: "optional" as const }
767
- : { mode: "required" as const };
768
-
769
- const userContext = await Fx.run(
770
- Fx.match(modeDispatch, modeDispatch.mode, {
771
- optional: async () => {
772
- const userId = await auth.user.id(ctx);
773
- if (!userId) {
774
- return null;
775
- }
776
- const user = await auth.user.get(ctx, userId);
777
- return { userId, user };
778
- },
779
- required: async () => {
780
- const userId = await auth.user.id(ctx);
781
- if (!userId) {
782
- return null;
783
- }
784
- const user = await auth.user.get(ctx, userId);
785
- return { userId, user };
786
- },
787
- }),
788
- );
789
-
790
- if (userContext === null) {
838
+ const getUserIdentity = nativeAuth.getUserIdentity.bind(nativeAuth);
839
+ const fallback = () => getAuthContext(auth, ctx);
840
+
841
+ const authOverride = config?.authResolve
842
+ ? await config.authResolve(ctx, fallback)
843
+ : undefined;
844
+ const resolved =
845
+ authOverride === undefined ? await fallback() : authOverride;
846
+
847
+ if (resolved === null) {
848
+ if (config?.optional !== true) {
849
+ throw Cv.error({
850
+ code: "NOT_SIGNED_IN",
851
+ message: "Authentication required.",
852
+ });
853
+ }
791
854
  return {
792
855
  ctx: {
793
856
  auth: {
794
- getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),
857
+ getUserIdentity,
795
858
  userId: null,
796
859
  user: null,
860
+ groupId: null,
861
+ role: null,
862
+ grants: [],
797
863
  },
798
864
  },
799
865
  args: {},
@@ -801,15 +867,14 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
801
867
  }
802
868
 
803
869
  const extra = config?.resolve
804
- ? await config.resolve(ctx, userContext.user)
870
+ ? await config.resolve(ctx, resolved.user, resolved)
805
871
  : {};
806
872
 
807
873
  return {
808
874
  ctx: {
809
875
  auth: {
810
- getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),
811
- userId: userContext.userId,
812
- user: userContext.user,
876
+ getUserIdentity,
877
+ ...resolved,
813
878
  ...extra,
814
879
  },
815
880
  },
@@ -824,9 +889,10 @@ export function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {
824
889
  *
825
890
  * Use this to type function parameters or variables that receive the
826
891
  * enriched auth context produced by `AuthCtx`. The inferred type includes
827
- * `userId`, `user`, `getUserIdentity`, and any additional fields added
828
- * by the `resolve` callback. This is the generic utility for reusing the
829
- * enriched auth shape without manually duplicating conditional auth types.
892
+ * `userId`, `user`, `groupId`, `role`, `grants`, `getUserIdentity`, and any
893
+ * additional fields added by the `resolve` callback. This is the generic
894
+ * utility for reusing the enriched auth shape without manually duplicating
895
+ * conditional auth types.
830
896
  *
831
897
  * @typeParam T - An `AuthCtx` return value (must have an `input` method
832
898
  * that returns `{ ctx: { auth: ... } }`).