@robelest/convex-auth 0.0.4-preview.21 → 0.0.4-preview.22
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.
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/model.d.ts +10 -10
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/schema.d.ts +51 -51
- package/dist/component/server/auth.d.ts +71 -25
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +13 -27
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/runtime.d.ts +2 -2
- package/dist/server/auth.d.ts +70 -24
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +13 -27
- package/dist/server/auth.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/mounts.d.ts +12 -12
- package/dist/server/mutations/account.d.ts +6 -6
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/invalidate.d.ts +4 -4
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/oauth.d.ts +9 -9
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/refresh.d.ts +3 -3
- package/dist/server/mutations/refresh.d.ts.map +1 -1
- package/dist/server/mutations/register.d.ts +11 -11
- package/dist/server/mutations/retrieve.d.ts +6 -6
- package/dist/server/mutations/retrieve.d.ts.map +1 -1
- package/dist/server/mutations/signature.d.ts +4 -4
- package/dist/server/mutations/signin.d.ts +5 -5
- package/dist/server/mutations/store.d.ts +100 -100
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/verify.d.ts +10 -10
- package/dist/server/runtime.d.ts +15 -15
- package/package.json +2 -2
- package/src/cli/index.ts +1 -1
- package/src/component/index.ts +1 -0
- package/src/server/auth.ts +103 -59
- package/src/server/index.ts +2 -0
|
@@ -10,6 +10,8 @@ import { GenericId } from "convex/values";
|
|
|
10
10
|
* minus `component` (which is passed as the first constructor argument).
|
|
11
11
|
*/
|
|
12
12
|
type AuthConfig = Omit<ConvexAuthConfig, "component">;
|
|
13
|
+
/** Canonical user document type exposed by Convex Auth. */
|
|
14
|
+
type UserDoc = Doc<"User">;
|
|
13
15
|
type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = Omit<ReturnType<typeof Auth>["auth"]["member"], "create" | "list" | "update" | "resolve"> & {
|
|
14
16
|
create: (ctx: Parameters<ReturnType<typeof Auth>["auth"]["member"]["create"]>[0], data: {
|
|
15
17
|
groupId: string;
|
|
@@ -151,19 +153,45 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
|
|
|
151
153
|
};
|
|
152
154
|
};
|
|
153
155
|
/**
|
|
154
|
-
* Resolved auth context injected into `ctx.auth` by `auth.ctx()
|
|
156
|
+
* Resolved auth context injected into `ctx.auth` by `auth.ctx()` and
|
|
157
|
+
* {@link AuthCtx}. Also the expected return shape for custom
|
|
158
|
+
* {@link AuthCtxConfig.authResolve | authResolve} hooks.
|
|
155
159
|
*
|
|
156
160
|
* - `null` when unauthenticated.
|
|
157
161
|
* - `groupId` is `null` when the user has no active group set.
|
|
158
162
|
* - `role` / `grants` are `null` / `[]` when no active group or no membership.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import type { AuthResolvedContext } from "@robelest/convex-auth/server";
|
|
167
|
+
*
|
|
168
|
+
* const mockAuth: AuthResolvedContext = {
|
|
169
|
+
* userId: "user123" as Id<"User">,
|
|
170
|
+
* user: { _id: "user123", email: "test@example.com" },
|
|
171
|
+
* groupId: "group456",
|
|
172
|
+
* role: "admin",
|
|
173
|
+
* grants: ["read", "write"],
|
|
174
|
+
* };
|
|
175
|
+
* ```
|
|
159
176
|
*/
|
|
160
177
|
type AuthResolvedContext = {
|
|
161
|
-
/** The authenticated user's document ID. */userId:
|
|
162
|
-
user:
|
|
178
|
+
/** The authenticated user's document ID. */userId: GenericId<"User">; /** The authenticated user's full document. */
|
|
179
|
+
user: UserDoc; /** The user's active group ID, or `null` if none set. */
|
|
163
180
|
groupId: string | null; /** The user's primary role in the active group, or `null`. */
|
|
164
181
|
role: string | null; /** Resolved grant strings from the user's role definitions. */
|
|
165
182
|
grants: string[];
|
|
166
183
|
};
|
|
184
|
+
type AuthCtxBase = {
|
|
185
|
+
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
186
|
+
};
|
|
187
|
+
type RequiredAuthCtxState = AuthCtxBase & AuthResolvedContext;
|
|
188
|
+
type OptionalAuthCtxState = AuthCtxBase & {
|
|
189
|
+
userId: GenericId<"User"> | null;
|
|
190
|
+
user: UserDoc | null;
|
|
191
|
+
groupId: string | null;
|
|
192
|
+
role: string | null;
|
|
193
|
+
grants: string[];
|
|
194
|
+
};
|
|
167
195
|
type InternalSsoApi = ReturnType<typeof Auth>["auth"]["sso"];
|
|
168
196
|
type PublicSsoAdminApi = {
|
|
169
197
|
connection: InternalSsoApi["connection"] & {
|
|
@@ -280,8 +308,6 @@ declare function createAuth<P extends AuthProviderConfig[], TAuthorization exten
|
|
|
280
308
|
providers: P;
|
|
281
309
|
authorization?: TAuthorization;
|
|
282
310
|
}): ConvexAuthResult<P, TAuthorization>;
|
|
283
|
-
/** Canonical user document type exposed by Convex Auth. */
|
|
284
|
-
type UserDoc = Doc<"User">;
|
|
285
311
|
/**
|
|
286
312
|
* Configuration for {@link AuthCtx} context enrichment.
|
|
287
313
|
*
|
|
@@ -291,16 +317,42 @@ type UserDoc = Doc<"User">;
|
|
|
291
317
|
type AuthCtxConfig<TResolve extends Record<string, unknown> = Record<string, never>> = {
|
|
292
318
|
/** Allow unauthenticated callers and return `userId: null` / `user: null`. */optional?: boolean;
|
|
293
319
|
/**
|
|
294
|
-
* Attach additional derived fields to the auth context after the
|
|
320
|
+
* Attach additional derived fields to the auth context after the base auth
|
|
321
|
+
* context is resolved.
|
|
322
|
+
*/
|
|
323
|
+
resolve?: (ctx: any, user: UserDoc, auth: AuthResolvedContext) => Promise<TResolve> | TResolve;
|
|
324
|
+
/**
|
|
325
|
+
* Override or wrap the base auth resolution used by {@link AuthCtx}.
|
|
326
|
+
*
|
|
327
|
+
* Return `undefined` to fall back to the built-in resolver,
|
|
328
|
+
* `null` for an explicit unauthenticated state, or an
|
|
329
|
+
* {@link AuthResolvedContext} object to provide a pre-resolved auth state.
|
|
330
|
+
* This is useful for tests, proxy auth, impersonation flows, or any
|
|
331
|
+
* environment that needs to inject auth without depending on the standard
|
|
332
|
+
* Convex auth tables.
|
|
333
|
+
*
|
|
334
|
+
* @param ctx - The Convex function context.
|
|
335
|
+
* @param fallback - The built-in auth resolver used by {@link AuthCtx}.
|
|
336
|
+
* @returns Resolved auth state, `null`, or `undefined` to use the fallback.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const authCtx = AuthCtx(auth, {
|
|
341
|
+
* authResolve: async (ctx, fallback) => {
|
|
342
|
+
* const injected = getInjectedAuth(ctx);
|
|
343
|
+
* return injected ?? (await fallback());
|
|
344
|
+
* },
|
|
345
|
+
* });
|
|
346
|
+
* ```
|
|
295
347
|
*/
|
|
296
|
-
|
|
348
|
+
authResolve?: (ctx: any, fallback: () => Promise<AuthResolvedContext | null>) => Promise<AuthResolvedContext | null | undefined> | AuthResolvedContext | null | undefined;
|
|
297
349
|
};
|
|
298
350
|
/**
|
|
299
351
|
* Create a context enrichment for `customQuery` / `customMutation` — optional auth.
|
|
300
352
|
*
|
|
301
353
|
* When `optional: true` is set, unauthenticated requests are allowed.
|
|
302
|
-
* The enriched `ctx.auth` will have `userId: null
|
|
303
|
-
* for unauthenticated callers.
|
|
354
|
+
* The enriched `ctx.auth` will have `userId: null`, `user: null`,
|
|
355
|
+
* `groupId: null`, `role: null`, and `grants: []` for unauthenticated callers.
|
|
304
356
|
*
|
|
305
357
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
306
358
|
* @param config - Configuration with `optional: true` and an optional
|
|
@@ -324,11 +376,7 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
324
376
|
args: {};
|
|
325
377
|
input: (ctx: any, _args: any, _extra?: any) => Promise<{
|
|
326
378
|
ctx: {
|
|
327
|
-
auth:
|
|
328
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
329
|
-
userId: GenericId<"User"> | null;
|
|
330
|
-
user: UserDoc | null;
|
|
331
|
-
} & TResolve;
|
|
379
|
+
auth: OptionalAuthCtxState & TResolve;
|
|
332
380
|
};
|
|
333
381
|
args: {};
|
|
334
382
|
}>;
|
|
@@ -338,8 +386,9 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
338
386
|
*
|
|
339
387
|
* When `optional` is omitted or `false`, the inferred type is the authenticated
|
|
340
388
|
* auth shape. At runtime this helper still resolves instead of throwing, so if
|
|
341
|
-
* no user is signed in the returned `ctx.auth.userId`
|
|
342
|
-
* `null
|
|
389
|
+
* no user is signed in the returned `ctx.auth.userId` / `ctx.auth.user` are
|
|
390
|
+
* `null`, `ctx.auth.groupId` / `ctx.auth.role` are `null`, and
|
|
391
|
+
* `ctx.auth.grants` is `[]`.
|
|
343
392
|
*
|
|
344
393
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
345
394
|
* @param config - Optional configuration with a `resolve` callback
|
|
@@ -360,11 +409,7 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
360
409
|
args: {};
|
|
361
410
|
input: (ctx: any, _args: any, _extra?: any) => Promise<{
|
|
362
411
|
ctx: {
|
|
363
|
-
auth:
|
|
364
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
365
|
-
userId: GenericId<"User">;
|
|
366
|
-
user: UserDoc;
|
|
367
|
-
} & TResolve;
|
|
412
|
+
auth: RequiredAuthCtxState & TResolve;
|
|
368
413
|
};
|
|
369
414
|
args: {};
|
|
370
415
|
}>;
|
|
@@ -374,9 +419,10 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
374
419
|
*
|
|
375
420
|
* Use this to type function parameters or variables that receive the
|
|
376
421
|
* enriched auth context produced by `AuthCtx`. The inferred type includes
|
|
377
|
-
* `userId`, `user`, `getUserIdentity`, and any
|
|
378
|
-
* by the `resolve` callback. This is the generic
|
|
379
|
-
* enriched auth shape without manually duplicating
|
|
422
|
+
* `userId`, `user`, `groupId`, `role`, `grants`, `getUserIdentity`, and any
|
|
423
|
+
* additional fields added by the `resolve` callback. This is the generic
|
|
424
|
+
* utility for reusing the enriched auth shape without manually duplicating
|
|
425
|
+
* conditional auth types.
|
|
380
426
|
*
|
|
381
427
|
* @typeParam T - An `AuthCtx` return value (must have an `input` method
|
|
382
428
|
* that returns `{ ctx: { auth: ... } }`).
|
|
@@ -400,5 +446,5 @@ type InferAuth<T extends {
|
|
|
400
446
|
}>;
|
|
401
447
|
}> = Awaited<ReturnType<T["input"]>>["ctx"]["auth"];
|
|
402
448
|
//#endregion
|
|
403
|
-
export { AuthApi, AuthConfig, AuthCtx, AuthCtxConfig, InferAuth, UserDoc, createAuth };
|
|
449
|
+
export { AuthApi, AuthConfig, AuthCtx, AuthCtxConfig, AuthResolvedContext, InferAuth, UserDoc, createAuth };
|
|
404
450
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/server/auth.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/server/auth.ts"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AuthError } from "./authError.js";
|
|
2
2
|
import { Auth } from "./runtime.js";
|
|
3
|
-
import { Fx } from "@robelest/fx";
|
|
4
3
|
|
|
5
4
|
//#region src/server/auth.ts
|
|
6
5
|
/**
|
|
@@ -186,39 +185,26 @@ function AuthCtx(auth, config) {
|
|
|
186
185
|
args: {},
|
|
187
186
|
input: async (ctx, _args, _extra) => {
|
|
188
187
|
const nativeAuth = ctx.auth;
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
userId,
|
|
196
|
-
user: await auth.user.get(ctx, userId)
|
|
197
|
-
};
|
|
198
|
-
},
|
|
199
|
-
required: async () => {
|
|
200
|
-
const userId = await auth.user.id(ctx);
|
|
201
|
-
if (!userId) return null;
|
|
202
|
-
return {
|
|
203
|
-
userId,
|
|
204
|
-
user: await auth.user.get(ctx, userId)
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
}));
|
|
208
|
-
if (userContext === null) return {
|
|
188
|
+
const getUserIdentity = nativeAuth.getUserIdentity.bind(nativeAuth);
|
|
189
|
+
const fallback = () => resolveAuthContext(auth, ctx);
|
|
190
|
+
const authOverride = config?.authResolve ? await config.authResolve(ctx, fallback) : void 0;
|
|
191
|
+
const resolved = authOverride === void 0 ? await fallback() : authOverride;
|
|
192
|
+
if (resolved === null) return {
|
|
209
193
|
ctx: { auth: {
|
|
210
|
-
getUserIdentity
|
|
194
|
+
getUserIdentity,
|
|
211
195
|
userId: null,
|
|
212
|
-
user: null
|
|
196
|
+
user: null,
|
|
197
|
+
groupId: null,
|
|
198
|
+
role: null,
|
|
199
|
+
grants: []
|
|
213
200
|
} },
|
|
214
201
|
args: {}
|
|
215
202
|
};
|
|
216
|
-
const extra = config?.resolve ? await config.resolve(ctx,
|
|
203
|
+
const extra = config?.resolve ? await config.resolve(ctx, resolved.user, resolved) : {};
|
|
217
204
|
return {
|
|
218
205
|
ctx: { auth: {
|
|
219
|
-
getUserIdentity
|
|
220
|
-
|
|
221
|
-
user: userContext.user,
|
|
206
|
+
getUserIdentity,
|
|
207
|
+
...resolved,
|
|
222
208
|
...extra
|
|
223
209
|
} },
|
|
224
210
|
args: {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Auth as AuthFactory } from \"./runtime\";\nimport { Fx } from \"@robelest/fx\";\nimport { AuthError } from \"./authError\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthAuthorizationConfig,\n AuthGrant,\n AuthProviderConfig,\n AuthRoleDefinition,\n AuthRoleId,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\ntype MemberApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = Omit<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"],\n \"create\" | \"list\" | \"update\" | \"resolve\"\n> & {\n create: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"create\"]\n >[0],\n data: {\n groupId: string;\n userId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n status?: string;\n extend?: Record<string, unknown>;\n },\n ) => Promise<{ ok: true; memberId: string }>;\n list: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]\n >[0],\n opts?: {\n where?: {\n groupId?: string;\n userId?: string;\n roleId?: AuthRoleId<TAuthorization>;\n status?: string;\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]>;\n update: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"update\"]\n >[0],\n memberId: string,\n data: Record<string, unknown> & { roleIds?: AuthRoleId<TAuthorization>[] },\n ) => Promise<{ ok: true; memberId: string }>;\n resolve: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"resolve\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n ancestry?: boolean;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"resolve\"]>;\n};\n\n\n/**\n * The base auth API surface returned by {@link createAuth}.\n *\n * Provides core namespaces — `signIn`, `signOut`, `user`, `session`,\n * `member`, `invite`, `group`, `key`, and `http` — that are\n * always available regardless of which providers are configured.\n * Enterprise namespaces (`sso`, `scim`) are added conditionally by\n * {@link AuthApi} when an SSO provider is present.\n *\n * Use this type when you want to describe code that only depends on the\n * standard auth surface and should not assume enterprise features exist.\n *\n * @typeParam TAuthorization - The authorization config, used to narrow\n * role IDs and grant strings on the `member` API.\n */\nexport type AuthApiBase<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: MemberApiWithAuthorization<TAuthorization>;\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n /**\n * Resolve the current user's auth context. Framework-agnostic — use\n * this in fluent-convex middleware, custom wrappers, or anywhere you\n * need the resolved `{ userId, user, groupId, role, grants }` object.\n *\n * Returns `null` when unauthenticated. Does not throw.\n *\n * @param ctx - Convex query, mutation, or action context.\n * @returns The resolved auth context, or `null`.\n *\n * @example fluent-convex middleware\n * ```ts\n * const withAuth = convex.createMiddleware(async (ctx, next) => {\n * return next({ ...ctx, auth: await auth.resolve(ctx) });\n * });\n * ```\n *\n * @example Direct usage in a handler\n * ```ts\n * const resolved = await auth.resolve(ctx);\n * if (!resolved) return { ok: false, code: \"NOT_SIGNED_IN\" };\n * const { userId, grants } = resolved;\n * ```\n */\n resolve: (ctx: any) => Promise<AuthResolvedContext | null>;\n /**\n * Context enrichment for convex-helpers `customQuery` / `customMutation` /\n * `customAction`.\n *\n * Resolves the current user's identity, active group, membership role,\n * and grants, then attaches them to `ctx.auth`. Returns a `Customization`\n * object compatible with convex-helpers' custom function builders.\n *\n * `ctx.auth` is `{ userId, user, groupId, role, grants }` when\n * authenticated, `null` when unauthenticated. No throwing — your\n * handler decides how to respond.\n *\n * @returns A convex-helpers `Customization` object.\n *\n * @example One-time setup in `convex/functions.ts`\n * ```ts\n * import { query, mutation, action } from \"./_generated/server\";\n * import { customQuery, customMutation, customAction } from \"convex-helpers/server/customFunctions\";\n * import { auth } from \"./auth\";\n *\n * export const authQuery = customQuery(query, auth.ctx());\n * export const authMutation = customMutation(mutation, auth.ctx());\n * export const authAction = customAction(action, auth.ctx());\n * ```\n *\n * @example Per-function usage\n * ```ts\n * import { authQuery } from \"./functions\";\n *\n * export const list = authQuery({\n * args: { workspaceId: v.string() },\n * handler: async (ctx, args) => {\n * if (!ctx.auth) return [];\n * const { userId, groupId, grants } = ctx.auth;\n * // business logic\n * },\n * });\n * ```\n */\n ctx: () => {\n args: Record<string, never>;\n input: (ctx: any) => Promise<{\n ctx: { auth: AuthResolvedContext | null };\n args: Record<string, never>;\n }>;\n };\n};\n\n/**\n * Resolved auth context injected into `ctx.auth` by `auth.ctx()`.\n *\n * - `null` when unauthenticated.\n * - `groupId` is `null` when the user has no active group set.\n * - `role` / `grants` are `null` / `[]` when no active group or no membership.\n */\nexport type AuthResolvedContext = {\n /** The authenticated user's document ID. */\n userId: string;\n /** The authenticated user's full document. */\n user: any;\n /** The user's active group ID, or `null` if none set. */\n groupId: string | null;\n /** The user's primary role in the active group, or `null`. */\n role: string | null;\n /** Resolved grant strings from the user's role definitions. */\n grants: string[];\n};\n\ntype InternalSsoApi = ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n\ntype PublicSsoAdminApi = {\n connection: InternalSsoApi[\"connection\"] & {\n domain: {\n list: InternalSsoApi[\"domain\"][\"list\"];\n validate: InternalSsoApi[\"domain\"][\"validate\"];\n set: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n enterpriseId: string,\n domains: Array<{\n domain: string;\n isPrimary?: boolean;\n }>,\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domains: Array<{\n domainId: string;\n domain: string;\n isPrimary: boolean;\n verified: boolean;\n verifiedAt: number | null;\n }>;\n }>;\n verification: {\n request: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domain: string;\n requestedAt: number;\n expiresAt: number;\n challenge: {\n recordType: \"TXT\";\n recordName: string;\n recordValue: string;\n };\n }>;\n confirm: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: boolean;\n enterpriseId: string;\n domain: string;\n verifiedAt?: number;\n checks: Array<{ name: string; ok: boolean; message?: string }>;\n }>;\n };\n };\n };\n oidc: Omit<InternalSsoApi[\"oidc\"], \"signIn\">;\n saml: Omit<InternalSsoApi[\"saml\"], \"metadata\">;\n policy: InternalSsoApi[\"policy\"];\n audit: {\n list: InternalSsoApi[\"audit\"][\"list\"];\n };\n webhook: {\n endpoint: InternalSsoApi[\"webhook\"][\"endpoint\"];\n delivery: {\n list: InternalSsoApi[\"webhook\"][\"delivery\"][\"list\"];\n };\n };\n};\n\ntype PublicSsoClientApi = {\n signIn: InternalSsoApi[\"oidc\"][\"signIn\"];\n metadata: InternalSsoApi[\"saml\"][\"metadata\"];\n};\n\ntype PublicSsoApi = {\n admin: PublicSsoAdminApi;\n client: PublicSsoClientApi;\n};\n\ntype PublicScimApi = {\n admin: Omit<InternalSsoApi[\"scim\"], \"getConfigByToken\" | \"identity\">;\n};\n\n/**\n * Extended auth API that includes enterprise SSO and SCIM namespaces.\n *\n * This type is the union of {@link AuthApiBase} plus `sso` (SSO connection\n * management, OIDC/SAML, domain verification, policies, audit, webhooks)\n * and `scim` (SCIM provisioning configuration). It is returned by\n * {@link createAuth} only when `new SSO()` is included in the providers\n * array; otherwise the narrower {@link AuthApiBase} is returned instead.\n * Attempting to access `auth.sso` or `auth.scim` without an SSO provider\n * produces a compile-time error because the return type narrows back to\n * {@link AuthApiBase}.\n *\n * @typeParam TAuthorization - The authorization config, forwarded to\n * {@link AuthApiBase} for typed role IDs and grant strings.\n */\nexport type AuthApi<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = AuthApiBase<TAuthorization> & {\n sso: PublicSsoApi;\n scim: PublicScimApi;\n};\n\n/**\n * The return type of {@link createAuth}.\n *\n * Resolves to {@link AuthApi} (with `sso` and `scim` namespaces) when\n * `new SSO()` is present in the providers array, or to the narrower\n * {@link AuthApiBase} otherwise. This conditional type ensures that\n * enterprise-only APIs are only accessible when the SSO provider is\n * configured, producing a compile-time error if you try to access\n * `auth.sso` without it.\n * This lets application code keep a single `createAuth()` call while still\n * getting provider-aware typing on the resulting API object.\n *\n * @typeParam P - The tuple of provider configs passed to `createAuth`.\n * @typeParam TAuthorization - Optional authorization config for typed roles/grants.\n */\nexport type ConvexAuthResult<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> =\n HasSSO<P> extends true\n ? AuthApi<TAuthorization>\n : AuthApiBase<TAuthorization>;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/server\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: api.auth });\n * ```\n *\n * @typeParam T - A ConvexAuthResult to extract the client API from.\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` and `auth.scim`\n * are available on the returned object. Without it, those namespaces are\n * absent and accessing them is a TypeScript compile error.\n *\n * @param component - The installed auth component reference from\n * `components.auth` in your Convex app definition.\n * @param config - Auth configuration including `providers` and optional\n * `authorization`. All fields from {@link AuthConfig} are accepted\n * except `component` (passed as the first argument).\n * @returns A {@link ConvexAuthResult} object — either {@link AuthApi}\n * (with `sso`/`scim`) or {@link AuthApiBase}, depending on whether\n * an SSO provider is present.\n *\n * @example\n * ```ts\n * export const auth = createAuth(components.auth, {\n * providers: [password(), google()],\n * authorization: { roles },\n * });\n * ```\n *\n * @see {@link AuthCtx}\n */\n\n// ---------------------------------------------------------------------------\n// Function builders — shared auth resolution logic\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve auth context for the current user. Returns the enriched\n * `ctx.auth` object or `null` when unauthenticated.\n *\n * Resolution flow:\n * 1. `user.id(ctx)` → userId or null (exit early)\n * 2. `user.get(ctx, userId)` → user doc (cached per-execution)\n * 3. `user.getActiveGroup(ctx, { userId })` → groupId or null\n * 4. If groupId → `member.resolve(ctx, { userId, groupId })` → role + grants\n */\nasync function resolveAuthContext(auth: any, ctx: any) {\n const userId = await auth.user.id(ctx);\n if (!userId) return null;\n const user = await auth.user.get(ctx, userId);\n const groupId = await auth.user.getActiveGroup(ctx, { userId });\n let role: string | null = null;\n let grants: string[] = [];\n if (groupId) {\n const resolved = await auth.member.resolve(ctx, { userId, groupId });\n if (resolved.membership) {\n role = resolved.roleIds[0] ?? null;\n grants = resolved.grants;\n }\n }\n return { userId, user, groupId, role, grants };\n}\n\nexport function createAuth<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\" | \"authorization\"> & {\n providers: P;\n authorization?: TAuthorization;\n },\n): ConvexAuthResult<P, TAuthorization> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n const {\n domain: domainApi,\n scim: scimApi,\n connection: connectionApi,\n audit: auditApi,\n webhook: webhookApi,\n oidc: oidcApi,\n saml: samlApi,\n ...restSso\n } = authResult.auth.sso as InternalSsoApi;\n\n type SetEnterpriseDomains = PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"];\n type EnterpriseDomainInput = Array<{\n domain: string;\n isPrimary?: boolean;\n }>;\n const setEnterpriseDomains: PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"] =\n async (\n ctx: Parameters<SetEnterpriseDomains>[0],\n enterpriseId: Parameters<SetEnterpriseDomains>[1],\n domains: EnterpriseDomainInput,\n ) => {\n const enterprise = await connectionApi.get(ctx, enterpriseId);\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n\n const normalized = domains.map((entry: (typeof domains)[number]) => ({\n ...entry,\n domain: entry.domain.trim().toLowerCase(),\n }));\n const deduped = new Map<string, (typeof normalized)[number]>();\n for (const entry of normalized) {\n if (entry.domain.length === 0) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain must not be empty.\",\n ).toConvexError();\n }\n if (deduped.has(entry.domain)) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n `Duplicate domain: ${entry.domain}`,\n ).toConvexError();\n }\n deduped.set(entry.domain, entry);\n }\n\n const nextDomains = [...deduped.values()];\n const primaryCount = nextDomains.filter(\n (entry) => entry.isPrimary,\n ).length;\n if (primaryCount > 1) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Only one primary domain may be set.\",\n ).toConvexError();\n }\n if (nextDomains.length > 0 && primaryCount === 0) {\n nextDomains[0] = { ...nextDomains[0], isPrimary: true };\n }\n\n const currentDomains = await domainApi.list(ctx, enterpriseId);\n const currentByDomain = new Map<string, (typeof currentDomains)[number]>(\n currentDomains.map((entry: (typeof currentDomains)[number]) => [\n entry.domain.toLowerCase(),\n entry,\n ]),\n );\n\n for (const existing of currentDomains) {\n if (!deduped.has(existing.domain.toLowerCase())) {\n await domainApi.remove(ctx, existing._id);\n }\n }\n\n for (const nextDomain of nextDomains) {\n const current = currentByDomain.get(nextDomain.domain);\n if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) {\n continue;\n }\n if (current) {\n await domainApi.remove(ctx, current._id);\n }\n const domainId = await domainApi.add(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain: nextDomain.domain,\n isPrimary: nextDomain.isPrimary,\n });\n if (current?.verifiedAt !== undefined) {\n await (ctx as any).runMutation(\n component.public.enterpriseDomainVerify,\n {\n domainId,\n verifiedAt: current.verifiedAt,\n },\n );\n }\n }\n\n const updatedDomains = await domainApi.list(ctx, enterpriseId);\n return {\n ok: true as const,\n enterpriseId,\n domains: updatedDomains.map(\n (domain: (typeof updatedDomains)[number]) => ({\n domainId: domain._id,\n domain: domain.domain,\n isPrimary: domain.isPrimary,\n verified: domain.verifiedAt !== undefined,\n verifiedAt: domain.verifiedAt ?? null,\n }),\n ),\n };\n };\n\n const publicSso: PublicSsoApi = {\n admin: {\n ...restSso,\n oidc: {\n ...oidcApi,\n },\n saml: {\n ...samlApi,\n },\n connection: {\n ...connectionApi,\n domain: {\n list: domainApi.list,\n validate: domainApi.validate,\n set: setEnterpriseDomains,\n verification: {\n request: domainApi.verification.request,\n confirm: domainApi.verification.confirm,\n },\n },\n },\n policy: restSso.policy,\n audit: {\n list: auditApi.list,\n },\n webhook: {\n endpoint: webhookApi.endpoint,\n delivery: {\n list: webhookApi.delivery.list,\n },\n },\n },\n client: {\n signIn: oidcApi.signIn,\n metadata: samlApi.metadata,\n },\n };\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: publicSso,\n scim: {\n admin: {\n configure: scimApi.configure,\n get: scimApi.get,\n validate: scimApi.validate,\n },\n },\n http: authResult.auth.http,\n\n resolve: (ctx: any) => resolveAuthContext(authResult.auth, ctx),\n\n ctx: () => ({\n args: {},\n input: async (ctx: any) => {\n const authCtx = await resolveAuthContext(authResult.auth, ctx);\n return { ctx: { auth: authCtx }, args: {} };\n },\n }),\n } as unknown as ConvexAuthResult<P, TAuthorization>;\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\n/** Canonical user document type exposed by Convex Auth. */\nexport type UserDoc = Doc<\"User\">;\n\n/**\n * Configuration for {@link AuthCtx} context enrichment.\n *\n * @typeParam TResolve - Extra fields returned from `resolve()` and merged into\n * the resulting `ctx.auth` object.\n */\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n /** Allow unauthenticated callers and return `userId: null` / `user: null`. */\n optional?: boolean;\n /**\n * Attach additional derived fields to the auth context after the user is resolved.\n */\n resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;\n};\n\n/**\n * Create a context enrichment for `customQuery` / `customMutation` — optional auth.\n *\n * When `optional: true` is set, unauthenticated requests are allowed.\n * The enriched `ctx.auth` will have `userId: null` and `user: null`\n * for unauthenticated callers.\n *\n * @param auth - The auth API object returned by {@link createAuth}.\n * @param config - Configuration with `optional: true` and an optional\n * `resolve` callback for attaching extra fields to the auth context.\n * @returns An object with `args` and `input` compatible with Convex\n * custom function builders.\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * optional: true,\n * resolve: async (_ctx, user) => ({ plan: user?.extend?.plan ?? null }),\n * });\n * ```\n *\n * @see {@link createAuth}\n */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n } & TResolve;\n };\n args: {};\n }>;\n};\n/**\n * Create a context enrichment for `customQuery` / `customMutation` — required auth (default).\n *\n * When `optional` is omitted or `false`, the inferred type is the authenticated\n * auth shape. At runtime this helper still resolves instead of throwing, so if\n * no user is signed in the returned `ctx.auth.userId` and `ctx.auth.user` are\n * `null`.\n *\n * @param auth - The auth API object returned by {@link createAuth}.\n * @param config - Optional configuration with a `resolve` callback\n * for attaching extra fields to the auth context.\n * @returns An object with `args` and `input` compatible with Convex\n * custom function builders.\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * resolve: async (_ctx, user) => ({ email: user.email }),\n * });\n * ```\n *\n * @see {@link createAuth}\n */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\">;\n user: UserDoc;\n } & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const modeDispatch =\n config?.optional === true\n ? { mode: \"optional\" as const }\n : { mode: \"required\" as const };\n\n const userContext = await Fx.run(\n Fx.match(modeDispatch, modeDispatch.mode, {\n optional: async () => {\n const userId = await auth.user.id(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n required: async () => {\n const userId = await auth.user.id(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n }),\n );\n\n if (userContext === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: null,\n user: null,\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, userContext.user)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: userContext.userId,\n user: userContext.user,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\n/**\n * Extract the resolved `auth` context type from an {@link AuthCtx} instance.\n *\n * Use this to type function parameters or variables that receive the\n * enriched auth context produced by `AuthCtx`. The inferred type includes\n * `userId`, `user`, `getUserIdentity`, and any additional fields added\n * by the `resolve` callback. This is the generic utility for reusing the\n * enriched auth shape without manually duplicating conditional auth types.\n *\n * @typeParam T - An `AuthCtx` return value (must have an `input` method\n * that returns `{ ctx: { auth: ... } }`).\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * resolve: async (ctx, user) => ({ orgId: user.orgId }),\n * });\n * type Auth = InferAuth<typeof authCtx>;\n * // Auth = { userId: Id<\"User\">; user: UserDoc; getUserIdentity: ...; orgId: string }\n * ```\n *\n * @see {@link createAuth}\n */\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmaA,eAAe,mBAAmB,MAAW,KAAU;CACrD,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG,IAAI;AACtC,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,OAAO,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;CAC7C,MAAM,UAAU,MAAM,KAAK,KAAK,eAAe,KAAK,EAAE,QAAQ,CAAC;CAC/D,IAAI,OAAsB;CAC1B,IAAI,SAAmB,EAAE;AACzB,KAAI,SAAS;EACX,MAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,KAAK;GAAE;GAAQ;GAAS,CAAC;AACpE,MAAI,SAAS,YAAY;AACvB,UAAO,SAAS,QAAQ,MAAM;AAC9B,YAAS,SAAS;;;AAGtB,QAAO;EAAE;EAAQ;EAAM;EAAS;EAAM;EAAQ;;AAGhD,SAAgB,WAId,WACA,QAIqC;CACrC,MAAM,aAAaA,KAAY;EAC7B,GAAG;EACH;EACA,WAAW,CAAC,GAAG,OAAO,UAAU;EACjC,CAAC;CACF,MAAM,EACJ,QAAQ,WACR,MAAM,SACN,YAAY,eACZ,OAAO,UACP,SAAS,YACT,MAAM,SACN,MAAM,SACN,GAAG,YACD,WAAW,KAAK;CAOpB,MAAM,uBACJ,OACE,KACA,cACA,YACG;EACH,MAAM,aAAa,MAAM,cAAc,IAAI,KAAK,aAAa;AAC7D,MAAI,eAAe,KACjB,OAAM,IAAI,UACR,sBACA,wBACD,CAAC,eAAe;EAGnB,MAAM,aAAa,QAAQ,KAAK,WAAqC;GACnE,GAAG;GACH,QAAQ,MAAM,OAAO,MAAM,CAAC,aAAa;GAC1C,EAAE;EACH,MAAM,0BAAU,IAAI,KAA0C;AAC9D,OAAK,MAAM,SAAS,YAAY;AAC9B,OAAI,MAAM,OAAO,WAAW,EAC1B,OAAM,IAAI,UACR,sBACA,4BACD,CAAC,eAAe;AAEnB,OAAI,QAAQ,IAAI,MAAM,OAAO,CAC3B,OAAM,IAAI,UACR,sBACA,qBAAqB,MAAM,SAC5B,CAAC,eAAe;AAEnB,WAAQ,IAAI,MAAM,QAAQ,MAAM;;EAGlC,MAAM,cAAc,CAAC,GAAG,QAAQ,QAAQ,CAAC;EACzC,MAAM,eAAe,YAAY,QAC9B,UAAU,MAAM,UAClB,CAAC;AACF,MAAI,eAAe,EACjB,OAAM,IAAI,UACR,sBACA,sCACD,CAAC,eAAe;AAEnB,MAAI,YAAY,SAAS,KAAK,iBAAiB,EAC7C,aAAY,KAAK;GAAE,GAAG,YAAY;GAAI,WAAW;GAAM;EAGzD,MAAM,iBAAiB,MAAM,UAAU,KAAK,KAAK,aAAa;EAC9D,MAAM,kBAAkB,IAAI,IAC1B,eAAe,KAAK,UAA2C,CAC7D,MAAM,OAAO,aAAa,EAC1B,MACD,CAAC,CACH;AAED,OAAK,MAAM,YAAY,eACrB,KAAI,CAAC,QAAQ,IAAI,SAAS,OAAO,aAAa,CAAC,CAC7C,OAAM,UAAU,OAAO,KAAK,SAAS,IAAI;AAI7C,OAAK,MAAM,cAAc,aAAa;GACpC,MAAM,UAAU,gBAAgB,IAAI,WAAW,OAAO;AACtD,OAAI,WAAW,QAAQ,cAAc,QAAQ,WAAW,UAAU,CAChE;AAEF,OAAI,QACF,OAAM,UAAU,OAAO,KAAK,QAAQ,IAAI;GAE1C,MAAM,WAAW,MAAM,UAAU,IAAI,KAAK;IACxC,cAAc,WAAW;IACzB,SAAS,WAAW;IACpB,QAAQ,WAAW;IACnB,WAAW,WAAW;IACvB,CAAC;AACF,OAAI,SAAS,eAAe,OAC1B,OAAO,IAAY,YACjB,UAAU,OAAO,wBACjB;IACE;IACA,YAAY,QAAQ;IACrB,CACF;;AAKL,SAAO;GACL,IAAI;GACJ;GACA,UAJqB,MAAM,UAAU,KAAK,KAAK,aAAa,EAIpC,KACrB,YAA6C;IAC5C,UAAU,OAAO;IACjB,QAAQ,OAAO;IACf,WAAW,OAAO;IAClB,UAAU,OAAO,eAAe;IAChC,YAAY,OAAO,cAAc;IAClC,EACF;GACF;;CAGL,MAAM,YAA0B;EAC9B,OAAO;GACL,GAAG;GACH,MAAM,EACJ,GAAG,SACJ;GACD,MAAM,EACJ,GAAG,SACJ;GACD,YAAY;IACV,GAAG;IACH,QAAQ;KACN,MAAM,UAAU;KAChB,UAAU,UAAU;KACpB,KAAK;KACL,cAAc;MACZ,SAAS,UAAU,aAAa;MAChC,SAAS,UAAU,aAAa;MACjC;KACF;IACF;GACD,QAAQ,QAAQ;GAChB,OAAO,EACL,MAAM,SAAS,MAChB;GACD,SAAS;IACP,UAAU,WAAW;IACrB,UAAU,EACR,MAAM,WAAW,SAAS,MAC3B;IACF;GACF;EACD,QAAQ;GACN,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GACnB;EACF;AAED,QAAO;EACL,QAAQ,WAAW;EACnB,SAAS,WAAW;EACpB,OAAO,WAAW;EAClB,MAAM,WAAW,KAAK;EACtB,SAAS,WAAW,KAAK;EACzB,UAAU,WAAW,KAAK;EAC1B,SAAS,WAAW,KAAK;EACzB,OAAO,WAAW,KAAK;EACvB,QAAQ,WAAW,KAAK;EACxB,QAAQ,WAAW,KAAK;EACxB,KAAK,WAAW,KAAK;EACrB,KAAK;EACL,MAAM,EACJ,OAAO;GACL,WAAW,QAAQ;GACnB,KAAK,QAAQ;GACb,UAAU,QAAQ;GACnB,EACF;EACD,MAAM,WAAW,KAAK;EAEtB,UAAU,QAAa,mBAAmB,WAAW,MAAM,IAAI;EAE/D,YAAY;GACV,MAAM,EAAE;GACR,OAAO,OAAO,QAAa;AAEzB,WAAO;KAAE,KAAK,EAAE,MADA,MAAM,mBAAmB,WAAW,MAAM,IAAI,EAC/B;KAAE,MAAM,EAAE;KAAE;;GAE9C;EACF;;AAsHH,SAAgB,QAAQ,MAAgB,QAA6B;AACnE,QAAO;EACL,MAAM,EAAE;EACR,OAAO,OAAO,KAAU,OAAY,WAAiB;GACnD,MAAM,aAAa,IAAI;GACvB,MAAM,eACJ,QAAQ,aAAa,OACjB,EAAE,MAAM,YAAqB,GAC7B,EAAE,MAAM,YAAqB;GAEnC,MAAM,cAAc,MAAM,GAAG,IAC3B,GAAG,MAAM,cAAc,aAAa,MAAM;IACxC,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG,IAAI;AACtC,SAAI,CAAC,OACH,QAAO;AAGT,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAEzB,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG,IAAI;AACtC,SAAI,CAAC,OACH,QAAO;AAGT,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAE1B,CAAC,CACH;AAED,OAAI,gBAAgB,KAClB,QAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ;KACR,MAAM;KACP,EACF;IACD,MAAM,EAAE;IACT;GAGH,MAAM,QAAQ,QAAQ,UAClB,MAAM,OAAO,QAAQ,KAAK,YAAY,KAAK,GAC3C,EAAE;AAEN,UAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ,YAAY;KACpB,MAAM,YAAY;KAClB,GAAG;KACJ,EACF;IACD,MAAM,EAAE;IACT;;EAEJ"}
|
|
1
|
+
{"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Auth as AuthFactory } from \"./runtime\";\nimport { AuthError } from \"./authError\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthAuthorizationConfig,\n AuthGrant,\n AuthProviderConfig,\n AuthRoleDefinition,\n AuthRoleId,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\n/** Canonical user document type exposed by Convex Auth. */\nexport type UserDoc = Doc<\"User\">;\n\ntype MemberApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = Omit<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"],\n \"create\" | \"list\" | \"update\" | \"resolve\"\n> & {\n create: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"create\"]\n >[0],\n data: {\n groupId: string;\n userId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n status?: string;\n extend?: Record<string, unknown>;\n },\n ) => Promise<{ ok: true; memberId: string }>;\n list: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]\n >[0],\n opts?: {\n where?: {\n groupId?: string;\n userId?: string;\n roleId?: AuthRoleId<TAuthorization>;\n status?: string;\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]>;\n update: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"update\"]\n >[0],\n memberId: string,\n data: Record<string, unknown> & { roleIds?: AuthRoleId<TAuthorization>[] },\n ) => Promise<{ ok: true; memberId: string }>;\n resolve: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"resolve\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n ancestry?: boolean;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"resolve\"]>;\n};\n\n\n/**\n * The base auth API surface returned by {@link createAuth}.\n *\n * Provides core namespaces — `signIn`, `signOut`, `user`, `session`,\n * `member`, `invite`, `group`, `key`, and `http` — that are\n * always available regardless of which providers are configured.\n * Enterprise namespaces (`sso`, `scim`) are added conditionally by\n * {@link AuthApi} when an SSO provider is present.\n *\n * Use this type when you want to describe code that only depends on the\n * standard auth surface and should not assume enterprise features exist.\n *\n * @typeParam TAuthorization - The authorization config, used to narrow\n * role IDs and grant strings on the `member` API.\n */\nexport type AuthApiBase<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: MemberApiWithAuthorization<TAuthorization>;\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n /**\n * Resolve the current user's auth context. Framework-agnostic — use\n * this in fluent-convex middleware, custom wrappers, or anywhere you\n * need the resolved `{ userId, user, groupId, role, grants }` object.\n *\n * Returns `null` when unauthenticated. Does not throw.\n *\n * @param ctx - Convex query, mutation, or action context.\n * @returns The resolved auth context, or `null`.\n *\n * @example fluent-convex middleware\n * ```ts\n * const withAuth = convex.createMiddleware(async (ctx, next) => {\n * return next({ ...ctx, auth: await auth.resolve(ctx) });\n * });\n * ```\n *\n * @example Direct usage in a handler\n * ```ts\n * const resolved = await auth.resolve(ctx);\n * if (!resolved) return { ok: false, code: \"NOT_SIGNED_IN\" };\n * const { userId, grants } = resolved;\n * ```\n */\n resolve: (ctx: any) => Promise<AuthResolvedContext | null>;\n /**\n * Context enrichment for convex-helpers `customQuery` / `customMutation` /\n * `customAction`.\n *\n * Resolves the current user's identity, active group, membership role,\n * and grants, then attaches them to `ctx.auth`. Returns a `Customization`\n * object compatible with convex-helpers' custom function builders.\n *\n * `ctx.auth` is `{ userId, user, groupId, role, grants }` when\n * authenticated, `null` when unauthenticated. No throwing — your\n * handler decides how to respond.\n *\n * @returns A convex-helpers `Customization` object.\n *\n * @example One-time setup in `convex/functions.ts`\n * ```ts\n * import { query, mutation, action } from \"./_generated/server\";\n * import { customQuery, customMutation, customAction } from \"convex-helpers/server/customFunctions\";\n * import { auth } from \"./auth\";\n *\n * export const authQuery = customQuery(query, auth.ctx());\n * export const authMutation = customMutation(mutation, auth.ctx());\n * export const authAction = customAction(action, auth.ctx());\n * ```\n *\n * @example Per-function usage\n * ```ts\n * import { authQuery } from \"./functions\";\n *\n * export const list = authQuery({\n * args: { workspaceId: v.string() },\n * handler: async (ctx, args) => {\n * if (!ctx.auth) return [];\n * const { userId, groupId, grants } = ctx.auth;\n * // business logic\n * },\n * });\n * ```\n */\n ctx: () => {\n args: Record<string, never>;\n input: (ctx: any) => Promise<{\n ctx: { auth: AuthResolvedContext | null };\n args: Record<string, never>;\n }>;\n };\n};\n\n/**\n * Resolved auth context injected into `ctx.auth` by `auth.ctx()` and\n * {@link AuthCtx}. Also the expected return shape for custom\n * {@link AuthCtxConfig.authResolve | authResolve} hooks.\n *\n * - `null` when unauthenticated.\n * - `groupId` is `null` when the user has no active group set.\n * - `role` / `grants` are `null` / `[]` when no active group or no membership.\n *\n * @example\n * ```ts\n * import type { AuthResolvedContext } from \"@robelest/convex-auth/server\";\n *\n * const mockAuth: AuthResolvedContext = {\n * userId: \"user123\" as Id<\"User\">,\n * user: { _id: \"user123\", email: \"test@example.com\" },\n * groupId: \"group456\",\n * role: \"admin\",\n * grants: [\"read\", \"write\"],\n * };\n * ```\n */\nexport type AuthResolvedContext = {\n /** The authenticated user's document ID. */\n userId: GenericId<\"User\">;\n /** The authenticated user's full document. */\n user: UserDoc;\n /** The user's active group ID, or `null` if none set. */\n groupId: string | null;\n /** The user's primary role in the active group, or `null`. */\n role: string | null;\n /** Resolved grant strings from the user's role definitions. */\n grants: string[];\n};\n\ntype AuthCtxBase = {\n getUserIdentity: () => Promise<UserIdentity | null>;\n};\n\ntype RequiredAuthCtxState = AuthCtxBase & AuthResolvedContext;\n\ntype OptionalAuthCtxState = AuthCtxBase & {\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n groupId: string | null;\n role: string | null;\n grants: string[];\n};\n\ntype InternalSsoApi = ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n\ntype PublicSsoAdminApi = {\n connection: InternalSsoApi[\"connection\"] & {\n domain: {\n list: InternalSsoApi[\"domain\"][\"list\"];\n validate: InternalSsoApi[\"domain\"][\"validate\"];\n set: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n enterpriseId: string,\n domains: Array<{\n domain: string;\n isPrimary?: boolean;\n }>,\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domains: Array<{\n domainId: string;\n domain: string;\n isPrimary: boolean;\n verified: boolean;\n verifiedAt: number | null;\n }>;\n }>;\n verification: {\n request: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domain: string;\n requestedAt: number;\n expiresAt: number;\n challenge: {\n recordType: \"TXT\";\n recordName: string;\n recordValue: string;\n };\n }>;\n confirm: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: boolean;\n enterpriseId: string;\n domain: string;\n verifiedAt?: number;\n checks: Array<{ name: string; ok: boolean; message?: string }>;\n }>;\n };\n };\n };\n oidc: Omit<InternalSsoApi[\"oidc\"], \"signIn\">;\n saml: Omit<InternalSsoApi[\"saml\"], \"metadata\">;\n policy: InternalSsoApi[\"policy\"];\n audit: {\n list: InternalSsoApi[\"audit\"][\"list\"];\n };\n webhook: {\n endpoint: InternalSsoApi[\"webhook\"][\"endpoint\"];\n delivery: {\n list: InternalSsoApi[\"webhook\"][\"delivery\"][\"list\"];\n };\n };\n};\n\ntype PublicSsoClientApi = {\n signIn: InternalSsoApi[\"oidc\"][\"signIn\"];\n metadata: InternalSsoApi[\"saml\"][\"metadata\"];\n};\n\ntype PublicSsoApi = {\n admin: PublicSsoAdminApi;\n client: PublicSsoClientApi;\n};\n\ntype PublicScimApi = {\n admin: Omit<InternalSsoApi[\"scim\"], \"getConfigByToken\" | \"identity\">;\n};\n\n/**\n * Extended auth API that includes enterprise SSO and SCIM namespaces.\n *\n * This type is the union of {@link AuthApiBase} plus `sso` (SSO connection\n * management, OIDC/SAML, domain verification, policies, audit, webhooks)\n * and `scim` (SCIM provisioning configuration). It is returned by\n * {@link createAuth} only when `new SSO()` is included in the providers\n * array; otherwise the narrower {@link AuthApiBase} is returned instead.\n * Attempting to access `auth.sso` or `auth.scim` without an SSO provider\n * produces a compile-time error because the return type narrows back to\n * {@link AuthApiBase}.\n *\n * @typeParam TAuthorization - The authorization config, forwarded to\n * {@link AuthApiBase} for typed role IDs and grant strings.\n */\nexport type AuthApi<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = AuthApiBase<TAuthorization> & {\n sso: PublicSsoApi;\n scim: PublicScimApi;\n};\n\n/**\n * The return type of {@link createAuth}.\n *\n * Resolves to {@link AuthApi} (with `sso` and `scim` namespaces) when\n * `new SSO()` is present in the providers array, or to the narrower\n * {@link AuthApiBase} otherwise. This conditional type ensures that\n * enterprise-only APIs are only accessible when the SSO provider is\n * configured, producing a compile-time error if you try to access\n * `auth.sso` without it.\n * This lets application code keep a single `createAuth()` call while still\n * getting provider-aware typing on the resulting API object.\n *\n * @typeParam P - The tuple of provider configs passed to `createAuth`.\n * @typeParam TAuthorization - Optional authorization config for typed roles/grants.\n */\nexport type ConvexAuthResult<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> =\n HasSSO<P> extends true\n ? AuthApi<TAuthorization>\n : AuthApiBase<TAuthorization>;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/server\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: api.auth });\n * ```\n *\n * @typeParam T - A ConvexAuthResult to extract the client API from.\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\" | \"member\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` and `auth.scim`\n * are available on the returned object. Without it, those namespaces are\n * absent and accessing them is a TypeScript compile error.\n *\n * @param component - The installed auth component reference from\n * `components.auth` in your Convex app definition.\n * @param config - Auth configuration including `providers` and optional\n * `authorization`. All fields from {@link AuthConfig} are accepted\n * except `component` (passed as the first argument).\n * @returns A {@link ConvexAuthResult} object — either {@link AuthApi}\n * (with `sso`/`scim`) or {@link AuthApiBase}, depending on whether\n * an SSO provider is present.\n *\n * @example\n * ```ts\n * export const auth = createAuth(components.auth, {\n * providers: [password(), google()],\n * authorization: { roles },\n * });\n * ```\n *\n * @see {@link AuthCtx}\n */\n\n// ---------------------------------------------------------------------------\n// Function builders — shared auth resolution logic\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve auth context for the current user. Returns the enriched\n * `ctx.auth` object or `null` when unauthenticated.\n *\n * Resolution flow:\n * 1. `user.id(ctx)` → userId or null (exit early)\n * 2. `user.get(ctx, userId)` → user doc (cached per-execution)\n * 3. `user.getActiveGroup(ctx, { userId })` → groupId or null\n * 4. If groupId → `member.resolve(ctx, { userId, groupId })` → role + grants\n */\nasync function resolveAuthContext(auth: AuthLike, ctx: any) {\n const userId = await auth.user.id(ctx);\n if (!userId) return null;\n const user = await auth.user.get(ctx, userId);\n const groupId = await auth.user.getActiveGroup(ctx, { userId });\n let role: string | null = null;\n let grants: string[] = [];\n if (groupId) {\n const resolved = await auth.member.resolve(ctx, { userId, groupId });\n if (resolved.membership) {\n role = resolved.roleIds[0] ?? null;\n grants = resolved.grants;\n }\n }\n return { userId, user, groupId, role, grants };\n}\n\nexport function createAuth<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\" | \"authorization\"> & {\n providers: P;\n authorization?: TAuthorization;\n },\n): ConvexAuthResult<P, TAuthorization> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n const {\n domain: domainApi,\n scim: scimApi,\n connection: connectionApi,\n audit: auditApi,\n webhook: webhookApi,\n oidc: oidcApi,\n saml: samlApi,\n ...restSso\n } = authResult.auth.sso as InternalSsoApi;\n\n type SetEnterpriseDomains = PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"];\n type EnterpriseDomainInput = Array<{\n domain: string;\n isPrimary?: boolean;\n }>;\n const setEnterpriseDomains: PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"] =\n async (\n ctx: Parameters<SetEnterpriseDomains>[0],\n enterpriseId: Parameters<SetEnterpriseDomains>[1],\n domains: EnterpriseDomainInput,\n ) => {\n const enterprise = await connectionApi.get(ctx, enterpriseId);\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n\n const normalized = domains.map((entry: (typeof domains)[number]) => ({\n ...entry,\n domain: entry.domain.trim().toLowerCase(),\n }));\n const deduped = new Map<string, (typeof normalized)[number]>();\n for (const entry of normalized) {\n if (entry.domain.length === 0) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain must not be empty.\",\n ).toConvexError();\n }\n if (deduped.has(entry.domain)) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n `Duplicate domain: ${entry.domain}`,\n ).toConvexError();\n }\n deduped.set(entry.domain, entry);\n }\n\n const nextDomains = [...deduped.values()];\n const primaryCount = nextDomains.filter(\n (entry) => entry.isPrimary,\n ).length;\n if (primaryCount > 1) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Only one primary domain may be set.\",\n ).toConvexError();\n }\n if (nextDomains.length > 0 && primaryCount === 0) {\n nextDomains[0] = { ...nextDomains[0], isPrimary: true };\n }\n\n const currentDomains = await domainApi.list(ctx, enterpriseId);\n const currentByDomain = new Map<string, (typeof currentDomains)[number]>(\n currentDomains.map((entry: (typeof currentDomains)[number]) => [\n entry.domain.toLowerCase(),\n entry,\n ]),\n );\n\n for (const existing of currentDomains) {\n if (!deduped.has(existing.domain.toLowerCase())) {\n await domainApi.remove(ctx, existing._id);\n }\n }\n\n for (const nextDomain of nextDomains) {\n const current = currentByDomain.get(nextDomain.domain);\n if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) {\n continue;\n }\n if (current) {\n await domainApi.remove(ctx, current._id);\n }\n const domainId = await domainApi.add(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain: nextDomain.domain,\n isPrimary: nextDomain.isPrimary,\n });\n if (current?.verifiedAt !== undefined) {\n await (ctx as any).runMutation(\n component.public.enterpriseDomainVerify,\n {\n domainId,\n verifiedAt: current.verifiedAt,\n },\n );\n }\n }\n\n const updatedDomains = await domainApi.list(ctx, enterpriseId);\n return {\n ok: true as const,\n enterpriseId,\n domains: updatedDomains.map(\n (domain: (typeof updatedDomains)[number]) => ({\n domainId: domain._id,\n domain: domain.domain,\n isPrimary: domain.isPrimary,\n verified: domain.verifiedAt !== undefined,\n verifiedAt: domain.verifiedAt ?? null,\n }),\n ),\n };\n };\n\n const publicSso: PublicSsoApi = {\n admin: {\n ...restSso,\n oidc: {\n ...oidcApi,\n },\n saml: {\n ...samlApi,\n },\n connection: {\n ...connectionApi,\n domain: {\n list: domainApi.list,\n validate: domainApi.validate,\n set: setEnterpriseDomains,\n verification: {\n request: domainApi.verification.request,\n confirm: domainApi.verification.confirm,\n },\n },\n },\n policy: restSso.policy,\n audit: {\n list: auditApi.list,\n },\n webhook: {\n endpoint: webhookApi.endpoint,\n delivery: {\n list: webhookApi.delivery.list,\n },\n },\n },\n client: {\n signIn: oidcApi.signIn,\n metadata: samlApi.metadata,\n },\n };\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: publicSso,\n scim: {\n admin: {\n configure: scimApi.configure,\n get: scimApi.get,\n validate: scimApi.validate,\n },\n },\n http: authResult.auth.http,\n\n resolve: (ctx: any) => resolveAuthContext(authResult.auth, ctx),\n\n ctx: () => ({\n args: {},\n input: async (ctx: any) => {\n const authCtx = await resolveAuthContext(authResult.auth, ctx);\n return { ctx: { auth: authCtx }, args: {} };\n },\n }),\n } as unknown as ConvexAuthResult<P, TAuthorization>;\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\n/**\n * Configuration for {@link AuthCtx} context enrichment.\n *\n * @typeParam TResolve - Extra fields returned from `resolve()` and merged into\n * the resulting `ctx.auth` object.\n */\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n /** Allow unauthenticated callers and return `userId: null` / `user: null`. */\n optional?: boolean;\n /**\n * Attach additional derived fields to the auth context after the base auth\n * context is resolved.\n */\n resolve?: (\n ctx: any,\n user: UserDoc,\n auth: AuthResolvedContext,\n ) => Promise<TResolve> | TResolve;\n /**\n * Override or wrap the base auth resolution used by {@link AuthCtx}.\n *\n * Return `undefined` to fall back to the built-in resolver,\n * `null` for an explicit unauthenticated state, or an\n * {@link AuthResolvedContext} object to provide a pre-resolved auth state.\n * This is useful for tests, proxy auth, impersonation flows, or any\n * environment that needs to inject auth without depending on the standard\n * Convex auth tables.\n *\n * @param ctx - The Convex function context.\n * @param fallback - The built-in auth resolver used by {@link AuthCtx}.\n * @returns Resolved auth state, `null`, or `undefined` to use the fallback.\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * authResolve: async (ctx, fallback) => {\n * const injected = getInjectedAuth(ctx);\n * return injected ?? (await fallback());\n * },\n * });\n * ```\n */\n authResolve?: (\n ctx: any,\n fallback: () => Promise<AuthResolvedContext | null>,\n ) =>\n | Promise<AuthResolvedContext | null | undefined>\n | AuthResolvedContext\n | null\n | undefined;\n};\n\n/**\n * Create a context enrichment for `customQuery` / `customMutation` — optional auth.\n *\n * When `optional: true` is set, unauthenticated requests are allowed.\n * The enriched `ctx.auth` will have `userId: null`, `user: null`,\n * `groupId: null`, `role: null`, and `grants: []` for unauthenticated callers.\n *\n * @param auth - The auth API object returned by {@link createAuth}.\n * @param config - Configuration with `optional: true` and an optional\n * `resolve` callback for attaching extra fields to the auth context.\n * @returns An object with `args` and `input` compatible with Convex\n * custom function builders.\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * optional: true,\n * resolve: async (_ctx, user) => ({ plan: user?.extend?.plan ?? null }),\n * });\n * ```\n *\n * @see {@link createAuth}\n */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: OptionalAuthCtxState & TResolve;\n };\n args: {};\n }>;\n};\n/**\n * Create a context enrichment for `customQuery` / `customMutation` — required auth (default).\n *\n * When `optional` is omitted or `false`, the inferred type is the authenticated\n * auth shape. At runtime this helper still resolves instead of throwing, so if\n * no user is signed in the returned `ctx.auth.userId` / `ctx.auth.user` are\n * `null`, `ctx.auth.groupId` / `ctx.auth.role` are `null`, and\n * `ctx.auth.grants` is `[]`.\n *\n * @param auth - The auth API object returned by {@link createAuth}.\n * @param config - Optional configuration with a `resolve` callback\n * for attaching extra fields to the auth context.\n * @returns An object with `args` and `input` compatible with Convex\n * custom function builders.\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * resolve: async (_ctx, user) => ({ email: user.email }),\n * });\n * ```\n *\n * @see {@link createAuth}\n */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: RequiredAuthCtxState & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const getUserIdentity = nativeAuth.getUserIdentity.bind(nativeAuth);\n const fallback = () => resolveAuthContext(auth, ctx);\n\n const authOverride = config?.authResolve\n ? await config.authResolve(ctx, fallback)\n : undefined;\n const resolved =\n authOverride === undefined ? await fallback() : authOverride;\n\n if (resolved === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity,\n userId: null,\n user: null,\n groupId: null,\n role: null,\n grants: [],\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, resolved.user, resolved)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity,\n ...resolved,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\n/**\n * Extract the resolved `auth` context type from an {@link AuthCtx} instance.\n *\n * Use this to type function parameters or variables that receive the\n * enriched auth context produced by `AuthCtx`. The inferred type includes\n * `userId`, `user`, `groupId`, `role`, `grants`, `getUserIdentity`, and any\n * additional fields added by the `resolve` callback. This is the generic\n * utility for reusing the enriched auth shape without manually duplicating\n * conditional auth types.\n *\n * @typeParam T - An `AuthCtx` return value (must have an `input` method\n * that returns `{ ctx: { auth: ... } }`).\n *\n * @example\n * ```ts\n * const authCtx = AuthCtx(auth, {\n * resolve: async (ctx, user) => ({ orgId: user.orgId }),\n * });\n * type Auth = InferAuth<typeof authCtx>;\n * // Auth = { userId: Id<\"User\">; user: UserDoc; getUserIdentity: ...; orgId: string }\n * ```\n *\n * @see {@link createAuth}\n */\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":"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"}
|
|
@@ -49,8 +49,8 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
49
49
|
store: convex_server25.RegisteredMutation<"internal", {
|
|
50
50
|
args: {
|
|
51
51
|
sessionId?: string | undefined;
|
|
52
|
-
userId: string;
|
|
53
52
|
type: "signIn";
|
|
53
|
+
userId: string;
|
|
54
54
|
generateTokens: boolean;
|
|
55
55
|
} | {
|
|
56
56
|
type: "signOut";
|
|
@@ -112,8 +112,8 @@ declare function Auth(config_: ConvexAuthConfig): {
|
|
|
112
112
|
};
|
|
113
113
|
} | {
|
|
114
114
|
except?: string[] | undefined;
|
|
115
|
-
userId: string;
|
|
116
115
|
type: "invalidateSessions";
|
|
116
|
+
userId: string;
|
|
117
117
|
};
|
|
118
118
|
}, Promise<string | void | {
|
|
119
119
|
userId: convex_values926.GenericId<"User">;
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { GenericId } from "convex/values";
|
|
|
11
11
|
* minus `component` (which is passed as the first constructor argument).
|
|
12
12
|
*/
|
|
13
13
|
type AuthConfig = Omit<ConvexAuthConfig, "component">;
|
|
14
|
+
/** Canonical user document type exposed by Convex Auth. */
|
|
15
|
+
type UserDoc = Doc<"User">;
|
|
14
16
|
type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = Omit<ReturnType<typeof Auth$1>["auth"]["member"], "create" | "list" | "update" | "resolve"> & {
|
|
15
17
|
create: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["create"]>[0], data: {
|
|
16
18
|
groupId: string;
|
|
@@ -152,19 +154,45 @@ type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = un
|
|
|
152
154
|
};
|
|
153
155
|
};
|
|
154
156
|
/**
|
|
155
|
-
* Resolved auth context injected into `ctx.auth` by `auth.ctx()
|
|
157
|
+
* Resolved auth context injected into `ctx.auth` by `auth.ctx()` and
|
|
158
|
+
* {@link AuthCtx}. Also the expected return shape for custom
|
|
159
|
+
* {@link AuthCtxConfig.authResolve | authResolve} hooks.
|
|
156
160
|
*
|
|
157
161
|
* - `null` when unauthenticated.
|
|
158
162
|
* - `groupId` is `null` when the user has no active group set.
|
|
159
163
|
* - `role` / `grants` are `null` / `[]` when no active group or no membership.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* import type { AuthResolvedContext } from "@robelest/convex-auth/server";
|
|
168
|
+
*
|
|
169
|
+
* const mockAuth: AuthResolvedContext = {
|
|
170
|
+
* userId: "user123" as Id<"User">,
|
|
171
|
+
* user: { _id: "user123", email: "test@example.com" },
|
|
172
|
+
* groupId: "group456",
|
|
173
|
+
* role: "admin",
|
|
174
|
+
* grants: ["read", "write"],
|
|
175
|
+
* };
|
|
176
|
+
* ```
|
|
160
177
|
*/
|
|
161
178
|
type AuthResolvedContext = {
|
|
162
|
-
/** The authenticated user's document ID. */userId:
|
|
163
|
-
user:
|
|
179
|
+
/** The authenticated user's document ID. */userId: GenericId<"User">; /** The authenticated user's full document. */
|
|
180
|
+
user: UserDoc; /** The user's active group ID, or `null` if none set. */
|
|
164
181
|
groupId: string | null; /** The user's primary role in the active group, or `null`. */
|
|
165
182
|
role: string | null; /** Resolved grant strings from the user's role definitions. */
|
|
166
183
|
grants: string[];
|
|
167
184
|
};
|
|
185
|
+
type AuthCtxBase = {
|
|
186
|
+
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
187
|
+
};
|
|
188
|
+
type RequiredAuthCtxState = AuthCtxBase & AuthResolvedContext;
|
|
189
|
+
type OptionalAuthCtxState = AuthCtxBase & {
|
|
190
|
+
userId: GenericId<"User"> | null;
|
|
191
|
+
user: UserDoc | null;
|
|
192
|
+
groupId: string | null;
|
|
193
|
+
role: string | null;
|
|
194
|
+
grants: string[];
|
|
195
|
+
};
|
|
168
196
|
type InternalSsoApi = ReturnType<typeof Auth$1>["auth"]["sso"];
|
|
169
197
|
type PublicSsoAdminApi = {
|
|
170
198
|
connection: InternalSsoApi["connection"] & {
|
|
@@ -299,8 +327,6 @@ declare function createAuth<P extends AuthProviderConfig[], TAuthorization exten
|
|
|
299
327
|
providers: P;
|
|
300
328
|
authorization?: TAuthorization;
|
|
301
329
|
}): ConvexAuthResult<P, TAuthorization>;
|
|
302
|
-
/** Canonical user document type exposed by Convex Auth. */
|
|
303
|
-
type UserDoc = Doc<"User">;
|
|
304
330
|
/**
|
|
305
331
|
* Configuration for {@link AuthCtx} context enrichment.
|
|
306
332
|
*
|
|
@@ -310,16 +336,42 @@ type UserDoc = Doc<"User">;
|
|
|
310
336
|
type AuthCtxConfig<TResolve extends Record<string, unknown> = Record<string, never>> = {
|
|
311
337
|
/** Allow unauthenticated callers and return `userId: null` / `user: null`. */optional?: boolean;
|
|
312
338
|
/**
|
|
313
|
-
* Attach additional derived fields to the auth context after the
|
|
339
|
+
* Attach additional derived fields to the auth context after the base auth
|
|
340
|
+
* context is resolved.
|
|
341
|
+
*/
|
|
342
|
+
resolve?: (ctx: any, user: UserDoc, auth: AuthResolvedContext) => Promise<TResolve> | TResolve;
|
|
343
|
+
/**
|
|
344
|
+
* Override or wrap the base auth resolution used by {@link AuthCtx}.
|
|
345
|
+
*
|
|
346
|
+
* Return `undefined` to fall back to the built-in resolver,
|
|
347
|
+
* `null` for an explicit unauthenticated state, or an
|
|
348
|
+
* {@link AuthResolvedContext} object to provide a pre-resolved auth state.
|
|
349
|
+
* This is useful for tests, proxy auth, impersonation flows, or any
|
|
350
|
+
* environment that needs to inject auth without depending on the standard
|
|
351
|
+
* Convex auth tables.
|
|
352
|
+
*
|
|
353
|
+
* @param ctx - The Convex function context.
|
|
354
|
+
* @param fallback - The built-in auth resolver used by {@link AuthCtx}.
|
|
355
|
+
* @returns Resolved auth state, `null`, or `undefined` to use the fallback.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```ts
|
|
359
|
+
* const authCtx = AuthCtx(auth, {
|
|
360
|
+
* authResolve: async (ctx, fallback) => {
|
|
361
|
+
* const injected = getInjectedAuth(ctx);
|
|
362
|
+
* return injected ?? (await fallback());
|
|
363
|
+
* },
|
|
364
|
+
* });
|
|
365
|
+
* ```
|
|
314
366
|
*/
|
|
315
|
-
|
|
367
|
+
authResolve?: (ctx: any, fallback: () => Promise<AuthResolvedContext | null>) => Promise<AuthResolvedContext | null | undefined> | AuthResolvedContext | null | undefined;
|
|
316
368
|
};
|
|
317
369
|
/**
|
|
318
370
|
* Create a context enrichment for `customQuery` / `customMutation` — optional auth.
|
|
319
371
|
*
|
|
320
372
|
* When `optional: true` is set, unauthenticated requests are allowed.
|
|
321
|
-
* The enriched `ctx.auth` will have `userId: null
|
|
322
|
-
* for unauthenticated callers.
|
|
373
|
+
* The enriched `ctx.auth` will have `userId: null`, `user: null`,
|
|
374
|
+
* `groupId: null`, `role: null`, and `grants: []` for unauthenticated callers.
|
|
323
375
|
*
|
|
324
376
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
325
377
|
* @param config - Configuration with `optional: true` and an optional
|
|
@@ -343,11 +395,7 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
343
395
|
args: {};
|
|
344
396
|
input: (ctx: any, _args: any, _extra?: any) => Promise<{
|
|
345
397
|
ctx: {
|
|
346
|
-
auth:
|
|
347
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
348
|
-
userId: GenericId<"User"> | null;
|
|
349
|
-
user: UserDoc | null;
|
|
350
|
-
} & TResolve;
|
|
398
|
+
auth: OptionalAuthCtxState & TResolve;
|
|
351
399
|
};
|
|
352
400
|
args: {};
|
|
353
401
|
}>;
|
|
@@ -357,8 +405,9 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
357
405
|
*
|
|
358
406
|
* When `optional` is omitted or `false`, the inferred type is the authenticated
|
|
359
407
|
* auth shape. At runtime this helper still resolves instead of throwing, so if
|
|
360
|
-
* no user is signed in the returned `ctx.auth.userId`
|
|
361
|
-
* `null
|
|
408
|
+
* no user is signed in the returned `ctx.auth.userId` / `ctx.auth.user` are
|
|
409
|
+
* `null`, `ctx.auth.groupId` / `ctx.auth.role` are `null`, and
|
|
410
|
+
* `ctx.auth.grants` is `[]`.
|
|
362
411
|
*
|
|
363
412
|
* @param auth - The auth API object returned by {@link createAuth}.
|
|
364
413
|
* @param config - Optional configuration with a `resolve` callback
|
|
@@ -379,11 +428,7 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
379
428
|
args: {};
|
|
380
429
|
input: (ctx: any, _args: any, _extra?: any) => Promise<{
|
|
381
430
|
ctx: {
|
|
382
|
-
auth:
|
|
383
|
-
getUserIdentity: () => Promise<UserIdentity | null>;
|
|
384
|
-
userId: GenericId<"User">;
|
|
385
|
-
user: UserDoc;
|
|
386
|
-
} & TResolve;
|
|
431
|
+
auth: RequiredAuthCtxState & TResolve;
|
|
387
432
|
};
|
|
388
433
|
args: {};
|
|
389
434
|
}>;
|
|
@@ -393,9 +438,10 @@ declare function AuthCtx<TResolve extends Record<string, unknown> = Record<strin
|
|
|
393
438
|
*
|
|
394
439
|
* Use this to type function parameters or variables that receive the
|
|
395
440
|
* enriched auth context produced by `AuthCtx`. The inferred type includes
|
|
396
|
-
* `userId`, `user`, `getUserIdentity`, and any
|
|
397
|
-
* by the `resolve` callback. This is the generic
|
|
398
|
-
* enriched auth shape without manually duplicating
|
|
441
|
+
* `userId`, `user`, `groupId`, `role`, `grants`, `getUserIdentity`, and any
|
|
442
|
+
* additional fields added by the `resolve` callback. This is the generic
|
|
443
|
+
* utility for reusing the enriched auth shape without manually duplicating
|
|
444
|
+
* conditional auth types.
|
|
399
445
|
*
|
|
400
446
|
* @typeParam T - An `AuthCtx` return value (must have an `input` method
|
|
401
447
|
* that returns `{ ctx: { auth: ... } }`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/server/auth.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/server/auth.ts"],"mappings":"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"}
|